Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F163231380
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
632 KB
Referenced Files
None
Subscribers
None
View Options
This file is larger than 256 KB, so syntax highlighting was skipped.
Index: stable/8/sys/amd64/include/xen
===================================================================
--- stable/8/sys/amd64/include/xen (revision 221216)
+++ stable/8/sys/amd64/include/xen (revision 221217)
Property changes on: stable/8/sys/amd64/include/xen
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/amd64/include/xen:r220648
Index: stable/8/sys/cddl/contrib/opensolaris
===================================================================
--- stable/8/sys/cddl/contrib/opensolaris (revision 221216)
+++ stable/8/sys/cddl/contrib/opensolaris (revision 221217)
Property changes on: stable/8/sys/cddl/contrib/opensolaris
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/cddl/contrib/opensolaris:r220648
Index: stable/8/sys/contrib/dev/acpica
===================================================================
--- stable/8/sys/contrib/dev/acpica (revision 221216)
+++ stable/8/sys/contrib/dev/acpica (revision 221217)
Property changes on: stable/8/sys/contrib/dev/acpica
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/contrib/dev/acpica:r220648
Index: stable/8/sys/contrib/pf
===================================================================
--- stable/8/sys/contrib/pf (revision 221216)
+++ stable/8/sys/contrib/pf (revision 221217)
Property changes on: stable/8/sys/contrib/pf
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/contrib/pf:r220648
Index: stable/8/sys/fs/nfs/nfs_commonacl.c
===================================================================
--- stable/8/sys/fs/nfs/nfs_commonacl.c (revision 221216)
+++ stable/8/sys/fs/nfs/nfs_commonacl.c (revision 221217)
@@ -1,497 +1,497 @@
/*-
* Copyright (c) 2009 Rick Macklem, University of Guelph
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$");
#ifndef APPLEKEXT
#include <fs/nfs/nfsport.h>
extern int nfsrv_useacl;
#endif
static int nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
enum vtype type, acl_perm_t *permp);
/*
* Handle xdr for an ace.
*/
APPLESTATIC int
nfsrv_dissectace(struct nfsrv_descript *nd, struct acl_entry *acep,
int *aceerrp, int *acesizep, NFSPROC_T *p)
{
u_int32_t *tl;
int len, gotid = 0, owner = 0, error = 0, aceerr = 0;
u_char *name, namestr[NFSV4_SMALLSTR + 1];
u_int32_t flag, mask, acetype;
gid_t gid;
uid_t uid;
*aceerrp = 0;
acep->ae_flags = 0;
NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
acetype = fxdr_unsigned(u_int32_t, *tl++);
flag = fxdr_unsigned(u_int32_t, *tl++);
mask = fxdr_unsigned(u_int32_t, *tl++);
len = fxdr_unsigned(int, *tl);
if (len < 0) {
return (NFSERR_BADXDR);
} else if (len == 0) {
/* Netapp filers return a 0 length who for nil users */
acep->ae_tag = ACL_UNDEFINED_TAG;
acep->ae_id = ACL_UNDEFINED_ID;
acep->ae_perm = (acl_perm_t)0;
acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
if (acesizep)
*acesizep = 4 * NFSX_UNSIGNED;
return (0);
}
if (len > NFSV4_SMALLSTR)
name = malloc(len + 1, M_NFSSTRING, M_WAITOK);
else
name = namestr;
error = nfsrv_mtostr(nd, name, len);
if (error) {
if (len > NFSV4_SMALLSTR)
free(name, M_NFSSTRING);
return (error);
}
if (len == 6) {
if (!NFSBCMP(name, "OWNER@", 6)) {
acep->ae_tag = ACL_USER_OBJ;
acep->ae_id = ACL_UNDEFINED_ID;
owner = 1;
gotid = 1;
} else if (!NFSBCMP(name, "GROUP@", 6)) {
acep->ae_tag = ACL_GROUP_OBJ;
acep->ae_id = ACL_UNDEFINED_ID;
gotid = 1;
}
} else if (len == 9 && !NFSBCMP(name, "EVERYONE@", 9)) {
acep->ae_tag = ACL_EVERYONE;
acep->ae_id = ACL_UNDEFINED_ID;
gotid = 1;
}
if (gotid == 0) {
if (flag & NFSV4ACE_IDENTIFIERGROUP) {
acep->ae_tag = ACL_GROUP;
aceerr = nfsv4_strtogid(name, len, &gid, p);
if (aceerr == 0)
acep->ae_id = (uid_t)gid;
} else {
acep->ae_tag = ACL_USER;
aceerr = nfsv4_strtouid(name, len, &uid, p);
if (aceerr == 0)
acep->ae_id = uid;
}
}
if (len > NFSV4_SMALLSTR)
free(name, M_NFSSTRING);
if (aceerr == 0) {
/*
* Handle the flags.
*/
flag &= ~NFSV4ACE_IDENTIFIERGROUP;
if (flag & NFSV4ACE_FILEINHERIT) {
flag &= ~NFSV4ACE_FILEINHERIT;
acep->ae_flags |= ACL_ENTRY_FILE_INHERIT;
}
if (flag & NFSV4ACE_DIRECTORYINHERIT) {
flag &= ~NFSV4ACE_DIRECTORYINHERIT;
acep->ae_flags |= ACL_ENTRY_DIRECTORY_INHERIT;
}
if (flag & NFSV4ACE_NOPROPAGATEINHERIT) {
flag &= ~NFSV4ACE_NOPROPAGATEINHERIT;
acep->ae_flags |= ACL_ENTRY_NO_PROPAGATE_INHERIT;
}
if (flag & NFSV4ACE_INHERITONLY) {
flag &= ~NFSV4ACE_INHERITONLY;
acep->ae_flags |= ACL_ENTRY_INHERIT_ONLY;
}
if (flag & NFSV4ACE_SUCCESSFULACCESS) {
flag &= ~NFSV4ACE_SUCCESSFULACCESS;
acep->ae_flags |= ACL_ENTRY_SUCCESSFUL_ACCESS;
}
if (flag & NFSV4ACE_FAILEDACCESS) {
flag &= ~NFSV4ACE_FAILEDACCESS;
acep->ae_flags |= ACL_ENTRY_FAILED_ACCESS;
}
/*
* Set ae_entry_type.
*/
if (acetype == NFSV4ACE_ALLOWEDTYPE)
acep->ae_entry_type = ACL_ENTRY_TYPE_ALLOW;
else if (acetype == NFSV4ACE_DENIEDTYPE)
acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
else if (acetype == NFSV4ACE_AUDITTYPE)
acep->ae_entry_type = ACL_ENTRY_TYPE_AUDIT;
else if (acetype == NFSV4ACE_ALARMTYPE)
acep->ae_entry_type = ACL_ENTRY_TYPE_ALARM;
else
aceerr = NFSERR_ATTRNOTSUPP;
}
/*
* Now, check for unsupported flag bits.
*/
if (aceerr == 0 && flag != 0)
aceerr = NFSERR_ATTRNOTSUPP;
/*
* And turn the mask into perm bits.
*/
if (aceerr == 0)
aceerr = nfsrv_acemasktoperm(acetype, mask, owner, VREG,
&acep->ae_perm);
*aceerrp = aceerr;
if (acesizep)
*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
return (0);
nfsmout:
return (error);
}
/*
* Turn an NFSv4 ace mask into R/W/X flag bits.
*/
static int
nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
enum vtype type, acl_perm_t *permp)
{
acl_perm_t perm = 0x0;
if (mask & NFSV4ACE_READDATA) {
mask &= ~NFSV4ACE_READDATA;
perm |= ACL_READ_DATA;
}
if (mask & NFSV4ACE_LISTDIRECTORY) {
mask &= ~NFSV4ACE_LISTDIRECTORY;
perm |= ACL_LIST_DIRECTORY;
}
if (mask & NFSV4ACE_WRITEDATA) {
mask &= ~NFSV4ACE_WRITEDATA;
perm |= ACL_WRITE_DATA;
}
if (mask & NFSV4ACE_ADDFILE) {
mask &= ~NFSV4ACE_ADDFILE;
perm |= ACL_ADD_FILE;
}
if (mask & NFSV4ACE_APPENDDATA) {
mask &= ~NFSV4ACE_APPENDDATA;
perm |= ACL_APPEND_DATA;
}
if (mask & NFSV4ACE_ADDSUBDIRECTORY) {
mask &= ~NFSV4ACE_ADDSUBDIRECTORY;
perm |= ACL_ADD_SUBDIRECTORY;
}
if (mask & NFSV4ACE_READNAMEDATTR) {
mask &= ~NFSV4ACE_READNAMEDATTR;
perm |= ACL_READ_NAMED_ATTRS;
}
if (mask & NFSV4ACE_WRITENAMEDATTR) {
mask &= ~NFSV4ACE_WRITENAMEDATTR;
perm |= ACL_WRITE_NAMED_ATTRS;
}
if (mask & NFSV4ACE_EXECUTE) {
mask &= ~NFSV4ACE_EXECUTE;
perm |= ACL_EXECUTE;
}
if (mask & NFSV4ACE_SEARCH) {
mask &= ~NFSV4ACE_SEARCH;
perm |= ACL_EXECUTE;
}
if (mask & NFSV4ACE_DELETECHILD) {
mask &= ~NFSV4ACE_DELETECHILD;
perm |= ACL_DELETE_CHILD;
}
if (mask & NFSV4ACE_READATTRIBUTES) {
mask &= ~NFSV4ACE_READATTRIBUTES;
perm |= ACL_READ_ATTRIBUTES;
}
if (mask & NFSV4ACE_WRITEATTRIBUTES) {
mask &= ~NFSV4ACE_WRITEATTRIBUTES;
perm |= ACL_WRITE_ATTRIBUTES;
}
if (mask & NFSV4ACE_DELETE) {
mask &= ~NFSV4ACE_DELETE;
perm |= ACL_DELETE;
}
if (mask & NFSV4ACE_READACL) {
mask &= ~NFSV4ACE_READACL;
perm |= ACL_READ_ACL;
}
if (mask & NFSV4ACE_WRITEACL) {
mask &= ~NFSV4ACE_WRITEACL;
perm |= ACL_WRITE_ACL;
}
if (mask & NFSV4ACE_WRITEOWNER) {
mask &= ~NFSV4ACE_WRITEOWNER;
perm |= ACL_WRITE_OWNER;
}
if (mask & NFSV4ACE_SYNCHRONIZE) {
mask &= ~NFSV4ACE_SYNCHRONIZE;
perm |= ACL_SYNCHRONIZE;
}
if (mask != 0)
return (NFSERR_ATTRNOTSUPP);
*permp = perm;
return (0);
}
/* local functions */
static int nfsrv_buildace(struct nfsrv_descript *, u_char *, int,
enum vtype, int, int, struct acl_entry *);
/*
* This function builds an NFS ace.
*/
static int
nfsrv_buildace(struct nfsrv_descript *nd, u_char *name, int namelen,
enum vtype type, int group, int owner, struct acl_entry *ace)
{
u_int32_t *tl, aceflag = 0x0, acemask = 0x0, acetype;
int full_len;
full_len = NFSM_RNDUP(namelen);
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED + full_len);
/*
* Fill in the ace type.
*/
if (ace->ae_entry_type & ACL_ENTRY_TYPE_ALLOW)
acetype = NFSV4ACE_ALLOWEDTYPE;
else if (ace->ae_entry_type & ACL_ENTRY_TYPE_DENY)
acetype = NFSV4ACE_DENIEDTYPE;
else if (ace->ae_entry_type & ACL_ENTRY_TYPE_AUDIT)
acetype = NFSV4ACE_AUDITTYPE;
else
acetype = NFSV4ACE_ALARMTYPE;
*tl++ = txdr_unsigned(acetype);
/*
* Set the flag bits from the ACL.
*/
if (ace->ae_flags & ACL_ENTRY_FILE_INHERIT)
aceflag |= NFSV4ACE_FILEINHERIT;
if (ace->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT)
aceflag |= NFSV4ACE_DIRECTORYINHERIT;
if (ace->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)
aceflag |= NFSV4ACE_NOPROPAGATEINHERIT;
if (ace->ae_flags & ACL_ENTRY_INHERIT_ONLY)
aceflag |= NFSV4ACE_INHERITONLY;
if (ace->ae_flags & ACL_ENTRY_SUCCESSFUL_ACCESS)
aceflag |= NFSV4ACE_SUCCESSFULACCESS;
if (ace->ae_flags & ACL_ENTRY_FAILED_ACCESS)
aceflag |= NFSV4ACE_FAILEDACCESS;
if (group)
aceflag |= NFSV4ACE_IDENTIFIERGROUP;
*tl++ = txdr_unsigned(aceflag);
if (type == VDIR) {
if (ace->ae_perm & ACL_LIST_DIRECTORY)
acemask |= NFSV4ACE_LISTDIRECTORY;
if (ace->ae_perm & ACL_ADD_FILE)
acemask |= NFSV4ACE_ADDFILE;
if (ace->ae_perm & ACL_ADD_SUBDIRECTORY)
acemask |= NFSV4ACE_ADDSUBDIRECTORY;
if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
acemask |= NFSV4ACE_READNAMEDATTR;
if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
acemask |= NFSV4ACE_WRITENAMEDATTR;
if (ace->ae_perm & ACL_EXECUTE)
acemask |= NFSV4ACE_SEARCH;
if (ace->ae_perm & ACL_DELETE_CHILD)
acemask |= NFSV4ACE_DELETECHILD;
if (ace->ae_perm & ACL_READ_ATTRIBUTES)
acemask |= NFSV4ACE_READATTRIBUTES;
if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
acemask |= NFSV4ACE_WRITEATTRIBUTES;
if (ace->ae_perm & ACL_DELETE)
acemask |= NFSV4ACE_DELETE;
if (ace->ae_perm & ACL_READ_ACL)
acemask |= NFSV4ACE_READACL;
if (ace->ae_perm & ACL_WRITE_ACL)
acemask |= NFSV4ACE_WRITEACL;
if (ace->ae_perm & ACL_WRITE_OWNER)
acemask |= NFSV4ACE_WRITEOWNER;
} else {
if (ace->ae_perm & ACL_READ_DATA)
acemask |= NFSV4ACE_READDATA;
if (ace->ae_perm & ACL_WRITE_DATA)
acemask |= NFSV4ACE_WRITEDATA;
if (ace->ae_perm & ACL_APPEND_DATA)
acemask |= NFSV4ACE_APPENDDATA;
if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
acemask |= NFSV4ACE_READNAMEDATTR;
if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
acemask |= NFSV4ACE_WRITENAMEDATTR;
if (ace->ae_perm & ACL_EXECUTE)
acemask |= NFSV4ACE_EXECUTE;
if (ace->ae_perm & ACL_READ_ATTRIBUTES)
acemask |= NFSV4ACE_READATTRIBUTES;
if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
acemask |= NFSV4ACE_WRITEATTRIBUTES;
if (ace->ae_perm & ACL_DELETE)
acemask |= NFSV4ACE_DELETE;
if (ace->ae_perm & ACL_READ_ACL)
acemask |= NFSV4ACE_READACL;
if (ace->ae_perm & ACL_WRITE_ACL)
acemask |= NFSV4ACE_WRITEACL;
if (ace->ae_perm & ACL_WRITE_OWNER)
acemask |= NFSV4ACE_WRITEOWNER;
if (ace->ae_perm & ACL_SYNCHRONIZE)
acemask |= NFSV4ACE_SYNCHRONIZE;
}
*tl++ = txdr_unsigned(acemask);
*tl++ = txdr_unsigned(namelen);
if (full_len - namelen)
*(tl + (namelen / NFSX_UNSIGNED)) = 0x0;
NFSBCOPY(name, (caddr_t)tl, namelen);
return (full_len + 4 * NFSX_UNSIGNED);
}
/*
* Build an NFSv4 ACL.
*/
APPLESTATIC int
nfsrv_buildacl(struct nfsrv_descript *nd, NFSACL_T *aclp, enum vtype type,
NFSPROC_T *p)
{
int i, entrycnt = 0, retlen;
u_int32_t *entrycntp;
int isowner, isgroup, namelen, malloced;
u_char *name, namestr[NFSV4_SMALLSTR];
NFSM_BUILD(entrycntp, u_int32_t *, NFSX_UNSIGNED);
retlen = NFSX_UNSIGNED;
/*
* Loop through the acl entries, building each one.
*/
for (i = 0; i < aclp->acl_cnt; i++) {
isowner = isgroup = malloced = 0;
switch (aclp->acl_entry[i].ae_tag) {
case ACL_USER_OBJ:
isowner = 1;
name = "OWNER@";
namelen = 6;
break;
case ACL_GROUP_OBJ:
isgroup = 1;
name = "GROUP@";
namelen = 6;
break;
case ACL_EVERYONE:
name = "EVERYONE@";
namelen = 9;
break;
case ACL_USER:
name = namestr;
nfsv4_uidtostr(aclp->acl_entry[i].ae_id, &name,
&namelen, p);
if (name != namestr)
malloced = 1;
break;
case ACL_GROUP:
isgroup = 1;
name = namestr;
nfsv4_gidtostr((gid_t)aclp->acl_entry[i].ae_id, &name,
&namelen, p);
if (name != namestr)
malloced = 1;
break;
default:
continue;
};
retlen += nfsrv_buildace(nd, name, namelen, type, isgroup,
isowner, &aclp->acl_entry[i]);
entrycnt++;
if (malloced)
free(name, M_NFSSTRING);
}
*entrycntp = txdr_unsigned(entrycnt);
return (retlen);
}
/*
* Set an NFSv4 acl.
*/
APPLESTATIC int
nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, struct ucred *cred,
NFSPROC_T *p)
{
int error;
- if (nfsrv_useacl == 0 || !NFSHASNFS4ACL(vnode_mount(vp)))
+ if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0)
return (NFSERR_ATTRNOTSUPP);
/*
* With NFSv4 ACLs, chmod(2) may need to add additional entries.
* Make sure it has enough room for that - splitting every entry
* into two and appending "canonical six" entries at the end.
* Cribbed out of kern/vfs_acl.c - Rick M.
*/
if (aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
return (NFSERR_ATTRNOTSUPP);
error = VOP_ACLCHECK(vp, ACL_TYPE_NFS4, aclp, cred, p);
if (!error)
error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, p);
return (error);
}
/*
* Compare two NFSv4 acls.
* Return 0 if they are the same, 1 if not the same.
*/
APPLESTATIC int
nfsrv_compareacl(NFSACL_T *aclp1, NFSACL_T *aclp2)
{
int i;
struct acl_entry *acep1, *acep2;
if (aclp1->acl_cnt != aclp2->acl_cnt)
return (1);
acep1 = aclp1->acl_entry;
acep2 = aclp2->acl_entry;
for (i = 0; i < aclp1->acl_cnt; i++) {
if (acep1->ae_tag != acep2->ae_tag)
return (1);
switch (acep1->ae_tag) {
case ACL_GROUP:
case ACL_USER:
if (acep1->ae_id != acep2->ae_id)
return (1);
/* fall through */
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_OTHER:
if (acep1->ae_perm != acep2->ae_perm)
return (1);
};
acep1++;
acep2++;
}
return (0);
}
Index: stable/8/sys/fs/nfs/nfs_commonport.c
===================================================================
--- stable/8/sys/fs/nfs/nfs_commonport.c (revision 221216)
+++ stable/8/sys/fs/nfs/nfs_commonport.c (revision 221217)
@@ -1,519 +1,519 @@
/*-
* 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.
* 4. 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$");
/*
* Functions that need to be different for different versions of BSD
* kernel should be kept here, along with any global storage specific
* to this BSD variant.
*/
#include <fs/nfs/nfsport.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_param.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/uma.h>
#include <vm/uma_int.h>
extern int nfscl_ticks;
extern int nfsrv_nfsuserd;
extern struct nfssockreq nfsrv_nfsuserdsock;
extern void (*nfsd_call_recall)(struct vnode *, int, struct ucred *,
struct thread *);
extern int nfsrv_useacl;
struct mount nfsv4root_mnt;
int newnfs_numnfsd = 0;
struct nfsstats newnfsstats;
int nfs_numnfscbd = 0;
char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
struct callout newnfsd_callout;
void (*nfsd_call_servertimer)(void) = NULL;
void (*ncl_call_invalcaches)(struct vnode *) = NULL;
static int nfs_realign_test;
static int nfs_realign_count;
SYSCTL_NODE(_vfs, OID_AUTO, newnfs, CTLFLAG_RW, 0, "New NFS filesystem");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, newnfs_realign_test, CTLFLAG_RW, &nfs_realign_test, 0, "");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, newnfs_realign_count, CTLFLAG_RW, &nfs_realign_count, 0, "");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs4acl_enable, CTLFLAG_RW, &nfsrv_useacl, 0, "");
SYSCTL_STRING(_vfs_newnfs, OID_AUTO, callback_addr, CTLFLAG_RW,
nfsv4_callbackaddr, sizeof(nfsv4_callbackaddr), "");
/*
* Defines for malloc
* (Here for FreeBSD, since they allocate storage.)
*/
MALLOC_DEFINE(M_NEWNFSRVCACHE, "NFSD srvcache", "NFSD Server Request Cache");
MALLOC_DEFINE(M_NEWNFSDCLIENT, "NFSD V4client", "NFSD V4 Client Id");
MALLOC_DEFINE(M_NEWNFSDSTATE, "NFSD V4state",
"NFSD V4 State (Openowner, Open, Lockowner, Delegation");
MALLOC_DEFINE(M_NEWNFSDLOCK, "NFSD V4lock", "NFSD V4 byte range lock");
MALLOC_DEFINE(M_NEWNFSDLOCKFILE, "NFSD lckfile", "NFSD Open/Lock file");
MALLOC_DEFINE(M_NEWNFSSTRING, "NFSD string", "NFSD V4 long string");
MALLOC_DEFINE(M_NEWNFSUSERGROUP, "NFSD usrgroup", "NFSD V4 User/group map");
MALLOC_DEFINE(M_NEWNFSDREQ, "NFS req", "NFS request header");
MALLOC_DEFINE(M_NEWNFSFH, "NFS fh", "NFS file handle");
MALLOC_DEFINE(M_NEWNFSCLOWNER, "NFSCL owner", "NFSCL Open Owner");
MALLOC_DEFINE(M_NEWNFSCLOPEN, "NFSCL open", "NFSCL Open");
MALLOC_DEFINE(M_NEWNFSCLDELEG, "NFSCL deleg", "NFSCL Delegation");
MALLOC_DEFINE(M_NEWNFSCLCLIENT, "NFSCL client", "NFSCL Client");
MALLOC_DEFINE(M_NEWNFSCLLOCKOWNER, "NFSCL lckown", "NFSCL Lock Owner");
MALLOC_DEFINE(M_NEWNFSCLLOCK, "NFSCL lck", "NFSCL Lock");
MALLOC_DEFINE(M_NEWNFSV4NODE, "NEWNFSnode", "New nfs vnode");
MALLOC_DEFINE(M_NEWNFSDIRECTIO, "NEWdirectio", "New nfs Direct IO buffer");
MALLOC_DEFINE(M_NEWNFSDIROFF, "NFSCL diroffdiroff",
"New NFS directory offset data");
MALLOC_DEFINE(M_NEWNFSDROLLBACK, "NFSD rollback",
"New NFS local lock rollback");
/*
* Definition of mutex locks.
* newnfsd_mtx is used in nfsrvd_nfsd() to protect the nfs socket list
* and assorted other nfsd structures.
* Giant is used to protect the nfsd list and count, which is just
* updated when nfsd's start/stop and is grabbed for nfsrvd_dorpc()
* for the VFS ops.
*/
struct mtx newnfsd_mtx;
struct mtx nfs_sockl_mutex;
struct mtx nfs_state_mutex;
struct mtx nfs_nameid_mutex;
struct mtx nfs_req_mutex;
struct mtx nfs_slock_mutex;
/* local functions */
static int nfssvc_call(struct thread *, struct nfssvc_args *, struct ucred *);
#ifdef __NO_STRICT_ALIGNMENT
/*
* These architectures don't need re-alignment, so just return.
*/
void
newnfs_realign(struct mbuf **pm)
{
return;
}
#else /* !__NO_STRICT_ALIGNMENT */
/*
* newnfs_realign:
*
* Check for badly aligned mbuf data and realign by copying the unaligned
* portion of the data into a new mbuf chain and freeing the portions
* of the old chain that were replaced.
*
* We cannot simply realign the data within the existing mbuf chain
* because the underlying buffers may contain other rpc commands and
* we cannot afford to overwrite them.
*
* We would prefer to avoid this situation entirely. The situation does
* not occur with NFS/UDP and is supposed to only occassionally occur
* with TCP. Use vfs.nfs.realign_count and realign_test to check this.
*
*/
void
newnfs_realign(struct mbuf **pm)
{
struct mbuf *m, *n;
int off, space;
++nfs_realign_test;
while ((m = *pm) != NULL) {
if ((m->m_len & 0x3) || (mtod(m, intptr_t) & 0x3)) {
/*
* NB: we can't depend on m_pkthdr.len to help us
* decide what to do here. May not be worth doing
* the m_length calculation as m_copyback will
* expand the mbuf chain below as needed.
*/
space = m_length(m, NULL);
if (space >= MINCLSIZE) {
/* NB: m_copyback handles space > MCLBYTES */
n = m_getcl(M_WAITOK, MT_DATA, 0);
} else
n = m_get(M_WAITOK, MT_DATA);
if (n == NULL)
return;
/*
* Align the remainder of the mbuf chain.
*/
n->m_len = 0;
off = 0;
while (m != NULL) {
m_copyback(n, off, m->m_len, mtod(m, caddr_t));
off += m->m_len;
m = m->m_next;
}
m_freem(*pm);
*pm = n;
++nfs_realign_count;
break;
}
pm = &m->m_next;
}
}
#endif /* __NO_STRICT_ALIGNMENT */
#ifdef notdef
static void
nfsrv_object_create(struct vnode *vp, struct thread *td)
{
if (vp == NULL || vp->v_type != VREG)
return;
(void) vfs_object_create(vp, td, td->td_ucred);
}
#endif
/*
* Look up a file name. Basically just initialize stuff and call namei().
*/
int
nfsrv_lookupfilename(struct nameidata *ndp, char *fname, NFSPROC_T *p)
{
int error;
NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE, UIO_USERSPACE, fname,
p);
error = namei(ndp);
if (!error) {
NDFREE(ndp, NDF_ONLY_PNBUF);
}
return (error);
}
/*
* Copy NFS uid, gids to the cred structure.
*/
void
newnfs_copycred(struct nfscred *nfscr, struct ucred *cr)
{
KASSERT(nfscr->nfsc_ngroups >= 0,
("newnfs_copycred: negative nfsc_ngroups"));
cr->cr_uid = nfscr->nfsc_uid;
crsetgroups(cr, nfscr->nfsc_ngroups, nfscr->nfsc_groups);
}
/*
* Map args from nfsmsleep() to msleep().
*/
int
nfsmsleep(void *chan, void *mutex, int prio, const char *wmesg,
struct timespec *ts)
{
u_int64_t nsecval;
int error, timeo;
if (ts) {
timeo = hz * ts->tv_sec;
nsecval = (u_int64_t)ts->tv_nsec;
nsecval = ((nsecval * ((u_int64_t)hz)) + 500000000) /
1000000000;
timeo += (int)nsecval;
} else {
timeo = 0;
}
error = msleep(chan, (struct mtx *)mutex, prio, wmesg, timeo);
return (error);
}
/*
* Get the file system info for the server. For now, just assume FFS.
*/
void
nfsvno_getfs(struct nfsfsinfo *sip, int isdgram)
{
int pref;
/*
* XXX
* There should be file system VFS OP(s) to get this information.
* For now, assume ufs.
*/
if (isdgram)
pref = NFS_MAXDGRAMDATA;
else
pref = NFS_MAXDATA;
sip->fs_rtmax = NFS_MAXDATA;
sip->fs_rtpref = pref;
sip->fs_rtmult = NFS_FABLKSIZE;
sip->fs_wtmax = NFS_MAXDATA;
sip->fs_wtpref = pref;
sip->fs_wtmult = NFS_FABLKSIZE;
sip->fs_dtpref = pref;
sip->fs_maxfilesize = 0xffffffffffffffffull;
sip->fs_timedelta.tv_sec = 0;
sip->fs_timedelta.tv_nsec = 1;
sip->fs_properties = (NFSV3FSINFO_LINK |
NFSV3FSINFO_SYMLINK | NFSV3FSINFO_HOMOGENEOUS |
NFSV3FSINFO_CANSETTIME);
}
/* Fake nfsrv_atroot. Just return 0 */
int
nfsrv_atroot(struct vnode *vp, long *retp)
{
return (0);
}
/*
* Set the credentials to refer to root.
* If only the various BSDen could agree on whether cr_gid is a separate
* field or cr_groups[0]...
*/
void
newnfs_setroot(struct ucred *cred)
{
cred->cr_uid = 0;
cred->cr_groups[0] = 0;
cred->cr_ngroups = 1;
}
/*
* Get the client credential. Used for Renew and recovery.
*/
struct ucred *
newnfs_getcred(void)
{
struct ucred *cred;
struct thread *td = curthread;
cred = crdup(td->td_ucred);
newnfs_setroot(cred);
return (cred);
}
/*
* Nfs timer routine
* Call the nfsd's timer function once/sec.
*/
void
newnfs_timer(void *arg)
{
static time_t lasttime = 0;
/*
* Call the server timer, if set up.
* The argument indicates if it is the next second and therefore
* leases should be checked.
*/
if (lasttime != NFSD_MONOSEC) {
lasttime = NFSD_MONOSEC;
if (nfsd_call_servertimer != NULL)
(*nfsd_call_servertimer)();
}
callout_reset(&newnfsd_callout, nfscl_ticks, newnfs_timer, NULL);
}
/*
* Sleep for a short period of time unless errval == NFSERR_GRACE, where
* the sleep should be for 5 seconds.
* Since lbolt doesn't exist in FreeBSD-CURRENT, just use a timeout on
* an event that never gets a wakeup. Only return EINTR or 0.
*/
int
nfs_catnap(int prio, int errval, const char *wmesg)
{
static int non_event;
int ret;
if (errval == NFSERR_GRACE)
ret = tsleep(&non_event, prio, wmesg, 5 * hz);
else
ret = tsleep(&non_event, prio, wmesg, 1);
if (ret != EINTR)
ret = 0;
return (ret);
}
/*
* Get referral. For now, just fail.
*/
struct nfsreferral *
nfsv4root_getreferral(struct vnode *vp, struct vnode *dvp, u_int32_t fileno)
{
return (NULL);
}
static int
nfssvc_nfscommon(struct thread *td, struct nfssvc_args *uap)
{
int error;
error = nfssvc_call(td, uap, td->td_ucred);
return (error);
}
static int
nfssvc_call(struct thread *p, struct nfssvc_args *uap, struct ucred *cred)
{
int error = EINVAL;
struct nfsd_idargs nid;
if (uap->flag & NFSSVC_IDNAME) {
error = copyin(uap->argp, (caddr_t)&nid, sizeof (nid));
if (error)
return (error);
error = nfssvc_idname(&nid);
return (error);
} else if (uap->flag & NFSSVC_GETSTATS) {
error = copyout(&newnfsstats,
CAST_USER_ADDR_T(uap->argp), sizeof (newnfsstats));
return (error);
} else if (uap->flag & NFSSVC_NFSUSERDPORT) {
u_short sockport;
error = copyin(uap->argp, (caddr_t)&sockport,
sizeof (u_short));
if (!error)
error = nfsrv_nfsuserdport(sockport, p);
} else if (uap->flag & NFSSVC_NFSUSERDDELPORT) {
nfsrv_nfsuserddelport();
error = 0;
}
return (error);
}
/*
* called by all three modevent routines, so that it gets things
* initialized soon enough.
*/
void
newnfs_portinit(void)
{
static int inited = 0;
if (inited)
return;
inited = 1;
/* Initialize SMP locks used by both client and server. */
mtx_init(&newnfsd_mtx, "newnfsd_mtx", NULL, MTX_DEF);
mtx_init(&nfs_state_mutex, "nfs_state_mutex", NULL, MTX_DEF);
}
/*
* Determine if the file system supports NFSv4 ACLs.
* Return 1 if it does, 0 otherwise.
*/
int
-nfs_supportsnfsv4acls(struct mount *mp)
+nfs_supportsnfsv4acls(struct vnode *vp)
{
+ int error;
+ register_t retval;
- if (mp->mnt_stat.f_fstypename == NULL)
+ ASSERT_VOP_LOCKED(vp, "nfs supports nfsv4acls");
+
+ if (nfsrv_useacl == 0)
return (0);
- if (strcmp(mp->mnt_stat.f_fstypename, "ufs") == 0) {
- /* Not yet */
- return (0);
- } else if (strcmp(mp->mnt_stat.f_fstypename, "zfs") == 0) {
- /* Always supports them */
+ error = VOP_PATHCONF(vp, _PC_ACL_NFS4, &retval);
+ if (error == 0 && retval != 0)
return (1);
- }
return (0);
}
extern int (*nfsd_call_nfscommon)(struct thread *, struct nfssvc_args *);
/*
* Called once to initialize data structures...
*/
static int
nfscommon_modevent(module_t mod, int type, void *data)
{
int error = 0;
static int loaded = 0;
switch (type) {
case MOD_LOAD:
if (loaded)
return (0);
newnfs_portinit();
mtx_init(&nfs_nameid_mutex, "nfs_nameid_mutex", NULL, MTX_DEF);
mtx_init(&nfs_sockl_mutex, "nfs_sockl_mutex", NULL, MTX_DEF);
mtx_init(&nfs_slock_mutex, "nfs_slock_mutex", NULL, MTX_DEF);
mtx_init(&nfs_req_mutex, "nfs_req_mutex", NULL, MTX_DEF);
mtx_init(&nfsrv_nfsuserdsock.nr_mtx, "nfsuserd", NULL,
MTX_DEF);
callout_init(&newnfsd_callout, CALLOUT_MPSAFE);
newnfs_init();
nfsd_call_nfscommon = nfssvc_nfscommon;
loaded = 1;
break;
case MOD_UNLOAD:
if (newnfs_numnfsd != 0 || nfsrv_nfsuserd != 0 ||
nfs_numnfscbd != 0) {
error = EBUSY;
break;
}
nfsd_call_nfscommon = NULL;
callout_drain(&newnfsd_callout);
/* and get rid of the mutexes */
mtx_destroy(&nfs_nameid_mutex);
mtx_destroy(&newnfsd_mtx);
mtx_destroy(&nfs_state_mutex);
mtx_destroy(&nfs_sockl_mutex);
mtx_destroy(&nfs_slock_mutex);
mtx_destroy(&nfs_req_mutex);
mtx_destroy(&nfsrv_nfsuserdsock.nr_mtx);
loaded = 0;
break;
default:
error = EOPNOTSUPP;
break;
}
return error;
}
static moduledata_t nfscommon_mod = {
"nfscommon",
nfscommon_modevent,
NULL,
};
DECLARE_MODULE(nfscommon, nfscommon_mod, SI_SUB_VFS, SI_ORDER_ANY);
/* So that loader and kldload(2) can find us, wherever we are.. */
MODULE_VERSION(nfscommon, 1);
MODULE_DEPEND(nfscommon, nfssvc, 1, 1, 1);
MODULE_DEPEND(nfscommon, krpc, 1, 1, 1);
Index: stable/8/sys/fs/nfs/nfs_commonsubs.c
===================================================================
--- stable/8/sys/fs/nfs/nfs_commonsubs.c (revision 221216)
+++ stable/8/sys/fs/nfs/nfs_commonsubs.c (revision 221217)
@@ -1,3407 +1,3407 @@
/*-
* 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.
* 4. 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$");
/*
* These functions support the macros and help fiddle mbuf chains for
* the nfs op functions. They do things like create the rpc header and
* copy data between mbuf chains and uio lists.
*/
#ifndef APPLEKEXT
#include <fs/nfs/nfsport.h>
/*
* Data items converted to xdr at startup, since they are constant
* This is kinda hokey, but may save a little time doing byte swaps
*/
u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
/* And other global data */
nfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
NFFIFO, NFNON };
enum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
enum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
struct timeval nfsboottime; /* Copy boottime once, so it never changes */
int nfscl_ticks;
int nfsrv_useacl = 1;
struct nfssockreq nfsrv_nfsuserdsock;
int nfsrv_nfsuserd = 0;
struct nfsreqhead nfsd_reqq;
uid_t nfsrv_defaultuid;
gid_t nfsrv_defaultgid;
int nfsrv_lease = NFSRV_LEASE;
int ncl_mbuf_mlen = MLEN;
NFSNAMEIDMUTEX;
NFSSOCKMUTEX;
/*
* This array of structures indicates, for V4:
* retfh - which of 3 types of calling args are used
* 0 - doesn't change cfh or use a sfh
* 1 - replaces cfh with a new one (unless it returns an error status)
* 2 - uses cfh and sfh
* needscfh - if the op wants a cfh and premtime
* 0 - doesn't use a cfh
* 1 - uses a cfh, but doesn't want pre-op attributes
* 2 - uses a cfh and wants pre-op attributes
* savereply - indicates a non-idempotent Op
* 0 - not non-idempotent
* 1 - non-idempotent
* Ops that are ordered via seqid# are handled separately from these
* non-idempotent Ops.
* Define it here, since it is used by both the client and server.
*/
struct nfsv4_opflag nfsv4_opflag[NFSV4OP_NOPS] = {
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* undef */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* undef */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* undef */
{ 0, 1, 0, 0, LK_SHARED }, /* Access */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* Close */
{ 0, 2, 0, 1, LK_EXCLUSIVE }, /* Commit */
{ 1, 2, 1, 1, LK_EXCLUSIVE }, /* Create */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* Delegpurge */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* Delegreturn */
{ 0, 1, 0, 0, LK_SHARED }, /* Getattr */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* GetFH */
{ 2, 1, 1, 1, LK_EXCLUSIVE }, /* Link */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* Lock */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* LockT */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* LockU */
{ 1, 1, 0, 0, LK_EXCLUSIVE }, /* Lookup */
{ 1, 1, 0, 0, LK_EXCLUSIVE }, /* Lookupp */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* NVerify */
{ 1, 1, 0, 1, LK_EXCLUSIVE }, /* Open */
{ 1, 1, 0, 0, LK_EXCLUSIVE }, /* OpenAttr */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* OpenConfirm */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* OpenDowngrade */
{ 1, 0, 0, 0, LK_EXCLUSIVE }, /* PutFH */
{ 1, 0, 0, 0, LK_EXCLUSIVE }, /* PutPubFH */
{ 1, 0, 0, 0, LK_EXCLUSIVE }, /* PutRootFH */
{ 0, 1, 0, 0, LK_SHARED }, /* Read */
{ 0, 1, 0, 0, LK_SHARED }, /* Readdir */
{ 0, 1, 0, 0, LK_SHARED }, /* ReadLink */
{ 0, 2, 1, 1, LK_EXCLUSIVE }, /* Remove */
{ 2, 1, 1, 1, LK_EXCLUSIVE }, /* Rename */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* Renew */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* RestoreFH */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* SaveFH */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* SecInfo */
{ 0, 2, 1, 1, LK_EXCLUSIVE }, /* Setattr */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* SetClientID */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* SetClientIDConfirm */
{ 0, 1, 0, 0, LK_EXCLUSIVE }, /* Verify */
{ 0, 2, 1, 1, LK_EXCLUSIVE }, /* Write */
{ 0, 0, 0, 0, LK_EXCLUSIVE }, /* ReleaseLockOwner */
};
#endif /* !APPLEKEXT */
static int ncl_mbuf_mhlen = MHLEN;
static int nfsrv_usercnt = 0;
static int nfsrv_dnsnamelen;
static u_char *nfsrv_dnsname = NULL;
static int nfsrv_usermax = 999999999;
static struct nfsuserhashhead nfsuserhash[NFSUSERHASHSIZE];
static struct nfsuserhashhead nfsusernamehash[NFSUSERHASHSIZE];
static struct nfsuserhashhead nfsgrouphash[NFSGROUPHASHSIZE];
static struct nfsuserhashhead nfsgroupnamehash[NFSGROUPHASHSIZE];
static struct nfsuserlruhead nfsuserlruhead;
/*
* This static array indicates whether or not the RPC generates a large
* reply. This is used by nfs_reply() to decide whether or not an mbuf
* cluster should be allocated. (If a cluster is required by an RPC
* marked 0 in this array, the code will still work, just not quite as
* efficiently.)
*/
static int nfs_bigreply[NFS_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0 };
/* local functions */
static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
static void nfsv4_wanted(struct nfsv4lock *lp);
static int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
NFSPROC_T *p);
static void nfsrv_removeuser(struct nfsusrgrp *usrp);
static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
int *, int *);
static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
#ifndef APPLE
/*
* copies mbuf chain to the uio scatter/gather list
*/
int
nfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
{
char *mbufcp, *uiocp;
int xfer, left, len;
mbuf_t mp;
long uiosiz, rem;
int error = 0;
mp = nd->nd_md;
mbufcp = nd->nd_dpos;
len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - mbufcp;
rem = NFSM_RNDUP(siz) - siz;
while (siz > 0) {
if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
return (EBADRPC);
left = uiop->uio_iov->iov_len;
uiocp = uiop->uio_iov->iov_base;
if (left > siz)
left = siz;
uiosiz = left;
while (left > 0) {
while (len == 0) {
mp = mbuf_next(mp);
if (mp == NULL)
return (EBADRPC);
mbufcp = NFSMTOD(mp, caddr_t);
len = mbuf_len(mp);
}
xfer = (left > len) ? len : left;
#ifdef notdef
/* Not Yet.. */
if (uiop->uio_iov->iov_op != NULL)
(*(uiop->uio_iov->iov_op))
(mbufcp, uiocp, xfer);
else
#endif
if (uiop->uio_segflg == UIO_SYSSPACE)
NFSBCOPY(mbufcp, uiocp, xfer);
else
copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
left -= xfer;
len -= xfer;
mbufcp += xfer;
uiocp += xfer;
uiop->uio_offset += xfer;
uiop->uio_resid -= xfer;
}
if (uiop->uio_iov->iov_len <= siz) {
uiop->uio_iovcnt--;
uiop->uio_iov++;
} else {
uiop->uio_iov->iov_base = (void *)
((char *)uiop->uio_iov->iov_base + uiosiz);
uiop->uio_iov->iov_len -= uiosiz;
}
siz -= uiosiz;
}
nd->nd_dpos = mbufcp;
nd->nd_md = mp;
if (rem > 0) {
if (len < rem)
error = nfsm_advance(nd, rem, len);
else
nd->nd_dpos += rem;
}
return (error);
}
#endif /* !APPLE */
/*
* Help break down an mbuf chain by setting the first siz bytes contiguous
* pointed to by returned val.
* This is used by the macro NFSM_DISSECT for tough
* cases.
*/
APPLESTATIC void *
nfsm_dissct(struct nfsrv_descript *nd, int siz)
{
mbuf_t mp2;
int siz2, xfer;
caddr_t p;
int left;
caddr_t retp;
retp = NULL;
left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
while (left == 0) {
nd->nd_md = mbuf_next(nd->nd_md);
if (nd->nd_md == NULL)
return (retp);
left = mbuf_len(nd->nd_md);
nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
}
if (left >= siz) {
retp = nd->nd_dpos;
nd->nd_dpos += siz;
} else if (mbuf_next(nd->nd_md) == NULL) {
return (retp);
} else if (siz > ncl_mbuf_mhlen) {
panic("nfs S too big");
} else {
NFSMGET(mp2);
mbuf_setnext(mp2, mbuf_next(nd->nd_md));
mbuf_setnext(nd->nd_md, mp2);
mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
nd->nd_md = mp2;
retp = p = NFSMTOD(mp2, caddr_t);
NFSBCOPY(nd->nd_dpos, p, left); /* Copy what was left */
siz2 = siz - left;
p += left;
mp2 = mbuf_next(mp2);
/* Loop around copying up the siz2 bytes */
while (siz2 > 0) {
if (mp2 == NULL)
return (NULL);
xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
if (xfer > 0) {
NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
NFSM_DATAP(mp2, xfer);
mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
p += xfer;
siz2 -= xfer;
}
if (siz2 > 0)
mp2 = mbuf_next(mp2);
}
mbuf_setlen(nd->nd_md, siz);
nd->nd_md = mp2;
nd->nd_dpos = NFSMTOD(mp2, caddr_t);
}
return (retp);
}
/*
* Advance the position in the mbuf chain.
* If offs == 0, this is a no-op, but it is simpler to just return from
* here than check for offs > 0 for all calls to nfsm_advance.
* If left == -1, it should be calculated here.
*/
APPLESTATIC int
nfsm_advance(struct nfsrv_descript *nd, int offs, int left)
{
if (offs == 0)
return (0);
/*
* A negative offs should be considered a serious problem.
*/
if (offs < 0)
panic("nfsrv_advance");
/*
* If left == -1, calculate it here.
*/
if (left == -1)
left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
nd->nd_dpos;
/*
* Loop around, advancing over the mbuf data.
*/
while (offs > left) {
offs -= left;
nd->nd_md = mbuf_next(nd->nd_md);
if (nd->nd_md == NULL)
return (EBADRPC);
left = mbuf_len(nd->nd_md);
nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
}
nd->nd_dpos += offs;
return (0);
}
/*
* Copy a string into mbuf(s).
* Return the number of bytes output, including XDR overheads.
*/
APPLESTATIC int
nfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
{
mbuf_t m2;
int xfer, left;
mbuf_t m1;
int rem, bytesize;
u_int32_t *tl;
char *cp2;
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(siz);
rem = NFSM_RNDUP(siz) - siz;
bytesize = NFSX_UNSIGNED + siz + rem;
m2 = nd->nd_mb;
cp2 = nd->nd_bpos;
left = M_TRAILINGSPACE(m2);
/*
* Loop around copying the string to mbuf(s).
*/
while (siz > 0) {
if (left == 0) {
if (siz > ncl_mbuf_mlen)
NFSMCLGET(m1, M_WAIT);
else
NFSMGET(m1);
mbuf_setlen(m1, 0);
mbuf_setnext(m2, m1);
m2 = m1;
cp2 = NFSMTOD(m2, caddr_t);
left = M_TRAILINGSPACE(m2);
}
if (left >= siz)
xfer = siz;
else
xfer = left;
NFSBCOPY(cp, cp2, xfer);
cp += xfer;
mbuf_setlen(m2, mbuf_len(m2) + xfer);
siz -= xfer;
left -= xfer;
if (siz == 0 && rem) {
if (left < rem)
panic("nfsm_strtom");
NFSBZERO(cp2 + xfer, rem);
mbuf_setlen(m2, mbuf_len(m2) + rem);
}
}
nd->nd_mb = m2;
nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
return (bytesize);
}
/*
* Called once to initialize data structures...
*/
APPLESTATIC void
newnfs_init(void)
{
static int nfs_inited = 0;
if (nfs_inited)
return;
nfs_inited = 1;
newnfs_true = txdr_unsigned(TRUE);
newnfs_false = txdr_unsigned(FALSE);
newnfs_xdrneg1 = txdr_unsigned(-1);
nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
if (nfscl_ticks < 1)
nfscl_ticks = 1;
NFSSETBOOTTIME(nfsboottime);
/*
* Initialize reply list and start timer
*/
TAILQ_INIT(&nfsd_reqq);
NFS_TIMERINIT;
}
/*
* Put a file handle in an mbuf list.
* If the size argument == 0, just use the default size.
* set_true == 1 if there should be an newnfs_true prepended on the file handle.
* Return the number of bytes output, including XDR overhead.
*/
APPLESTATIC int
nfsm_fhtom(struct nfsrv_descript *nd, u_int8_t *fhp, int size, int set_true)
{
u_int32_t *tl;
u_int8_t *cp;
int fullsiz, rem, bytesize = 0;
if (size == 0)
size = NFSX_MYFH;
switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
case ND_NFSV2:
if (size > NFSX_V2FH)
panic("fh size > NFSX_V2FH for NFSv2");
NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
NFSBCOPY(fhp, cp, size);
if (size < NFSX_V2FH)
NFSBZERO(cp + size, NFSX_V2FH - size);
bytesize = NFSX_V2FH;
break;
case ND_NFSV3:
case ND_NFSV4:
fullsiz = NFSM_RNDUP(size);
rem = fullsiz - size;
if (set_true) {
bytesize = 2 * NFSX_UNSIGNED + fullsiz;
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_true;
} else {
bytesize = NFSX_UNSIGNED + fullsiz;
}
(void) nfsm_strtom(nd, fhp, size);
break;
};
return (bytesize);
}
/*
* This function compares two net addresses by family and returns TRUE
* if they are the same host.
* If there is any doubt, return FALSE.
* The AF_INET family is handled as a special case so that address mbufs
* don't need to be saved to store "struct in_addr", which is only 4 bytes.
*/
APPLESTATIC int
nfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
{
struct sockaddr_in *inetaddr;
switch (family) {
case AF_INET:
inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
if (inetaddr->sin_family == AF_INET &&
inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
return (1);
break;
#ifdef INET6
case AF_INET6:
{
struct sockaddr_in6 *inetaddr6;
inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
/* XXX - should test sin6_scope_id ? */
if (inetaddr6->sin6_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
&haddr->had_inet6))
return (1);
}
break;
#endif
};
return (0);
}
/*
* Similar to the above, but takes to NFSSOCKADDR_T args.
*/
APPLESTATIC int
nfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
{
struct sockaddr_in *addr1, *addr2;
struct sockaddr *inaddr;
inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
switch (inaddr->sa_family) {
case AF_INET:
addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
if (addr2->sin_family == AF_INET &&
addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
return (1);
break;
#ifdef INET6
case AF_INET6:
{
struct sockaddr_in6 *inet6addr1, *inet6addr2;
inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
/* XXX - should test sin6_scope_id ? */
if (inet6addr2->sin6_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
&inet6addr2->sin6_addr))
return (1);
}
break;
#endif
};
return (0);
}
/*
* Trim the stuff already dissected off the mbuf list.
*/
APPLESTATIC void
newnfs_trimleading(nd)
struct nfsrv_descript *nd;
{
mbuf_t m, n;
int offs;
/*
* First, free up leading mbufs.
*/
if (nd->nd_mrep != nd->nd_md) {
m = nd->nd_mrep;
while (mbuf_next(m) != nd->nd_md) {
if (mbuf_next(m) == NULL)
panic("nfsm trim leading");
m = mbuf_next(m);
}
mbuf_setnext(m, NULL);
mbuf_freem(nd->nd_mrep);
}
m = nd->nd_md;
/*
* Now, adjust this mbuf, based on nd_dpos.
*/
offs = nd->nd_dpos - NFSMTOD(m, caddr_t);
if (offs == mbuf_len(m)) {
n = m;
m = mbuf_next(m);
if (m == NULL)
panic("nfsm trim leading2");
mbuf_setnext(n, NULL);
mbuf_freem(n);
} else if (offs > 0) {
mbuf_setlen(m, mbuf_len(m) - offs);
NFSM_DATAP(m, offs);
} else if (offs < 0)
panic("nfsm trimleading offs");
nd->nd_mrep = m;
nd->nd_md = m;
nd->nd_dpos = NFSMTOD(m, caddr_t);
}
/*
* Trim trailing data off the mbuf list being built.
*/
APPLESTATIC void
newnfs_trimtrailing(nd, mb, bpos)
struct nfsrv_descript *nd;
mbuf_t mb;
caddr_t bpos;
{
if (mbuf_next(mb)) {
mbuf_freem(mbuf_next(mb));
mbuf_setnext(mb, NULL);
}
mbuf_setlen(mb, bpos - NFSMTOD(mb, caddr_t));
nd->nd_mb = mb;
nd->nd_bpos = bpos;
}
/*
* Dissect a file handle on the client.
*/
APPLESTATIC int
nfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
{
u_int32_t *tl;
struct nfsfh *nfhp;
int error, len;
*nfhpp = NULL;
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
len > NFSX_FHMAX)
return (EBADRPC);
} else
len = NFSX_V2FH;
MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + len,
M_NFSFH, M_WAITOK);
error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
if (error) {
FREE((caddr_t)nfhp, M_NFSFH);
return (error);
}
nfhp->nfh_len = len;
*nfhpp = nfhp;
nfsmout:
return (error);
}
/*
* Break down the nfsv4 acl.
* If the aclp == NULL or won't fit in an acl, just discard the acl info.
*/
APPLESTATIC int
nfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, int *aclerrp,
int *aclsizep, __unused NFSPROC_T *p)
{
u_int32_t *tl;
int i, aclsize;
int acecnt, error = 0, aceerr = 0, acesize;
*aclerrp = 0;
if (aclp)
aclp->acl_cnt = 0;
/*
* Parse out the ace entries and expect them to conform to
* what can be supported by R/W/X bits.
*/
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
aclsize = NFSX_UNSIGNED;
acecnt = fxdr_unsigned(int, *tl);
if (acecnt > ACL_MAX_ENTRIES)
aceerr = 1;
if (nfsrv_useacl == 0)
aceerr = 1;
for (i = 0; i < acecnt; i++) {
if (aclp && !aceerr)
error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
&aceerr, &acesize, p);
else
error = nfsrv_skipace(nd, &acesize);
if (error)
return (error);
aclsize += acesize;
}
if (aclp && !aceerr)
aclp->acl_cnt = acecnt;
if (aceerr)
*aclerrp = aceerr;
if (aclsizep)
*aclsizep = aclsize;
nfsmout:
return (error);
}
/*
* Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
*/
static int
nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
{
u_int32_t *tl;
int error, len = 0;
NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
len = fxdr_unsigned(int, *(tl + 3));
error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
nfsmout:
*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
return (error);
}
/*
* Get attribute bits from an mbuf list.
* Returns EBADRPC for a parsing error, 0 otherwise.
* If the clearinvalid flag is set, clear the bits not supported.
*/
APPLESTATIC int
nfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
int *retnotsupp)
{
u_int32_t *tl;
int cnt, i, outcnt;
int error = 0;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
cnt = fxdr_unsigned(int, *tl);
if (cnt < 0)
return (NFSERR_BADXDR);
if (cnt > NFSATTRBIT_MAXWORDS) {
outcnt = NFSATTRBIT_MAXWORDS;
if (retnotsupp)
*retnotsupp = NFSERR_ATTRNOTSUPP;
} else {
outcnt = cnt;
}
NFSZERO_ATTRBIT(attrbitp);
if (outcnt > 0) {
NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
for (i = 0; i < outcnt; i++)
attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
}
if (cnt > outcnt)
error = nfsm_advance(nd, (cnt - outcnt) * NFSX_UNSIGNED, -1);
if (cntp)
*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
nfsmout:
return (error);
}
/*
* Get the attributes for V4.
* If the compare flag is true, test for any attribute changes,
* otherwise return the attribute values.
* These attributes cover fields in "struct vattr", "struct statfs",
* "struct nfsfsinfo", the file handle and the lease duration.
* The value of retcmpp is set to 1 if all attributes are the same,
* and 0 otherwise.
* Returns EBADRPC if it can't be parsed, 0 otherwise.
*/
APPLESTATIC int
nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
{
u_int32_t *tl;
int i = 0, j, k, l, m, bitpos, attrsum = 0;
int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
nfsattrbit_t attrbits, retattrbits, checkattrbits;
struct nfsfh *tnfhp;
struct nfsreferral *refp;
u_quad_t tquad;
nfsquad_t tnfsquad;
struct timespec temptime;
uid_t uid;
gid_t gid;
long fid;
u_int32_t freenum = 0, tuint;
u_int64_t uquad = 0, thyp, thyp2;
#ifdef QUOTA
struct dqblk dqb;
uid_t savuid;
#endif
if (compare) {
retnotsup = 0;
error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
} else {
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
}
if (error)
return (error);
if (compare) {
*retcmpp = retnotsup;
} else {
/*
* Just set default values to some of the important ones.
*/
if (nap != NULL) {
nap->na_type = VREG;
nap->na_mode = 0;
nap->na_rdev = (NFSDEV_T)0;
nap->na_mtime.tv_sec = 0;
nap->na_mtime.tv_nsec = 0;
nap->na_gen = 0;
nap->na_flags = 0;
nap->na_blocksize = NFS_FABLKSIZE;
}
if (sbp != NULL) {
sbp->f_bsize = NFS_FABLKSIZE;
sbp->f_blocks = 0;
sbp->f_bfree = 0;
sbp->f_bavail = 0;
sbp->f_files = 0;
sbp->f_ffree = 0;
}
if (fsp != NULL) {
fsp->fs_rtmax = 8192;
fsp->fs_rtpref = 8192;
fsp->fs_maxname = NFS_MAXNAMLEN;
fsp->fs_wtmax = 8192;
fsp->fs_wtpref = 8192;
fsp->fs_wtmult = NFS_FABLKSIZE;
fsp->fs_dtpref = 8192;
fsp->fs_maxfilesize = 0xffffffffffffffffull;
fsp->fs_timedelta.tv_sec = 0;
fsp->fs_timedelta.tv_nsec = 1;
fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
}
if (pc != NULL) {
pc->pc_linkmax = LINK_MAX;
pc->pc_namemax = NAME_MAX;
pc->pc_notrunc = 0;
pc->pc_chownrestricted = 0;
pc->pc_caseinsensitive = 0;
pc->pc_casepreserving = 1;
}
}
/*
* Loop around getting the attributes.
*/
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
attrsize = fxdr_unsigned(int, *tl);
for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
if (attrsum > attrsize) {
error = NFSERR_BADXDR;
goto nfsmout;
}
if (NFSISSET_ATTRBIT(&attrbits, bitpos))
switch (bitpos) {
case NFSATTRBIT_SUPPORTEDATTRS:
retnotsup = 0;
if (compare || nap == NULL)
error = nfsrv_getattrbits(nd, &retattrbits,
&cnt, &retnotsup);
else
error = nfsrv_getattrbits(nd, &nap->na_suppattr,
&cnt, &retnotsup);
if (error)
return (error);
if (compare && !(*retcmpp)) {
NFSSETSUPP_ATTRBIT(&checkattrbits);
if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
|| retnotsup)
*retcmpp = NFSERR_NOTSAME;
}
attrsum += cnt;
break;
case NFSATTRBIT_TYPE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (nap->na_type != nfsv34tov_type(*tl))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_type = nfsv34tov_type(*tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_FHEXPIRETYPE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare && !(*retcmpp)) {
if (fxdr_unsigned(int, *tl) !=
NFSV4FHTYPE_PERSISTENT)
*retcmpp = NFSERR_NOTSAME;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CHANGE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp)) {
if (nap->na_filerev != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_filerev = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_SIZE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp)) {
if (nap->na_size != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_size = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_LINKSUPPORT:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (fsp->fs_properties & NFSV3_FSFLINK) {
if (*tl == newnfs_false)
*retcmpp = NFSERR_NOTSAME;
} else {
if (*tl == newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
}
} else if (fsp != NULL) {
if (*tl == newnfs_true)
fsp->fs_properties |= NFSV3_FSFLINK;
else
fsp->fs_properties &= ~NFSV3_FSFLINK;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_SYMLINKSUPPORT:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
if (*tl == newnfs_false)
*retcmpp = NFSERR_NOTSAME;
} else {
if (*tl == newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
}
} else if (fsp != NULL) {
if (*tl == newnfs_true)
fsp->fs_properties |= NFSV3_FSFSYMLINK;
else
fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_NAMEDATTR:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare && !(*retcmpp)) {
if (*tl != newnfs_false)
*retcmpp = NFSERR_NOTSAME;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_FSID:
NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
thyp = fxdr_hyper(tl);
tl += 2;
thyp2 = fxdr_hyper(tl);
if (compare) {
if (*retcmpp == 0) {
if (thyp != (u_int64_t)
vfs_statfs(vnode_mount(vp))->f_fsid.val[0] ||
thyp2 != (u_int64_t)
vfs_statfs(vnode_mount(vp))->f_fsid.val[1])
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_filesid[0] = thyp;
nap->na_filesid[1] = thyp2;
}
attrsum += (4 * NFSX_UNSIGNED);
break;
case NFSATTRBIT_UNIQUEHANDLES:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare && !(*retcmpp)) {
if (*tl != newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_LEASETIME:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
!(*retcmpp))
*retcmpp = NFSERR_NOTSAME;
} else if (leasep != NULL) {
*leasep = fxdr_unsigned(u_int32_t, *tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_RDATTRERROR:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp))
*retcmpp = NFSERR_INVAL;
} else if (rderrp != NULL) {
*rderrp = fxdr_unsigned(u_int32_t, *tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_ACL:
if (compare) {
if (!(*retcmpp)) {
if (nfsrv_useacl) {
NFSACL_T *naclp;
naclp = acl_alloc(M_WAITOK);
error = nfsrv_dissectacl(nd, naclp, &aceerr,
&cnt, p);
if (error) {
acl_free(naclp);
return (error);
}
if (aceerr || nfsrv_compareacl(aclp, naclp))
*retcmpp = NFSERR_NOTSAME;
acl_free(naclp);
} else {
error = nfsrv_dissectacl(nd, NULL, &aceerr,
&cnt, p);
*retcmpp = NFSERR_ATTRNOTSUPP;
}
}
} else {
if (vp != NULL && aclp != NULL)
error = nfsrv_dissectacl(nd, aclp, &aceerr,
&cnt, p);
else
error = nfsrv_dissectacl(nd, NULL, &aceerr,
&cnt, p);
if (error)
return (error);
}
attrsum += cnt;
break;
case NFSATTRBIT_ACLSUPPORT:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare && !(*retcmpp)) {
if (nfsrv_useacl) {
if (fxdr_unsigned(u_int32_t, *tl) !=
NFSV4ACE_SUPTYPES)
*retcmpp = NFSERR_NOTSAME;
} else {
*retcmpp = NFSERR_ATTRNOTSUPP;
}
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_ARCHIVE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare && !(*retcmpp))
*retcmpp = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CANSETTIME:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
if (*tl == newnfs_false)
*retcmpp = NFSERR_NOTSAME;
} else {
if (*tl == newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
}
} else if (fsp != NULL) {
if (*tl == newnfs_true)
fsp->fs_properties |= NFSV3_FSFCANSETTIME;
else
fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CASEINSENSITIVE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (*tl != newnfs_false)
*retcmpp = NFSERR_NOTSAME;
}
} else if (pc != NULL) {
pc->pc_caseinsensitive =
fxdr_unsigned(u_int32_t, *tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CASEPRESERVING:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (*tl != newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
} else if (pc != NULL) {
pc->pc_casepreserving =
fxdr_unsigned(u_int32_t, *tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CHOWNRESTRICTED:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (*tl != newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
} else if (pc != NULL) {
pc->pc_chownrestricted =
fxdr_unsigned(u_int32_t, *tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_FILEHANDLE:
error = nfsm_getfh(nd, &tnfhp);
if (error)
return (error);
tfhsize = tnfhp->nfh_len;
if (compare) {
if (!(*retcmpp) &&
!NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
fhp, fhsize))
*retcmpp = NFSERR_NOTSAME;
FREE((caddr_t)tnfhp, M_NFSFH);
} else if (nfhpp != NULL) {
*nfhpp = tnfhp;
} else {
FREE((caddr_t)tnfhp, M_NFSFH);
}
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
break;
case NFSATTRBIT_FILEID:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
thyp = fxdr_hyper(tl);
if (compare) {
if (!(*retcmpp)) {
if ((u_int64_t)nap->na_fileid != thyp)
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
if (*tl++)
printf("NFSv4 fileid > 32bits\n");
nap->na_fileid = thyp;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_FILESAVAIL:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp) &&
sfp->sf_afiles != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
} else if (sfp != NULL) {
sfp->sf_afiles = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_FILESFREE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp) &&
sfp->sf_ffiles != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
} else if (sfp != NULL) {
sfp->sf_ffiles = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_FILESTOTAL:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp) &&
sfp->sf_tfiles != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
} else if (sfp != NULL) {
sfp->sf_tfiles = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_FSLOCATIONS:
error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
if (error)
return (error);
attrsum += l;
if (compare && !(*retcmpp)) {
refp = nfsv4root_getreferral(vp, NULL, 0);
if (refp != NULL) {
if (cp == NULL || cp2 == NULL ||
strcmp(cp, "/") ||
strcmp(cp2, refp->nfr_srvlist))
*retcmpp = NFSERR_NOTSAME;
} else if (m == 0) {
*retcmpp = NFSERR_NOTSAME;
}
}
if (cp != NULL)
free(cp, M_NFSSTRING);
if (cp2 != NULL)
free(cp2, M_NFSSTRING);
break;
case NFSATTRBIT_HIDDEN:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare && !(*retcmpp))
*retcmpp = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HOMOGENEOUS:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (fsp->fs_properties &
NFSV3_FSFHOMOGENEOUS) {
if (*tl == newnfs_false)
*retcmpp = NFSERR_NOTSAME;
} else {
if (*tl == newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
}
} else if (fsp != NULL) {
if (*tl == newnfs_true)
fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
else
fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MAXFILESIZE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
tnfsquad.qval = fxdr_hyper(tl);
if (compare) {
if (!(*retcmpp)) {
tquad = NFSRV_MAXFILESIZE;
if (tquad != tnfsquad.qval)
*retcmpp = NFSERR_NOTSAME;
}
} else if (fsp != NULL) {
fsp->fs_maxfilesize = tnfsquad.qval;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_MAXLINK:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (fxdr_unsigned(int, *tl) != LINK_MAX)
*retcmpp = NFSERR_NOTSAME;
}
} else if (pc != NULL) {
pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MAXNAME:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (fsp->fs_maxname !=
fxdr_unsigned(u_int32_t, *tl))
*retcmpp = NFSERR_NOTSAME;
}
} else {
tuint = fxdr_unsigned(u_int32_t, *tl);
/*
* Some Linux NFSv4 servers report this
* as 0 or 4billion, so I'll set it to
* NFS_MAXNAMLEN. If a server actually creates
* a name longer than NFS_MAXNAMLEN, it will
* get an error back.
*/
if (tuint == 0 || tuint > NFS_MAXNAMLEN)
tuint = NFS_MAXNAMLEN;
if (fsp != NULL)
fsp->fs_maxname = tuint;
if (pc != NULL)
pc->pc_namemax = tuint;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MAXREAD:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp)) {
if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
*(tl + 1)) || *tl != 0)
*retcmpp = NFSERR_NOTSAME;
}
} else if (fsp != NULL) {
fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
fsp->fs_rtpref = fsp->fs_rtmax;
fsp->fs_dtpref = fsp->fs_rtpref;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_MAXWRITE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp)) {
if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
*(tl + 1)) || *tl != 0)
*retcmpp = NFSERR_NOTSAME;
}
} else if (fsp != NULL) {
fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
fsp->fs_wtpref = fsp->fs_wtmax;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_MIMETYPE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
i = fxdr_unsigned(int, *tl);
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
if (error)
goto nfsmout;
if (compare && !(*retcmpp))
*retcmpp = NFSERR_ATTRNOTSUPP;
break;
case NFSATTRBIT_MODE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (nap->na_mode != nfstov_mode(*tl))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_mode = nfstov_mode(*tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_NOTRUNC:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
if (*tl != newnfs_true)
*retcmpp = NFSERR_NOTSAME;
}
} else if (pc != NULL) {
pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_NUMLINKS:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
tuint = fxdr_unsigned(u_int32_t, *tl);
if (compare) {
if (!(*retcmpp)) {
if ((u_int32_t)nap->na_nlink != tuint)
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_nlink = tuint;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_OWNER:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
j = fxdr_unsigned(int, *tl);
if (j < 0)
return (NFSERR_BADXDR);
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
if (j > NFSV4_SMALLSTR)
cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
else
cp = namestr;
error = nfsrv_mtostr(nd, cp, j);
if (error) {
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
return (error);
}
if (compare) {
if (!(*retcmpp)) {
if (nfsv4_strtouid(cp, j, &uid, p) ||
nap->na_uid != uid)
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
if (nfsv4_strtouid(cp, j, &uid, p))
nap->na_uid = nfsrv_defaultuid;
else
nap->na_uid = uid;
}
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
break;
case NFSATTRBIT_OWNERGROUP:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
j = fxdr_unsigned(int, *tl);
if (j < 0)
return (NFSERR_BADXDR);
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
if (j > NFSV4_SMALLSTR)
cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
else
cp = namestr;
error = nfsrv_mtostr(nd, cp, j);
if (error) {
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
return (error);
}
if (compare) {
if (!(*retcmpp)) {
if (nfsv4_strtogid(cp, j, &gid, p) ||
nap->na_gid != gid)
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
if (nfsv4_strtogid(cp, j, &gid, p))
nap->na_gid = nfsrv_defaultgid;
else
nap->na_gid = gid;
}
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
break;
case NFSATTRBIT_QUOTAHARD:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (sbp != NULL) {
if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
freenum = sbp->f_bfree;
else
freenum = sbp->f_bavail;
#ifdef QUOTA
/*
* ufs_quotactl() insists that the uid argument
* equal p_ruid for non-root quota access, so
* we'll just make sure that's the case.
*/
savuid = p->p_cred->p_ruid;
p->p_cred->p_ruid = cred->cr_uid;
if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
freenum = min(dqb.dqb_bhardlimit, freenum);
p->p_cred->p_ruid = savuid;
#endif /* QUOTA */
uquad = (u_int64_t)freenum;
NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
}
if (compare && !(*retcmpp)) {
if (uquad != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_QUOTASOFT:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (sbp != NULL) {
if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
freenum = sbp->f_bfree;
else
freenum = sbp->f_bavail;
#ifdef QUOTA
/*
* ufs_quotactl() insists that the uid argument
* equal p_ruid for non-root quota access, so
* we'll just make sure that's the case.
*/
savuid = p->p_cred->p_ruid;
p->p_cred->p_ruid = cred->cr_uid;
if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
freenum = min(dqb.dqb_bsoftlimit, freenum);
p->p_cred->p_ruid = savuid;
#endif /* QUOTA */
uquad = (u_int64_t)freenum;
NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
}
if (compare && !(*retcmpp)) {
if (uquad != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_QUOTAUSED:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (sbp != NULL) {
freenum = 0;
#ifdef QUOTA
/*
* ufs_quotactl() insists that the uid argument
* equal p_ruid for non-root quota access, so
* we'll just make sure that's the case.
*/
savuid = p->p_cred->p_ruid;
p->p_cred->p_ruid = cred->cr_uid;
if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
freenum = dqb.dqb_curblocks;
p->p_cred->p_ruid = savuid;
#endif /* QUOTA */
uquad = (u_int64_t)freenum;
NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
}
if (compare && !(*retcmpp)) {
if (uquad != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_RAWDEV:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
j = fxdr_unsigned(int, *tl++);
k = fxdr_unsigned(int, *tl);
if (compare) {
if (!(*retcmpp)) {
if (nap->na_rdev != NFSMAKEDEV(j, k))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_rdev = NFSMAKEDEV(j, k);
}
attrsum += NFSX_V4SPECDATA;
break;
case NFSATTRBIT_SPACEAVAIL:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp) &&
sfp->sf_abytes != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
} else if (sfp != NULL) {
sfp->sf_abytes = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_SPACEFREE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp) &&
sfp->sf_fbytes != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
} else if (sfp != NULL) {
sfp->sf_fbytes = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_SPACETOTAL:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
if (compare) {
if (!(*retcmpp) &&
sfp->sf_tbytes != fxdr_hyper(tl))
*retcmpp = NFSERR_NOTSAME;
} else if (sfp != NULL) {
sfp->sf_tbytes = fxdr_hyper(tl);
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_SPACEUSED:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
thyp = fxdr_hyper(tl);
if (compare) {
if (!(*retcmpp)) {
if ((u_int64_t)nap->na_bytes != thyp)
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_bytes = thyp;
}
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_SYSTEM:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare && !(*retcmpp))
*retcmpp = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESS:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
fxdr_nfsv4time(tl, &temptime);
if (compare) {
if (!(*retcmpp)) {
if (!NFS_CMPTIME(temptime, nap->na_atime))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_atime = temptime;
}
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEACCESSSET:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
attrsum += NFSX_UNSIGNED;
i = fxdr_unsigned(int, *tl);
if (i == NFSV4SATTRTIME_TOCLIENT) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
attrsum += NFSX_V4TIME;
}
if (compare && !(*retcmpp))
*retcmpp = NFSERR_INVAL;
break;
case NFSATTRBIT_TIMEBACKUP:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
if (compare && !(*retcmpp))
*retcmpp = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMECREATE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
if (compare && !(*retcmpp))
*retcmpp = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEDELTA:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
if (fsp != NULL) {
if (compare) {
if (!(*retcmpp)) {
if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
fxdr_unsigned(u_int32_t, *(tl + 1)) ||
(u_int32_t)fsp->fs_timedelta.tv_nsec !=
(fxdr_unsigned(u_int32_t, *(tl + 2)) %
1000000000) ||
*tl != 0)
*retcmpp = NFSERR_NOTSAME;
}
} else {
fxdr_nfsv4time(tl, &fsp->fs_timedelta);
}
}
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMETADATA:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
fxdr_nfsv4time(tl, &temptime);
if (compare) {
if (!(*retcmpp)) {
if (!NFS_CMPTIME(temptime, nap->na_ctime))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_ctime = temptime;
}
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMODIFY:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
fxdr_nfsv4time(tl, &temptime);
if (compare) {
if (!(*retcmpp)) {
if (!NFS_CMPTIME(temptime, nap->na_mtime))
*retcmpp = NFSERR_NOTSAME;
}
} else if (nap != NULL) {
nap->na_mtime = temptime;
}
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMODIFYSET:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
attrsum += NFSX_UNSIGNED;
i = fxdr_unsigned(int, *tl);
if (i == NFSV4SATTRTIME_TOCLIENT) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
attrsum += NFSX_V4TIME;
}
if (compare && !(*retcmpp))
*retcmpp = NFSERR_INVAL;
break;
case NFSATTRBIT_MOUNTEDONFILEID:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
thyp = fxdr_hyper(tl);
if (compare) {
if (!(*retcmpp)) {
if (*tl++) {
*retcmpp = NFSERR_NOTSAME;
} else {
if (!vp || !nfsrv_atroot(vp, &fid))
fid = nap->na_fileid;
if ((u_int64_t)fid != thyp)
*retcmpp = NFSERR_NOTSAME;
}
}
} else if (nap != NULL) {
if (*tl++)
printf("NFSv4 mounted on fileid > 32bits\n");
nap->na_mntonfileno = thyp;
}
attrsum += NFSX_HYPER;
break;
default:
printf("EEK! nfsv4_loadattr unknown attr=%d\n",
bitpos);
if (compare && !(*retcmpp))
*retcmpp = NFSERR_ATTRNOTSUPP;
/*
* and get out of the loop, since we can't parse
* the unknown attrbute data.
*/
bitpos = NFSATTRBIT_MAX;
break;
};
}
/*
* some clients pad the attrlist, so we need to skip over the
* padding.
*/
if (attrsum > attrsize) {
error = NFSERR_BADXDR;
} else {
attrsize = NFSM_RNDUP(attrsize);
if (attrsum < attrsize)
error = nfsm_advance(nd, attrsize - attrsum, -1);
}
nfsmout:
return (error);
}
/*
* Implement sleep locks for newnfs. The nfslock_usecnt allows for a
* shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
* The first argument is a pointer to an nfsv4lock structure.
* The second argument is 1 iff a blocking lock is wanted.
* If this argument is 0, the call waits until no thread either wants nor
* holds an exclusive lock.
* It returns 1 if the lock was acquired, 0 otherwise.
* If several processes call this function concurrently wanting the exclusive
* lock, one will get the lock and the rest will return without getting the
* lock. (If the caller must have the lock, it simply calls this function in a
* loop until the function returns 1 to indicate the lock was acquired.)
* Any usecnt must be decremented by calling nfsv4_relref() before
* calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
* be called in a loop.
* The last argument is set to indicate if the call slept, iff not NULL.
*/
APPLESTATIC int
nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
void *mutex)
{
if (isleptp)
*isleptp = 0;
/*
* If a lock is wanted, loop around until the lock is acquired by
* someone and then released. If I want the lock, try to acquire it.
* For a lock to be issued, no lock must be in force and the usecnt
* must be zero.
*/
if (iwantlock) {
if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
lp->nfslock_usecnt == 0) {
lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
lp->nfslock_lock |= NFSV4LOCK_LOCK;
return (1);
}
lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
}
while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
lp->nfslock_lock |= NFSV4LOCK_WANTED;
if (isleptp)
*isleptp = 1;
(void) nfsmsleep(&lp->nfslock_lock, mutex,
PZERO - 1, "nfsv4lck", NULL);
if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
lp->nfslock_usecnt == 0) {
lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
lp->nfslock_lock |= NFSV4LOCK_LOCK;
return (1);
}
}
return (0);
}
/*
* Release the lock acquired by nfsv4_lock().
* The second argument is set to 1 to indicate the nfslock_usecnt should be
* incremented, as well.
*/
APPLESTATIC void
nfsv4_unlock(struct nfsv4lock *lp, int incref)
{
lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
if (incref)
lp->nfslock_usecnt++;
nfsv4_wanted(lp);
}
/*
* Release a reference cnt.
*/
APPLESTATIC void
nfsv4_relref(struct nfsv4lock *lp)
{
if (lp->nfslock_usecnt <= 0)
panic("nfsv4root ref cnt");
lp->nfslock_usecnt--;
if (lp->nfslock_usecnt == 0)
nfsv4_wanted(lp);
}
/*
* Get a reference cnt.
* This function will wait for any exclusive lock to be released, but will
* not wait for threads that want the exclusive lock. If priority needs
* to be given to threads that need the exclusive lock, a call to nfsv4_lock()
* with the 2nd argument == 0 should be done before calling nfsv4_getref().
*/
APPLESTATIC void
nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex)
{
if (isleptp)
*isleptp = 0;
/*
* Wait for a lock held.
*/
while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
lp->nfslock_lock |= NFSV4LOCK_WANTED;
if (isleptp)
*isleptp = 1;
(void) nfsmsleep(&lp->nfslock_lock, mutex,
PZERO - 1, "nfsv4lck", NULL);
}
lp->nfslock_usecnt++;
}
/*
* Get a reference as above, but return failure instead of sleeping if
* an exclusive lock is held.
*/
APPLESTATIC int
nfsv4_getref_nonblock(struct nfsv4lock *lp)
{
if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
return (0);
lp->nfslock_usecnt++;
return (1);
}
/*
* Test for a lock. Return 1 if locked, 0 otherwise.
*/
APPLESTATIC int
nfsv4_testlock(struct nfsv4lock *lp)
{
if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
lp->nfslock_usecnt == 0)
return (0);
return (1);
}
/*
* Wake up anyone sleeping, waiting for this lock.
*/
static void
nfsv4_wanted(struct nfsv4lock *lp)
{
if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
wakeup((caddr_t)&lp->nfslock_lock);
}
}
/*
* Copy a string from an mbuf list into a character array.
* Return EBADRPC if there is an mbuf error,
* 0 otherwise.
*/
APPLESTATIC int
nfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
{
char *cp;
int xfer, len;
mbuf_t mp;
int rem, error = 0;
mp = nd->nd_md;
cp = nd->nd_dpos;
len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - cp;
rem = NFSM_RNDUP(siz) - siz;
while (siz > 0) {
if (len > siz)
xfer = siz;
else
xfer = len;
NFSBCOPY(cp, str, xfer);
str += xfer;
siz -= xfer;
if (siz > 0) {
mp = mbuf_next(mp);
if (mp == NULL)
return (EBADRPC);
cp = NFSMTOD(mp, caddr_t);
len = mbuf_len(mp);
} else {
cp += xfer;
len -= xfer;
}
}
*str = '\0';
nd->nd_dpos = cp;
nd->nd_md = mp;
if (rem > 0) {
if (len < rem)
error = nfsm_advance(nd, rem, len);
else
nd->nd_dpos += rem;
}
return (error);
}
/*
* Fill in the attributes as marked by the bitmap (V4).
*/
APPLESTATIC int
nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
- int reterr, int at_root, uint64_t mounted_on_fileno)
+ int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
{
int bitpos, retnum = 0;
u_int32_t *tl;
int siz, prefixnum, error;
u_char *cp, namestr[NFSV4_SMALLSTR];
nfsattrbit_t attrbits, retbits;
nfsattrbit_t *retbitp = &retbits;
u_int32_t freenum, *retnump;
u_int64_t uquad;
struct statfs fs;
struct nfsfsinfo fsinf;
struct timespec temptime;
struct timeval curtime;
NFSACL_T *aclp, *naclp = NULL;
#ifdef QUOTA
struct dqblk dqb;
uid_t savuid;
#endif
/*
* First, set the bits that can be filled and get fsinfo.
*/
NFSSET_ATTRBIT(retbitp, attrbitp);
/* If p and cred are NULL, it is a client side call */
if (p == NULL && cred == NULL) {
NFSCLRNOTSETABLE_ATTRBIT(retbitp);
aclp = saclp;
} else {
NFSCLRNOTFILLABLE_ATTRBIT(retbitp);
naclp = acl_alloc(M_WAITOK);
aclp = naclp;
}
nfsvno_getfs(&fsinf, isdgram);
#ifndef APPLE
/*
* Get the VFS_STATFS(), since some attributes need them.
*/
if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
error = VFS_STATFS(mp, &fs);
if (error != 0) {
if (reterr) {
nd->nd_repstat = NFSERR_ACCES;
return (0);
}
NFSCLRSTATFS_ATTRBIT(retbitp);
}
}
#endif
/*
* And the NFSv4 ACL...
*/
if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
(nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
- !NFSHASNFS4ACL(mp)))) {
+ supports_nfsv4acls == 0))) {
NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
}
if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
- !NFSHASNFS4ACL(mp))) {
+ supports_nfsv4acls == 0)) {
NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
} else if (naclp != NULL) {
if (vn_lock(vp, LK_SHARED) == 0) {
error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
if (error == 0)
error = VOP_GETACL(vp, ACL_TYPE_NFS4,
naclp, cred, p);
VOP_UNLOCK(vp, 0);
} else
error = NFSERR_PERM;
if (error != 0) {
if (reterr) {
nd->nd_repstat = NFSERR_ACCES;
return (0);
}
NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
}
}
}
/*
* Put out the attribute bitmap for the ones being filled in
* and get the field for the number of attributes returned.
*/
prefixnum = nfsrv_putattrbit(nd, retbitp);
NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
prefixnum += NFSX_UNSIGNED;
/*
* Now, loop around filling in the attributes for each bit set.
*/
for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
switch (bitpos) {
case NFSATTRBIT_SUPPORTEDATTRS:
NFSSETSUPP_ATTRBIT(&attrbits);
if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
- && !NFSHASNFS4ACL(mp))) {
+ && supports_nfsv4acls == 0)) {
NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
}
retnum += nfsrv_putattrbit(nd, &attrbits);
break;
case NFSATTRBIT_TYPE:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = vtonfsv34_type(vap->va_type);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_FHEXPIRETYPE:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CHANGE:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
txdr_hyper(vap->va_filerev, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_SIZE:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
txdr_hyper(vap->va_size, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_LINKSUPPORT:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (fsinf.fs_properties & NFSV3FSINFO_LINK)
*tl = newnfs_true;
else
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_SYMLINKSUPPORT:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
*tl = newnfs_true;
else
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_NAMEDATTR:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_FSID:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
*tl++ = 0;
*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
*tl++ = 0;
*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
retnum += NFSX_V4FSID;
break;
case NFSATTRBIT_UNIQUEHANDLES:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_true;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_LEASETIME:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(nfsrv_lease);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_RDATTRERROR:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(rderror);
retnum += NFSX_UNSIGNED;
break;
/*
* Recommended Attributes. (Only the supported ones.)
*/
case NFSATTRBIT_ACL:
retnum += nfsrv_buildacl(nd, aclp, vnode_vtype(vp), p);
break;
case NFSATTRBIT_ACLSUPPORT:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CANSETTIME:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
*tl = newnfs_true;
else
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CASEINSENSITIVE:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CASEPRESERVING:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_true;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_CHOWNRESTRICTED:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_true;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_FILEHANDLE:
retnum += nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
break;
case NFSATTRBIT_FILEID:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
*tl++ = 0;
*tl = txdr_unsigned(vap->va_fileid);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_FILESAVAIL:
/*
* Check quota and use min(quota, f_ffree).
*/
freenum = fs.f_ffree;
#ifdef QUOTA
/*
* ufs_quotactl() insists that the uid argument
* equal p_ruid for non-root quota access, so
* we'll just make sure that's the case.
*/
savuid = p->p_cred->p_ruid;
p->p_cred->p_ruid = cred->cr_uid;
if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
cred->cr_uid, (caddr_t)&dqb))
freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
freenum);
p->p_cred->p_ruid = savuid;
#endif /* QUOTA */
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
*tl++ = 0;
*tl = txdr_unsigned(freenum);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_FILESFREE:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
*tl++ = 0;
*tl = txdr_unsigned(fs.f_ffree);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_FILESTOTAL:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
*tl++ = 0;
*tl = txdr_unsigned(fs.f_files);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_FSLOCATIONS:
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = 0;
*tl = 0;
retnum += 2 * NFSX_UNSIGNED;
break;
case NFSATTRBIT_HOMOGENEOUS:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
*tl = newnfs_true;
else
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MAXFILESIZE:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
uquad = NFSRV_MAXFILESIZE;
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_MAXLINK:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(LINK_MAX);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MAXNAME:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFS_MAXNAMLEN);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MAXREAD:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
*tl++ = 0;
*tl = txdr_unsigned(fsinf.fs_rtmax);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_MAXWRITE:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
*tl++ = 0;
*tl = txdr_unsigned(fsinf.fs_wtmax);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_MODE:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = vtonfsv34_mode(vap->va_mode);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_NOTRUNC:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_true;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_NUMLINKS:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(vap->va_nlink);
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_OWNER:
cp = namestr;
nfsv4_uidtostr(vap->va_uid, &cp, &siz, p);
retnum += nfsm_strtom(nd, cp, siz);
if (cp != namestr)
free(cp, M_NFSSTRING);
break;
case NFSATTRBIT_OWNERGROUP:
cp = namestr;
nfsv4_gidtostr(vap->va_gid, &cp, &siz, p);
retnum += nfsm_strtom(nd, cp, siz);
if (cp != namestr)
free(cp, M_NFSSTRING);
break;
case NFSATTRBIT_QUOTAHARD:
if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
freenum = fs.f_bfree;
else
freenum = fs.f_bavail;
#ifdef QUOTA
/*
* ufs_quotactl() insists that the uid argument
* equal p_ruid for non-root quota access, so
* we'll just make sure that's the case.
*/
savuid = p->p_cred->p_ruid;
p->p_cred->p_ruid = cred->cr_uid;
if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
cred->cr_uid, (caddr_t)&dqb))
freenum = min(dqb.dqb_bhardlimit, freenum);
p->p_cred->p_ruid = savuid;
#endif /* QUOTA */
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
uquad = (u_int64_t)freenum;
NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_QUOTASOFT:
if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
freenum = fs.f_bfree;
else
freenum = fs.f_bavail;
#ifdef QUOTA
/*
* ufs_quotactl() insists that the uid argument
* equal p_ruid for non-root quota access, so
* we'll just make sure that's the case.
*/
savuid = p->p_cred->p_ruid;
p->p_cred->p_ruid = cred->cr_uid;
if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
cred->cr_uid, (caddr_t)&dqb))
freenum = min(dqb.dqb_bsoftlimit, freenum);
p->p_cred->p_ruid = savuid;
#endif /* QUOTA */
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
uquad = (u_int64_t)freenum;
NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_QUOTAUSED:
freenum = 0;
#ifdef QUOTA
/*
* ufs_quotactl() insists that the uid argument
* equal p_ruid for non-root quota access, so
* we'll just make sure that's the case.
*/
savuid = p->p_cred->p_ruid;
p->p_cred->p_ruid = cred->cr_uid;
if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
cred->cr_uid, (caddr_t)&dqb))
freenum = dqb.dqb_curblocks;
p->p_cred->p_ruid = savuid;
#endif /* QUOTA */
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
uquad = (u_int64_t)freenum;
NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_RAWDEV:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
retnum += NFSX_V4SPECDATA;
break;
case NFSATTRBIT_SPACEAVAIL:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE, 0))
uquad = (u_int64_t)fs.f_bfree;
else
uquad = (u_int64_t)fs.f_bavail;
uquad *= fs.f_bsize;
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_SPACEFREE:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
uquad = (u_int64_t)fs.f_bfree;
uquad *= fs.f_bsize;
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_SPACETOTAL:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
uquad = (u_int64_t)fs.f_blocks;
uquad *= fs.f_bsize;
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_SPACEUSED:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
txdr_hyper(vap->va_bytes, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_TIMEACCESS:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
txdr_nfsv4time(&vap->va_atime, tl);
retnum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEACCESSSET:
NFSGETTIME(&curtime);
if (vap->va_atime.tv_sec != curtime.tv_sec) {
NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
txdr_nfsv4time(&vap->va_atime, tl);
retnum += NFSX_V4SETTIME;
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
retnum += NFSX_UNSIGNED;
}
break;
case NFSATTRBIT_TIMEDELTA:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
temptime.tv_sec = 0;
temptime.tv_nsec = 1000000000 / hz;
txdr_nfsv4time(&temptime, tl);
retnum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMETADATA:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
txdr_nfsv4time(&vap->va_ctime, tl);
retnum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMODIFY:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
txdr_nfsv4time(&vap->va_mtime, tl);
retnum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMODIFYSET:
NFSGETTIME(&curtime);
if (vap->va_mtime.tv_sec != curtime.tv_sec) {
NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
txdr_nfsv4time(&vap->va_mtime, tl);
retnum += NFSX_V4SETTIME;
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
retnum += NFSX_UNSIGNED;
}
break;
case NFSATTRBIT_MOUNTEDONFILEID:
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
if (at_root != 0)
uquad = mounted_on_fileno;
else
uquad = (u_int64_t)vap->va_fileid;
txdr_hyper(uquad, tl);
retnum += NFSX_HYPER;
break;
default:
printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
};
}
}
if (naclp != NULL)
acl_free(naclp);
*retnump = txdr_unsigned(retnum);
return (retnum + prefixnum);
}
/*
* Put the attribute bits onto an mbuf list.
* Return the number of bytes of output generated.
*/
APPLESTATIC int
nfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
{
u_int32_t *tl;
int cnt, i, bytesize;
for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
if (attrbitp->bits[cnt - 1])
break;
bytesize = (cnt + 1) * NFSX_UNSIGNED;
NFSM_BUILD(tl, u_int32_t *, bytesize);
*tl++ = txdr_unsigned(cnt);
for (i = 0; i < cnt; i++)
*tl++ = txdr_unsigned(attrbitp->bits[i]);
return (bytesize);
}
/*
* Convert a uid to a string.
* If the lookup fails, just output the digits.
* uid - the user id
* cpp - points to a buffer of size NFSV4_SMALLSTR
* (malloc a larger one, as required)
* retlenp - pointer to length to be returned
*/
APPLESTATIC void
nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
{
int i;
struct nfsusrgrp *usrp;
u_char *cp = *cpp;
uid_t tmp;
int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
cnt = 0;
tryagain:
NFSLOCKNAMEID();
if (nfsrv_dnsname) {
/*
* Always map nfsrv_defaultuid to "nobody".
*/
if (uid == nfsrv_defaultuid) {
i = nfsrv_dnsnamelen + 7;
if (i > len) {
NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
*cpp = cp;
len = i;
goto tryagain;
}
*retlenp = i;
NFSBCOPY("nobody@", cp, 7);
cp += 7;
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
NFSUNLOCKNAMEID();
return;
}
hasampersand = 0;
LIST_FOREACH(usrp, NFSUSERHASH(uid), lug_numhash) {
if (usrp->lug_uid == uid) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
/*
* If the name doesn't already have an '@'
* in it, append @domainname to it.
*/
for (i = 0; i < usrp->lug_namelen; i++) {
if (usrp->lug_name[i] == '@') {
hasampersand = 1;
break;
}
}
if (hasampersand)
i = usrp->lug_namelen;
else
i = usrp->lug_namelen +
nfsrv_dnsnamelen + 1;
if (i > len) {
NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
*cpp = cp;
len = i;
goto tryagain;
}
*retlenp = i;
NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
if (!hasampersand) {
cp += usrp->lug_namelen;
*cp++ = '@';
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
}
TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
NFSUNLOCKNAMEID();
return;
}
}
NFSUNLOCKNAMEID();
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
NULL, p);
if (ret == 0 && cnt < 2)
goto tryagain;
} else {
NFSUNLOCKNAMEID();
}
/*
* No match, just return a string of digits.
*/
tmp = uid;
i = 0;
while (tmp || i == 0) {
tmp /= 10;
i++;
}
len = (i > len) ? len : i;
*retlenp = len;
cp += (len - 1);
tmp = uid;
for (i = 0; i < len; i++) {
*cp-- = '0' + (tmp % 10);
tmp /= 10;
}
return;
}
/*
* Convert a string to a uid.
* If no conversion is possible return NFSERR_BADOWNER, otherwise
* return 0.
*/
APPLESTATIC int
nfsv4_strtouid(u_char *str, int len, uid_t *uidp, NFSPROC_T *p)
{
int i;
u_char *cp;
struct nfsusrgrp *usrp;
int cnt, ret;
if (len == 0)
return (NFSERR_BADOWNER);
/*
* Look for an '@'.
*/
cp = str;
for (i = 0; i < len; i++)
if (*cp++ == '@')
break;
cnt = 0;
tryagain:
NFSLOCKNAMEID();
/*
* If an '@' is found and the domain name matches, search for the name
* with dns stripped off.
* Mixed case alpahbetics will match for the domain name, but all
* upper case will not.
*/
if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
(len - 1 - i) == nfsrv_dnsnamelen &&
!nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
len -= (nfsrv_dnsnamelen + 1);
*(cp - 1) = '\0';
}
/*
* Check for the special case of "nobody".
*/
if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
*uidp = nfsrv_defaultuid;
NFSUNLOCKNAMEID();
return (0);
}
LIST_FOREACH(usrp, NFSUSERNAMEHASH(str, len), lug_namehash) {
if (usrp->lug_namelen == len &&
!NFSBCMP(usrp->lug_name, str, len)) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
*uidp = usrp->lug_uid;
TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
NFSUNLOCKNAMEID();
return (0);
}
}
NFSUNLOCKNAMEID();
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
str, p);
if (ret == 0 && cnt < 2)
goto tryagain;
return (NFSERR_BADOWNER);
}
/*
* Convert a gid to a string.
* gid - the group id
* cpp - points to a buffer of size NFSV4_SMALLSTR
* (malloc a larger one, as required)
* retlenp - pointer to length to be returned
*/
APPLESTATIC void
nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
{
int i;
struct nfsusrgrp *usrp;
u_char *cp = *cpp;
gid_t tmp;
int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
cnt = 0;
tryagain:
NFSLOCKNAMEID();
if (nfsrv_dnsname) {
/*
* Always map nfsrv_defaultgid to "nogroup".
*/
if (gid == nfsrv_defaultgid) {
i = nfsrv_dnsnamelen + 8;
if (i > len) {
NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
*cpp = cp;
len = i;
goto tryagain;
}
*retlenp = i;
NFSBCOPY("nogroup@", cp, 8);
cp += 8;
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
NFSUNLOCKNAMEID();
return;
}
hasampersand = 0;
LIST_FOREACH(usrp, NFSGROUPHASH(gid), lug_numhash) {
if (usrp->lug_gid == gid) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
/*
* If the name doesn't already have an '@'
* in it, append @domainname to it.
*/
for (i = 0; i < usrp->lug_namelen; i++) {
if (usrp->lug_name[i] == '@') {
hasampersand = 1;
break;
}
}
if (hasampersand)
i = usrp->lug_namelen;
else
i = usrp->lug_namelen +
nfsrv_dnsnamelen + 1;
if (i > len) {
NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
*cpp = cp;
len = i;
goto tryagain;
}
*retlenp = i;
NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
if (!hasampersand) {
cp += usrp->lug_namelen;
*cp++ = '@';
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
}
TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
NFSUNLOCKNAMEID();
return;
}
}
NFSUNLOCKNAMEID();
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
NULL, p);
if (ret == 0 && cnt < 2)
goto tryagain;
} else {
NFSUNLOCKNAMEID();
}
/*
* No match, just return a string of digits.
*/
tmp = gid;
i = 0;
while (tmp || i == 0) {
tmp /= 10;
i++;
}
len = (i > len) ? len : i;
*retlenp = len;
cp += (len - 1);
tmp = gid;
for (i = 0; i < len; i++) {
*cp-- = '0' + (tmp % 10);
tmp /= 10;
}
return;
}
/*
* Convert a string to a gid.
*/
APPLESTATIC int
nfsv4_strtogid(u_char *str, int len, gid_t *gidp, NFSPROC_T *p)
{
int i;
u_char *cp;
struct nfsusrgrp *usrp;
int cnt, ret;
if (len == 0)
return (NFSERR_BADOWNER);
/*
* Look for an '@'.
*/
cp = str;
for (i = 0; i < len; i++)
if (*cp++ == '@')
break;
cnt = 0;
tryagain:
NFSLOCKNAMEID();
/*
* If an '@' is found and the dns name matches, search for the name
* with the dns stripped off.
*/
if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
(len - 1 - i) == nfsrv_dnsnamelen &&
!nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
len -= (nfsrv_dnsnamelen + 1);
*(cp - 1) = '\0';
}
/*
* Check for the special case of "nogroup".
*/
if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
*gidp = nfsrv_defaultgid;
NFSUNLOCKNAMEID();
return (0);
}
LIST_FOREACH(usrp, NFSGROUPNAMEHASH(str, len), lug_namehash) {
if (usrp->lug_namelen == len &&
!NFSBCMP(usrp->lug_name, str, len)) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
*gidp = usrp->lug_gid;
TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
NFSUNLOCKNAMEID();
return (0);
}
}
NFSUNLOCKNAMEID();
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
str, p);
if (ret == 0 && cnt < 2)
goto tryagain;
return (NFSERR_BADOWNER);
}
/*
* Cmp len chars, allowing mixed case in the first argument to match lower
* case in the second, but not if the first argument is all upper case.
* Return 0 for a match, 1 otherwise.
*/
static int
nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len)
{
int i;
u_char tmp;
int fndlower = 0;
for (i = 0; i < len; i++) {
if (*cp >= 'A' && *cp <= 'Z') {
tmp = *cp++ + ('a' - 'A');
} else {
tmp = *cp++;
if (tmp >= 'a' && tmp <= 'z')
fndlower = 1;
}
if (tmp != *cp2++)
return (1);
}
if (fndlower)
return (0);
else
return (1);
}
/*
* Set the port for the nfsuserd.
*/
APPLESTATIC int
nfsrv_nfsuserdport(u_short port, NFSPROC_T *p)
{
struct nfssockreq *rp;
struct sockaddr_in *ad;
int error;
NFSLOCKNAMEID();
if (nfsrv_nfsuserd) {
NFSUNLOCKNAMEID();
return (EPERM);
}
nfsrv_nfsuserd = 1;
NFSUNLOCKNAMEID();
/*
* Set up the socket record and connect.
*/
rp = &nfsrv_nfsuserdsock;
rp->nr_client = NULL;
rp->nr_sotype = SOCK_DGRAM;
rp->nr_soproto = IPPROTO_UDP;
rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
rp->nr_cred = NULL;
NFSSOCKADDRALLOC(rp->nr_nam);
NFSSOCKADDRSIZE(rp->nr_nam, sizeof (struct sockaddr_in));
ad = NFSSOCKADDR(rp->nr_nam, struct sockaddr_in *);
ad->sin_family = AF_INET;
ad->sin_addr.s_addr = htonl((u_int32_t)0x7f000001); /* 127.0.0.1 */
ad->sin_port = port;
rp->nr_prog = RPCPROG_NFSUSERD;
rp->nr_vers = RPCNFSUSERD_VERS;
error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0);
if (error) {
NFSSOCKADDRFREE(rp->nr_nam);
nfsrv_nfsuserd = 0;
}
return (error);
}
/*
* Delete the nfsuserd port.
*/
APPLESTATIC void
nfsrv_nfsuserddelport(void)
{
NFSLOCKNAMEID();
if (nfsrv_nfsuserd == 0) {
NFSUNLOCKNAMEID();
return;
}
nfsrv_nfsuserd = 0;
NFSUNLOCKNAMEID();
newnfs_disconnect(&nfsrv_nfsuserdsock);
NFSSOCKADDRFREE(nfsrv_nfsuserdsock.nr_nam);
}
/*
* Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
* name<-->id cache.
* Returns 0 upon success, non-zero otherwise.
*/
static int
nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name, NFSPROC_T *p)
{
u_int32_t *tl;
struct nfsrv_descript *nd;
int len;
struct nfsrv_descript nfsd;
struct ucred *cred;
int error;
NFSLOCKNAMEID();
if (nfsrv_nfsuserd == 0) {
NFSUNLOCKNAMEID();
return (EPERM);
}
NFSUNLOCKNAMEID();
nd = &nfsd;
cred = newnfs_getcred();
nd->nd_flag = ND_GSSINITREPLY;
nfsrvd_rephead(nd);
nd->nd_procnum = procnum;
if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (procnum == RPCNFSUSERD_GETUID)
*tl = txdr_unsigned(uid);
else
*tl = txdr_unsigned(gid);
} else {
len = strlen(name);
(void) nfsm_strtom(nd, name, len);
}
error = newnfs_request(nd, NULL, NULL, &nfsrv_nfsuserdsock, NULL, NULL,
cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0, NULL);
NFSFREECRED(cred);
if (!error) {
mbuf_freem(nd->nd_mrep);
error = nd->nd_repstat;
}
return (error);
}
/*
* This function is called from the nfssvc(2) system call, to update the
* kernel user/group name list(s) for the V4 owner and ownergroup attributes.
*/
APPLESTATIC int
nfssvc_idname(struct nfsd_idargs *nidp)
{
struct nfsusrgrp *nusrp, *usrp, *newusrp;
struct nfsuserhashhead *hp;
int i;
int error = 0;
u_char *cp;
if (nidp->nid_flag & NFSID_INITIALIZE) {
cp = (u_char *)malloc(nidp->nid_namelen + 1,
M_NFSSTRING, M_WAITOK);
error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
nidp->nid_namelen);
NFSLOCKNAMEID();
if (nfsrv_dnsname) {
/*
* Free up all the old stuff and reinitialize hash lists.
*/
TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
nfsrv_removeuser(usrp);
}
free(nfsrv_dnsname, M_NFSSTRING);
nfsrv_dnsname = NULL;
}
TAILQ_INIT(&nfsuserlruhead);
for (i = 0; i < NFSUSERHASHSIZE; i++)
LIST_INIT(&nfsuserhash[i]);
for (i = 0; i < NFSGROUPHASHSIZE; i++)
LIST_INIT(&nfsgrouphash[i]);
for (i = 0; i < NFSUSERHASHSIZE; i++)
LIST_INIT(&nfsusernamehash[i]);
for (i = 0; i < NFSGROUPHASHSIZE; i++)
LIST_INIT(&nfsgroupnamehash[i]);
/*
* Put name in "DNS" string.
*/
if (!error) {
nfsrv_dnsname = cp;
nfsrv_dnsnamelen = nidp->nid_namelen;
nfsrv_defaultuid = nidp->nid_uid;
nfsrv_defaultgid = nidp->nid_gid;
nfsrv_usercnt = 0;
nfsrv_usermax = nidp->nid_usermax;
}
NFSUNLOCKNAMEID();
if (error)
free(cp, M_NFSSTRING);
return (error);
}
/*
* malloc the new one now, so any potential sleep occurs before
* manipulation of the lists.
*/
MALLOC(newusrp, struct nfsusrgrp *, sizeof (struct nfsusrgrp) +
nidp->nid_namelen, M_NFSUSERGROUP, M_WAITOK);
error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
nidp->nid_namelen);
if (error) {
free((caddr_t)newusrp, M_NFSUSERGROUP);
return (error);
}
newusrp->lug_namelen = nidp->nid_namelen;
NFSLOCKNAMEID();
/*
* Delete old entries, as required.
*/
if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
hp = NFSUSERHASH(nidp->nid_uid);
LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
if (usrp->lug_uid == nidp->nid_uid)
nfsrv_removeuser(usrp);
}
}
if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
hp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
if (usrp->lug_namelen == newusrp->lug_namelen &&
!NFSBCMP(usrp->lug_name, newusrp->lug_name,
usrp->lug_namelen))
nfsrv_removeuser(usrp);
}
}
if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
hp = NFSGROUPHASH(nidp->nid_gid);
LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
if (usrp->lug_gid == nidp->nid_gid)
nfsrv_removeuser(usrp);
}
}
if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
hp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
if (usrp->lug_namelen == newusrp->lug_namelen &&
!NFSBCMP(usrp->lug_name, newusrp->lug_name,
usrp->lug_namelen))
nfsrv_removeuser(usrp);
}
}
TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
if (usrp->lug_expiry < NFSD_MONOSEC)
nfsrv_removeuser(usrp);
}
while (nfsrv_usercnt >= nfsrv_usermax) {
usrp = TAILQ_FIRST(&nfsuserlruhead);
nfsrv_removeuser(usrp);
}
/*
* Now, we can add the new one.
*/
if (nidp->nid_usertimeout)
newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
else
newusrp->lug_expiry = NFSD_MONOSEC + 5;
if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
newusrp->lug_uid = nidp->nid_uid;
LIST_INSERT_HEAD(NFSUSERHASH(newusrp->lug_uid), newusrp,
lug_numhash);
LIST_INSERT_HEAD(NFSUSERNAMEHASH(newusrp->lug_name,
newusrp->lug_namelen), newusrp, lug_namehash);
TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
nfsrv_usercnt++;
} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
newusrp->lug_gid = nidp->nid_gid;
LIST_INSERT_HEAD(NFSGROUPHASH(newusrp->lug_gid), newusrp,
lug_numhash);
LIST_INSERT_HEAD(NFSGROUPNAMEHASH(newusrp->lug_name,
newusrp->lug_namelen), newusrp, lug_namehash);
TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
nfsrv_usercnt++;
} else
FREE((caddr_t)newusrp, M_NFSUSERGROUP);
NFSUNLOCKNAMEID();
return (error);
}
/*
* Remove a user/group name element.
*/
static void
nfsrv_removeuser(struct nfsusrgrp *usrp)
{
NFSNAMEIDREQUIRED();
LIST_REMOVE(usrp, lug_numhash);
LIST_REMOVE(usrp, lug_namehash);
TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
nfsrv_usercnt--;
FREE((caddr_t)usrp, M_NFSUSERGROUP);
}
/*
* This function scans a byte string and checks for UTF-8 compliance.
* It returns 0 if it conforms and NFSERR_INVAL if not.
*/
APPLESTATIC int
nfsrv_checkutf8(u_int8_t *cp, int len)
{
u_int32_t val = 0x0;
int cnt = 0, gotd = 0, shift = 0;
u_int8_t byte;
static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
/*
* Here are what the variables are used for:
* val - the calculated value of a multibyte char, used to check
* that it was coded with the correct range
* cnt - the number of 10xxxxxx bytes to follow
* gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
* shift - lower order bits of range (ie. "val >> shift" should
* not be 0, in other words, dividing by the lower bound
* of the range should get a non-zero value)
* byte - used to calculate cnt
*/
while (len > 0) {
if (cnt > 0) {
/* This handles the 10xxxxxx bytes */
if ((*cp & 0xc0) != 0x80 ||
(gotd && (*cp & 0x20)))
return (NFSERR_INVAL);
gotd = 0;
val <<= 6;
val |= (*cp & 0x3f);
cnt--;
if (cnt == 0 && (val >> shift) == 0x0)
return (NFSERR_INVAL);
} else if (*cp & 0x80) {
/* first byte of multi byte char */
byte = *cp;
while ((byte & 0x40) && cnt < 6) {
cnt++;
byte <<= 1;
}
if (cnt == 0 || cnt == 6)
return (NFSERR_INVAL);
val = (*cp & (0x3f >> cnt));
shift = utf8_shift[cnt - 1];
if (cnt == 2 && val == 0xd)
/* Check for the 0xd800-0xdfff case */
gotd = 1;
}
cp++;
len--;
}
if (cnt > 0)
return (NFSERR_INVAL);
return (0);
}
/*
* Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
* strings, one with the root path in it and the other with the list of
* locations. The list is in the same format as is found in nfr_refs.
* It is a "," separated list of entries, where each of them is of the
* form <server>:<rootpath>. For example
* "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
* The nilp argument is set to 1 for the special case of a null fs_root
* and an empty server list.
* It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
* number of xdr bytes parsed in sump.
*/
static int
nfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
int *sump, int *nilp)
{
u_int32_t *tl;
u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
int i, j, len, stringlen, cnt, slen, siz, xdrsum, error, nsrv;
struct list {
SLIST_ENTRY(list) next;
int len;
u_char host[1];
} *lsp, *nlsp;
SLIST_HEAD(, list) head;
*fsrootp = NULL;
*srvp = NULL;
*nilp = 0;
/*
* Get the fs_root path and check for the special case of null path
* and 0 length server list.
*/
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
len = fxdr_unsigned(int, *tl);
if (len < 0 || len > 10240)
return (NFSERR_BADXDR);
if (len == 0) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (*tl != 0)
return (NFSERR_BADXDR);
*nilp = 1;
*sump = 2 * NFSX_UNSIGNED;
return (0);
}
cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
error = nfsrv_mtostr(nd, cp, len);
if (!error) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
cnt = fxdr_unsigned(int, *tl);
if (cnt <= 0)
error = NFSERR_BADXDR;
}
if (error) {
free(cp, M_NFSSTRING);
return (error);
}
/*
* Now, loop through the location list and make up the srvlist.
*/
xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
slen = 1024;
siz = 0;
for (i = 0; i < cnt; i++) {
SLIST_INIT(&head);
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
nsrv = fxdr_unsigned(int, *tl);
if (nsrv <= 0) {
free(cp, M_NFSSTRING);
free(cp2, M_NFSSTRING);
return (NFSERR_BADXDR);
}
/*
* Handle the first server by putting it in the srvstr.
*/
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
len = fxdr_unsigned(int, *tl);
if (len <= 0 || len > 1024) {
free(cp, M_NFSSTRING);
free(cp2, M_NFSSTRING);
return (NFSERR_BADXDR);
}
nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
if (cp3 != cp2) {
*cp3++ = ',';
siz++;
}
error = nfsrv_mtostr(nd, cp3, len);
if (error) {
free(cp, M_NFSSTRING);
free(cp2, M_NFSSTRING);
return (error);
}
cp3 += len;
*cp3++ = ':';
siz += (len + 1);
xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
for (j = 1; j < nsrv; j++) {
/*
* Yuck, put them in an slist and process them later.
*/
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
len = fxdr_unsigned(int, *tl);
if (len <= 0 || len > 1024) {
free(cp, M_NFSSTRING);
free(cp2, M_NFSSTRING);
return (NFSERR_BADXDR);
}
lsp = (struct list *)malloc(sizeof (struct list)
+ len, M_TEMP, M_WAITOK);
error = nfsrv_mtostr(nd, lsp->host, len);
if (error) {
free(cp, M_NFSSTRING);
free(cp2, M_NFSSTRING);
return (error);
}
xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
lsp->len = len;
SLIST_INSERT_HEAD(&head, lsp, next);
}
/*
* Finally, we can get the path.
*/
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
len = fxdr_unsigned(int, *tl);
if (len <= 0 || len > 1024) {
free(cp, M_NFSSTRING);
free(cp2, M_NFSSTRING);
return (NFSERR_BADXDR);
}
nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
error = nfsrv_mtostr(nd, cp3, len);
if (error) {
free(cp, M_NFSSTRING);
free(cp2, M_NFSSTRING);
return (error);
}
xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
str = cp3;
stringlen = len;
cp3 += len;
siz += len;
SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
&cp2, &cp3, &slen);
*cp3++ = ',';
NFSBCOPY(lsp->host, cp3, lsp->len);
cp3 += lsp->len;
*cp3++ = ':';
NFSBCOPY(str, cp3, stringlen);
cp3 += stringlen;
*cp3 = '\0';
siz += (lsp->len + stringlen + 2);
free((caddr_t)lsp, M_TEMP);
}
}
*fsrootp = cp;
*srvp = cp2;
*sump = xdrsum;
return (0);
nfsmout:
if (cp != NULL)
free(cp, M_NFSSTRING);
if (cp2 != NULL)
free(cp2, M_NFSSTRING);
return (error);
}
/*
* Make the malloc'd space large enough. This is a pain, but the xdr
* doesn't set an upper bound on the side, so...
*/
static void
nfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
{
u_char *cp;
int i;
if (siz <= *slenp)
return;
cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
NFSBCOPY(*cpp, cp, *slenp);
free(*cpp, M_NFSSTRING);
i = *cpp2 - *cpp;
*cpp = cp;
*cpp2 = cp + i;
*slenp = siz + 1024;
}
/*
* Initialize the reply header data structures.
*/
APPLESTATIC void
nfsrvd_rephead(struct nfsrv_descript *nd)
{
mbuf_t mreq;
/*
* If this is a big reply, use a cluster.
*/
if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
nfs_bigreply[nd->nd_procnum]) {
NFSMCLGET(mreq, M_WAIT);
nd->nd_mreq = mreq;
nd->nd_mb = mreq;
} else {
NFSMGET(mreq);
nd->nd_mreq = mreq;
nd->nd_mb = mreq;
}
nd->nd_bpos = NFSMTOD(mreq, caddr_t);
mbuf_setlen(mreq, 0);
if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
}
/*
* Lock a socket against others.
* Currently used to serialize connect/disconnect attempts.
*/
int
newnfs_sndlock(int *flagp)
{
struct timespec ts;
NFSLOCKSOCK();
while (*flagp & NFSR_SNDLOCK) {
*flagp |= NFSR_WANTSND;
ts.tv_sec = 0;
ts.tv_nsec = 0;
(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
PZERO - 1, "nfsndlck", &ts);
}
*flagp |= NFSR_SNDLOCK;
NFSUNLOCKSOCK();
return (0);
}
/*
* Unlock the stream socket for others.
*/
void
newnfs_sndunlock(int *flagp)
{
NFSLOCKSOCK();
if ((*flagp & NFSR_SNDLOCK) == 0)
panic("nfs sndunlock");
*flagp &= ~NFSR_SNDLOCK;
if (*flagp & NFSR_WANTSND) {
*flagp &= ~NFSR_WANTSND;
wakeup((caddr_t)flagp);
}
NFSUNLOCKSOCK();
}
Index: stable/8/sys/fs/nfs/nfs_var.h
===================================================================
--- stable/8/sys/fs/nfs/nfs_var.h (revision 221216)
+++ stable/8/sys/fs/nfs/nfs_var.h (revision 221217)
@@ -1,600 +1,601 @@
/*-
* 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.
* 4. 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.
*
* $FreeBSD$
*/
/*
* XXX needs <nfs/rpcv2.h> and <nfs/nfs.h> because of typedefs
*/
struct uio;
struct ucred;
struct nfscred;
NFSPROC_T;
struct buf;
struct nfs_diskless;
struct sockaddr_in;
struct nfs_dlmount;
struct file;
struct nfsmount;
struct socket;
struct nfsreq;
struct nfssockreq;
struct vattr;
struct nameidata;
struct nfsnode;
struct nfsfh;
struct sillyrename;
struct componentname;
struct nfsd_srvargs;
struct nfsrv_descript;
struct nfs_fattr;
union nethostaddr;
struct nfsstate;
struct nfslock;
struct nfsclient;
struct nfslockconflict;
struct nfsd_idargs;
struct nfsd_clid;
struct nfsusrgrp;
struct nfsclowner;
struct nfsclopen;
struct nfsclopenhead;
struct nfsclclient;
struct nfscllockowner;
struct nfscllock;
struct nfscldeleg;
struct nfsv4lock;
struct nfsvattr;
struct nfs_vattr;
struct NFSSVCARGS;
#ifdef __FreeBSD__
NFS_ACCESS_ARGS;
NFS_OPEN_ARGS;
NFS_GETATTR_ARGS;
NFS_LOOKUP_ARGS;
NFS_READDIR_ARGS;
#endif
/* nfs_nfsdstate.c */
int nfsrv_setclient(struct nfsrv_descript *, struct nfsclient **,
nfsquad_t *, nfsquad_t *, NFSPROC_T *);
int nfsrv_getclient(nfsquad_t, int, struct nfsclient **, nfsquad_t,
struct nfsrv_descript *, NFSPROC_T *);
int nfsrv_adminrevoke(struct nfsd_clid *, NFSPROC_T *);
void nfsrv_dumpclients(struct nfsd_dumpclients *, int);
void nfsrv_dumplocks(vnode_t, struct nfsd_dumplocks *, int, NFSPROC_T *);
int nfsrv_lockctrl(vnode_t, struct nfsstate **,
struct nfslock **, struct nfslockconflict *, nfsquad_t, nfsv4stateid_t *,
struct nfsexstuff *, struct nfsrv_descript *, NFSPROC_T *);
int nfsrv_openctrl(struct nfsrv_descript *, vnode_t,
struct nfsstate **, nfsquad_t, nfsv4stateid_t *, nfsv4stateid_t *,
u_int32_t *, struct nfsexstuff *, NFSPROC_T *, u_quad_t);
int nfsrv_opencheck(nfsquad_t, nfsv4stateid_t *, struct nfsstate *,
vnode_t, struct nfsrv_descript *, NFSPROC_T *, int);
int nfsrv_openupdate(vnode_t, struct nfsstate *, nfsquad_t,
nfsv4stateid_t *, struct nfsrv_descript *, NFSPROC_T *);
int nfsrv_delegupdate(nfsquad_t, nfsv4stateid_t *, vnode_t, int,
struct ucred *, NFSPROC_T *);
int nfsrv_releaselckown(struct nfsstate *, nfsquad_t, NFSPROC_T *);
void nfsrv_zapclient(struct nfsclient *, NFSPROC_T *);
int nfssvc_idname(struct nfsd_idargs *);
void nfsrv_servertimer(void);
int nfsrv_getclientipaddr(struct nfsrv_descript *, struct nfsclient *);
void nfsrv_setupstable(NFSPROC_T *);
void nfsrv_updatestable(NFSPROC_T *);
void nfsrv_writestable(u_char *, int, int, NFSPROC_T *);
void nfsrv_throwawayopens(NFSPROC_T *);
int nfsrv_checkremove(vnode_t, int, NFSPROC_T *);
void nfsd_recalldelegation(vnode_t, NFSPROC_T *);
void nfsd_disabledelegation(vnode_t, NFSPROC_T *);
int nfsrv_checksetattr(vnode_t, struct nfsrv_descript *,
nfsv4stateid_t *, struct nfsvattr *, nfsattrbit_t *, struct nfsexstuff *,
NFSPROC_T *);
int nfsrv_checkgetattr(struct nfsrv_descript *, vnode_t,
struct nfsvattr *, nfsattrbit_t *, struct ucred *, NFSPROC_T *);
int nfsrv_nfsuserdport(u_short, NFSPROC_T *);
void nfsrv_nfsuserddelport(void);
void nfsrv_throwawayallstate(NFSPROC_T *);
/* nfs_nfsdserv.c */
int nfsrvd_access(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_getattr(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_setattr(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_lookup(struct nfsrv_descript *, int,
vnode_t, vnode_t *, fhandle_t *, NFSPROC_T *,
struct nfsexstuff *);
int nfsrvd_readlink(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_read(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_write(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_create(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_mknod(struct nfsrv_descript *, int,
vnode_t, vnode_t *, fhandle_t *, NFSPROC_T *,
struct nfsexstuff *);
int nfsrvd_remove(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_rename(struct nfsrv_descript *, int,
vnode_t, vnode_t, NFSPROC_T *, struct nfsexstuff *,
struct nfsexstuff *);
int nfsrvd_link(struct nfsrv_descript *, int,
vnode_t, vnode_t, NFSPROC_T *, struct nfsexstuff *,
struct nfsexstuff *);
int nfsrvd_symlink(struct nfsrv_descript *, int,
vnode_t, vnode_t *, fhandle_t *, NFSPROC_T *,
struct nfsexstuff *);
int nfsrvd_mkdir(struct nfsrv_descript *, int,
vnode_t, vnode_t *, fhandle_t *, NFSPROC_T *,
struct nfsexstuff *);
int nfsrvd_readdir(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_readdirplus(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_commit(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_statfs(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_fsinfo(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_close(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_delegpurge(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_delegreturn(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_getfh(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_lock(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_lockt(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_locku(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_openconfirm(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_opendowngrade(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_renew(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_secinfo(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_setclientid(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_setclientidcfrm(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_verify(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_open(struct nfsrv_descript *, int,
vnode_t, vnode_t *, fhandle_t *, NFSPROC_T *,
struct nfsexstuff *);
int nfsrvd_openattr(struct nfsrv_descript *, int,
vnode_t, vnode_t *, fhandle_t *, NFSPROC_T *,
struct nfsexstuff *);
int nfsrvd_releaselckown(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
int nfsrvd_pathconf(struct nfsrv_descript *, int,
vnode_t, NFSPROC_T *, struct nfsexstuff *);
/* nfs_nfsdsocket.c */
void nfsrvd_rephead(struct nfsrv_descript *);
void nfsrvd_dorpc(struct nfsrv_descript *, int, NFSPROC_T *);
/* nfs_nfsdcache.c */
void nfsrvd_initcache(void);
int nfsrvd_getcache(struct nfsrv_descript *, struct socket *);
struct nfsrvcache *nfsrvd_updatecache(struct nfsrv_descript *,
struct socket *);
void nfsrvd_sentcache(struct nfsrvcache *, struct socket *, int);
void nfsrvd_cleancache(void);
void nfsrvd_refcache(struct nfsrvcache *);
void nfsrvd_derefcache(struct nfsrvcache *);
void nfsrvd_delcache(struct nfsrvcache *);
/* nfs_commonsubs.c */
void newnfs_init(void);
int nfsaddr_match(int, union nethostaddr *, NFSSOCKADDR_T);
int nfsaddr2_match(NFSSOCKADDR_T, NFSSOCKADDR_T);
int nfsm_strtom(struct nfsrv_descript *, const char *, int);
int nfsm_mbufuio(struct nfsrv_descript *, struct uio *, int);
int nfsm_fhtom(struct nfsrv_descript *, u_int8_t *, int, int);
int nfsm_advance(struct nfsrv_descript *, int, int);
void *nfsm_dissct(struct nfsrv_descript *, int);
void newnfs_trimleading(struct nfsrv_descript *);
void newnfs_trimtrailing(struct nfsrv_descript *, mbuf_t,
caddr_t);
void newnfs_copycred(struct nfscred *, struct ucred *);
void newnfs_copyincred(struct ucred *, struct nfscred *);
int nfsrv_dissectacl(struct nfsrv_descript *, NFSACL_T *, int *,
int *, NFSPROC_T *);
int nfsrv_getattrbits(struct nfsrv_descript *, nfsattrbit_t *, int *,
int *);
int nfsv4_loadattr(struct nfsrv_descript *, vnode_t,
struct nfsvattr *, struct nfsfh **, fhandle_t *, int,
struct nfsv3_pathconf *, struct statfs *, struct nfsstatfs *,
struct nfsfsinfo *, NFSACL_T *,
int, int *, u_int32_t *, u_int32_t *, NFSPROC_T *, struct ucred *);
int nfsv4_lock(struct nfsv4lock *, int, int *, void *);
void nfsv4_unlock(struct nfsv4lock *, int);
void nfsv4_relref(struct nfsv4lock *);
void nfsv4_getref(struct nfsv4lock *, int *, void *);
int nfsv4_getref_nonblock(struct nfsv4lock *);
int nfsv4_testlock(struct nfsv4lock *);
int nfsrv_mtostr(struct nfsrv_descript *, char *, int);
int nfsrv_checkutf8(u_int8_t *, int);
int newnfs_sndlock(int *);
void newnfs_sndunlock(int *);
/* nfs_clcomsubs.c */
void nfsm_uiombuf(struct nfsrv_descript *, struct uio *, int);
void nfscl_reqstart(struct nfsrv_descript *, int, struct nfsmount *,
u_int8_t *, int, u_int32_t **);
nfsuint64 *nfscl_getcookie(struct nfsnode *, off_t off, int);
void nfscl_fillsattr(struct nfsrv_descript *, struct vattr *,
vnode_t, int, u_int32_t);
u_int8_t *nfscl_getmyip(struct nfsmount *, int *);
int nfsm_getfh(struct nfsrv_descript *, struct nfsfh **);
int nfscl_mtofh(struct nfsrv_descript *, struct nfsfh **,
struct nfsvattr *, int *);
int nfscl_postop_attr(struct nfsrv_descript *, struct nfsvattr *, int *,
void *);
int nfscl_wcc_data(struct nfsrv_descript *, vnode_t,
struct nfsvattr *, int *, int *, void *);
int nfsm_loadattr(struct nfsrv_descript *, struct nfsvattr *);
int nfscl_request(struct nfsrv_descript *, vnode_t,
NFSPROC_T *, struct ucred *, void *);
void nfsm_stateidtom(struct nfsrv_descript *, nfsv4stateid_t *, int);
/* nfs_nfsdsubs.c */
void nfsd_fhtovp(struct nfsrv_descript *, struct nfsrvfh *, int,
vnode_t *, struct nfsexstuff *,
mount_t *, int, NFSPROC_T *);
int nfsd_excred(struct nfsrv_descript *, struct nfsexstuff *, struct ucred *);
int nfsrv_mtofh(struct nfsrv_descript *, struct nfsrvfh *);
int nfsrv_putattrbit(struct nfsrv_descript *, nfsattrbit_t *);
void nfsrv_wcc(struct nfsrv_descript *, int, struct nfsvattr *, int,
struct nfsvattr *);
int nfsv4_fillattr(struct nfsrv_descript *, struct mount *, vnode_t, NFSACL_T *,
struct vattr *, fhandle_t *, int, nfsattrbit_t *,
- struct ucred *, NFSPROC_T *, int, int, int, uint64_t);
+ struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t);
void nfsrv_fillattr(struct nfsrv_descript *, struct nfsvattr *);
void nfsrv_adj(mbuf_t, int, int);
void nfsrv_postopattr(struct nfsrv_descript *, int, struct nfsvattr *);
int nfsd_errmap(struct nfsrv_descript *);
void nfsv4_uidtostr(uid_t, u_char **, int *, NFSPROC_T *);
int nfsv4_strtouid(u_char *, int, uid_t *, NFSPROC_T *);
void nfsv4_gidtostr(gid_t, u_char **, int *, NFSPROC_T *);
int nfsv4_strtogid(u_char *, int, gid_t *, NFSPROC_T *);
int nfsrv_checkuidgid(struct nfsrv_descript *, struct nfsvattr *);
void nfsrv_fixattr(struct nfsrv_descript *, vnode_t,
struct nfsvattr *, NFSACL_T *, NFSPROC_T *, nfsattrbit_t *,
struct nfsexstuff *);
int nfsrv_errmoved(int);
int nfsrv_putreferralattr(struct nfsrv_descript *, nfsattrbit_t *,
struct nfsreferral *, int, int *);
int nfsrv_parsename(struct nfsrv_descript *, char *, u_long *,
NFSPATHLEN_T *);
void nfsd_init(void);
int nfsd_checkrootexp(struct nfsrv_descript *);
/* nfs_clvfsops.c */
/* nfs_commonport.c */
int nfsrv_checksockseqnum(struct socket *, tcp_seq);
int nfsrv_getsockseqnum(struct socket *, tcp_seq *);
int nfsrv_getsocksndseq(struct socket *, tcp_seq *, tcp_seq *);
int nfsrv_lookupfilename(struct nameidata *, char *, NFSPROC_T *);
void nfsrv_object_create(vnode_t, NFSPROC_T *);
int nfsrv_mallocmget_limit(void);
int nfsvno_v4rootexport(struct nfsrv_descript *);
void newnfs_portinit(void);
struct ucred *newnfs_getcred(void);
void newnfs_setroot(struct ucred *);
int nfs_catnap(int, int, const char *);
struct nfsreferral *nfsv4root_getreferral(vnode_t, vnode_t, u_int32_t);
int nfsrv_atroot(vnode_t, long *);
void newnfs_timer(void *);
+int nfs_supportsnfsv4acls(vnode_t);
/* nfs_commonacl.c */
int nfsrv_dissectace(struct nfsrv_descript *, struct acl_entry *,
int *, int *, NFSPROC_T *);
int nfsrv_buildacl(struct nfsrv_descript *, NFSACL_T *, enum vtype,
NFSPROC_T *);
int nfsrv_setacl(vnode_t, NFSACL_T *, struct ucred *,
NFSPROC_T *);
int nfsrv_compareacl(NFSACL_T *, NFSACL_T *);
/* nfs_clrpcops.c */
int nfsrpc_null(vnode_t, struct ucred *, NFSPROC_T *);
int nfsrpc_access(vnode_t, int, struct ucred *, NFSPROC_T *,
struct nfsvattr *, int *);
int nfsrpc_accessrpc(vnode_t, u_int32_t, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *, u_int32_t *, void *);
int nfsrpc_open(vnode_t, int, struct ucred *, NFSPROC_T *);
int nfsrpc_openrpc(struct nfsmount *, vnode_t, u_int8_t *, int, u_int8_t *, int,
u_int32_t, struct nfsclopen *, u_int8_t *, int, struct nfscldeleg **, int,
u_int32_t, struct ucred *, NFSPROC_T *, int, int);
int nfsrpc_opendowngrade(vnode_t, u_int32_t, struct nfsclopen *,
struct ucred *, NFSPROC_T *);
int nfsrpc_close(vnode_t, int, NFSPROC_T *);
int nfsrpc_closerpc(struct nfsrv_descript *, struct nfsmount *,
struct nfsclopen *, struct ucred *, NFSPROC_T *, int);
int nfsrpc_openconfirm(vnode_t, u_int8_t *, int, struct nfsclopen *,
struct ucred *, NFSPROC_T *);
int nfsrpc_setclient(struct nfsmount *, struct nfsclclient *,
struct ucred *, NFSPROC_T *);
int nfsrpc_getattr(vnode_t, struct ucred *, NFSPROC_T *,
struct nfsvattr *, void *);
int nfsrpc_getattrnovp(struct nfsmount *, u_int8_t *, int, int,
struct ucred *, NFSPROC_T *, struct nfsvattr *, u_int64_t *);
int nfsrpc_setattr(vnode_t, struct vattr *, NFSACL_T *, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *, void *);
int nfsrpc_lookup(vnode_t, char *, int, struct ucred *, NFSPROC_T *,
struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *, int *,
void *);
int nfsrpc_readlink(vnode_t, struct uio *, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *, void *);
int nfsrpc_read(vnode_t, struct uio *, struct ucred *, NFSPROC_T *,
struct nfsvattr *, int *, void *);
int nfsrpc_write(vnode_t, struct uio *, int *, u_char *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *, int);
int nfsrpc_mknod(vnode_t, char *, int, struct vattr *, u_int32_t,
enum vtype, struct ucred *, NFSPROC_T *, struct nfsvattr *,
struct nfsvattr *, struct nfsfh **, int *, int *, void *);
int nfsrpc_create(vnode_t, char *, int, struct vattr *, nfsquad_t,
int, struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
struct nfsfh **, int *, int *, void *);
int nfsrpc_remove(vnode_t, char *, int, vnode_t, struct ucred *, NFSPROC_T *,
struct nfsvattr *, int *, void *);
int nfsrpc_rename(vnode_t, vnode_t, char *, int, vnode_t, vnode_t, char *, int,
struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
int *, int *, void *, void *);
int nfsrpc_link(vnode_t, vnode_t, char *, int,
struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
int *, int *, void *);
int nfsrpc_symlink(vnode_t, char *, int, char *, struct vattr *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
struct nfsfh **, int *, int *, void *);
int nfsrpc_mkdir(vnode_t, char *, int, struct vattr *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
struct nfsfh **, int *, int *, void *);
int nfsrpc_rmdir(vnode_t, char *, int, struct ucred *, NFSPROC_T *,
struct nfsvattr *, int *, void *);
int nfsrpc_readdir(vnode_t, struct uio *, nfsuint64 *, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *, int *, void *);
int nfsrpc_readdirplus(vnode_t, struct uio *, nfsuint64 *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, int *, void *);
int nfsrpc_commit(vnode_t, u_quad_t, int, struct ucred *,
NFSPROC_T *, u_char *, struct nfsvattr *, int *, void *);
int nfsrpc_advlock(vnode_t, off_t, int, struct flock *, int,
struct ucred *, NFSPROC_T *);
int nfsrpc_lockt(struct nfsrv_descript *, vnode_t,
struct nfsclclient *, u_int64_t, u_int64_t, struct flock *,
struct ucred *, NFSPROC_T *);
int nfsrpc_lock(struct nfsrv_descript *, struct nfsmount *, vnode_t,
u_int8_t *, int, struct nfscllockowner *, int, int, u_int64_t,
u_int64_t, short, struct ucred *, NFSPROC_T *, int);
int nfsrpc_statfs(vnode_t, struct nfsstatfs *, struct nfsfsinfo *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
int nfsrpc_fsinfo(vnode_t, struct nfsfsinfo *, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *, void *);
int nfsrpc_pathconf(vnode_t, struct nfsv3_pathconf *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
int nfsrpc_renew(struct nfsclclient *, struct ucred *,
NFSPROC_T *);
int nfsrpc_rellockown(struct nfsmount *, struct nfscllockowner *,
struct ucred *, NFSPROC_T *);
int nfsrpc_getdirpath(struct nfsmount *, u_char *, struct ucred *,
NFSPROC_T *);
int nfsrpc_delegreturn(struct nfscldeleg *, struct ucred *,
struct nfsmount *, NFSPROC_T *, int);
int nfsrpc_getacl(vnode_t, struct ucred *, NFSPROC_T *, NFSACL_T *, void *);
int nfsrpc_setacl(vnode_t, struct ucred *, NFSPROC_T *, NFSACL_T *, void *);
/* nfs_clstate.c */
int nfscl_open(vnode_t, u_int8_t *, int, u_int32_t, int,
struct ucred *, NFSPROC_T *, struct nfsclowner **, struct nfsclopen **,
int *, int *, int);
int nfscl_getstateid(vnode_t, u_int8_t *, int, u_int32_t, struct ucred *,
NFSPROC_T *, nfsv4stateid_t *, void **);
void nfscl_ownerrelease(struct nfsclowner *, int, int, int);
void nfscl_openrelease(struct nfsclopen *, int, int);
int nfscl_getcl(vnode_t, struct ucred *, NFSPROC_T *,
struct nfsclclient **);
struct nfsclclient *nfscl_findcl(struct nfsmount *);
void nfscl_clientrelease(struct nfsclclient *);
void nfscl_freelock(struct nfscllock *, int);
int nfscl_getbytelock(vnode_t, u_int64_t, u_int64_t, short,
struct ucred *, NFSPROC_T *, struct nfsclclient *, int, u_int8_t *,
u_int8_t *, struct nfscllockowner **, int *, int *);
int nfscl_relbytelock(vnode_t, u_int64_t, u_int64_t,
struct ucred *, NFSPROC_T *, int, struct nfsclclient *,
struct nfscllockowner **, int *);
int nfscl_checkwritelocked(vnode_t, struct flock *,
struct ucred *, NFSPROC_T *);
void nfscl_lockrelease(struct nfscllockowner *, int, int);
void nfscl_fillclid(u_int64_t, char *, u_int8_t *, u_int16_t);
void nfscl_filllockowner(NFSPROC_T *, u_int8_t *);
void nfscl_freeopen(struct nfsclopen *, int);
void nfscl_umount(struct nfsmount *, NFSPROC_T *);
void nfscl_renewthread(struct nfsclclient *, NFSPROC_T *);
void nfscl_initiate_recovery(struct nfsclclient *);
int nfscl_hasexpired(struct nfsclclient *, u_int32_t, NFSPROC_T *);
void nfscl_dumpstate(struct nfsmount *, int, int, int, int);
void nfscl_dupopen(vnode_t, int);
int nfscl_getclose(vnode_t, struct nfsclclient **);
int nfscl_doclose(vnode_t, struct nfsclclient **, NFSPROC_T *);
void nfsrpc_doclose(struct nfsmount *, struct nfsclopen *, NFSPROC_T *);
int nfscl_deleg(mount_t, struct nfsclclient *, u_int8_t *, int,
struct ucred *, NFSPROC_T *, struct nfscldeleg **);
void nfscl_lockinit(struct nfsv4lock *);
void nfscl_lockexcl(struct nfsv4lock *, void *);
void nfscl_lockunlock(struct nfsv4lock *);
void nfscl_lockderef(struct nfsv4lock *);
void nfscl_docb(struct nfsrv_descript *, NFSPROC_T *);
void nfscl_releasealllocks(struct nfsclclient *, vnode_t, NFSPROC_T *);
int nfscl_lockt(vnode_t, struct nfsclclient *, u_int64_t,
u_int64_t, struct flock *, NFSPROC_T *);
int nfscl_mustflush(vnode_t);
int nfscl_nodeleg(vnode_t, int);
int nfscl_removedeleg(vnode_t, NFSPROC_T *, nfsv4stateid_t *);
int nfscl_getref(struct nfsmount *);
void nfscl_relref(struct nfsmount *);
int nfscl_renamedeleg(vnode_t, nfsv4stateid_t *, int *, vnode_t,
nfsv4stateid_t *, int *, NFSPROC_T *);
void nfscl_reclaimnode(vnode_t);
void nfscl_newnode(vnode_t);
void nfscl_delegmodtime(vnode_t);
void nfscl_deleggetmodtime(vnode_t, struct timespec *);
int nfscl_tryclose(struct nfsclopen *, struct ucred *,
struct nfsmount *, NFSPROC_T *);
void nfscl_cleanup(NFSPROC_T *);
/* nfs_clport.c */
int nfscl_nget(mount_t, vnode_t, struct nfsfh *,
struct componentname *, NFSPROC_T *, struct nfsnode **, void *);
NFSPROC_T *nfscl_getparent(NFSPROC_T *);
void nfscl_start_renewthread(struct nfsclclient *);
void nfscl_loadsbinfo(struct nfsmount *, struct nfsstatfs *, void *);
void nfscl_loadfsinfo (struct nfsmount *, struct nfsfsinfo *);
void nfscl_delegreturn(struct nfscldeleg *, int, struct nfsmount *,
struct ucred *, NFSPROC_T *);
void nfsrvd_cbinit(int);
int nfscl_checksattr(struct vattr *, struct nfsvattr *);
int nfscl_ngetreopen(mount_t, u_int8_t *, int, NFSPROC_T *,
struct nfsnode **);
int nfscl_procdoesntexist(u_int8_t *);
int nfscl_maperr(NFSPROC_T *, int, uid_t, gid_t);
/* nfs_clsubs.c */
void nfscl_init(void);
/* nfs_clbio.c */
int ncl_flush(vnode_t, int, struct ucred *, NFSPROC_T *, int, int);
/* nfs_clnode.c */
void ncl_invalcaches(vnode_t);
/* nfs_nfsdport.c */
int nfsvno_getattr(vnode_t, struct nfsvattr *, struct ucred *,
NFSPROC_T *, int);
int nfsvno_setattr(vnode_t, struct nfsvattr *, struct ucred *,
NFSPROC_T *, struct nfsexstuff *);
int nfsvno_getfh(vnode_t, fhandle_t *, NFSPROC_T *);
int nfsvno_accchk(vnode_t, accmode_t, struct ucred *,
struct nfsexstuff *, NFSPROC_T *, int, int, u_int32_t *);
int nfsvno_namei(struct nfsrv_descript *, struct nameidata *,
vnode_t, int, struct nfsexstuff *, NFSPROC_T *, vnode_t *);
void nfsvno_setpathbuf(struct nameidata *, char **, u_long **);
void nfsvno_relpathbuf(struct nameidata *);
int nfsvno_readlink(vnode_t, struct ucred *, NFSPROC_T *, mbuf_t *,
mbuf_t *, int *);
int nfsvno_read(vnode_t, off_t, int, struct ucred *, NFSPROC_T *,
mbuf_t *, mbuf_t *);
int nfsvno_write(vnode_t, off_t, int, int, int, mbuf_t,
char *, struct ucred *, NFSPROC_T *);
int nfsvno_createsub(struct nfsrv_descript *, struct nameidata *,
vnode_t *, struct nfsvattr *, int *, int32_t *, NFSDEV_T, NFSPROC_T *,
struct nfsexstuff *);
int nfsvno_mknod(struct nameidata *, struct nfsvattr *, struct ucred *,
NFSPROC_T *);
int nfsvno_mkdir(struct nameidata *,
struct nfsvattr *, uid_t, struct ucred *, NFSPROC_T *,
struct nfsexstuff *);
int nfsvno_symlink(struct nameidata *, struct nfsvattr *, char *, int, int,
uid_t, struct ucred *, NFSPROC_T *, struct nfsexstuff *);
int nfsvno_getsymlink(struct nfsrv_descript *, struct nfsvattr *,
NFSPROC_T *, char **, int *);
int nfsvno_removesub(struct nameidata *, int, struct ucred *, NFSPROC_T *,
struct nfsexstuff *);
int nfsvno_rmdirsub(struct nameidata *, int, struct ucred *, NFSPROC_T *,
struct nfsexstuff *);
int nfsvno_rename(struct nameidata *, struct nameidata *, u_int32_t,
u_int32_t, struct ucred *, NFSPROC_T *);
int nfsvno_link(struct nameidata *, vnode_t, struct ucred *,
NFSPROC_T *, struct nfsexstuff *);
int nfsvno_fsync(vnode_t, u_int64_t, int, struct ucred *, NFSPROC_T *);
int nfsvno_statfs(vnode_t, struct statfs *);
void nfsvno_getfs(struct nfsfsinfo *, int);
void nfsvno_open(struct nfsrv_descript *, struct nameidata *, nfsquad_t,
nfsv4stateid_t *, struct nfsstate *, int *, struct nfsvattr *, int32_t *,
int, NFSACL_T *, nfsattrbit_t *, struct ucred *, NFSPROC_T *,
struct nfsexstuff *, vnode_t *);
void nfsvno_updfilerev(vnode_t, struct nfsvattr *, struct ucred *,
NFSPROC_T *);
int nfsvno_fillattr(struct nfsrv_descript *, struct mount *, vnode_t,
struct nfsvattr *, fhandle_t *, int, nfsattrbit_t *,
- struct ucred *, NFSPROC_T *, int, int, int, uint64_t);
+ struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t);
int nfsrv_sattr(struct nfsrv_descript *, struct nfsvattr *, nfsattrbit_t *,
NFSACL_T *, NFSPROC_T *);
int nfsv4_sattr(struct nfsrv_descript *, struct nfsvattr *, nfsattrbit_t *,
NFSACL_T *, NFSPROC_T *);
int nfsvno_checkexp(mount_t, NFSSOCKADDR_T, struct nfsexstuff *,
struct ucred **);
int nfsvno_fhtovp(mount_t, fhandle_t *, NFSSOCKADDR_T, int,
vnode_t *, struct nfsexstuff *, struct ucred **);
int nfsvno_pathconf(vnode_t, int, register_t *, struct ucred *,
NFSPROC_T *);
vnode_t nfsvno_getvp(fhandle_t *);
int nfsvno_advlock(vnode_t, int, u_int64_t, u_int64_t, NFSPROC_T *);
int nfsrv_v4rootexport(void *, struct ucred *, NFSPROC_T *);
int nfsvno_testexp(struct nfsrv_descript *, struct nfsexstuff *);
uint32_t nfsrv_hashfh(fhandle_t *);
void nfsrv_backupstable(void);
/* nfs_commonkrpc.c */
int newnfs_nmcancelreqs(struct nfsmount *);
void newnfs_set_sigmask(struct thread *, sigset_t *);
void newnfs_restore_sigmask(struct thread *, sigset_t *);
int newnfs_msleep(struct thread *, void *, struct mtx *, int, char *, int);
int newnfs_request(struct nfsrv_descript *, struct nfsmount *,
struct nfsclient *, struct nfssockreq *, vnode_t, NFSPROC_T *,
struct ucred *, u_int32_t, u_int32_t, u_char *, int, u_int64_t *);
int newnfs_connect(struct nfsmount *, struct nfssockreq *,
struct ucred *, NFSPROC_T *, int);
void newnfs_disconnect(struct nfssockreq *);
int newnfs_sigintr(struct nfsmount *, NFSPROC_T *);
/* nfs_nfsdkrpc.c */
int nfsrvd_addsock(struct file *);
int nfsrvd_nfsd(NFSPROC_T *, struct nfsd_nfsd_args *);
void nfsrvd_init(int);
/* nfs_clkrpc.c */
int nfscbd_addsock(struct file *);
int nfscbd_nfsd(NFSPROC_T *, struct nfsd_nfscbd_args *);
Index: stable/8/sys/fs/nfs/nfsport.h
===================================================================
--- stable/8/sys/fs/nfs/nfsport.h (revision 221216)
+++ stable/8/sys/fs/nfs/nfsport.h (revision 221217)
@@ -1,914 +1,908 @@
/*-
* 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.
* 4. 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.
*
* $FreeBSD$
*/
#ifndef _NFS_NFSPORT_H_
#define _NFS_NFSPORT_H_
/*
* In general, I'm not fond of #includes in .h files, but this seems
* to be the cleanest way to handle #include files for the ports.
*/
#ifdef _KERNEL
#include <sys/unistd.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/dirent.h>
#include <sys/domain.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lockf.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/reboot.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/sysproto.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/vnode.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/acl.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/syscall.h>
#include <sys/priv.h>
#include <sys/kthread.h>
#include <sys/syscallsubr.h>
#include <fs/fifofs/fifo.h>
#include <net/if.h>
#include <net/radix.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <machine/in_cksum.h>
#include <crypto/des/des.h>
#include <sys/md5.h>
#include <rpc/rpc.h>
#include <rpc/rpcsec_gss.h>
/*
* For Darwin, these functions should be "static" when built in a kext.
* (This is always defined as nil otherwise.)
*/
#define APPLESTATIC
#include <ufs/ufs/dir.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/extattr.h>
#include <ufs/ufs/ufsmount.h>
#include <vm/uma.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_extern.h>
#include <nfs/nfssvc.h>
#include "opt_nfs.h"
#include "opt_ufs.h"
/*
* These types must be defined before the nfs includes.
*/
#define NFSSOCKADDR_T struct sockaddr *
#define NFSPROC_T struct thread
#define NFSDEV_T dev_t
#define NFSSVCARGS nfssvc_args
#define NFSACL_T struct acl
/*
* These should be defined as the types used for the corresponding VOP's
* argument type.
*/
#define NFS_ACCESS_ARGS struct vop_access_args
#define NFS_OPEN_ARGS struct vop_open_args
#define NFS_GETATTR_ARGS struct vop_getattr_args
#define NFS_LOOKUP_ARGS struct vop_lookup_args
#define NFS_READDIR_ARGS struct vop_readdir_args
/*
* Allocate mbufs. Must succeed and never set the mbuf ptr to NULL.
*/
#define NFSMGET(m) do { \
MGET((m), M_TRYWAIT, MT_DATA); \
while ((m) == NULL ) { \
(void) nfs_catnap(PZERO, 0, "nfsmget"); \
MGET((m), M_TRYWAIT, MT_DATA); \
} \
} while (0)
#define NFSMGETHDR(m) do { \
MGETHDR((m), M_TRYWAIT, MT_DATA); \
while ((m) == NULL ) { \
(void) nfs_catnap(PZERO, 0, "nfsmget"); \
MGETHDR((m), M_TRYWAIT, MT_DATA); \
} \
} while (0)
#define NFSMCLGET(m, w) do { \
MGET((m), M_TRYWAIT, MT_DATA); \
while ((m) == NULL ) { \
(void) nfs_catnap(PZERO, 0, "nfsmget"); \
MGET((m), M_TRYWAIT, MT_DATA); \
} \
MCLGET((m), (w)); \
} while (0)
#define NFSMCLGETHDR(m, w) do { \
MGETHDR((m), M_TRYWAIT, MT_DATA); \
while ((m) == NULL ) { \
(void) nfs_catnap(PZERO, 0, "nfsmget"); \
MGETHDR((m), M_TRYWAIT, MT_DATA); \
} \
} while (0)
#define NFSMTOD mtod
/*
* Client side constant for size of a lockowner name.
*/
#define NFSV4CL_LOCKNAMELEN 12
/*
* Type for a mutex lock.
*/
#define NFSMUTEX_T struct mtx
#endif /* _KERNEL */
/*
* NFSv4 Operation numbers.
*/
#define NFSV4OP_ACCESS 3
#define NFSV4OP_CLOSE 4
#define NFSV4OP_COMMIT 5
#define NFSV4OP_CREATE 6
#define NFSV4OP_DELEGPURGE 7
#define NFSV4OP_DELEGRETURN 8
#define NFSV4OP_GETATTR 9
#define NFSV4OP_GETFH 10
#define NFSV4OP_LINK 11
#define NFSV4OP_LOCK 12
#define NFSV4OP_LOCKT 13
#define NFSV4OP_LOCKU 14
#define NFSV4OP_LOOKUP 15
#define NFSV4OP_LOOKUPP 16
#define NFSV4OP_NVERIFY 17
#define NFSV4OP_OPEN 18
#define NFSV4OP_OPENATTR 19
#define NFSV4OP_OPENCONFIRM 20
#define NFSV4OP_OPENDOWNGRADE 21
#define NFSV4OP_PUTFH 22
#define NFSV4OP_PUTPUBFH 23
#define NFSV4OP_PUTROOTFH 24
#define NFSV4OP_READ 25
#define NFSV4OP_READDIR 26
#define NFSV4OP_READLINK 27
#define NFSV4OP_REMOVE 28
#define NFSV4OP_RENAME 29
#define NFSV4OP_RENEW 30
#define NFSV4OP_RESTOREFH 31
#define NFSV4OP_SAVEFH 32
#define NFSV4OP_SECINFO 33
#define NFSV4OP_SETATTR 34
#define NFSV4OP_SETCLIENTID 35
#define NFSV4OP_SETCLIENTIDCFRM 36
#define NFSV4OP_VERIFY 37
#define NFSV4OP_WRITE 38
#define NFSV4OP_RELEASELCKOWN 39
/*
* Must be one greater than the last Operation#.
*/
#define NFSV4OP_NOPS 40
/* Quirky case if the illegal op code */
#define NFSV4OP_OPILLEGAL 10044
/*
* Fake NFSV4OP_xxx used for nfsstat. Start at NFSV4OP_NOPS.
*/
#define NFSV4OP_SYMLINK (NFSV4OP_NOPS)
#define NFSV4OP_MKDIR (NFSV4OP_NOPS + 1)
#define NFSV4OP_RMDIR (NFSV4OP_NOPS + 2)
#define NFSV4OP_READDIRPLUS (NFSV4OP_NOPS + 3)
#define NFSV4OP_MKNOD (NFSV4OP_NOPS + 4)
#define NFSV4OP_FSSTAT (NFSV4OP_NOPS + 5)
#define NFSV4OP_FSINFO (NFSV4OP_NOPS + 6)
#define NFSV4OP_PATHCONF (NFSV4OP_NOPS + 7)
#define NFSV4OP_V3CREATE (NFSV4OP_NOPS + 8)
/*
* This is the count of the fake operations listed above.
*/
#define NFSV4OP_FAKENOPS 9
/*
* and the Callback OPs
*/
#define NFSV4OP_CBGETATTR 3
#define NFSV4OP_CBRECALL 4
/*
* Must be one greater than the last Callback Operation#.
*/
#define NFSV4OP_CBNOPS 5
/*
* The lower numbers -> 21 are used by NFSv2 and v3. These define higher
* numbers used by NFSv4.
* NFS_V3NPROCS is one greater than the last V3 op and NFS_NPROCS is
* one greater than the last number.
*/
#define NFS_V3NPROCS 22
#define NFSPROC_LOOKUPP 22
#define NFSPROC_SETCLIENTID 23
#define NFSPROC_SETCLIENTIDCFRM 24
#define NFSPROC_LOCK 25
#define NFSPROC_LOCKU 26
#define NFSPROC_OPEN 27
#define NFSPROC_CLOSE 28
#define NFSPROC_OPENCONFIRM 29
#define NFSPROC_LOCKT 30
#define NFSPROC_OPENDOWNGRADE 31
#define NFSPROC_RENEW 32
#define NFSPROC_PUTROOTFH 33
#define NFSPROC_RELEASELCKOWN 34
#define NFSPROC_DELEGRETURN 35
#define NFSPROC_RETDELEGREMOVE 36
#define NFSPROC_RETDELEGRENAME1 37
#define NFSPROC_RETDELEGRENAME2 38
#define NFSPROC_GETACL 39
#define NFSPROC_SETACL 40
/*
* Must be defined as one higher than the last Proc# above.
*/
#define NFSV4_NPROCS 41
/*
* Stats structure
*/
struct ext_nfsstats {
int attrcache_hits;
int attrcache_misses;
int lookupcache_hits;
int lookupcache_misses;
int direofcache_hits;
int direofcache_misses;
int accesscache_hits;
int accesscache_misses;
int biocache_reads;
int read_bios;
int read_physios;
int biocache_writes;
int write_bios;
int write_physios;
int biocache_readlinks;
int readlink_bios;
int biocache_readdirs;
int readdir_bios;
int rpccnt[NFSV4_NPROCS];
int rpcretries;
int srvrpccnt[NFSV4OP_NOPS + NFSV4OP_FAKENOPS];
int srvrpc_errs;
int srv_errs;
int rpcrequests;
int rpctimeouts;
int rpcunexpected;
int rpcinvalid;
int srvcache_inproghits;
int srvcache_idemdonehits;
int srvcache_nonidemdonehits;
int srvcache_misses;
int srvcache_tcppeak;
int srvcache_size;
int srvclients;
int srvopenowners;
int srvopens;
int srvlockowners;
int srvlocks;
int srvdelegates;
int cbrpccnt[NFSV4OP_CBNOPS];
int clopenowners;
int clopens;
int cllockowners;
int cllocks;
int cldelegates;
int cllocalopenowners;
int cllocalopens;
int cllocallockowners;
int cllocallocks;
};
#ifdef _KERNEL
/*
* Define the ext_nfsstats as nfsstats for the kernel code.
*/
#define nfsstats ext_nfsstats
/*
* Define NFS_NPROCS as NFSV4_NPROCS for the experimental kernel code.
*/
#define NFS_NPROCS NFSV4_NPROCS
#include <fs/nfs/nfskpiport.h>
#include <fs/nfs/nfsdport.h>
#include <fs/nfs/rpcv2.h>
#include <fs/nfs/nfsproto.h>
#include <fs/nfs/nfs.h>
#include <fs/nfs/nfs_var.h>
#include <fs/nfs/nfsm_subs.h>
#include <fs/nfs/nfsrvcache.h>
#include <fs/nfs/nfsrvstate.h>
#include <fs/nfs/xdr_subs.h>
#include <fs/nfs/nfscl.h>
#include <fs/nfs/nfsclstate.h>
#include <fs/nfsclient/nfsargs.h>
#include <fs/nfsclient/nfsmount.h>
/*
* Just to keep nfs_var.h happy.
*/
struct nfs_vattr {
int junk;
};
struct nfsvattr {
struct vattr na_vattr;
nfsattrbit_t na_suppattr;
u_int32_t na_mntonfileno;
u_int64_t na_filesid[2];
};
#define na_type na_vattr.va_type
#define na_mode na_vattr.va_mode
#define na_nlink na_vattr.va_nlink
#define na_uid na_vattr.va_uid
#define na_gid na_vattr.va_gid
#define na_fsid na_vattr.va_fsid
#define na_fileid na_vattr.va_fileid
#define na_size na_vattr.va_size
#define na_blocksize na_vattr.va_blocksize
#define na_atime na_vattr.va_atime
#define na_mtime na_vattr.va_mtime
#define na_ctime na_vattr.va_ctime
#define na_gen na_vattr.va_gen
#define na_flags na_vattr.va_flags
#define na_rdev na_vattr.va_rdev
#define na_bytes na_vattr.va_bytes
#define na_filerev na_vattr.va_filerev
#define na_vaflags na_vattr.va_vaflags
#include <fs/nfsclient/nfsnode.h>
/*
* This is the header structure used for the lists, etc. (It has the
* above record in it.
*/
struct nfsrv_stablefirst {
LIST_HEAD(, nfsrv_stable) nsf_head; /* Head of nfsrv_stable list */
time_t nsf_eograce; /* Time grace period ends */
time_t *nsf_bootvals; /* Previous boottime values */
struct file *nsf_fp; /* File table pointer */
u_char nsf_flags; /* NFSNSF_ flags */
struct nfsf_rec nsf_rec; /* and above first record */
};
#define nsf_lease nsf_rec.lease
#define nsf_numboots nsf_rec.numboots
/* NFSNSF_xxx flags */
#define NFSNSF_UPDATEDONE 0x01
#define NFSNSF_GRACEOVER 0x02
#define NFSNSF_NEEDLOCK 0x04
#define NFSNSF_EXPIREDCLIENT 0x08
#define NFSNSF_NOOPENS 0x10
#define NFSNSF_OK 0x20
/*
* Maximum number of boot times allowed in record. Although there is
* really no need for a fixed upper bound, this serves as a sanity check
* for a corrupted file.
*/
#define NFSNSF_MAXNUMBOOTS 10000
/*
* This structure defines the other records in the file. The
* nst_client array is actually the size of the client string name.
*/
struct nfst_rec {
u_int16_t len;
u_char flag;
u_char client[1];
};
/* and the values for flag */
#define NFSNST_NEWSTATE 0x1
#define NFSNST_REVOKE 0x2
#define NFSNST_GOTSTATE 0x4
/*
* This structure is linked onto nfsrv_stablefirst for the duration of
* reclaim.
*/
struct nfsrv_stable {
LIST_ENTRY(nfsrv_stable) nst_list;
struct nfsclient *nst_clp;
struct nfst_rec nst_rec;
};
#define nst_timestamp nst_rec.timestamp
#define nst_len nst_rec.len
#define nst_flag nst_rec.flag
#define nst_client nst_rec.client
/*
* At some point the server will run out of kernel storage for
* state structures. For FreeBSD5.2, this results in a panic
* kmem_map is full. It happens at well over 1000000 opens plus
* locks on a PIII-800 with 256Mbytes, so that is where I've set
* the limit. If your server panics due to too many opens/locks,
* decrease the size of NFSRV_V4STATELIMIT. If you find the server
* returning NFS4ERR_RESOURCE a lot and have lots of memory, try
* increasing it.
*/
#define NFSRV_V4STATELIMIT 500000 /* Max # of Opens + Locks */
/*
* The type required differs with BSDen (just the second arg).
*/
void nfsrvd_rcv(struct socket *, void *, int);
/*
* Macros for handling socket addresses. (Hopefully this makes the code
* more portable, since I've noticed some 'BSD don't have sockaddrs in
* mbufs any more.)
*/
#define NFSSOCKADDR(a, t) ((t)(a))
#define NFSSOCKADDRALLOC(a) \
do { \
MALLOC((a), struct sockaddr *, sizeof (struct sockaddr), \
M_SONAME, M_WAITOK); \
NFSBZERO((a), sizeof (struct sockaddr)); \
} while (0)
#define NFSSOCKADDRSIZE(a, s) ((a)->sa_len = (s))
#define NFSSOCKADDRFREE(a) \
do { \
if (a) \
FREE((caddr_t)(a), M_SONAME); \
} while (0)
/*
* These should be defined as a process or thread structure, as required
* for signal handling, etc.
*/
#define NFSNEWCRED(c) (crdup(c))
#define NFSPROCCRED(p) ((p)->td_ucred)
#define NFSFREECRED(c) (crfree(c))
#define NFSUIOPROC(u, p) ((u)->uio_td = NULL)
#define NFSPROCP(p) ((p)->td_proc)
/*
* Define these so that cn_hash and its length is ignored.
*/
#define NFSCNHASHZERO(c)
#define NFSCNHASH(c, v)
#define NCHNAMLEN 9999999
/*
* Define these to use the time of day clock.
*/
#define NFSGETTIME(t) (getmicrotime(t))
#define NFSGETNANOTIME(t) (getnanotime(t))
/*
* These macros are defined to initialize and set the timer routine.
*/
#define NFS_TIMERINIT \
newnfs_timer(NULL)
/*
* Handle SMP stuff:
*/
#define NFSSTATESPINLOCK extern struct mtx nfs_state_mutex
#define NFSLOCKSTATE() mtx_lock(&nfs_state_mutex)
#define NFSUNLOCKSTATE() mtx_unlock(&nfs_state_mutex)
#define NFSSTATEMUTEXPTR (&nfs_state_mutex)
#define NFSREQSPINLOCK extern struct mtx nfs_req_mutex
#define NFSLOCKREQ() mtx_lock(&nfs_req_mutex)
#define NFSUNLOCKREQ() mtx_unlock(&nfs_req_mutex)
#define NFSCACHEMUTEX extern struct mtx nfs_cache_mutex
#define NFSCACHEMUTEXPTR (&nfs_cache_mutex)
#define NFSLOCKCACHE() mtx_lock(&nfs_cache_mutex)
#define NFSUNLOCKCACHE() mtx_unlock(&nfs_cache_mutex)
#define NFSCACHELOCKREQUIRED() mtx_assert(&nfs_cache_mutex, MA_OWNED)
#define NFSSOCKMUTEX extern struct mtx nfs_slock_mutex
#define NFSSOCKMUTEXPTR (&nfs_slock_mutex)
#define NFSLOCKSOCK() mtx_lock(&nfs_slock_mutex)
#define NFSUNLOCKSOCK() mtx_unlock(&nfs_slock_mutex)
#define NFSNAMEIDMUTEX extern struct mtx nfs_nameid_mutex
#define NFSLOCKNAMEID() mtx_lock(&nfs_nameid_mutex)
#define NFSUNLOCKNAMEID() mtx_unlock(&nfs_nameid_mutex)
#define NFSNAMEIDREQUIRED() mtx_assert(&nfs_nameid_mutex, MA_OWNED)
#define NFSCLSTATEMUTEX extern struct mtx nfs_clstate_mutex
#define NFSCLSTATEMUTEXPTR (&nfs_clstate_mutex)
#define NFSLOCKCLSTATE() mtx_lock(&nfs_clstate_mutex)
#define NFSUNLOCKCLSTATE() mtx_unlock(&nfs_clstate_mutex)
#define NFSDLOCKMUTEX extern struct mtx newnfsd_mtx
#define NFSDLOCKMUTEXPTR (&newnfsd_mtx)
#define NFSD_LOCK() mtx_lock(&newnfsd_mtx)
#define NFSD_UNLOCK() mtx_unlock(&newnfsd_mtx)
#define NFSD_LOCK_ASSERT() mtx_assert(&newnfsd_mtx, MA_OWNED)
#define NFSD_UNLOCK_ASSERT() mtx_assert(&newnfsd_mtx, MA_NOTOWNED)
#define NFSV4ROOTLOCKMUTEX extern struct mtx nfs_v4root_mutex
#define NFSV4ROOTLOCKMUTEXPTR (&nfs_v4root_mutex)
#define NFSLOCKV4ROOTMUTEX() mtx_lock(&nfs_v4root_mutex)
#define NFSUNLOCKV4ROOTMUTEX() mtx_unlock(&nfs_v4root_mutex)
#define NFSLOCKNODE(n) mtx_lock(&((n)->n_mtx))
#define NFSUNLOCKNODE(n) mtx_unlock(&((n)->n_mtx))
#define NFSLOCKMNT(m) mtx_lock(&((m)->nm_mtx))
#define NFSUNLOCKMNT(m) mtx_unlock(&((m)->nm_mtx))
#define NFSLOCKREQUEST(r) mtx_lock(&((r)->r_mtx))
#define NFSUNLOCKREQUEST(r) mtx_unlock(&((r)->r_mtx))
#define NFSPROCLISTLOCK() sx_slock(&allproc_lock)
#define NFSPROCLISTUNLOCK() sx_sunlock(&allproc_lock)
#define NFSLOCKSOCKREQ(r) mtx_lock(&((r)->nr_mtx))
#define NFSUNLOCKSOCKREQ(r) mtx_unlock(&((r)->nr_mtx))
/*
* Use these macros to initialize/free a mutex.
*/
#define NFSINITSOCKMUTEX(m) mtx_init((m), "nfssock", NULL, MTX_DEF)
#define NFSFREEMUTEX(m) mtx_destroy((m))
int nfsmsleep(void *, void *, int, const char *, struct timespec *);
/*
* And weird vm stuff in the nfs server.
*/
#define PDIRUNLOCK 0x0
#define MAX_COMMIT_COUNT (1024 * 1024)
/*
* Define these to handle the type of va_rdev.
*/
#define NFSMAKEDEV(m, n) makedev((m), (n))
#define NFSMAJOR(d) major(d)
#define NFSMINOR(d) minor(d)
/*
* Define this to be the macro that returns the minimum size required
* for a directory entry.
*/
#define DIRENT_SIZE(dp) GENERIC_DIRSIZ(dp)
/*
* The vnode tag for nfsv4root.
*/
#define VT_NFSV4ROOT "nfsv4root"
/*
* Define whatever it takes to do a vn_rdwr().
*/
#define NFSD_RDWR(r, v, b, l, o, s, i, c, a, p) \
vn_rdwr((r), (v), (b), (l), (o), (s), (i), (c), NULL, (a), (p))
/*
* Macros for handling memory for different BSDen.
* NFSBCOPY(src, dst, len) - copies len bytes, non-overlapping
* NFSOVBCOPY(src, dst, len) - ditto, but data areas might overlap
* NFSBCMP(cp1, cp2, len) - compare len bytes, return 0 if same
* NFSBZERO(cp, len) - set len bytes to 0x0
*/
#define NFSBCOPY(s, d, l) bcopy((s), (d), (l))
#define NFSOVBCOPY(s, d, l) ovbcopy((s), (d), (l))
#define NFSBCMP(s, d, l) bcmp((s), (d), (l))
#define NFSBZERO(s, l) bzero((s), (l))
/*
* Some queue.h files don't have these dfined in them.
*/
#define LIST_END(head) NULL
#define SLIST_END(head) NULL
#define TAILQ_END(head) NULL
/*
* This must be defined to be a global variable the increments once
* per second, but never stops or goes backwards, even when a "date"
* command changes the tod clock. It is used for delta times for
* leases, etc.
*/
#define NFSD_MONOSEC time_uptime
/*
* Declare the malloc types.
*/
MALLOC_DECLARE(M_NEWNFSRVCACHE);
MALLOC_DECLARE(M_NEWNFSDCLIENT);
MALLOC_DECLARE(M_NEWNFSDSTATE);
MALLOC_DECLARE(M_NEWNFSDLOCK);
MALLOC_DECLARE(M_NEWNFSDLOCKFILE);
MALLOC_DECLARE(M_NEWNFSSTRING);
MALLOC_DECLARE(M_NEWNFSUSERGROUP);
MALLOC_DECLARE(M_NEWNFSDREQ);
MALLOC_DECLARE(M_NEWNFSFH);
MALLOC_DECLARE(M_NEWNFSCLOWNER);
MALLOC_DECLARE(M_NEWNFSCLOPEN);
MALLOC_DECLARE(M_NEWNFSCLDELEG);
MALLOC_DECLARE(M_NEWNFSCLCLIENT);
MALLOC_DECLARE(M_NEWNFSCLLOCKOWNER);
MALLOC_DECLARE(M_NEWNFSCLLOCK);
MALLOC_DECLARE(M_NEWNFSDIROFF);
MALLOC_DECLARE(M_NEWNFSV4NODE);
MALLOC_DECLARE(M_NEWNFSDIRECTIO);
MALLOC_DECLARE(M_NEWNFSMNT);
MALLOC_DECLARE(M_NEWNFSDROLLBACK);
#define M_NFSRVCACHE M_NEWNFSRVCACHE
#define M_NFSDCLIENT M_NEWNFSDCLIENT
#define M_NFSDSTATE M_NEWNFSDSTATE
#define M_NFSDLOCK M_NEWNFSDLOCK
#define M_NFSDLOCKFILE M_NEWNFSDLOCKFILE
#define M_NFSSTRING M_NEWNFSSTRING
#define M_NFSUSERGROUP M_NEWNFSUSERGROUP
#define M_NFSDREQ M_NEWNFSDREQ
#define M_NFSFH M_NEWNFSFH
#define M_NFSCLOWNER M_NEWNFSCLOWNER
#define M_NFSCLOPEN M_NEWNFSCLOPEN
#define M_NFSCLDELEG M_NEWNFSCLDELEG
#define M_NFSCLCLIENT M_NEWNFSCLCLIENT
#define M_NFSCLLOCKOWNER M_NEWNFSCLLOCKOWNER
#define M_NFSCLLOCK M_NEWNFSCLLOCK
#define M_NFSDIROFF M_NEWNFSDIROFF
#define M_NFSV4NODE M_NEWNFSV4NODE
#define M_NFSDIRECTIO M_NEWNFSDIRECTIO
#define M_NFSDROLLBACK M_NEWNFSDROLLBACK
#define NFSINT_SIGMASK(set) \
(SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) || \
SIGISMEMBER(set, SIGHUP) || SIGISMEMBER(set, SIGKILL) || \
SIGISMEMBER(set, SIGQUIT))
/*
* Convert a quota block count to byte count.
*/
#define NFSQUOTABLKTOBYTE(q, b) (q) *= (b)
/*
* Define this as the largest file size supported. (It should probably
* be available via a VFS_xxx Op, but it isn't.
*/
#define NFSRV_MAXFILESIZE ((u_int64_t)0x800000000000)
/*
* Set this macro to index() or strchr(), whichever is supported.
*/
#define STRCHR(s, c) index((s), (c))
/*
* Set the n_time in the client write rpc, as required.
*/
#define NFSWRITERPC_SETTIME(w, n, v4) \
do { \
if (w) { \
(n)->n_mtime = (n)->n_vattr.na_vattr.va_mtime; \
if (v4) \
(n)->n_change = (n)->n_vattr.na_vattr.va_filerev; \
} \
} while (0)
/*
* Fake value, just to make the client work.
*/
#define NFS_LATTR_NOSHRINK 1
/*
* Prototypes for functions where the arguments vary for different ports.
*/
int nfscl_loadattrcache(struct vnode **, struct nfsvattr *, void *, void *,
int, int);
void newnfs_realign(struct mbuf **);
/*
* If the port runs on an SMP box that can enforce Atomic ops with low
* overheads, define these as atomic increments/decrements. If not,
* don't worry about it, since these are used for stats that can be
* "out by one" without disastrous consequences.
*/
#define NFSINCRGLOBAL(a) ((a)++)
/*
* Assorted funky stuff to make things work under Darwin8.
*/
/*
* These macros checks for a field in vattr being set.
*/
#define NFSATTRISSET(t, v, a) ((v)->a != (t)VNOVAL)
#define NFSATTRISSETTIME(v, a) ((v)->a.tv_sec != VNOVAL)
/*
* Manipulate mount flags.
*/
#define NFSSTA_HASWRITEVERF 0x00040000 /* Has write verifier */
#define NFSSTA_GOTFSINFO 0x00100000 /* Got the fsinfo */
#define NFSSTA_TIMEO 0x10000000 /* Experiencing a timeout */
#define NFSSTA_LOCKTIMEO 0x20000000 /* Experiencing a lockd timeout */
#define NFSSTA_HASSETFSID 0x40000000 /* Has set the fsid */
#define NFSHASNFSV3(n) ((n)->nm_flag & NFSMNT_NFSV3)
#define NFSHASNFSV4(n) ((n)->nm_flag & NFSMNT_NFSV4)
#define NFSHASNFSV3OR4(n) ((n)->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4))
#define NFSHASGOTFSINFO(n) ((n)->nm_state & NFSSTA_GOTFSINFO)
#define NFSHASHASSETFSID(n) ((n)->nm_state & NFSSTA_HASSETFSID)
#define NFSHASSTRICT3530(n) ((n)->nm_flag & NFSMNT_STRICT3530)
#define NFSHASWRITEVERF(n) ((n)->nm_state & NFSSTA_HASWRITEVERF)
#define NFSHASINT(n) ((n)->nm_flag & NFSMNT_INT)
#define NFSHASSOFT(n) ((n)->nm_flag & NFSMNT_SOFT)
#define NFSHASINTORSOFT(n) ((n)->nm_flag & (NFSMNT_INT | NFSMNT_SOFT))
#define NFSHASDUMBTIMR(n) ((n)->nm_flag & NFSMNT_DUMBTIMR)
#define NFSHASNOCONN(n) ((n)->nm_flag & NFSMNT_MNTD)
#define NFSHASKERB(n) ((n)->nm_flag & NFSMNT_KERB)
#define NFSHASALLGSSNAME(n) ((n)->nm_flag & NFSMNT_ALLGSSNAME)
#define NFSHASINTEGRITY(n) ((n)->nm_flag & NFSMNT_INTEGRITY)
#define NFSHASPRIVACY(n) ((n)->nm_flag & NFSMNT_PRIVACY)
#define NFSSETWRITEVERF(n) ((n)->nm_state |= NFSSTA_HASWRITEVERF)
#define NFSSETHASSETFSID(n) ((n)->nm_state |= NFSSTA_HASSETFSID)
-#ifdef NFS4_ACL_EXTATTR_NAME
-#define NFSHASNFS4ACL(m) nfs_supportsnfsv4acls(m)
-int nfs_supportsnfsv4acls(struct mount *);
-#else
-#define NFSHASNFS4ACL(m) 0
-#endif
/*
* Gets the stats field out of the mount structure.
*/
#define vfs_statfs(m) (&((m)->mnt_stat))
/*
* Set boottime.
*/
#define NFSSETBOOTTIME(b) ((b) = boottime)
/*
* The size of directory blocks in the buffer cache.
* MUST BE in the range of PAGE_SIZE <= NFS_DIRBLKSIZ <= MAXBSIZE!!
*/
#define NFS_DIRBLKSIZ (16 * DIRBLKSIZ) /* Must be a multiple of DIRBLKSIZ */
/*
* Define these macros to access mnt_flag fields.
*/
#define NFSMNT_RDONLY(m) ((m)->mnt_flag & MNT_RDONLY)
#endif /* _KERNEL */
/*
* Define a structure similar to ufs_args for use in exporting the V4 root.
*/
struct nfsex_args {
char *fspec;
struct export_args export;
};
/*
* These export flags should be defined, but there are no bits left.
* Maybe a separate mnt_exflag field could be added or the mnt_flag
* field increased to 64 bits?
*/
#ifndef MNT_EXSTRICTACCESS
#define MNT_EXSTRICTACCESS 0x0
#endif
#ifndef MNT_EXV4ONLY
#define MNT_EXV4ONLY 0x0
#endif
#ifdef _KERNEL
/*
* Define this to invalidate the attribute cache for the nfs node.
*/
#define NFSINVALATTRCACHE(n) ((n)->n_attrstamp = 0)
/* Used for FreeBSD only */
void nfsd_mntinit(void);
/*
* Define these for vnode lock/unlock ops.
*/
#define NFSVOPLOCK(v, f, p) vn_lock((v), (f))
#define NFSVOPUNLOCK(v, f, p) VOP_UNLOCK((v), (f))
#define NFSVOPISLOCKED(v, p) VOP_ISLOCKED((v))
/*
* Define ncl_hash().
*/
#define ncl_hash(f, l) (fnv_32_buf((f), (l), FNV1_32_INIT))
int newnfs_iosize(struct nfsmount *);
#ifdef NFS_DEBUG
extern int nfs_debug;
#define NFS_DEBUG_ASYNCIO 1 /* asynchronous i/o */
#define NFS_DEBUG_WG 2 /* server write gathering */
#define NFS_DEBUG_RC 4 /* server request caching */
#define NFS_DPF(cat, args) \
do { \
if (nfs_debug & NFS_DEBUG_##cat) printf args; \
} while (0)
#else
#define NFS_DPF(cat, args)
#endif
int newnfs_vncmpf(struct vnode *, void *);
#ifndef NFS_MINDIRATTRTIMO
#define NFS_MINDIRATTRTIMO 3 /* VDIR attrib cache timeout in sec */
#endif
#ifndef NFS_MAXDIRATTRTIMO
#define NFS_MAXDIRATTRTIMO 60
#endif
/*
* Nfs outstanding request list element
*/
struct nfsreq {
TAILQ_ENTRY(nfsreq) r_chain;
u_int32_t r_flags; /* flags on request, see below */
struct nfsmount *r_nmp; /* Client mnt ptr */
struct mtx r_mtx; /* Mutex lock for this structure */
};
#ifndef NFS_MAXBSIZE
#define NFS_MAXBSIZE MAXBSIZE
#endif
/*
* This macro checks to see if issuing of delegations is allowed for this
* vnode.
*/
#ifdef VV_DISABLEDELEG
#define NFSVNO_DELEGOK(v) \
((v) == NULL || ((v)->v_vflag & VV_DISABLEDELEG) == 0)
#else
#define NFSVNO_DELEGOK(v) (1)
#endif
/*
* Define this as the flags argument for msleep() when catching signals
* while holding a resource that other threads would block for, such as
* a vnode lock.
*/
#define NFS_PCATCH (PCATCH | PBDRY)
#endif /* _KERNEL */
#endif /* _NFS_NFSPORT_H */
Index: stable/8/sys/fs/nfsclient/nfs_clport.c
===================================================================
--- stable/8/sys/fs/nfsclient/nfs_clport.c (revision 221216)
+++ stable/8/sys/fs/nfsclient/nfs_clport.c (revision 221217)
@@ -1,1284 +1,1284 @@
/*-
* 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.
* 4. 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$");
/*
* generally, I don't like #includes inside .h files, but it seems to
* be the easiest way to handle the port.
*/
#include <sys/hash.h>
#include <fs/nfs/nfsport.h>
#include <netinet/if_ether.h>
#include <net/if_types.h>
extern u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
extern struct vop_vector newnfs_vnodeops;
extern struct vop_vector newnfs_fifoops;
extern uma_zone_t newnfsnode_zone;
extern struct buf_ops buf_ops_newnfs;
extern int ncl_pbuf_freecnt;
extern short nfsv4_cbport;
extern int nfscl_enablecallb;
extern int nfs_numnfscbd;
extern int nfscl_inited;
struct mtx nfs_clstate_mutex;
struct mtx ncl_iod_mutex;
NFSDLOCKMUTEX;
extern void (*ncl_call_invalcaches)(struct vnode *);
/*
* Comparison function for vfs_hash functions.
*/
int
newnfs_vncmpf(struct vnode *vp, void *arg)
{
struct nfsfh *nfhp = (struct nfsfh *)arg;
struct nfsnode *np = VTONFS(vp);
if (np->n_fhp->nfh_len != nfhp->nfh_len ||
NFSBCMP(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len))
return (1);
return (0);
}
/*
* Look up a vnode/nfsnode by file handle.
* Callers must check for mount points!!
* In all cases, a pointer to a
* nfsnode structure is returned.
* This variant takes a "struct nfsfh *" as second argument and uses
* that structure up, either by hanging off the nfsnode or FREEing it.
*/
int
nfscl_nget(struct mount *mntp, struct vnode *dvp, struct nfsfh *nfhp,
struct componentname *cnp, struct thread *td, struct nfsnode **npp,
void *stuff)
{
struct nfsnode *np, *dnp;
struct vnode *vp, *nvp;
struct nfsv4node *newd, *oldd;
int error;
u_int hash;
struct nfsmount *nmp;
nmp = VFSTONFS(mntp);
dnp = VTONFS(dvp);
*npp = NULL;
hash = fnv_32_buf(nfhp->nfh_fh, nfhp->nfh_len, FNV1_32_INIT);
error = vfs_hash_get(mntp, hash, LK_EXCLUSIVE,
td, &nvp, newnfs_vncmpf, nfhp);
if (error == 0 && nvp != NULL) {
/*
* I believe there is a slight chance that vgonel() could
* get called on this vnode between when vn_lock() drops
* the VI_LOCK() and vget() acquires it again, so that it
* hasn't yet had v_usecount incremented. If this were to
* happen, the VI_DOOMED flag would be set, so check for
* that here. Since we now have the v_usecount incremented,
* we should be ok until we vrele() it, if the VI_DOOMED
* flag isn't set now.
*/
VI_LOCK(nvp);
if ((nvp->v_iflag & VI_DOOMED)) {
VI_UNLOCK(nvp);
vrele(nvp);
error = ENOENT;
} else {
VI_UNLOCK(nvp);
}
}
if (error) {
FREE((caddr_t)nfhp, M_NFSFH);
return (error);
}
if (nvp != NULL) {
np = VTONFS(nvp);
/*
* For NFSv4, check to see if it is the same name and
* replace the name, if it is different.
*/
oldd = newd = NULL;
if ((nmp->nm_flag & NFSMNT_NFSV4) && np->n_v4 != NULL &&
nvp->v_type == VREG &&
(np->n_v4->n4_namelen != cnp->cn_namelen ||
NFSBCMP(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
cnp->cn_namelen) ||
dnp->n_fhp->nfh_len != np->n_v4->n4_fhlen ||
NFSBCMP(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
dnp->n_fhp->nfh_len))) {
MALLOC(newd, struct nfsv4node *,
sizeof (struct nfsv4node) + dnp->n_fhp->nfh_len +
+ cnp->cn_namelen - 1, M_NFSV4NODE, M_WAITOK);
NFSLOCKNODE(np);
if (newd != NULL && np->n_v4 != NULL && nvp->v_type == VREG
&& (np->n_v4->n4_namelen != cnp->cn_namelen ||
NFSBCMP(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
cnp->cn_namelen) ||
dnp->n_fhp->nfh_len != np->n_v4->n4_fhlen ||
NFSBCMP(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
dnp->n_fhp->nfh_len))) {
oldd = np->n_v4;
np->n_v4 = newd;
newd = NULL;
np->n_v4->n4_fhlen = dnp->n_fhp->nfh_len;
np->n_v4->n4_namelen = cnp->cn_namelen;
NFSBCOPY(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
dnp->n_fhp->nfh_len);
NFSBCOPY(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
cnp->cn_namelen);
}
NFSUNLOCKNODE(np);
}
if (newd != NULL)
FREE((caddr_t)newd, M_NFSV4NODE);
if (oldd != NULL)
FREE((caddr_t)oldd, M_NFSV4NODE);
*npp = np;
FREE((caddr_t)nfhp, M_NFSFH);
return (0);
}
/*
* Allocate before getnewvnode since doing so afterward
* might cause a bogus v_data pointer to get dereferenced
* elsewhere if zalloc should block.
*/
np = uma_zalloc(newnfsnode_zone, M_WAITOK | M_ZERO);
error = getnewvnode("newnfs", mntp, &newnfs_vnodeops, &nvp);
if (error) {
uma_zfree(newnfsnode_zone, np);
FREE((caddr_t)nfhp, M_NFSFH);
return (error);
}
vp = nvp;
vp->v_bufobj.bo_ops = &buf_ops_newnfs;
vp->v_data = np;
np->n_vnode = vp;
/*
* Initialize the mutex even if the vnode is going to be a loser.
* This simplifies the logic in reclaim, which can then unconditionally
* destroy the mutex (in the case of the loser, or if hash_insert
* happened to return an error no special casing is needed).
*/
mtx_init(&np->n_mtx, "NEWNFSnode lock", NULL, MTX_DEF | MTX_DUPOK);
/*
* Are we getting the root? If so, make sure the vnode flags
* are correct
*/
if ((nfhp->nfh_len == nmp->nm_fhsize) &&
!bcmp(nfhp->nfh_fh, nmp->nm_fh, nfhp->nfh_len)) {
if (vp->v_type == VNON)
vp->v_type = VDIR;
vp->v_vflag |= VV_ROOT;
}
np->n_fhp = nfhp;
/*
* For NFSv4, we have to attach the directory file handle and
* file name, so that Open Ops can be done later.
*/
if (nmp->nm_flag & NFSMNT_NFSV4) {
MALLOC(np->n_v4, struct nfsv4node *, sizeof (struct nfsv4node)
+ dnp->n_fhp->nfh_len + cnp->cn_namelen - 1, M_NFSV4NODE,
M_WAITOK);
np->n_v4->n4_fhlen = dnp->n_fhp->nfh_len;
np->n_v4->n4_namelen = cnp->cn_namelen;
NFSBCOPY(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
dnp->n_fhp->nfh_len);
NFSBCOPY(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
cnp->cn_namelen);
} else {
np->n_v4 = NULL;
}
/*
* NFS supports recursive and shared locking.
*/
VN_LOCK_AREC(vp);
VN_LOCK_ASHARE(vp);
lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_NOWITNESS, NULL);
error = insmntque(vp, mntp);
if (error != 0) {
*npp = NULL;
mtx_destroy(&np->n_mtx);
FREE((caddr_t)nfhp, M_NFSFH);
if (np->n_v4 != NULL)
FREE((caddr_t)np->n_v4, M_NFSV4NODE);
uma_zfree(newnfsnode_zone, np);
return (error);
}
error = vfs_hash_insert(vp, hash, LK_EXCLUSIVE,
td, &nvp, newnfs_vncmpf, nfhp);
if (error)
return (error);
if (nvp != NULL) {
*npp = VTONFS(nvp);
/* vfs_hash_insert() vput()'s the losing vnode */
return (0);
}
*npp = np;
return (0);
}
/*
* Anothe variant of nfs_nget(). This one is only used by reopen. It
* takes almost the same args as nfs_nget(), but only succeeds if an entry
* exists in the cache. (Since files should already be "open" with a
* vnode ref cnt on the node when reopen calls this, it should always
* succeed.)
* Also, don't get a vnode lock, since it may already be locked by some
* other process that is handling it. This is ok, since all other threads
* on the client are blocked by the nfsc_lock being exclusively held by the
* caller of this function.
*/
int
nfscl_ngetreopen(struct mount *mntp, u_int8_t *fhp, int fhsize,
struct thread *td, struct nfsnode **npp)
{
struct vnode *nvp;
u_int hash;
struct nfsfh *nfhp;
int error;
*npp = NULL;
/* For forced dismounts, just return error. */
if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF))
return (EINTR);
MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + fhsize,
M_NFSFH, M_WAITOK);
bcopy(fhp, &nfhp->nfh_fh[0], fhsize);
nfhp->nfh_len = fhsize;
hash = fnv_32_buf(fhp, fhsize, FNV1_32_INIT);
/*
* First, try to get the vnode locked, but don't block for the lock.
*/
error = vfs_hash_get(mntp, hash, (LK_EXCLUSIVE | LK_NOWAIT), td, &nvp,
newnfs_vncmpf, nfhp);
if (error == 0 && nvp != NULL) {
VOP_UNLOCK(nvp, 0);
} else if (error == EBUSY) {
/*
* The LK_EXCLOTHER lock type tells nfs_lock1() to not try
* and lock the vnode, but just get a v_usecount on it.
* LK_NOWAIT is set so that when vget() returns ENOENT,
* vfs_hash_get() fails instead of looping.
* If this succeeds, it is safe so long as a vflush() with
* FORCECLOSE has not been done. Since the Renew thread is
* stopped and the MNTK_UNMOUNTF flag is set before doing
* a vflush() with FORCECLOSE, we should be ok here.
*/
if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF))
error = EINTR;
else
error = vfs_hash_get(mntp, hash,
(LK_EXCLOTHER | LK_NOWAIT), td, &nvp,
newnfs_vncmpf, nfhp);
}
FREE(nfhp, M_NFSFH);
if (error)
return (error);
if (nvp != NULL) {
*npp = VTONFS(nvp);
return (0);
}
return (EINVAL);
}
/*
* Load the attribute cache (that lives in the nfsnode entry) with
* the attributes of the second argument and
* Iff vaper not NULL
* copy the attributes to *vaper
* Similar to nfs_loadattrcache(), except the attributes are passed in
* instead of being parsed out of the mbuf list.
*/
int
nfscl_loadattrcache(struct vnode **vpp, struct nfsvattr *nap, void *nvaper,
void *stuff, int writeattr, int dontshrink)
{
struct vnode *vp = *vpp;
struct vattr *vap, *nvap = &nap->na_vattr, *vaper = nvaper;
struct nfsnode *np;
struct nfsmount *nmp;
struct timespec mtime_save;
/*
* If v_type == VNON it is a new node, so fill in the v_type,
* n_mtime fields. Check to see if it represents a special
* device, and if so, check for a possible alias. Once the
* correct vnode has been obtained, fill in the rest of the
* information.
*/
np = VTONFS(vp);
NFSLOCKNODE(np);
if (vp->v_type != nvap->va_type) {
vp->v_type = nvap->va_type;
if (vp->v_type == VFIFO)
vp->v_op = &newnfs_fifoops;
np->n_mtime = nvap->va_mtime;
}
nmp = VFSTONFS(vp->v_mount);
vap = &np->n_vattr.na_vattr;
mtime_save = vap->va_mtime;
if (writeattr) {
np->n_vattr.na_filerev = nap->na_filerev;
np->n_vattr.na_size = nap->na_size;
np->n_vattr.na_mtime = nap->na_mtime;
np->n_vattr.na_ctime = nap->na_ctime;
np->n_vattr.na_fsid = nap->na_fsid;
} else {
NFSBCOPY((caddr_t)nap, (caddr_t)&np->n_vattr,
sizeof (struct nfsvattr));
}
/*
* For NFSv4, if the node's fsid is not equal to the mount point's
* fsid, return the low order 32bits of the node's fsid. This
* allows getcwd(3) to work. There is a chance that the fsid might
* be the same as a local fs, but since this is in an NFS mount
* point, I don't think that will cause any problems?
*/
if (NFSHASNFSV4(nmp) && NFSHASHASSETFSID(nmp) &&
(nmp->nm_fsid[0] != np->n_vattr.na_filesid[0] ||
nmp->nm_fsid[1] != np->n_vattr.na_filesid[1])) {
/*
* va_fsid needs to be set to some value derived from
* np->n_vattr.na_filesid that is not equal
* vp->v_mount->mnt_stat.f_fsid[0], so that it changes
* from the value used for the top level server volume
* in the mounted subtree.
*/
if (vp->v_mount->mnt_stat.f_fsid.val[0] !=
(uint32_t)np->n_vattr.na_filesid[0])
vap->va_fsid = (uint32_t)np->n_vattr.na_filesid[0];
else
vap->va_fsid = (uint32_t)hash32_buf(
np->n_vattr.na_filesid, 2 * sizeof(uint64_t), 0);
} else
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
np->n_attrstamp = time_second;
if (vap->va_size != np->n_size) {
if (vap->va_type == VREG) {
if (dontshrink && vap->va_size < np->n_size) {
/*
* We've been told not to shrink the file;
* zero np->n_attrstamp to indicate that
* the attributes are stale.
*/
vap->va_size = np->n_size;
np->n_attrstamp = 0;
} else if (np->n_flag & NMODIFIED) {
/*
* We've modified the file: Use the larger
* of our size, and the server's size.
*/
if (vap->va_size < np->n_size) {
vap->va_size = np->n_size;
} else {
np->n_size = vap->va_size;
np->n_flag |= NSIZECHANGED;
}
} else {
np->n_size = vap->va_size;
np->n_flag |= NSIZECHANGED;
}
vnode_pager_setsize(vp, np->n_size);
} else {
np->n_size = vap->va_size;
}
}
/*
* The following checks are added to prevent a race between (say)
* a READDIR+ and a WRITE.
* READDIR+, WRITE requests sent out.
* READDIR+ resp, WRITE resp received on client.
* However, the WRITE resp was handled before the READDIR+ resp
* causing the post op attrs from the write to be loaded first
* and the attrs from the READDIR+ to be loaded later. If this
* happens, we have stale attrs loaded into the attrcache.
* We detect this by for the mtime moving back. We invalidate the
* attrcache when this happens.
*/
if (timespeccmp(&mtime_save, &vap->va_mtime, >))
/* Size changed or mtime went backwards */
np->n_attrstamp = 0;
if (vaper != NULL) {
NFSBCOPY((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
if (np->n_flag & NCHG) {
if (np->n_flag & NACC)
vaper->va_atime = np->n_atim;
if (np->n_flag & NUPD)
vaper->va_mtime = np->n_mtim;
}
}
NFSUNLOCKNODE(np);
return (0);
}
/*
* Fill in the client id name. For these bytes:
* 1 - they must be unique
* 2 - they should be persistent across client reboots
* 1 is more critical than 2
* Use the mount point's unique id plus either the uuid or, if that
* isn't set, random junk.
*/
void
nfscl_fillclid(u_int64_t clval, char *uuid, u_int8_t *cp, u_int16_t idlen)
{
int uuidlen;
/*
* First, put in the 64bit mount point identifier.
*/
if (idlen >= sizeof (u_int64_t)) {
NFSBCOPY((caddr_t)&clval, cp, sizeof (u_int64_t));
cp += sizeof (u_int64_t);
idlen -= sizeof (u_int64_t);
}
/*
* If uuid is non-zero length, use it.
*/
uuidlen = strlen(uuid);
if (uuidlen > 0 && idlen >= uuidlen) {
NFSBCOPY(uuid, cp, uuidlen);
cp += uuidlen;
idlen -= uuidlen;
}
/*
* This only normally happens if the uuid isn't set.
*/
while (idlen > 0) {
*cp++ = (u_int8_t)(arc4random() % 256);
idlen--;
}
}
/*
* Fill in a lock owner name. For now, pid + the process's creation time.
*/
void
nfscl_filllockowner(struct thread *td, u_int8_t *cp)
{
union {
u_int32_t lval;
u_int8_t cval[4];
} tl;
struct proc *p;
if (td == NULL) {
printf("NULL td\n");
bzero(cp, 12);
return;
}
p = td->td_proc;
if (p == NULL) {
printf("NULL pid\n");
bzero(cp, 12);
return;
}
tl.lval = p->p_pid;
*cp++ = tl.cval[0];
*cp++ = tl.cval[1];
*cp++ = tl.cval[2];
*cp++ = tl.cval[3];
if (p->p_stats == NULL) {
printf("pstats null\n");
bzero(cp, 8);
return;
}
tl.lval = p->p_stats->p_start.tv_sec;
*cp++ = tl.cval[0];
*cp++ = tl.cval[1];
*cp++ = tl.cval[2];
*cp++ = tl.cval[3];
tl.lval = p->p_stats->p_start.tv_usec;
*cp++ = tl.cval[0];
*cp++ = tl.cval[1];
*cp++ = tl.cval[2];
*cp = tl.cval[3];
}
/*
* Find the parent process for the thread passed in as an argument.
* If none exists, return NULL, otherwise return a thread for the parent.
* (Can be any of the threads, since it is only used for td->td_proc.)
*/
NFSPROC_T *
nfscl_getparent(struct thread *td)
{
struct proc *p;
struct thread *ptd;
if (td == NULL)
return (NULL);
p = td->td_proc;
if (p->p_pid == 0)
return (NULL);
p = p->p_pptr;
if (p == NULL)
return (NULL);
ptd = TAILQ_FIRST(&p->p_threads);
return (ptd);
}
/*
* Start up the renew kernel thread.
*/
static void
start_nfscl(void *arg)
{
struct nfsclclient *clp;
struct thread *td;
clp = (struct nfsclclient *)arg;
td = TAILQ_FIRST(&clp->nfsc_renewthread->p_threads);
nfscl_renewthread(clp, td);
kproc_exit(0);
}
void
nfscl_start_renewthread(struct nfsclclient *clp)
{
kproc_create(start_nfscl, (void *)clp, &clp->nfsc_renewthread, 0, 0,
"nfscl");
}
/*
* Handle wcc_data.
* For NFSv4, it assumes that nfsv4_wccattr() was used to set up the getattr
* as the first Op after PutFH.
* (For NFSv4, the postop attributes are after the Op, so they can't be
* parsed here. A separate call to nfscl_postop_attr() is required.)
*/
int
nfscl_wcc_data(struct nfsrv_descript *nd, struct vnode *vp,
struct nfsvattr *nap, int *flagp, int *wccflagp, void *stuff)
{
u_int32_t *tl;
struct nfsnode *np = VTONFS(vp);
struct nfsvattr nfsva;
int error = 0;
if (wccflagp != NULL)
*wccflagp = 0;
if (nd->nd_flag & ND_NFSV3) {
*flagp = 0;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (*tl == newnfs_true) {
NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
if (wccflagp != NULL) {
mtx_lock(&np->n_mtx);
*wccflagp = (np->n_mtime.tv_sec ==
fxdr_unsigned(u_int32_t, *(tl + 2)) &&
np->n_mtime.tv_nsec ==
fxdr_unsigned(u_int32_t, *(tl + 3)));
mtx_unlock(&np->n_mtx);
}
}
error = nfscl_postop_attr(nd, nap, flagp, stuff);
} else if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR))
== (ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
NULL, NULL, NULL, NULL, NULL);
if (error)
return (error);
/*
* Get rid of Op# and status for next op.
*/
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*++tl)
nd->nd_flag |= ND_NOMOREDATA;
if (wccflagp != NULL &&
nfsva.na_vattr.va_mtime.tv_sec != 0) {
mtx_lock(&np->n_mtx);
*wccflagp = (np->n_mtime.tv_sec ==
nfsva.na_vattr.va_mtime.tv_sec &&
np->n_mtime.tv_nsec ==
nfsva.na_vattr.va_mtime.tv_sec);
mtx_unlock(&np->n_mtx);
}
}
nfsmout:
return (error);
}
/*
* Get postop attributes.
*/
int
nfscl_postop_attr(struct nfsrv_descript *nd, struct nfsvattr *nap, int *retp,
void *stuff)
{
u_int32_t *tl;
int error = 0;
*retp = 0;
if (nd->nd_flag & ND_NOMOREDATA)
return (error);
if (nd->nd_flag & ND_NFSV3) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
*retp = fxdr_unsigned(int, *tl);
} else if (nd->nd_flag & ND_NFSV4) {
/*
* For NFSv4, the postop attr are at the end, so no point
* in looking if nd_repstat != 0.
*/
if (!nd->nd_repstat) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*(tl + 1))
/* should never happen since nd_repstat != 0 */
nd->nd_flag |= ND_NOMOREDATA;
else
*retp = 1;
}
} else if (!nd->nd_repstat) {
/* For NFSv2, the attributes are here iff nd_repstat == 0 */
*retp = 1;
}
if (*retp) {
error = nfsm_loadattr(nd, nap);
if (error)
*retp = 0;
}
nfsmout:
return (error);
}
/*
* Fill in the setable attributes. The full argument indicates whether
* to fill in them all or just mode and time.
*/
void
nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
struct vnode *vp, int flags, u_int32_t rdev)
{
u_int32_t *tl;
struct nfsv2_sattr *sp;
nfsattrbit_t attrbits;
struct timeval curtime;
switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
case ND_NFSV2:
NFSM_BUILD(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
if (vap->va_mode == (mode_t)VNOVAL)
sp->sa_mode = newnfs_xdrneg1;
else
sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
if (vap->va_uid == (uid_t)VNOVAL)
sp->sa_uid = newnfs_xdrneg1;
else
sp->sa_uid = txdr_unsigned(vap->va_uid);
if (vap->va_gid == (gid_t)VNOVAL)
sp->sa_gid = newnfs_xdrneg1;
else
sp->sa_gid = txdr_unsigned(vap->va_gid);
if (flags & NFSSATTR_SIZE0)
sp->sa_size = 0;
else if (flags & NFSSATTR_SIZENEG1)
sp->sa_size = newnfs_xdrneg1;
else if (flags & NFSSATTR_SIZERDEV)
sp->sa_size = txdr_unsigned(rdev);
else
sp->sa_size = txdr_unsigned(vap->va_size);
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
break;
case ND_NFSV3:
getmicrotime(&curtime);
if (vap->va_mode != (mode_t)VNOVAL) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
*tl = txdr_unsigned(vap->va_mode);
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_false;
}
if ((flags & NFSSATTR_FULL) && vap->va_uid != (uid_t)VNOVAL) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
*tl = txdr_unsigned(vap->va_uid);
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_false;
}
if ((flags & NFSSATTR_FULL) && vap->va_gid != (gid_t)VNOVAL) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
*tl = txdr_unsigned(vap->va_gid);
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_false;
}
if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL) {
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
txdr_hyper(vap->va_size, tl);
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_false;
}
if (vap->va_atime.tv_sec != VNOVAL) {
if (vap->va_atime.tv_sec != curtime.tv_sec) {
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_atime, tl);
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
if (vap->va_mtime.tv_sec != VNOVAL) {
if (vap->va_mtime.tv_sec != curtime.tv_sec) {
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_mtime, tl);
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
break;
case ND_NFSV4:
NFSZERO_ATTRBIT(&attrbits);
if (vap->va_mode != (mode_t)VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_MODE);
if ((flags & NFSSATTR_FULL) && vap->va_uid != (uid_t)VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
if ((flags & NFSSATTR_FULL) && vap->va_gid != (gid_t)VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP);
if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SIZE);
if (vap->va_atime.tv_sec != VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
if (vap->va_mtime.tv_sec != VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET);
(void) nfsv4_fillattr(nd, vp->v_mount, vp, NULL, vap, NULL, 0,
- &attrbits, NULL, NULL, 0, 0, 0, (uint64_t)0);
+ &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0);
break;
};
}
/*
* nfscl_request() - mostly a wrapper for newnfs_request().
*/
int
nfscl_request(struct nfsrv_descript *nd, struct vnode *vp, NFSPROC_T *p,
struct ucred *cred, void *stuff)
{
int ret, vers;
struct nfsmount *nmp;
nmp = VFSTONFS(vp->v_mount);
if (nd->nd_flag & ND_NFSV4)
vers = NFS_VER4;
else if (nd->nd_flag & ND_NFSV3)
vers = NFS_VER3;
else
vers = NFS_VER2;
ret = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
NFS_PROG, vers, NULL, 1, NULL);
return (ret);
}
/*
* fill in this bsden's variant of statfs using nfsstatfs.
*/
void
nfscl_loadsbinfo(struct nfsmount *nmp, struct nfsstatfs *sfp, void *statfs)
{
struct statfs *sbp = (struct statfs *)statfs;
nfsquad_t tquad;
if (nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) {
sbp->f_bsize = NFS_FABLKSIZE;
tquad.qval = sfp->sf_tbytes;
sbp->f_blocks = (long)(tquad.qval / ((u_quad_t)NFS_FABLKSIZE));
tquad.qval = sfp->sf_fbytes;
sbp->f_bfree = (long)(tquad.qval / ((u_quad_t)NFS_FABLKSIZE));
tquad.qval = sfp->sf_abytes;
sbp->f_bavail = (long)(tquad.qval / ((u_quad_t)NFS_FABLKSIZE));
tquad.qval = sfp->sf_tfiles;
sbp->f_files = (tquad.lval[0] & 0x7fffffff);
tquad.qval = sfp->sf_ffiles;
sbp->f_ffree = (tquad.lval[0] & 0x7fffffff);
} else if ((nmp->nm_flag & NFSMNT_NFSV4) == 0) {
sbp->f_bsize = (int32_t)sfp->sf_bsize;
sbp->f_blocks = (int32_t)sfp->sf_blocks;
sbp->f_bfree = (int32_t)sfp->sf_bfree;
sbp->f_bavail = (int32_t)sfp->sf_bavail;
sbp->f_files = 0;
sbp->f_ffree = 0;
}
}
/*
* Use the fsinfo stuff to update the mount point.
*/
void
nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp)
{
if ((nmp->nm_wsize == 0 || fsp->fs_wtpref < nmp->nm_wsize) &&
fsp->fs_wtpref >= NFS_FABLKSIZE)
nmp->nm_wsize = (fsp->fs_wtpref + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
if (fsp->fs_wtmax < nmp->nm_wsize && fsp->fs_wtmax > 0) {
nmp->nm_wsize = fsp->fs_wtmax & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_wsize == 0)
nmp->nm_wsize = fsp->fs_wtmax;
}
if (nmp->nm_wsize < NFS_FABLKSIZE)
nmp->nm_wsize = NFS_FABLKSIZE;
if ((nmp->nm_rsize == 0 || fsp->fs_rtpref < nmp->nm_rsize) &&
fsp->fs_rtpref >= NFS_FABLKSIZE)
nmp->nm_rsize = (fsp->fs_rtpref + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
if (fsp->fs_rtmax < nmp->nm_rsize && fsp->fs_rtmax > 0) {
nmp->nm_rsize = fsp->fs_rtmax & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_rsize == 0)
nmp->nm_rsize = fsp->fs_rtmax;
}
if (nmp->nm_rsize < NFS_FABLKSIZE)
nmp->nm_rsize = NFS_FABLKSIZE;
if ((nmp->nm_readdirsize == 0 || fsp->fs_dtpref < nmp->nm_readdirsize)
&& fsp->fs_dtpref >= NFS_DIRBLKSIZ)
nmp->nm_readdirsize = (fsp->fs_dtpref + NFS_DIRBLKSIZ - 1) &
~(NFS_DIRBLKSIZ - 1);
if (fsp->fs_rtmax < nmp->nm_readdirsize && fsp->fs_rtmax > 0) {
nmp->nm_readdirsize = fsp->fs_rtmax & ~(NFS_DIRBLKSIZ - 1);
if (nmp->nm_readdirsize == 0)
nmp->nm_readdirsize = fsp->fs_rtmax;
}
if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
nmp->nm_readdirsize = NFS_DIRBLKSIZ;
if (fsp->fs_maxfilesize > 0 &&
fsp->fs_maxfilesize < nmp->nm_maxfilesize)
nmp->nm_maxfilesize = fsp->fs_maxfilesize;
nmp->nm_mountp->mnt_stat.f_iosize = newnfs_iosize(nmp);
nmp->nm_state |= NFSSTA_GOTFSINFO;
}
/*
* Get a pointer to my IP addrress and return it.
* Return NULL if you can't find one.
*/
u_int8_t *
nfscl_getmyip(struct nfsmount *nmp, int *isinet6p)
{
struct sockaddr_in sad, *sin;
struct rtentry *rt;
u_int8_t *retp = NULL;
static struct in_addr laddr;
*isinet6p = 0;
/*
* Loop up a route for the destination address.
*/
if (nmp->nm_nam->sa_family == AF_INET) {
bzero(&sad, sizeof (sad));
sin = (struct sockaddr_in *)nmp->nm_nam;
sad.sin_family = AF_INET;
sad.sin_len = sizeof (struct sockaddr_in);
sad.sin_addr.s_addr = sin->sin_addr.s_addr;
rt = rtalloc1((struct sockaddr *)&sad, 0, 0UL);
if (rt != NULL) {
if (rt->rt_ifp != NULL &&
rt->rt_ifa != NULL &&
((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) &&
rt->rt_ifa->ifa_addr->sa_family == AF_INET) {
sin = (struct sockaddr_in *)
rt->rt_ifa->ifa_addr;
laddr.s_addr = sin->sin_addr.s_addr;
retp = (u_int8_t *)&laddr;
}
RTFREE_LOCKED(rt);
}
#ifdef INET6
} else if (nmp->nm_nam->sa_family == AF_INET6) {
struct sockaddr_in6 sad6, *sin6;
static struct in6_addr laddr6;
bzero(&sad6, sizeof (sad6));
sin6 = (struct sockaddr_in6 *)nmp->nm_nam;
sad6.sin6_family = AF_INET6;
sad6.sin6_len = sizeof (struct sockaddr_in6);
sad6.sin6_addr = sin6->sin6_addr;
rt = rtalloc1((struct sockaddr *)&sad6, 0, 0UL);
if (rt != NULL) {
if (rt->rt_ifp != NULL &&
rt->rt_ifa != NULL &&
((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) &&
rt->rt_ifa->ifa_addr->sa_family == AF_INET6) {
sin6 = (struct sockaddr_in6 *)
rt->rt_ifa->ifa_addr;
laddr6 = sin6->sin6_addr;
retp = (u_int8_t *)&laddr6;
*isinet6p = 1;
}
RTFREE_LOCKED(rt);
}
#endif
}
return (retp);
}
/*
* Copy NFS uid, gids from the cred structure.
*/
void
newnfs_copyincred(struct ucred *cr, struct nfscred *nfscr)
{
int i;
KASSERT(cr->cr_ngroups >= 0,
("newnfs_copyincred: negative cr_ngroups"));
nfscr->nfsc_uid = cr->cr_uid;
nfscr->nfsc_ngroups = MIN(cr->cr_ngroups, NFS_MAXGRPS + 1);
for (i = 0; i < nfscr->nfsc_ngroups; i++)
nfscr->nfsc_groups[i] = cr->cr_groups[i];
}
/*
* Do any client specific initialization.
*/
void
nfscl_init(void)
{
static int inited = 0;
if (inited)
return;
inited = 1;
nfscl_inited = 1;
ncl_pbuf_freecnt = nswbuf / 2 + 1;
}
/*
* Check each of the attributes to be set, to ensure they aren't already
* the correct value. Disable setting ones already correct.
*/
int
nfscl_checksattr(struct vattr *vap, struct nfsvattr *nvap)
{
if (vap->va_mode != (mode_t)VNOVAL) {
if (vap->va_mode == nvap->na_mode)
vap->va_mode = (mode_t)VNOVAL;
}
if (vap->va_uid != (uid_t)VNOVAL) {
if (vap->va_uid == nvap->na_uid)
vap->va_uid = (uid_t)VNOVAL;
}
if (vap->va_gid != (gid_t)VNOVAL) {
if (vap->va_gid == nvap->na_gid)
vap->va_gid = (gid_t)VNOVAL;
}
if (vap->va_size != VNOVAL) {
if (vap->va_size == nvap->na_size)
vap->va_size = VNOVAL;
}
/*
* We are normally called with only a partially initialized
* VAP. Since the NFSv3 spec says that server may use the
* file attributes to store the verifier, the spec requires
* us to do a SETATTR RPC. FreeBSD servers store the verifier
* in atime, but we can't really assume that all servers will
* so we ensure that our SETATTR sets both atime and mtime.
*/
if (vap->va_mtime.tv_sec == VNOVAL)
vfs_timestamp(&vap->va_mtime);
if (vap->va_atime.tv_sec == VNOVAL)
vap->va_atime = vap->va_mtime;
return (1);
}
/*
* Map nfsv4 errors to errno.h errors.
* The uid and gid arguments are only used for NFSERR_BADOWNER and that
* error should only be returned for the Open, Create and Setattr Ops.
* As such, most calls can just pass in 0 for those arguments.
*/
APPLESTATIC int
nfscl_maperr(struct thread *td, int error, uid_t uid, gid_t gid)
{
struct proc *p;
if (error < 10000)
return (error);
if (td != NULL)
p = td->td_proc;
else
p = NULL;
switch (error) {
case NFSERR_BADOWNER:
tprintf(p, LOG_INFO,
"No name and/or group mapping for uid,gid:(%d,%d)\n",
uid, gid);
return (EPERM);
case NFSERR_STALECLIENTID:
case NFSERR_STALESTATEID:
case NFSERR_EXPIRED:
case NFSERR_BADSTATEID:
printf("nfsv4 recover err returned %d\n", error);
return (EIO);
case NFSERR_BADHANDLE:
case NFSERR_SERVERFAULT:
case NFSERR_BADTYPE:
case NFSERR_FHEXPIRED:
case NFSERR_RESOURCE:
case NFSERR_MOVED:
case NFSERR_NOFILEHANDLE:
case NFSERR_MINORVERMISMATCH:
case NFSERR_OLDSTATEID:
case NFSERR_BADSEQID:
case NFSERR_LEASEMOVED:
case NFSERR_RECLAIMBAD:
case NFSERR_BADXDR:
case NFSERR_BADCHAR:
case NFSERR_BADNAME:
case NFSERR_OPILLEGAL:
printf("nfsv4 client/server protocol prob err=%d\n",
error);
return (EIO);
default:
tprintf(p, LOG_INFO, "nfsv4 err=%d\n", error);
return (EIO);
};
}
/*
* Locate a process by number; return only "live" processes -- i.e., neither
* zombies nor newly born but incompletely initialized processes. By not
* returning processes in the PRS_NEW state, we allow callers to avoid
* testing for that condition to avoid dereferencing p_ucred, et al.
* Identical to pfind() in kern_proc.c, except it assume the list is
* already locked.
*/
static struct proc *
pfind_locked(pid_t pid)
{
struct proc *p;
LIST_FOREACH(p, PIDHASH(pid), p_hash)
if (p->p_pid == pid) {
if (p->p_state == PRS_NEW) {
p = NULL;
break;
}
PROC_LOCK(p);
break;
}
return (p);
}
/*
* Check to see if the process for this owner exists. Return 1 if it doesn't
* and 0 otherwise.
*/
int
nfscl_procdoesntexist(u_int8_t *own)
{
union {
u_int32_t lval;
u_int8_t cval[4];
} tl;
struct proc *p;
pid_t pid;
int ret = 0;
tl.cval[0] = *own++;
tl.cval[1] = *own++;
tl.cval[2] = *own++;
tl.cval[3] = *own++;
pid = tl.lval;
p = pfind_locked(pid);
if (p == NULL)
return (1);
if (p->p_stats == NULL) {
PROC_UNLOCK(p);
return (0);
}
tl.cval[0] = *own++;
tl.cval[1] = *own++;
tl.cval[2] = *own++;
tl.cval[3] = *own++;
if (tl.lval != p->p_stats->p_start.tv_sec) {
ret = 1;
} else {
tl.cval[0] = *own++;
tl.cval[1] = *own++;
tl.cval[2] = *own++;
tl.cval[3] = *own;
if (tl.lval != p->p_stats->p_start.tv_usec)
ret = 1;
}
PROC_UNLOCK(p);
return (ret);
}
/*
* - nfs pseudo system call for the client
*/
/*
* MPSAFE
*/
static int
nfssvc_nfscl(struct thread *td, struct nfssvc_args *uap)
{
struct file *fp;
struct nfscbd_args nfscbdarg;
struct nfsd_nfscbd_args nfscbdarg2;
int error;
if (uap->flag & NFSSVC_CBADDSOCK) {
error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg));
if (error)
return (error);
if ((error = fget(td, nfscbdarg.sock, &fp)) != 0) {
return (error);
}
if (fp->f_type != DTYPE_SOCKET) {
fdrop(fp, td);
return (EPERM);
}
error = nfscbd_addsock(fp);
fdrop(fp, td);
if (!error && nfscl_enablecallb == 0) {
nfsv4_cbport = nfscbdarg.port;
nfscl_enablecallb = 1;
}
} else if (uap->flag & NFSSVC_NFSCBD) {
if (uap->argp == NULL)
return (EINVAL);
error = copyin(uap->argp, (caddr_t)&nfscbdarg2,
sizeof(nfscbdarg2));
if (error)
return (error);
error = nfscbd_nfsd(td, &nfscbdarg2);
} else {
error = EINVAL;
}
return (error);
}
extern int (*nfsd_call_nfscl)(struct thread *, struct nfssvc_args *);
/*
* Called once to initialize data structures...
*/
static int
nfscl_modevent(module_t mod, int type, void *data)
{
int error = 0;
static int loaded = 0;
switch (type) {
case MOD_LOAD:
if (loaded)
return (0);
newnfs_portinit();
mtx_init(&nfs_clstate_mutex, "nfs_clstate_mutex", NULL,
MTX_DEF);
mtx_init(&ncl_iod_mutex, "ncl_iod_mutex", NULL, MTX_DEF);
nfscl_init();
NFSD_LOCK();
nfsrvd_cbinit(0);
NFSD_UNLOCK();
ncl_call_invalcaches = ncl_invalcaches;
nfsd_call_nfscl = nfssvc_nfscl;
loaded = 1;
break;
case MOD_UNLOAD:
if (nfs_numnfscbd != 0) {
error = EBUSY;
break;
}
/*
* XXX: Unloading of nfscl module is unsupported.
*/
#if 0
ncl_call_invalcaches = NULL;
nfsd_call_nfscl = NULL;
/* and get rid of the mutexes */
mtx_destroy(&nfs_clstate_mutex);
mtx_destroy(&ncl_iod_mutex);
loaded = 0;
break;
#else
/* FALLTHROUGH */
#endif
default:
error = EOPNOTSUPP;
break;
}
return error;
}
static moduledata_t nfscl_mod = {
"nfscl",
nfscl_modevent,
NULL,
};
DECLARE_MODULE(nfscl, nfscl_mod, SI_SUB_VFS, SI_ORDER_FIRST);
/* So that loader and kldload(2) can find us, wherever we are.. */
MODULE_VERSION(nfscl, 1);
MODULE_DEPEND(nfscl, nfscommon, 1, 1, 1);
MODULE_DEPEND(nfscl, krpc, 1, 1, 1);
MODULE_DEPEND(nfscl, nfssvc, 1, 1, 1);
MODULE_DEPEND(nfscl, nfslock, 1, 1, 1);
Index: stable/8/sys/fs/nfsclient/nfs_clrpcops.c
===================================================================
--- stable/8/sys/fs/nfsclient/nfs_clrpcops.c (revision 221216)
+++ stable/8/sys/fs/nfsclient/nfs_clrpcops.c (revision 221217)
@@ -1,4187 +1,4187 @@
/*-
* 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.
* 4. 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$");
/*
* Rpc op calls, generally called from the vnode op calls or through the
* buffer cache, for NFS v2, 3 and 4.
* These do not normally make any changes to vnode arguments or use
* structures that might change between the VFS variants. The returned
* arguments are all at the end, after the NFSPROC_T *p one.
*/
#ifndef APPLEKEXT
#include <fs/nfs/nfsport.h>
/*
* Global variables
*/
extern int nfs_numnfscbd;
extern struct timeval nfsboottime;
extern u_int32_t newnfs_false, newnfs_true;
extern nfstype nfsv34_type[9];
extern int nfsrv_useacl;
extern char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
NFSCLSTATEMUTEX;
int nfstest_outofseq = 0;
int nfscl_assumeposixlocks = 1;
int nfscl_enablecallb = 0;
short nfsv4_cbport = NFSV4_CBPORT;
int nfstest_openallsetattr = 0;
#endif /* !APPLEKEXT */
#define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
static int nfsrpc_setattrrpc(vnode_t , struct vattr *, nfsv4stateid_t *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_readrpc(vnode_t , struct uio *, struct ucred *,
nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_writerpc(vnode_t , struct uio *, int *, u_char *,
struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *,
void *);
static int nfsrpc_createv23(vnode_t , char *, int, struct vattr *,
nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *,
struct nfsvattr *, struct nfsfh **, int *, int *, void *);
static int nfsrpc_createv4(vnode_t , char *, int, struct vattr *,
nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **, struct ucred *,
NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *,
int *, void *, int *);
static int nfsrpc_locku(struct nfsrv_descript *, struct nfsmount *,
struct nfscllockowner *, u_int64_t, u_int64_t,
u_int32_t, struct ucred *, NFSPROC_T *, int);
static int nfsrpc_setaclrpc(vnode_t, struct ucred *, NFSPROC_T *,
struct acl *, nfsv4stateid_t *, void *);
/*
* nfs null call from vfs.
*/
APPLESTATIC int
nfsrpc_null(vnode_t vp, struct ucred *cred, NFSPROC_T *p)
{
int error;
struct nfsrv_descript nfsd, *nd = &nfsd;
NFSCL_REQSTART(nd, NFSPROC_NULL, vp);
error = nfscl_request(nd, vp, p, cred, NULL);
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs access rpc op.
* For nfs version 3 and 4, use the access rpc to check accessibility. If file
* modes are changed on the server, accesses might still fail later.
*/
APPLESTATIC int
nfsrpc_access(vnode_t vp, int acmode, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
{
int error;
u_int32_t mode, rmode;
if (acmode & VREAD)
mode = NFSACCESS_READ;
else
mode = 0;
if (vnode_vtype(vp) == VDIR) {
if (acmode & VWRITE)
mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND |
NFSACCESS_DELETE);
if (acmode & VEXEC)
mode |= NFSACCESS_LOOKUP;
} else {
if (acmode & VWRITE)
mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
if (acmode & VEXEC)
mode |= NFSACCESS_EXECUTE;
}
/*
* Now, just call nfsrpc_accessrpc() to do the actual RPC.
*/
error = nfsrpc_accessrpc(vp, mode, cred, p, nap, attrflagp, &rmode,
NULL);
/*
* The NFS V3 spec does not clarify whether or not
* the returned access bits can be a superset of
* the ones requested, so...
*/
if (!error && (rmode & mode) != mode)
error = EACCES;
return (error);
}
/*
* The actual rpc, separated out for Darwin.
*/
APPLESTATIC int
nfsrpc_accessrpc(vnode_t vp, u_int32_t mode, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, u_int32_t *rmodep,
void *stuff)
{
u_int32_t *tl;
u_int32_t supported, rmode;
int error;
struct nfsrv_descript nfsd, *nd = &nfsd;
nfsattrbit_t attrbits;
*attrflagp = 0;
supported = mode;
NFSCL_REQSTART(nd, NFSPROC_ACCESS, vp);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(mode);
if (nd->nd_flag & ND_NFSV4) {
/*
* And do a Getattr op.
*/
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSGETATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV3) {
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (error)
goto nfsmout;
}
if (!nd->nd_repstat) {
if (nd->nd_flag & ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
supported = fxdr_unsigned(u_int32_t, *tl++);
} else {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
}
rmode = fxdr_unsigned(u_int32_t, *tl);
if (nd->nd_flag & ND_NFSV4)
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
/*
* It's not obvious what should be done about
* unsupported access modes. For now, be paranoid
* and clear the unsupported ones.
*/
rmode &= supported;
*rmodep = rmode;
} else
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs open rpc
*/
APPLESTATIC int
nfsrpc_open(vnode_t vp, int amode, struct ucred *cred, NFSPROC_T *p)
{
struct nfsclopen *op;
struct nfscldeleg *dp;
struct nfsfh *nfhp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
u_int32_t mode, clidrev;
int ret, newone, error, expireret = 0, retrycnt;
/*
* For NFSv4, Open Ops are only done on Regular Files.
*/
if (vnode_vtype(vp) != VREG)
return (0);
mode = 0;
if (amode & FREAD)
mode |= NFSV4OPEN_ACCESSREAD;
if (amode & FWRITE)
mode |= NFSV4OPEN_ACCESSWRITE;
nfhp = np->n_fhp;
retrycnt = 0;
#ifdef notdef
{ char name[100]; int namel;
namel = (np->n_v4->n4_namelen < 100) ? np->n_v4->n4_namelen : 99;
bcopy(NFS4NODENAME(np->n_v4), name, namel);
name[namel] = '\0';
printf("rpcopen p=0x%x name=%s",p->p_pid,name);
if (nfhp->nfh_len > 0) printf(" fh=0x%x\n",nfhp->nfh_fh[12]);
else printf(" fhl=0\n");
}
#endif
do {
dp = NULL;
error = nfscl_open(vp, nfhp->nfh_fh, nfhp->nfh_len, mode, 1,
cred, p, NULL, &op, &newone, &ret, 1);
if (error) {
return (error);
}
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
else
clidrev = 0;
if (ret == NFSCLOPEN_DOOPEN) {
if (np->n_v4 != NULL) {
error = nfsrpc_openrpc(nmp, vp, np->n_v4->n4_data,
np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len, mode, op,
NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &dp,
0, 0x0, cred, p, 0, 0);
if (dp != NULL) {
#ifdef APPLE
OSBitAndAtomic((int32_t)~NDELEGMOD, (UInt32 *)&np->n_flag);
#else
NFSLOCKNODE(np);
np->n_flag &= ~NDELEGMOD;
/*
* Invalidate the attribute cache, so that
* attributes that pre-date the issue of a
* delegation are not cached, since the
* cached attributes will remain valid while
* the delegation is held.
*/
NFSINVALATTRCACHE(np);
NFSUNLOCKNODE(np);
#endif
(void) nfscl_deleg(nmp->nm_mountp,
op->nfso_own->nfsow_clp,
nfhp->nfh_fh, nfhp->nfh_len, cred, p, &dp);
}
} else {
error = EIO;
}
newnfs_copyincred(cred, &op->nfso_cred);
} else if (ret == NFSCLOPEN_SETCRED)
/*
* This is a new local open on a delegation. It needs
* to have credentials so that an open can be done
* against the server during recovery.
*/
newnfs_copyincred(cred, &op->nfso_cred);
/*
* nfso_opencnt is the count of how many VOP_OPEN()s have
* been done on this Open successfully and a VOP_CLOSE()
* is expected for each of these.
* If error is non-zero, don't increment it, since the Open
* hasn't succeeded yet.
*/
if (!error)
op->nfso_opencnt++;
nfscl_openrelease(op, error, newone);
if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY) {
(void) nfs_catnap(PZERO, error, "nfs_open");
} else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
&& clidrev != 0) {
expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
retrycnt++;
}
} while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
expireret == 0 && clidrev != 0 && retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
return (error);
}
/*
* the actual open rpc
*/
APPLESTATIC int
nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
u_int8_t *name, int namelen, struct nfscldeleg **dpp,
int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p,
int syscred, int recursed)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfscldeleg *dp, *ndp = NULL;
struct nfsvattr nfsva;
u_int32_t rflags, deleg;
nfsattrbit_t attrbits;
int error, ret, acesize, limitby;
dp = *dpp;
*dpp = NULL;
nfscl_reqstart(nd, NFSPROC_OPEN, nmp, nfhp, fhlen, NULL);
NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
*tl++ = op->nfso_own->nfsow_clp->nfsc_clientid.lval[0];
*tl = op->nfso_own->nfsow_clp->nfsc_clientid.lval[1];
(void) nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
if (reclaim) {
*tl = txdr_unsigned(NFSV4OPEN_CLAIMPREVIOUS);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(delegtype);
} else {
if (dp != NULL) {
*tl = txdr_unsigned(NFSV4OPEN_CLAIMDELEGATECUR);
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
*tl++ = dp->nfsdl_stateid.seqid;
*tl++ = dp->nfsdl_stateid.other[0];
*tl++ = dp->nfsdl_stateid.other[1];
*tl = dp->nfsdl_stateid.other[2];
} else {
*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
}
(void) nfsm_strtom(nd, name, namelen);
}
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY);
(void) nfsrv_putattrbit(nd, &attrbits);
if (syscred)
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
if (!nd->nd_repstat) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
6 * NFSX_UNSIGNED);
op->nfso_stateid.seqid = *tl++;
op->nfso_stateid.other[0] = *tl++;
op->nfso_stateid.other[1] = *tl++;
op->nfso_stateid.other[2] = *tl;
rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
if (error)
goto nfsmout;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
deleg = fxdr_unsigned(u_int32_t, *tl);
if (deleg == NFSV4OPEN_DELEGATEREAD ||
deleg == NFSV4OPEN_DELEGATEWRITE) {
if (!(op->nfso_own->nfsow_clp->nfsc_flags &
NFSCLFLAGS_FIRSTDELEG))
op->nfso_own->nfsow_clp->nfsc_flags |=
(NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
MALLOC(ndp, struct nfscldeleg *,
sizeof (struct nfscldeleg) + newfhlen,
M_NFSCLDELEG, M_WAITOK);
LIST_INIT(&ndp->nfsdl_owner);
LIST_INIT(&ndp->nfsdl_lock);
ndp->nfsdl_clp = op->nfso_own->nfsow_clp;
ndp->nfsdl_fhlen = newfhlen;
NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen);
newnfs_copyincred(cred, &ndp->nfsdl_cred);
nfscl_lockinit(&ndp->nfsdl_rwlock);
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
NFSX_UNSIGNED);
ndp->nfsdl_stateid.seqid = *tl++;
ndp->nfsdl_stateid.other[0] = *tl++;
ndp->nfsdl_stateid.other[1] = *tl++;
ndp->nfsdl_stateid.other[2] = *tl++;
ret = fxdr_unsigned(int, *tl);
if (deleg == NFSV4OPEN_DELEGATEWRITE) {
ndp->nfsdl_flags = NFSCLDL_WRITE;
/*
* Indicates how much the file can grow.
*/
NFSM_DISSECT(tl, u_int32_t *,
3 * NFSX_UNSIGNED);
limitby = fxdr_unsigned(int, *tl++);
switch (limitby) {
case NFSV4OPEN_LIMITSIZE:
ndp->nfsdl_sizelimit = fxdr_hyper(tl);
break;
case NFSV4OPEN_LIMITBLOCKS:
ndp->nfsdl_sizelimit =
fxdr_unsigned(u_int64_t, *tl++);
ndp->nfsdl_sizelimit *=
fxdr_unsigned(u_int64_t, *tl);
break;
default:
error = NFSERR_BADXDR;
goto nfsmout;
};
} else {
ndp->nfsdl_flags = NFSCLDL_READ;
}
if (ret)
ndp->nfsdl_flags |= NFSCLDL_RECALL;
error = nfsrv_dissectace(nd, &ndp->nfsdl_ace, &ret,
&acesize, p);
if (error)
goto nfsmout;
} else if (deleg != NFSV4OPEN_DELEGATENONE) {
error = NFSERR_BADXDR;
goto nfsmout;
}
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
NULL, NULL, NULL, p, cred);
if (error)
goto nfsmout;
if (ndp != NULL) {
ndp->nfsdl_change = nfsva.na_filerev;
ndp->nfsdl_modtime = nfsva.na_mtime;
ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
}
if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM)) {
do {
ret = nfsrpc_openconfirm(vp, newfhp, newfhlen, op,
cred, p);
if (ret == NFSERR_DELAY)
(void) nfs_catnap(PZERO, ret, "nfs_open");
} while (ret == NFSERR_DELAY);
error = ret;
}
if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) ||
nfscl_assumeposixlocks)
op->nfso_posixlock = 1;
else
op->nfso_posixlock = 0;
/*
* If the server is handing out delegations, but we didn't
* get one because an OpenConfirm was required, try the
* Open again, to get a delegation. This is a harmless no-op,
* from a server's point of view.
*/
if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM) &&
(op->nfso_own->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG)
&& !error && dp == NULL && ndp == NULL && !recursed) {
do {
ret = nfsrpc_openrpc(nmp, vp, nfhp, fhlen, newfhp,
newfhlen, mode, op, name, namelen, &ndp, 0, 0x0,
cred, p, syscred, 1);
if (ret == NFSERR_DELAY)
(void) nfs_catnap(PZERO, ret, "nfs_open2");
} while (ret == NFSERR_DELAY);
if (ret) {
if (ndp != NULL)
FREE((caddr_t)ndp, M_NFSCLDELEG);
if (ret == NFSERR_STALECLIENTID ||
ret == NFSERR_STALEDONTRECOVER)
error = ret;
}
}
}
if (nd->nd_repstat != 0 && error == 0)
error = nd->nd_repstat;
if (error == NFSERR_STALECLIENTID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
if (!error)
*dpp = ndp;
else if (ndp != NULL)
FREE((caddr_t)ndp, M_NFSCLDELEG);
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* open downgrade rpc
*/
APPLESTATIC int
nfsrpc_opendowngrade(vnode_t vp, u_int32_t mode, struct nfsclopen *op,
struct ucred *cred, NFSPROC_T *p)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
NFSCL_REQSTART(nd, NFSPROC_OPENDOWNGRADE, vp);
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
*tl++ = op->nfso_stateid.seqid;
*tl++ = op->nfso_stateid.other[0];
*tl++ = op->nfso_stateid.other[1];
*tl++ = op->nfso_stateid.other[2];
*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
*tl = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
error = nfscl_request(nd, vp, p, cred, NULL);
if (error)
return (error);
NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
if (!nd->nd_repstat) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
op->nfso_stateid.seqid = *tl++;
op->nfso_stateid.other[0] = *tl++;
op->nfso_stateid.other[1] = *tl++;
op->nfso_stateid.other[2] = *tl;
}
if (nd->nd_repstat && error == 0)
error = nd->nd_repstat;
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* V4 Close operation.
*/
APPLESTATIC int
nfsrpc_close(vnode_t vp, int doclose, NFSPROC_T *p)
{
struct nfsclclient *clp;
int error;
if (vnode_vtype(vp) != VREG)
return (0);
if (doclose)
error = nfscl_doclose(vp, &clp, p);
else
error = nfscl_getclose(vp, &clp);
if (error)
return (error);
nfscl_clientrelease(clp);
return (0);
}
/*
* Close the open.
*/
APPLESTATIC void
nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfscllockowner *lp;
struct nfscllock *lop, *nlop;
struct ucred *tcred;
u_int64_t off = 0, len = 0;
u_int32_t type = NFSV4LOCKT_READ;
int error, do_unlock, trycnt;
tcred = newnfs_getcred();
newnfs_copycred(&op->nfso_cred, tcred);
/*
* (Theoretically this could be done in the same
* compound as the close, but having multiple
* sequenced Ops in the same compound might be
* too scary for some servers.)
*/
if (op->nfso_posixlock) {
off = 0;
len = NFS64BITSSET;
type = NFSV4LOCKT_READ;
}
/*
* Since this function is only called from VOP_INACTIVE(), no
* other thread will be manipulating this Open. As such, the
* lock lists are not being changed by other threads, so it should
* be safe to do this without locking.
*/
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
do_unlock = 1;
LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
if (op->nfso_posixlock == 0) {
off = lop->nfslo_first;
len = lop->nfslo_end - lop->nfslo_first;
if (lop->nfslo_type == F_WRLCK)
type = NFSV4LOCKT_WRITE;
else
type = NFSV4LOCKT_READ;
}
if (do_unlock) {
trycnt = 0;
do {
error = nfsrpc_locku(nd, nmp, lp, off,
len, type, tcred, p, 0);
if ((nd->nd_repstat == NFSERR_GRACE ||
nd->nd_repstat == NFSERR_DELAY) &&
error == 0)
(void) nfs_catnap(PZERO,
(int)nd->nd_repstat,
"nfs_close");
} while ((nd->nd_repstat == NFSERR_GRACE ||
nd->nd_repstat == NFSERR_DELAY) &&
error == 0 && trycnt++ < 5);
if (op->nfso_posixlock)
do_unlock = 0;
}
nfscl_freelock(lop, 0);
}
}
/*
* There could be other Opens for different files on the same
* OpenOwner, so locking is required.
*/
NFSLOCKCLSTATE();
nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
NFSUNLOCKCLSTATE();
do {
error = nfscl_tryclose(op, tcred, nmp, p);
if (error == NFSERR_GRACE)
(void) nfs_catnap(PZERO, error, "nfs_close");
} while (error == NFSERR_GRACE);
NFSLOCKCLSTATE();
nfscl_lockunlock(&op->nfso_own->nfsow_rwlock);
/*
* Move the lockowner to nfsc_defunctlockowner,
* so the Renew thread will do the ReleaseLockOwner
* Op on it later. There might still be other
* opens using the same lockowner name.
*/
lp = LIST_FIRST(&op->nfso_lock);
if (lp != NULL) {
while (LIST_NEXT(lp, nfsl_list) != NULL)
lp = LIST_NEXT(lp, nfsl_list);
LIST_PREPEND(&nmp->nm_clp->nfsc_defunctlockowner,
&op->nfso_lock, lp, nfsl_list);
LIST_INIT(&op->nfso_lock);
}
nfscl_freeopen(op, 0);
NFSUNLOCKCLSTATE();
NFSFREECRED(tcred);
}
/*
* The actual Close RPC.
*/
APPLESTATIC int
nfsrpc_closerpc(struct nfsrv_descript *nd, struct nfsmount *nmp,
struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p,
int syscred)
{
u_int32_t *tl;
int error;
nfscl_reqstart(nd, NFSPROC_CLOSE, nmp, op->nfso_fh,
op->nfso_fhlen, NULL);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
*tl++ = op->nfso_stateid.seqid;
*tl++ = op->nfso_stateid.other[0];
*tl++ = op->nfso_stateid.other[1];
*tl = op->nfso_stateid.other[2];
if (syscred)
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
if (nd->nd_repstat == 0)
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
error = nd->nd_repstat;
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* V4 Open Confirm RPC.
*/
APPLESTATIC int
nfsrpc_openconfirm(vnode_t vp, u_int8_t *nfhp, int fhlen,
struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
nfscl_reqstart(nd, NFSPROC_OPENCONFIRM, VFSTONFS(vnode_mount(vp)),
nfhp, fhlen, NULL);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
*tl++ = op->nfso_stateid.seqid;
*tl++ = op->nfso_stateid.other[0];
*tl++ = op->nfso_stateid.other[1];
*tl++ = op->nfso_stateid.other[2];
*tl = txdr_unsigned(op->nfso_own->nfsow_seqid);
error = nfscl_request(nd, vp, p, cred, NULL);
if (error)
return (error);
NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
if (!nd->nd_repstat) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
op->nfso_stateid.seqid = *tl++;
op->nfso_stateid.other[0] = *tl++;
op->nfso_stateid.other[1] = *tl++;
op->nfso_stateid.other[2] = *tl;
}
error = nd->nd_repstat;
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* Do the setclientid and setclientid confirm RPCs. Called from nfs_statfs()
* when a mount has just occurred and when the server replies NFSERR_EXPIRED.
*/
APPLESTATIC int
nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp,
struct ucred *cred, NFSPROC_T *p)
{
u_int32_t *tl;
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
nfsattrbit_t attrbits;
u_int8_t *cp = NULL, *cp2, addr[INET6_ADDRSTRLEN + 9];
u_short port;
int error, isinet6 = 0, callblen;
nfsquad_t confirm;
u_int32_t lease;
static u_int32_t rev = 0;
if (nfsboottime.tv_sec == 0)
NFSSETBOOTTIME(nfsboottime);
nfscl_reqstart(nd, NFSPROC_SETCLIENTID, nmp, NULL, 0, NULL);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(nfsboottime.tv_sec);
*tl = txdr_unsigned(rev++);
(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
/*
* set up the callback address
*/
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFS_CALLBCKPROG);
callblen = strlen(nfsv4_callbackaddr);
if (callblen == 0)
cp = nfscl_getmyip(nmp, &isinet6);
if (nfscl_enablecallb && nfs_numnfscbd > 0 &&
(callblen > 0 || cp != NULL)) {
port = htons(nfsv4_cbport);
cp2 = (u_int8_t *)&port;
#ifdef INET6
if ((callblen > 0 &&
strchr(nfsv4_callbackaddr, ':')) || isinet6) {
char ip6buf[INET6_ADDRSTRLEN], *ip6add;
(void) nfsm_strtom(nd, "tcp6", 4);
if (callblen == 0) {
ip6_sprintf(ip6buf, (struct in6_addr *)cp);
ip6add = ip6buf;
} else {
ip6add = nfsv4_callbackaddr;
}
snprintf(addr, INET6_ADDRSTRLEN + 9, "%s.%d.%d",
ip6add, cp2[0], cp2[1]);
} else
#endif
{
(void) nfsm_strtom(nd, "tcp", 3);
if (callblen == 0)
snprintf(addr, INET6_ADDRSTRLEN + 9,
"%d.%d.%d.%d.%d.%d", cp[0], cp[1],
cp[2], cp[3], cp2[0], cp2[1]);
else
snprintf(addr, INET6_ADDRSTRLEN + 9,
"%s.%d.%d", nfsv4_callbackaddr,
cp2[0], cp2[1]);
}
(void) nfsm_strtom(nd, addr, strlen(addr));
} else {
(void) nfsm_strtom(nd, "tcp", 3);
(void) nfsm_strtom(nd, "0.0.0.0.0.0", 11);
}
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(clp->nfsc_cbident);
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
clp->nfsc_clientid.lval[0] = *tl++;
clp->nfsc_clientid.lval[1] = *tl++;
confirm.lval[0] = *tl++;
confirm.lval[1] = *tl;
mbuf_freem(nd->nd_mrep);
nd->nd_mrep = NULL;
/*
* and confirm it.
*/
nfscl_reqstart(nd, NFSPROC_SETCLIENTIDCFRM, nmp, NULL, 0, NULL);
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
*tl++ = clp->nfsc_clientid.lval[0];
*tl++ = clp->nfsc_clientid.lval[1];
*tl++ = confirm.lval[0];
*tl = confirm.lval[1];
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
cred, NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
mbuf_freem(nd->nd_mrep);
nd->nd_mrep = NULL;
if (nd->nd_repstat == 0) {
nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, nmp->nm_fh,
nmp->nm_fhsize, NULL);
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_LEASETIME);
(void) nfsrv_putattrbit(nd, &attrbits);
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
cred, NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, NULL, NULL, NULL, 0, NULL,
NULL, NULL, NULL, NULL, 0, NULL, &lease, NULL, p, cred);
if (error)
goto nfsmout;
clp->nfsc_renew = NFSCL_RENEW(lease);
clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
clp->nfsc_clientidrev++;
if (clp->nfsc_clientidrev == 0)
clp->nfsc_clientidrev++;
}
}
}
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs getattr call.
*/
APPLESTATIC int
nfsrpc_getattr(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
struct nfsvattr *nap, void *stuff)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
nfsattrbit_t attrbits;
NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
if (nd->nd_flag & ND_NFSV4) {
NFSGETATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (!nd->nd_repstat)
error = nfsm_loadattr(nd, nap);
else
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs getattr call with non-vnode arguemnts.
*/
APPLESTATIC int
nfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, u_int64_t *xidp)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
int error, vers = NFS_VER2;
nfsattrbit_t attrbits;
nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, fhp, fhlen, NULL);
if (nd->nd_flag & ND_NFSV4) {
vers = NFS_VER4;
NFSGETATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
} else if (nd->nd_flag & ND_NFSV3) {
vers = NFS_VER3;
}
if (syscred)
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, vers, NULL, 1, xidp);
if (error)
return (error);
if (!nd->nd_repstat)
error = nfsm_loadattr(nd, nap);
else
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* Do an nfs setattr operation.
*/
APPLESTATIC int
nfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *rnap, int *attrflagp,
void *stuff)
{
int error, expireret = 0, openerr, retrycnt;
u_int32_t clidrev = 0, mode;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
struct nfsfh *nfhp;
nfsv4stateid_t stateid;
void *lckp;
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
if (vap != NULL && NFSATTRISSET(u_quad_t, vap, va_size))
mode = NFSV4OPEN_ACCESSWRITE;
else
mode = NFSV4OPEN_ACCESSREAD;
retrycnt = 0;
do {
lckp = NULL;
openerr = 1;
if (NFSHASNFSV4(nmp)) {
nfhp = VTONFS(vp)->n_fhp;
error = nfscl_getstateid(vp, nfhp->nfh_fh,
nfhp->nfh_len, mode, cred, p, &stateid, &lckp);
if (error && vnode_vtype(vp) == VREG &&
(mode == NFSV4OPEN_ACCESSWRITE ||
nfstest_openallsetattr)) {
/*
* No Open stateid, so try and open the file
* now.
*/
if (mode == NFSV4OPEN_ACCESSWRITE)
openerr = nfsrpc_open(vp, FWRITE, cred,
p);
else
openerr = nfsrpc_open(vp, FREAD, cred,
p);
if (!openerr)
(void) nfscl_getstateid(vp,
nfhp->nfh_fh, nfhp->nfh_len,
mode, cred, p, &stateid, &lckp);
}
}
if (vap != NULL)
error = nfsrpc_setattrrpc(vp, vap, &stateid, cred, p,
rnap, attrflagp, stuff);
else
error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
stuff);
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
nfscl_lockderef(lckp);
if (!openerr)
(void) nfsrpc_close(vp, 0, p);
if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
error == NFSERR_OLDSTATEID) {
(void) nfs_catnap(PZERO, error, "nfs_setattr");
} else if ((error == NFSERR_EXPIRED ||
error == NFSERR_BADSTATEID) && clidrev != 0) {
expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
}
retrycnt++;
} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
(error == NFSERR_OLDSTATEID && retrycnt < 20) ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
expireret == 0 && clidrev != 0 && retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
return (error);
}
static int
nfsrpc_setattrrpc(vnode_t vp, struct vattr *vap,
nfsv4stateid_t *stateidp, struct ucred *cred, NFSPROC_T *p,
struct nfsvattr *rnap, int *attrflagp, void *stuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
nfsattrbit_t attrbits;
*attrflagp = 0;
NFSCL_REQSTART(nd, NFSPROC_SETATTR, vp);
if (nd->nd_flag & ND_NFSV4)
nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
vap->va_type = vnode_vtype(vp);
nfscl_fillsattr(nd, vap, vp, NFSSATTR_FULL, 0);
if (nd->nd_flag & ND_NFSV3) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = newnfs_false;
} else if (nd->nd_flag & ND_NFSV4) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSGETATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
error = nfscl_wcc_data(nd, vp, rnap, attrflagp, NULL, stuff);
if ((nd->nd_flag & ND_NFSV4) && !error)
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
if (!(nd->nd_flag & ND_NFSV3) && !nd->nd_repstat && !error)
error = nfscl_postop_attr(nd, rnap, attrflagp, stuff);
mbuf_freem(nd->nd_mrep);
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
return (error);
}
/*
* nfs lookup rpc
*/
APPLESTATIC int
nfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *dnap, struct nfsvattr *nap,
struct nfsfh **nfhpp, int *attrflagp, int *dattrflagp, void *stuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
struct nfsnode *np;
struct nfsfh *nfhp;
nfsattrbit_t attrbits;
int error = 0, lookupp = 0;
*attrflagp = 0;
*dattrflagp = 0;
if (vnode_vtype(dvp) != VDIR)
return (ENOTDIR);
nmp = VFSTONFS(vnode_mount(dvp));
if (len > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
if (NFSHASNFSV4(nmp) && len == 1 &&
name[0] == '.') {
/*
* Just return the current dir's fh.
*/
np = VTONFS(dvp);
MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
nfhp->nfh_len = np->n_fhp->nfh_len;
NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
*nfhpp = nfhp;
return (0);
}
if (NFSHASNFSV4(nmp) && len == 2 &&
name[0] == '.' && name[1] == '.') {
lookupp = 1;
NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, dvp);
} else {
NFSCL_REQSTART(nd, NFSPROC_LOOKUP, dvp);
(void) nfsm_strtom(nd, name, len);
}
if (nd->nd_flag & ND_NFSV4) {
NFSGETATTR_ATTRBIT(&attrbits);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OP_GETFH);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, dvp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_repstat) {
/*
* When an NFSv4 Lookupp returns ENOENT, it means that
* the lookup is at the root of an fs, so return this dir.
*/
if (nd->nd_repstat == NFSERR_NOENT && lookupp) {
np = VTONFS(dvp);
MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
nfhp->nfh_len = np->n_fhp->nfh_len;
NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
*nfhpp = nfhp;
mbuf_freem(nd->nd_mrep);
return (0);
}
if (nd->nd_flag & ND_NFSV3)
error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
goto nfsmout;
}
if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*(tl + 1)) {
nd->nd_flag |= ND_NOMOREDATA;
goto nfsmout;
}
}
error = nfsm_getfh(nd, nfhpp);
if (error)
goto nfsmout;
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if ((nd->nd_flag & ND_NFSV3) && !error)
error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
nfsmout:
mbuf_freem(nd->nd_mrep);
if (!error && nd->nd_repstat)
error = nd->nd_repstat;
return (error);
}
/*
* Do a readlink rpc.
*/
APPLESTATIC int
nfsrpc_readlink(vnode_t vp, struct uio *uiop, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsnode *np = VTONFS(vp);
nfsattrbit_t attrbits;
int error, len, cangetattr = 1;
*attrflagp = 0;
NFSCL_REQSTART(nd, NFSPROC_READLINK, vp);
if (nd->nd_flag & ND_NFSV4) {
/*
* And do a Getattr op.
*/
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSGETATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV3)
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (!nd->nd_repstat && !error) {
NFSM_STRSIZ(len, NFS_MAXPATHLEN);
/*
* This seems weird to me, but must have been added to
* FreeBSD for some reason. The only thing I can think of
* is that there was/is some server that replies with
* more link data than it should?
*/
if (len == NFS_MAXPATHLEN) {
NFSLOCKNODE(np);
if (np->n_size > 0 && np->n_size < NFS_MAXPATHLEN) {
len = np->n_size;
cangetattr = 0;
}
NFSUNLOCKNODE(np);
}
error = nfsm_mbufuio(nd, uiop, len);
if ((nd->nd_flag & ND_NFSV4) && !error && cangetattr)
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
}
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* Read operation.
*/
APPLESTATIC int
nfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
{
int error, expireret = 0, retrycnt;
u_int32_t clidrev = 0;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
struct nfsnode *np = VTONFS(vp);
struct ucred *newcred;
struct nfsfh *nfhp = NULL;
nfsv4stateid_t stateid;
void *lckp;
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
newcred = cred;
if (NFSHASNFSV4(nmp)) {
nfhp = np->n_fhp;
if (p == NULL)
newcred = NFSNEWCRED(cred);
}
retrycnt = 0;
do {
lckp = NULL;
if (NFSHASNFSV4(nmp))
(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
NFSV4OPEN_ACCESSREAD, newcred, p, &stateid, &lckp);
error = nfsrpc_readrpc(vp, uiop, newcred, &stateid, p, nap,
attrflagp, stuff);
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
nfscl_lockderef(lckp);
if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
error == NFSERR_OLDSTATEID) {
(void) nfs_catnap(PZERO, error, "nfs_read");
} else if ((error == NFSERR_EXPIRED ||
error == NFSERR_BADSTATEID) && clidrev != 0) {
expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
}
retrycnt++;
} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
(error == NFSERR_OLDSTATEID && retrycnt < 20) ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
expireret == 0 && clidrev != 0 && retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
if (NFSHASNFSV4(nmp) && p == NULL)
NFSFREECRED(newcred);
return (error);
}
/*
* The actual read RPC.
*/
static int
nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
nfsv4stateid_t *stateidp, NFSPROC_T *p, struct nfsvattr *nap,
int *attrflagp, void *stuff)
{
u_int32_t *tl;
int error = 0, len, retlen, tsiz, eof = 0;
struct nfsrv_descript nfsd;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
struct nfsrv_descript *nd = &nfsd;
*attrflagp = 0;
tsiz = uio_uio_resid(uiop);
if (uiop->uio_offset + tsiz > 0xffffffff &&
!NFSHASNFSV3OR4(nmp))
return (EFBIG);
nd->nd_mrep = NULL;
while (tsiz > 0) {
*attrflagp = 0;
len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
NFSCL_REQSTART(nd, NFSPROC_READ, vp);
if (nd->nd_flag & ND_NFSV4)
nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
if (nd->nd_flag & ND_NFSV2) {
*tl++ = txdr_unsigned(uiop->uio_offset);
*tl++ = txdr_unsigned(len);
*tl = 0;
} else {
txdr_hyper(uiop->uio_offset, tl);
*(tl + 2) = txdr_unsigned(len);
}
/*
* Since I can't do a Getattr for NFSv4 for Write, there
* doesn't seem any point in doing one here, either.
* (See the comment in nfsrpc_writerpc() for more info.)
*/
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV3) {
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
error = nfsm_loadattr(nd, nap);
if (!error)
*attrflagp = 1;
}
if (nd->nd_repstat || error) {
if (!error)
error = nd->nd_repstat;
goto nfsmout;
}
if (nd->nd_flag & ND_NFSV3) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
eof = fxdr_unsigned(int, *(tl + 1));
} else if (nd->nd_flag & ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
eof = fxdr_unsigned(int, *tl);
}
NFSM_STRSIZ(retlen, nmp->nm_rsize);
error = nfsm_mbufuio(nd, uiop, retlen);
if (error)
goto nfsmout;
mbuf_freem(nd->nd_mrep);
nd->nd_mrep = NULL;
tsiz -= retlen;
if (!(nd->nd_flag & ND_NFSV2)) {
if (eof || retlen == 0)
tsiz = 0;
} else if (retlen < len)
tsiz = 0;
}
return (0);
nfsmout:
if (nd->nd_mrep != NULL)
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs write operation
* When called_from_strategy != 0, it should return EIO for an error that
* indicates recovery is in progress, so that the buffer will be left
* dirty and be written back to the server later. If it loops around,
* the recovery thread could get stuck waiting for the buffer and recovery
* will then deadlock.
*/
APPLESTATIC int
nfsrpc_write(vnode_t vp, struct uio *uiop, int *iomode, u_char *verfp,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
void *stuff, int called_from_strategy)
{
int error, expireret = 0, retrycnt, nostateid;
u_int32_t clidrev = 0;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
struct nfsnode *np = VTONFS(vp);
struct ucred *newcred;
struct nfsfh *nfhp = NULL;
nfsv4stateid_t stateid;
void *lckp;
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
newcred = cred;
if (NFSHASNFSV4(nmp)) {
if (p == NULL)
newcred = NFSNEWCRED(cred);
nfhp = np->n_fhp;
}
retrycnt = 0;
do {
lckp = NULL;
nostateid = 0;
if (NFSHASNFSV4(nmp)) {
(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
NFSV4OPEN_ACCESSWRITE, newcred, p, &stateid, &lckp);
if (stateid.other[0] == 0 && stateid.other[1] == 0 &&
stateid.other[2] == 0) {
nostateid = 1;
printf("stateid0 in write\n");
}
}
/*
* If there is no stateid for NFSv4, it means this is an
* extraneous write after close. Basically a poorly
* implemented buffer cache. Just don't do the write.
*/
if (nostateid)
error = 0;
else
error = nfsrpc_writerpc(vp, uiop, iomode, verfp,
newcred, &stateid, p, nap, attrflagp, stuff);
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
nfscl_lockderef(lckp);
if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
error == NFSERR_OLDSTATEID) {
(void) nfs_catnap(PZERO, error, "nfs_write");
} else if ((error == NFSERR_EXPIRED ||
error == NFSERR_BADSTATEID) && clidrev != 0) {
expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
}
retrycnt++;
} while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
((error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER) && called_from_strategy == 0) ||
(error == NFSERR_OLDSTATEID && retrycnt < 20) ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
expireret == 0 && clidrev != 0 && retrycnt < 4));
if (error != 0 && (retrycnt >= 4 ||
((error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER) && called_from_strategy != 0)))
error = EIO;
if (NFSHASNFSV4(nmp) && p == NULL)
NFSFREECRED(newcred);
return (error);
}
/*
* The actual write RPC.
*/
static int
nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
u_char *verfp, struct ucred *cred, nfsv4stateid_t *stateidp,
NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
{
u_int32_t *tl;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
struct nfsnode *np = VTONFS(vp);
int error = 0, len, tsiz, rlen, commit, committed = NFSWRITE_FILESYNC;
int wccflag = 0, wsize;
int32_t backup;
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
nfsattrbit_t attrbits;
KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
*attrflagp = 0;
tsiz = uio_uio_resid(uiop);
NFSLOCKMNT(nmp);
if (uiop->uio_offset + tsiz > 0xffffffff &&
!NFSHASNFSV3OR4(nmp)) {
NFSUNLOCKMNT(nmp);
return (EFBIG);
}
wsize = nmp->nm_wsize;
NFSUNLOCKMNT(nmp);
nd->nd_mrep = NULL; /* NFSv2 sometimes does a write with */
nd->nd_repstat = 0; /* uio_resid == 0, so the while is not done */
while (tsiz > 0) {
nmp = VFSTONFS(vnode_mount(vp));
if (nmp == NULL) {
error = ENXIO;
goto nfsmout;
}
*attrflagp = 0;
len = (tsiz > wsize) ? wsize : tsiz;
NFSCL_REQSTART(nd, NFSPROC_WRITE, vp);
if (nd->nd_flag & ND_NFSV4) {
nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+2*NFSX_UNSIGNED);
txdr_hyper(uiop->uio_offset, tl);
tl += 2;
*tl++ = txdr_unsigned(*iomode);
*tl = txdr_unsigned(len);
} else if (nd->nd_flag & ND_NFSV3) {
NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+3*NFSX_UNSIGNED);
txdr_hyper(uiop->uio_offset, tl);
tl += 2;
*tl++ = txdr_unsigned(len);
*tl++ = txdr_unsigned(*iomode);
*tl = txdr_unsigned(len);
} else {
u_int32_t x;
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
/*
* Not sure why someone changed this, since the
* RFC clearly states that "beginoffset" and
* "totalcount" are ignored, but it wouldn't
* surprise me if there's a busted server out there.
*/
/* Set both "begin" and "current" to non-garbage. */
x = txdr_unsigned((u_int32_t)uiop->uio_offset);
*tl++ = x; /* "begin offset" */
*tl++ = x; /* "current offset" */
x = txdr_unsigned(len);
*tl++ = x; /* total to this offset */
*tl = x; /* size of this write */
}
nfsm_uiombuf(nd, uiop, len);
/*
* Although it is tempting to do a normal Getattr Op in the
* NFSv4 compound, the result can be a nearly hung client
* system if the Getattr asks for Owner and/or OwnerGroup.
* It occurs when the client can't map either the Owner or
* Owner_group name in the Getattr reply to a uid/gid. When
* there is a cache miss, the kernel does an upcall to the
* nfsuserd. Then, it can try and read the local /etc/passwd
* or /etc/group file. It can then block in getnewbuf(),
* waiting for dirty writes to be pushed to the NFS server.
* The only reason this doesn't result in a complete
* deadlock, is that the upcall times out and allows
* the write to complete. However, progress is so slow
* that it might just as well be deadlocked.
* So, we just get the attributes that change with each
* write Op.
* nb: nfscl_loadattrcache() needs to be told that these
* partial attributes from a write rpc are being
* passed in, via a argument flag.
*/
if (nd->nd_flag & ND_NFSV4) {
NFSWRITEGETATTR_ATTRBIT(&attrbits);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_repstat) {
/*
* In case the rpc gets retried, roll
* the uio fileds changed by nfsm_uiombuf()
* back.
*/
uiop->uio_offset -= len;
uio_uio_resid_add(uiop, len);
uio_iov_base_add(uiop, -len);
uio_iov_len_add(uiop, len);
}
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
error = nfscl_wcc_data(nd, vp, nap, attrflagp,
&wccflag, stuff);
if (error)
goto nfsmout;
}
if (!nd->nd_repstat) {
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED
+ NFSX_VERF);
rlen = fxdr_unsigned(int, *tl++);
if (rlen == 0) {
error = NFSERR_IO;
goto nfsmout;
} else if (rlen < len) {
backup = len - rlen;
uio_iov_base_add(uiop, -(backup));
uio_iov_len_add(uiop, backup);
uiop->uio_offset -= backup;
uio_uio_resid_add(uiop, backup);
len = rlen;
}
commit = fxdr_unsigned(int, *tl++);
/*
* Return the lowest committment level
* obtained by any of the RPCs.
*/
if (committed == NFSWRITE_FILESYNC)
committed = commit;
else if (committed == NFSWRITE_DATASYNC &&
commit == NFSWRITE_UNSTABLE)
committed = commit;
if (verfp != NULL)
NFSBCOPY((caddr_t)tl, verfp, NFSX_VERF);
NFSLOCKMNT(nmp);
if (!NFSHASWRITEVERF(nmp)) {
NFSBCOPY((caddr_t)tl,
(caddr_t)&nmp->nm_verf[0],
NFSX_VERF);
NFSSETWRITEVERF(nmp);
}
NFSUNLOCKMNT(nmp);
}
if (nd->nd_flag & ND_NFSV4)
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (nd->nd_flag & (ND_NFSV2 | ND_NFSV4)) {
error = nfsm_loadattr(nd, nap);
if (!error)
*attrflagp = NFS_LATTR_NOSHRINK;
}
} else {
error = nd->nd_repstat;
}
if (error)
goto nfsmout;
NFSWRITERPC_SETTIME(wccflag, np, (nd->nd_flag & ND_NFSV4));
mbuf_freem(nd->nd_mrep);
nd->nd_mrep = NULL;
tsiz -= len;
}
nfsmout:
if (nd->nd_mrep != NULL)
mbuf_freem(nd->nd_mrep);
*iomode = committed;
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
return (error);
}
/*
* nfs mknod rpc
* For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
* mode set to specify the file type and the size field for rdev.
*/
APPLESTATIC int
nfsrpc_mknod(vnode_t dvp, char *name, int namelen, struct vattr *vap,
u_int32_t rdev, enum vtype vtyp, struct ucred *cred, NFSPROC_T *p,
struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
int *attrflagp, int *dattrflagp, void *dstuff)
{
u_int32_t *tl;
int error = 0;
struct nfsrv_descript nfsd, *nd = &nfsd;
nfsattrbit_t attrbits;
*nfhpp = NULL;
*attrflagp = 0;
*dattrflagp = 0;
if (namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
NFSCL_REQSTART(nd, NFSPROC_MKNOD, dvp);
if (nd->nd_flag & ND_NFSV4) {
if (vtyp == VBLK || vtyp == VCHR) {
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = vtonfsv34_type(vtyp);
*tl++ = txdr_unsigned(NFSMAJOR(rdev));
*tl = txdr_unsigned(NFSMINOR(rdev));
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = vtonfsv34_type(vtyp);
}
}
(void) nfsm_strtom(nd, name, namelen);
if (nd->nd_flag & ND_NFSV3) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = vtonfsv34_type(vtyp);
}
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
nfscl_fillsattr(nd, vap, dvp, 0, 0);
if ((nd->nd_flag & ND_NFSV3) &&
(vtyp == VCHR || vtyp == VBLK)) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSMAJOR(rdev));
*tl = txdr_unsigned(NFSMINOR(rdev));
}
if (nd->nd_flag & ND_NFSV4) {
NFSGETATTR_ATTRBIT(&attrbits);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OP_GETFH);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
}
if (nd->nd_flag & ND_NFSV2)
nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZERDEV, rdev);
error = nfscl_request(nd, dvp, p, cred, dstuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV4)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if (!nd->nd_repstat) {
if (nd->nd_flag & ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
if (error)
goto nfsmout;
}
error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
if (error)
goto nfsmout;
}
if (nd->nd_flag & ND_NFSV3)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if (!error && nd->nd_repstat)
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs file create call
* Mostly just call the approriate routine. (I separated out v4, so that
* error recovery wouldn't be as difficult.)
*/
APPLESTATIC int
nfsrpc_create(vnode_t dvp, char *name, int namelen, struct vattr *vap,
nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
int *attrflagp, int *dattrflagp, void *dstuff)
{
int error = 0, newone, expireret = 0, retrycnt, unlocked;
struct nfsclowner *owp;
struct nfscldeleg *dp;
struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
u_int32_t clidrev;
if (NFSHASNFSV4(nmp)) {
retrycnt = 0;
do {
dp = NULL;
error = nfscl_open(dvp, NULL, 0, (NFSV4OPEN_ACCESSWRITE |
NFSV4OPEN_ACCESSREAD), 0, cred, p, &owp, NULL, &newone,
NULL, 1);
if (error)
return (error);
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
else
clidrev = 0;
error = nfsrpc_createv4(dvp, name, namelen, vap, cverf, fmode,
owp, &dp, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
dstuff, &unlocked);
/*
* There is no need to invalidate cached attributes here,
* since new post-delegation issue attributes are always
* returned by nfsrpc_createv4() and these will update the
* attribute cache.
*/
if (dp != NULL)
(void) nfscl_deleg(nmp->nm_mountp, owp->nfsow_clp,
(*nfhpp)->nfh_fh, (*nfhpp)->nfh_len, cred, p, &dp);
nfscl_ownerrelease(owp, error, newone, unlocked);
if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY) {
(void) nfs_catnap(PZERO, error, "nfs_open");
} else if ((error == NFSERR_EXPIRED ||
error == NFSERR_BADSTATEID) && clidrev != 0) {
expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
retrycnt++;
}
} while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
expireret == 0 && clidrev != 0 && retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
} else {
error = nfsrpc_createv23(dvp, name, namelen, vap, cverf,
fmode, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
dstuff);
}
return (error);
}
/*
* The create rpc for v2 and 3.
*/
static int
nfsrpc_createv23(vnode_t dvp, char *name, int namelen, struct vattr *vap,
nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
int *attrflagp, int *dattrflagp, void *dstuff)
{
u_int32_t *tl;
int error = 0;
struct nfsrv_descript nfsd, *nd = &nfsd;
*nfhpp = NULL;
*attrflagp = 0;
*dattrflagp = 0;
if (namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
(void) nfsm_strtom(nd, name, namelen);
if (nd->nd_flag & ND_NFSV3) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (fmode & O_EXCL) {
*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
*tl++ = cverf.lval[0];
*tl = cverf.lval[1];
} else {
*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
nfscl_fillsattr(nd, vap, dvp, 0, 0);
}
} else {
nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZE0, 0);
}
error = nfscl_request(nd, dvp, p, cred, dstuff);
if (error)
return (error);
if (nd->nd_repstat == 0) {
error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
if (error)
goto nfsmout;
}
if (nd->nd_flag & ND_NFSV3)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if (nd->nd_repstat != 0 && error == 0)
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
static int
nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
int *dattrflagp, void *dstuff, int *unlockedp)
{
u_int32_t *tl;
int error = 0, deleg, newone, ret, acesize, limitby;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsclopen *op;
struct nfscldeleg *dp = NULL;
struct nfsnode *np;
struct nfsfh *nfhp;
nfsattrbit_t attrbits;
nfsv4stateid_t stateid;
u_int32_t rflags;
*unlockedp = 0;
*nfhpp = NULL;
*dpp = NULL;
*attrflagp = 0;
*dattrflagp = 0;
if (namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
/*
* For V4, this is actually an Open op.
*/
NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(owp->nfsow_seqid);
*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
NFSV4OPEN_ACCESSREAD);
*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
*tl++ = owp->nfsow_clp->nfsc_clientid.lval[0];
*tl = owp->nfsow_clp->nfsc_clientid.lval[1];
(void) nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
if (fmode & O_EXCL) {
*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
*tl++ = cverf.lval[0];
*tl = cverf.lval[1];
} else {
*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
nfscl_fillsattr(nd, vap, dvp, 0, 0);
}
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
(void) nfsm_strtom(nd, name, namelen);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OP_GETFH);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSGETATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
error = nfscl_request(nd, dvp, p, cred, dstuff);
if (error)
return (error);
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if (error)
goto nfsmout;
NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
6 * NFSX_UNSIGNED);
stateid.seqid = *tl++;
stateid.other[0] = *tl++;
stateid.other[1] = *tl++;
stateid.other[2] = *tl;
rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
(void) nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
deleg = fxdr_unsigned(int, *tl);
if (deleg == NFSV4OPEN_DELEGATEREAD ||
deleg == NFSV4OPEN_DELEGATEWRITE) {
if (!(owp->nfsow_clp->nfsc_flags &
NFSCLFLAGS_FIRSTDELEG))
owp->nfsow_clp->nfsc_flags |=
(NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
MALLOC(dp, struct nfscldeleg *,
sizeof (struct nfscldeleg) + NFSX_V4FHMAX,
M_NFSCLDELEG, M_WAITOK);
LIST_INIT(&dp->nfsdl_owner);
LIST_INIT(&dp->nfsdl_lock);
dp->nfsdl_clp = owp->nfsow_clp;
newnfs_copyincred(cred, &dp->nfsdl_cred);
nfscl_lockinit(&dp->nfsdl_rwlock);
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
NFSX_UNSIGNED);
dp->nfsdl_stateid.seqid = *tl++;
dp->nfsdl_stateid.other[0] = *tl++;
dp->nfsdl_stateid.other[1] = *tl++;
dp->nfsdl_stateid.other[2] = *tl++;
ret = fxdr_unsigned(int, *tl);
if (deleg == NFSV4OPEN_DELEGATEWRITE) {
dp->nfsdl_flags = NFSCLDL_WRITE;
/*
* Indicates how much the file can grow.
*/
NFSM_DISSECT(tl, u_int32_t *,
3 * NFSX_UNSIGNED);
limitby = fxdr_unsigned(int, *tl++);
switch (limitby) {
case NFSV4OPEN_LIMITSIZE:
dp->nfsdl_sizelimit = fxdr_hyper(tl);
break;
case NFSV4OPEN_LIMITBLOCKS:
dp->nfsdl_sizelimit =
fxdr_unsigned(u_int64_t, *tl++);
dp->nfsdl_sizelimit *=
fxdr_unsigned(u_int64_t, *tl);
break;
default:
error = NFSERR_BADXDR;
goto nfsmout;
};
} else {
dp->nfsdl_flags = NFSCLDL_READ;
}
if (ret)
dp->nfsdl_flags |= NFSCLDL_RECALL;
error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
&acesize, p);
if (error)
goto nfsmout;
} else if (deleg != NFSV4OPEN_DELEGATENONE) {
error = NFSERR_BADXDR;
goto nfsmout;
}
error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
if (error)
goto nfsmout;
if (dp != NULL && *attrflagp) {
dp->nfsdl_change = nnap->na_filerev;
dp->nfsdl_modtime = nnap->na_mtime;
dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
}
/*
* We can now complete the Open state.
*/
nfhp = *nfhpp;
if (dp != NULL) {
dp->nfsdl_fhlen = nfhp->nfh_len;
NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh, nfhp->nfh_len);
}
/*
* Get an Open structure that will be
* attached to the OpenOwner, acquired already.
*/
error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len,
(NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
cred, p, NULL, &op, &newone, NULL, 0);
if (error)
goto nfsmout;
op->nfso_stateid = stateid;
newnfs_copyincred(cred, &op->nfso_cred);
if ((rflags & NFSV4OPEN_RESULTCONFIRM)) {
do {
ret = nfsrpc_openconfirm(dvp, nfhp->nfh_fh,
nfhp->nfh_len, op, cred, p);
if (ret == NFSERR_DELAY)
(void) nfs_catnap(PZERO, ret, "nfs_create");
} while (ret == NFSERR_DELAY);
error = ret;
}
/*
* If the server is handing out delegations, but we didn't
* get one because an OpenConfirm was required, try the
* Open again, to get a delegation. This is a harmless no-op,
* from a server's point of view.
*/
if ((rflags & NFSV4OPEN_RESULTCONFIRM) &&
(owp->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG) &&
!error && dp == NULL) {
np = VTONFS(dvp);
do {
ret = nfsrpc_openrpc(VFSTONFS(vnode_mount(dvp)), dvp,
np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
nfhp->nfh_fh, nfhp->nfh_len,
(NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), op,
name, namelen, &dp, 0, 0x0, cred, p, 0, 1);
if (ret == NFSERR_DELAY)
(void) nfs_catnap(PZERO, ret, "nfs_crt2");
} while (ret == NFSERR_DELAY);
if (ret) {
if (dp != NULL)
FREE((caddr_t)dp, M_NFSCLDELEG);
if (ret == NFSERR_STALECLIENTID ||
ret == NFSERR_STALEDONTRECOVER)
error = ret;
}
}
nfscl_openrelease(op, error, newone);
*unlockedp = 1;
}
if (nd->nd_repstat != 0 && error == 0)
error = nd->nd_repstat;
if (error == NFSERR_STALECLIENTID)
nfscl_initiate_recovery(owp->nfsow_clp);
nfsmout:
if (!error)
*dpp = dp;
else if (dp != NULL)
FREE((caddr_t)dp, M_NFSCLDELEG);
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* Nfs remove rpc
*/
APPLESTATIC int
nfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp,
void *dstuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsnode *np;
struct nfsmount *nmp;
nfsv4stateid_t dstateid;
int error, ret = 0, i;
*dattrflagp = 0;
if (namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
nmp = VFSTONFS(vnode_mount(dvp));
tryagain:
if (NFSHASNFSV4(nmp) && ret == 0) {
ret = nfscl_removedeleg(vp, p, &dstateid);
if (ret == 1) {
NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp);
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
NFSX_UNSIGNED);
*tl++ = dstateid.seqid;
*tl++ = dstateid.other[0];
*tl++ = dstateid.other[1];
*tl++ = dstateid.other[2];
*tl = txdr_unsigned(NFSV4OP_PUTFH);
np = VTONFS(dvp);
(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len, 0);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_REMOVE);
}
} else {
ret = 0;
}
if (ret == 0)
NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp);
(void) nfsm_strtom(nd, name, namelen);
error = nfscl_request(nd, dvp, p, cred, dstuff);
if (error)
return (error);
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
/* For NFSv4, parse out any Delereturn replies. */
if (ret > 0 && nd->nd_repstat != 0 &&
(nd->nd_flag & ND_NOMOREDATA)) {
/*
* If the Delegreturn failed, try again without
* it. The server will Recall, as required.
*/
mbuf_freem(nd->nd_mrep);
goto tryagain;
}
for (i = 0; i < (ret * 2); i++) {
if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*(tl + 1))
nd->nd_flag |= ND_NOMOREDATA;
}
}
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
}
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* Do an nfs rename rpc.
*/
APPLESTATIC int
nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen,
vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap,
int *fattrflagp, int *tattrflagp, void *fstuff, void *tstuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
struct nfsnode *np;
nfsattrbit_t attrbits;
nfsv4stateid_t fdstateid, tdstateid;
int error = 0, ret = 0, gottd = 0, gotfd = 0, i;
*fattrflagp = 0;
*tattrflagp = 0;
nmp = VFSTONFS(vnode_mount(fdvp));
if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
tryagain:
if (NFSHASNFSV4(nmp) && ret == 0) {
ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp,
&tdstateid, &gottd, p);
if (gotfd && gottd) {
NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME2, fvp);
} else if (gotfd) {
NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, fvp);
} else if (gottd) {
NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, tvp);
}
if (gotfd) {
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
*tl++ = fdstateid.seqid;
*tl++ = fdstateid.other[0];
*tl++ = fdstateid.other[1];
*tl = fdstateid.other[2];
if (gottd) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_PUTFH);
np = VTONFS(tvp);
(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len, 0);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_DELEGRETURN);
}
}
if (gottd) {
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
*tl++ = tdstateid.seqid;
*tl++ = tdstateid.other[0];
*tl++ = tdstateid.other[1];
*tl = tdstateid.other[2];
}
if (ret > 0) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_PUTFH);
np = VTONFS(fdvp);
(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len, 0);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_SAVEFH);
}
} else {
ret = 0;
}
if (ret == 0)
NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp);
if (nd->nd_flag & ND_NFSV4) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSWCCATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_PUTFH);
(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
VTONFS(tdvp)->n_fhp->nfh_len, 0);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
nd->nd_flag |= ND_V4WCCATTR;
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_RENAME);
}
(void) nfsm_strtom(nd, fnameptr, fnamelen);
if (!(nd->nd_flag & ND_NFSV4))
(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
VTONFS(tdvp)->n_fhp->nfh_len, 0);
(void) nfsm_strtom(nd, tnameptr, tnamelen);
error = nfscl_request(nd, fdvp, p, cred, fstuff);
if (error)
return (error);
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
/* For NFSv4, parse out any Delereturn replies. */
if (ret > 0 && nd->nd_repstat != 0 &&
(nd->nd_flag & ND_NOMOREDATA)) {
/*
* If the Delegreturn failed, try again without
* it. The server will Recall, as required.
*/
mbuf_freem(nd->nd_mrep);
goto tryagain;
}
for (i = 0; i < (ret * 2); i++) {
if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*(tl + 1)) {
if (i == 0 && ret > 1) {
/*
* If the Delegreturn failed, try again
* without it. The server will Recall, as
* required.
* If ret > 1, the first iteration of this
* loop is the second DelegReturn result.
*/
mbuf_freem(nd->nd_mrep);
goto tryagain;
} else {
nd->nd_flag |= ND_NOMOREDATA;
}
}
}
}
/* Now, the first wcc attribute reply. */
if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*(tl + 1))
nd->nd_flag |= ND_NOMOREDATA;
}
error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL,
fstuff);
/* and the second wcc attribute reply. */
if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
!error) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*(tl + 1))
nd->nd_flag |= ND_NOMOREDATA;
}
if (!error)
error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp,
NULL, tstuff);
}
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs hard link create rpc
*/
APPLESTATIC int
nfsrpc_link(vnode_t dvp, vnode_t vp, char *name, int namelen,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
struct nfsvattr *nap, int *attrflagp, int *dattrflagp, void *dstuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
nfsattrbit_t attrbits;
int error = 0;
*attrflagp = 0;
*dattrflagp = 0;
if (namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
NFSCL_REQSTART(nd, NFSPROC_LINK, vp);
if (nd->nd_flag & ND_NFSV4) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_PUTFH);
}
(void) nfsm_fhtom(nd, VTONFS(dvp)->n_fhp->nfh_fh,
VTONFS(dvp)->n_fhp->nfh_len, 0);
if (nd->nd_flag & ND_NFSV4) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSWCCATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
nd->nd_flag |= ND_V4WCCATTR;
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_LINK);
}
(void) nfsm_strtom(nd, name, namelen);
error = nfscl_request(nd, vp, p, cred, dstuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV3) {
error = nfscl_postop_attr(nd, nap, attrflagp, dstuff);
if (!error)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
NULL, dstuff);
} else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
/*
* First, parse out the PutFH and Getattr result.
*/
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (!(*(tl + 1)))
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
if (*(tl + 1))
nd->nd_flag |= ND_NOMOREDATA;
/*
* Get the pre-op attributes.
*/
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
}
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs symbolic link create rpc
*/
APPLESTATIC int
nfsrpc_symlink(vnode_t dvp, char *name, int namelen, char *target,
struct vattr *vap, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
int *dattrflagp, void *dstuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
int slen, error = 0;
*nfhpp = NULL;
*attrflagp = 0;
*dattrflagp = 0;
nmp = VFSTONFS(vnode_mount(dvp));
slen = strlen(target);
if (slen > NFS_MAXPATHLEN || namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
NFSCL_REQSTART(nd, NFSPROC_SYMLINK, dvp);
if (nd->nd_flag & ND_NFSV4) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFLNK);
(void) nfsm_strtom(nd, target, slen);
}
(void) nfsm_strtom(nd, name, namelen);
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
nfscl_fillsattr(nd, vap, dvp, 0, 0);
if (!(nd->nd_flag & ND_NFSV4))
(void) nfsm_strtom(nd, target, slen);
if (nd->nd_flag & ND_NFSV2)
nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
error = nfscl_request(nd, dvp, p, cred, dstuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV4)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if ((nd->nd_flag & ND_NFSV3) && !error) {
if (!nd->nd_repstat)
error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
if (!error)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
NULL, dstuff);
}
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
/*
* Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
*/
if (error == EEXIST)
error = 0;
return (error);
}
/*
* nfs make dir rpc
*/
APPLESTATIC int
nfsrpc_mkdir(vnode_t dvp, char *name, int namelen, struct vattr *vap,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
int *dattrflagp, void *dstuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
nfsattrbit_t attrbits;
int error = 0;
*nfhpp = NULL;
*attrflagp = 0;
*dattrflagp = 0;
if (namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
NFSCL_REQSTART(nd, NFSPROC_MKDIR, dvp);
if (nd->nd_flag & ND_NFSV4) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFDIR);
}
(void) nfsm_strtom(nd, name, namelen);
nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
if (nd->nd_flag & ND_NFSV4) {
NFSGETATTR_ATTRBIT(&attrbits);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OP_GETFH);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, dvp, p, cred, dstuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV4)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if (!nd->nd_repstat && !error) {
if (nd->nd_flag & ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
}
if (!error)
error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
}
if ((nd->nd_flag & ND_NFSV3) && !error)
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
/*
* Kludge: Map EEXIST => 0 assuming that you have a reply to a retry.
*/
if (error == EEXIST)
error = 0;
return (error);
}
/*
* nfs remove directory call
*/
APPLESTATIC int
nfsrpc_rmdir(vnode_t dvp, char *name, int namelen, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp, void *dstuff)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
int error = 0;
*dattrflagp = 0;
if (namelen > NFS_MAXNAMLEN)
return (ENAMETOOLONG);
NFSCL_REQSTART(nd, NFSPROC_RMDIR, dvp);
(void) nfsm_strtom(nd, name, namelen);
error = nfscl_request(nd, dvp, p, cred, dstuff);
if (error)
return (error);
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
/*
* Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
*/
if (error == ENOENT)
error = 0;
return (error);
}
/*
* Readdir rpc.
* Always returns with either uio_resid unchanged, if you are at the
* end of the directory, or uio_resid == 0, with all DIRBLKSIZ chunks
* filled in.
* I felt this would allow caching of directory blocks more easily
* than returning a pertially filled block.
* Directory offset cookies:
* Oh my, what to do with them...
* I can think of three ways to deal with them:
* 1 - have the layer above these RPCs maintain a map between logical
* directory byte offsets and the NFS directory offset cookies
* 2 - pass the opaque directory offset cookies up into userland
* and let the libc functions deal with them, via the system call
* 3 - return them to userland in the "struct dirent", so future versions
* of libc can use them and do whatever is necessary to amke things work
* above these rpc calls, in the meantime
* For now, I do #3 by "hiding" the directory offset cookies after the
* d_name field in struct dirent. This is space inside d_reclen that
* will be ignored by anything that doesn't know about them.
* The directory offset cookies are filled in as the last 8 bytes of
* each directory entry, after d_name. Someday, the userland libc
* functions may be able to use these. In the meantime, it satisfies
* OpenBSD's requirements for cookies being returned.
* If expects the directory offset cookie for the read to be in uio_offset
* and returns the one for the next entry after this directory block in
* there, as well.
*/
APPLESTATIC int
nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
int *eofp, void *stuff)
{
int len, left;
struct dirent *dp = NULL;
u_int32_t *tl;
nfsquad_t cookie, ncookie;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
struct nfsnode *dnp = VTONFS(vp);
struct nfsvattr nfsva;
struct nfsrv_descript nfsd, *nd = &nfsd;
int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
int reqsize, tryformoredirs = 1, readsize, eof = 0, gotmnton = 0;
long dotfileid, dotdotfileid = 0;
u_int32_t fakefileno = 0xffffffff, rderr;
char *cp;
nfsattrbit_t attrbits, dattrbits;
u_int32_t *tl2 = NULL;
size_t tresid;
KASSERT(uiop->uio_iovcnt == 1 &&
(uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
("nfs readdirrpc bad uio"));
/*
* There is no point in reading a lot more than uio_resid, however
* adding one additional DIRBLKSIZ makes sense. Since uio_resid
* and nm_readdirsize are both exact multiples of DIRBLKSIZ, this
* will never make readsize > nm_readdirsize.
*/
readsize = nmp->nm_readdirsize;
if (readsize > uio_uio_resid(uiop))
readsize = uio_uio_resid(uiop) + DIRBLKSIZ;
*attrflagp = 0;
if (eofp)
*eofp = 0;
tresid = uio_uio_resid(uiop);
cookie.lval[0] = cookiep->nfsuquad[0];
cookie.lval[1] = cookiep->nfsuquad[1];
nd->nd_mrep = NULL;
/*
* For NFSv4, first create the "." and ".." entries.
*/
if (NFSHASNFSV4(nmp)) {
reqsize = 6 * NFSX_UNSIGNED;
NFSGETATTR_ATTRBIT(&dattrbits);
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TYPE);
if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
NFSATTRBIT_MOUNTEDONFILEID)) {
NFSSETBIT_ATTRBIT(&attrbits,
NFSATTRBIT_MOUNTEDONFILEID);
gotmnton = 1;
} else {
/*
* Must fake it. Use the fileno, except when the
* fsid is != to that of the directory. For that
* case, generate a fake fileno that is not the same.
*/
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
gotmnton = 0;
}
/*
* Joy, oh joy. For V4 we get to hand craft '.' and '..'.
*/
if (uiop->uio_offset == 0) {
#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
error = VOP_GETATTR(vp, &nfsva.na_vattr, cred);
#else
error = VOP_GETATTR(vp, &nfsva.na_vattr, cred, p);
#endif
if (error)
return (error);
dotfileid = nfsva.na_fileid;
NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OP_GETFH);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
len = fxdr_unsigned(int, *(tl + 2));
if (len > 0 && len <= NFSX_V4FHMAX)
error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
else
error = EPERM;
if (!error) {
NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
nfsva.na_mntonfileno = 0xffffffff;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
NULL, NULL, NULL, p, cred);
if (error) {
dotdotfileid = dotfileid;
} else if (gotmnton) {
if (nfsva.na_mntonfileno != 0xffffffff)
dotdotfileid = nfsva.na_mntonfileno;
else
dotdotfileid = nfsva.na_fileid;
} else if (nfsva.na_filesid[0] ==
dnp->n_vattr.na_filesid[0] &&
nfsva.na_filesid[1] ==
dnp->n_vattr.na_filesid[1]) {
dotdotfileid = nfsva.na_fileid;
} else {
do {
fakefileno--;
} while (fakefileno ==
nfsva.na_fileid);
dotdotfileid = fakefileno;
}
}
} else if (nd->nd_repstat == NFSERR_NOENT) {
/*
* Lookupp returns NFSERR_NOENT when we are
* at the root, so just use the current dir.
*/
nd->nd_repstat = 0;
dotdotfileid = dotfileid;
} else {
error = nd->nd_repstat;
}
mbuf_freem(nd->nd_mrep);
if (error)
return (error);
nd->nd_mrep = NULL;
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_type = DT_DIR;
dp->d_fileno = dotfileid;
dp->d_namlen = 1;
dp->d_name[0] = '.';
dp->d_name[1] = '\0';
dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
/*
* Just make these offset cookie 0.
*/
tl = (u_int32_t *)&dp->d_name[4];
*tl++ = 0;
*tl = 0;
blksiz += dp->d_reclen;
uio_uio_resid_add(uiop, -(dp->d_reclen));
uiop->uio_offset += dp->d_reclen;
uio_iov_base_add(uiop, dp->d_reclen);
uio_iov_len_add(uiop, -(dp->d_reclen));
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_type = DT_DIR;
dp->d_fileno = dotdotfileid;
dp->d_namlen = 2;
dp->d_name[0] = '.';
dp->d_name[1] = '.';
dp->d_name[2] = '\0';
dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
/*
* Just make these offset cookie 0.
*/
tl = (u_int32_t *)&dp->d_name[4];
*tl++ = 0;
*tl = 0;
blksiz += dp->d_reclen;
uio_uio_resid_add(uiop, -(dp->d_reclen));
uiop->uio_offset += dp->d_reclen;
uio_iov_base_add(uiop, dp->d_reclen);
uio_iov_len_add(uiop, -(dp->d_reclen));
}
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_RDATTRERROR);
} else {
reqsize = 5 * NFSX_UNSIGNED;
}
/*
* Loop around doing readdir rpc's of size readsize.
* The stopping criteria is EOF or buffer full.
*/
while (more_dirs && bigenough) {
*attrflagp = 0;
NFSCL_REQSTART(nd, NFSPROC_READDIR, vp);
if (nd->nd_flag & ND_NFSV2) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = cookie.lval[1];
*tl = txdr_unsigned(readsize);
} else {
NFSM_BUILD(tl, u_int32_t *, reqsize);
*tl++ = cookie.lval[0];
*tl++ = cookie.lval[1];
if (cookie.qval == 0) {
*tl++ = 0;
*tl++ = 0;
} else {
NFSLOCKNODE(dnp);
*tl++ = dnp->n_cookieverf.nfsuquad[0];
*tl++ = dnp->n_cookieverf.nfsuquad[1];
NFSUNLOCKNODE(dnp);
}
if (nd->nd_flag & ND_NFSV4) {
*tl++ = txdr_unsigned(readsize);
*tl = txdr_unsigned(readsize);
(void) nfsrv_putattrbit(nd, &attrbits);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &dattrbits);
} else {
*tl = txdr_unsigned(readsize);
}
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (!(nd->nd_flag & ND_NFSV2)) {
if (nd->nd_flag & ND_NFSV3)
error = nfscl_postop_attr(nd, nap, attrflagp,
stuff);
if (!nd->nd_repstat && !error) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
NFSLOCKNODE(dnp);
dnp->n_cookieverf.nfsuquad[0] = *tl++;
dnp->n_cookieverf.nfsuquad[1] = *tl;
NFSUNLOCKNODE(dnp);
}
}
if (nd->nd_repstat || error) {
if (!error)
error = nd->nd_repstat;
goto nfsmout;
}
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
more_dirs = fxdr_unsigned(int, *tl);
if (!more_dirs)
tryformoredirs = 0;
/* loop thru the dir entries, doctoring them to 4bsd form */
while (more_dirs && bigenough) {
if (nd->nd_flag & ND_NFSV4) {
NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
ncookie.lval[0] = *tl++;
ncookie.lval[1] = *tl++;
len = fxdr_unsigned(int, *tl);
} else if (nd->nd_flag & ND_NFSV3) {
NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
nfsva.na_fileid = fxdr_hyper(tl);
tl += 2;
len = fxdr_unsigned(int, *tl);
} else {
NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
nfsva.na_fileid =
fxdr_unsigned(long, *tl++);
len = fxdr_unsigned(int, *tl);
}
if (len <= 0 || len > NFS_MAXNAMLEN) {
error = EBADRPC;
goto nfsmout;
}
tlen = NFSM_RNDUP(len);
if (tlen == len)
tlen += 4; /* To ensure null termination */
left = DIRBLKSIZ - blksiz;
if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > left) {
dp->d_reclen += left;
uio_iov_base_add(uiop, left);
uio_iov_len_add(uiop, -(left));
uio_uio_resid_add(uiop, -(left));
uiop->uio_offset += left;
blksiz = 0;
}
if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
bigenough = 0;
if (bigenough) {
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_namlen = len;
dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
dp->d_type = DT_UNKNOWN;
blksiz += dp->d_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uio_uio_resid_add(uiop, -(DIRHDSIZ));
uiop->uio_offset += DIRHDSIZ;
uio_iov_base_add(uiop, DIRHDSIZ);
uio_iov_len_add(uiop, -(DIRHDSIZ));
error = nfsm_mbufuio(nd, uiop, len);
if (error)
goto nfsmout;
cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
tlen -= len;
*cp = '\0'; /* null terminate */
cp += tlen; /* points to cookie storage */
tl2 = (u_int32_t *)cp;
uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
uiop->uio_offset += (tlen + NFSX_HYPER);
} else {
error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
if (error)
goto nfsmout;
}
if (nd->nd_flag & ND_NFSV4) {
rderr = 0;
nfsva.na_mntonfileno = 0xffffffff;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
NULL, NULL, &rderr, p, cred);
if (error)
goto nfsmout;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
} else if (nd->nd_flag & ND_NFSV3) {
NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
ncookie.lval[0] = *tl++;
ncookie.lval[1] = *tl++;
} else {
NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
ncookie.lval[0] = 0;
ncookie.lval[1] = *tl++;
}
if (bigenough) {
if (nd->nd_flag & ND_NFSV4) {
if (rderr) {
dp->d_fileno = 0;
} else {
if (gotmnton) {
if (nfsva.na_mntonfileno != 0xffffffff)
dp->d_fileno = nfsva.na_mntonfileno;
else
dp->d_fileno = nfsva.na_fileid;
} else if (nfsva.na_filesid[0] ==
dnp->n_vattr.na_filesid[0] &&
nfsva.na_filesid[1] ==
dnp->n_vattr.na_filesid[1]) {
dp->d_fileno = nfsva.na_fileid;
} else {
do {
fakefileno--;
} while (fakefileno ==
nfsva.na_fileid);
dp->d_fileno = fakefileno;
}
dp->d_type = vtonfs_dtype(nfsva.na_type);
}
} else {
dp->d_fileno = nfsva.na_fileid;
}
*tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
ncookie.lval[0];
*tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
ncookie.lval[1];
}
more_dirs = fxdr_unsigned(int, *tl);
}
/*
* If at end of rpc data, get the eof boolean
*/
if (!more_dirs) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
eof = fxdr_unsigned(int, *tl);
if (tryformoredirs)
more_dirs = !eof;
if (nd->nd_flag & ND_NFSV4) {
error = nfscl_postop_attr(nd, nap, attrflagp,
stuff);
if (error)
goto nfsmout;
}
}
mbuf_freem(nd->nd_mrep);
nd->nd_mrep = NULL;
}
/*
* Fill last record, iff any, out to a multiple of DIRBLKSIZ
* by increasing d_reclen for the last record.
*/
if (blksiz > 0) {
left = DIRBLKSIZ - blksiz;
dp->d_reclen += left;
uio_iov_base_add(uiop, left);
uio_iov_len_add(uiop, -(left));
uio_uio_resid_add(uiop, -(left));
uiop->uio_offset += left;
}
/*
* If returning no data, assume end of file.
* If not bigenough, return not end of file, since you aren't
* returning all the data
* Otherwise, return the eof flag from the server.
*/
if (eofp) {
if (tresid == ((size_t)(uio_uio_resid(uiop))))
*eofp = 1;
else if (!bigenough)
*eofp = 0;
else
*eofp = eof;
}
/*
* Add extra empty records to any remaining DIRBLKSIZ chunks.
*/
while (uio_uio_resid(uiop) > 0 && ((size_t)(uio_uio_resid(uiop))) != tresid) {
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_type = DT_UNKNOWN;
dp->d_fileno = 0;
dp->d_namlen = 0;
dp->d_name[0] = '\0';
tl = (u_int32_t *)&dp->d_name[4];
*tl++ = cookie.lval[0];
*tl = cookie.lval[1];
dp->d_reclen = DIRBLKSIZ;
uio_iov_base_add(uiop, DIRBLKSIZ);
uio_iov_len_add(uiop, -(DIRBLKSIZ));
uio_uio_resid_add(uiop, -(DIRBLKSIZ));
uiop->uio_offset += DIRBLKSIZ;
}
nfsmout:
if (nd->nd_mrep != NULL)
mbuf_freem(nd->nd_mrep);
return (error);
}
#ifndef APPLE
/*
* NFS V3 readdir plus RPC. Used in place of nfsrpc_readdir().
* (Also used for NFS V4 when mount flag set.)
* (ditto above w.r.t. multiple of DIRBLKSIZ, etc.)
*/
APPLESTATIC int
nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
int *eofp, void *stuff)
{
int len, left;
struct dirent *dp = NULL;
u_int32_t *tl;
vnode_t newvp = NULLVP;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nameidata nami, *ndp = &nami;
struct componentname *cnp = &ndp->ni_cnd;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
struct nfsnode *dnp = VTONFS(vp), *np;
struct nfsvattr nfsva;
struct nfsfh *nfhp;
nfsquad_t cookie, ncookie;
int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
int attrflag, tryformoredirs = 1, eof = 0, gotmnton = 0;
int unlocknewvp = 0;
long dotfileid, dotdotfileid = 0, fileno = 0;
char *cp;
nfsattrbit_t attrbits, dattrbits;
size_t tresid;
u_int32_t *tl2 = NULL, fakefileno = 0xffffffff, rderr;
KASSERT(uiop->uio_iovcnt == 1 &&
(uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
("nfs readdirplusrpc bad uio"));
*attrflagp = 0;
if (eofp != NULL)
*eofp = 0;
ndp->ni_dvp = vp;
nd->nd_mrep = NULL;
cookie.lval[0] = cookiep->nfsuquad[0];
cookie.lval[1] = cookiep->nfsuquad[1];
tresid = uio_uio_resid(uiop);
/*
* For NFSv4, first create the "." and ".." entries.
*/
if (NFSHASNFSV4(nmp)) {
NFSGETATTR_ATTRBIT(&dattrbits);
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
NFSATTRBIT_MOUNTEDONFILEID)) {
NFSSETBIT_ATTRBIT(&attrbits,
NFSATTRBIT_MOUNTEDONFILEID);
gotmnton = 1;
} else {
/*
* Must fake it. Use the fileno, except when the
* fsid is != to that of the directory. For that
* case, generate a fake fileno that is not the same.
*/
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
gotmnton = 0;
}
/*
* Joy, oh joy. For V4 we get to hand craft '.' and '..'.
*/
if (uiop->uio_offset == 0) {
#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
error = VOP_GETATTR(vp, &nfsva.na_vattr, cred);
#else
error = VOP_GETATTR(vp, &nfsva.na_vattr, cred, p);
#endif
if (error)
return (error);
dotfileid = nfsva.na_fileid;
NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OP_GETFH);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &attrbits);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
len = fxdr_unsigned(int, *(tl + 2));
if (len > 0 && len <= NFSX_V4FHMAX)
error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
else
error = EPERM;
if (!error) {
NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
nfsva.na_mntonfileno = 0xffffffff;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
NULL, NULL, NULL, p, cred);
if (error) {
dotdotfileid = dotfileid;
} else if (gotmnton) {
if (nfsva.na_mntonfileno != 0xffffffff)
dotdotfileid = nfsva.na_mntonfileno;
else
dotdotfileid = nfsva.na_fileid;
} else if (nfsva.na_filesid[0] ==
dnp->n_vattr.na_filesid[0] &&
nfsva.na_filesid[1] ==
dnp->n_vattr.na_filesid[1]) {
dotdotfileid = nfsva.na_fileid;
} else {
do {
fakefileno--;
} while (fakefileno ==
nfsva.na_fileid);
dotdotfileid = fakefileno;
}
}
} else if (nd->nd_repstat == NFSERR_NOENT) {
/*
* Lookupp returns NFSERR_NOENT when we are
* at the root, so just use the current dir.
*/
nd->nd_repstat = 0;
dotdotfileid = dotfileid;
} else {
error = nd->nd_repstat;
}
mbuf_freem(nd->nd_mrep);
if (error)
return (error);
nd->nd_mrep = NULL;
dp = (struct dirent *)uio_iov_base(uiop);
dp->d_type = DT_DIR;
dp->d_fileno = dotfileid;
dp->d_namlen = 1;
dp->d_name[0] = '.';
dp->d_name[1] = '\0';
dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
/*
* Just make these offset cookie 0.
*/
tl = (u_int32_t *)&dp->d_name[4];
*tl++ = 0;
*tl = 0;
blksiz += dp->d_reclen;
uio_uio_resid_add(uiop, -(dp->d_reclen));
uiop->uio_offset += dp->d_reclen;
uio_iov_base_add(uiop, dp->d_reclen);
uio_iov_len_add(uiop, -(dp->d_reclen));
dp = (struct dirent *)uio_iov_base(uiop);
dp->d_type = DT_DIR;
dp->d_fileno = dotdotfileid;
dp->d_namlen = 2;
dp->d_name[0] = '.';
dp->d_name[1] = '.';
dp->d_name[2] = '\0';
dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
/*
* Just make these offset cookie 0.
*/
tl = (u_int32_t *)&dp->d_name[4];
*tl++ = 0;
*tl = 0;
blksiz += dp->d_reclen;
uio_uio_resid_add(uiop, -(dp->d_reclen));
uiop->uio_offset += dp->d_reclen;
uio_iov_base_add(uiop, dp->d_reclen);
uio_iov_len_add(uiop, -(dp->d_reclen));
}
NFSREADDIRPLUS_ATTRBIT(&attrbits);
if (gotmnton)
NFSSETBIT_ATTRBIT(&attrbits,
NFSATTRBIT_MOUNTEDONFILEID);
}
/*
* Loop around doing readdir rpc's of size nm_readdirsize.
* The stopping criteria is EOF or buffer full.
*/
while (more_dirs && bigenough) {
*attrflagp = 0;
NFSCL_REQSTART(nd, NFSPROC_READDIRPLUS, vp);
NFSM_BUILD(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
*tl++ = cookie.lval[0];
*tl++ = cookie.lval[1];
if (cookie.qval == 0) {
*tl++ = 0;
*tl++ = 0;
} else {
NFSLOCKNODE(dnp);
*tl++ = dnp->n_cookieverf.nfsuquad[0];
*tl++ = dnp->n_cookieverf.nfsuquad[1];
NFSUNLOCKNODE(dnp);
}
*tl++ = txdr_unsigned(nmp->nm_readdirsize);
*tl = txdr_unsigned(nmp->nm_readdirsize);
if (nd->nd_flag & ND_NFSV4) {
(void) nfsrv_putattrbit(nd, &attrbits);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
(void) nfsrv_putattrbit(nd, &dattrbits);
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV3)
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (nd->nd_repstat || error) {
if (!error)
error = nd->nd_repstat;
goto nfsmout;
}
NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
NFSLOCKNODE(dnp);
dnp->n_cookieverf.nfsuquad[0] = *tl++;
dnp->n_cookieverf.nfsuquad[1] = *tl++;
NFSUNLOCKNODE(dnp);
more_dirs = fxdr_unsigned(int, *tl);
if (!more_dirs)
tryformoredirs = 0;
/* loop thru the dir entries, doctoring them to 4bsd form */
while (more_dirs && bigenough) {
NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
if (nd->nd_flag & ND_NFSV4) {
ncookie.lval[0] = *tl++;
ncookie.lval[1] = *tl++;
} else {
fileno = fxdr_unsigned(long, *++tl);
tl++;
}
len = fxdr_unsigned(int, *tl);
if (len <= 0 || len > NFS_MAXNAMLEN) {
error = EBADRPC;
goto nfsmout;
}
tlen = NFSM_RNDUP(len);
if (tlen == len)
tlen += 4; /* To ensure null termination */
left = DIRBLKSIZ - blksiz;
if ((tlen + DIRHDSIZ + NFSX_HYPER) > left) {
dp->d_reclen += left;
uio_iov_base_add(uiop, left);
uio_iov_len_add(uiop, -(left));
uio_uio_resid_add(uiop, -(left));
uiop->uio_offset += left;
blksiz = 0;
}
if ((tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
bigenough = 0;
if (bigenough) {
dp = (struct dirent *)uio_iov_base(uiop);
dp->d_namlen = len;
dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
dp->d_type = DT_UNKNOWN;
blksiz += dp->d_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uio_uio_resid_add(uiop, -(DIRHDSIZ));
uiop->uio_offset += DIRHDSIZ;
uio_iov_base_add(uiop, DIRHDSIZ);
uio_iov_len_add(uiop, -(DIRHDSIZ));
cnp->cn_nameptr = uio_iov_base(uiop);
cnp->cn_namelen = len;
NFSCNHASHZERO(cnp);
error = nfsm_mbufuio(nd, uiop, len);
if (error)
goto nfsmout;
cp = uio_iov_base(uiop);
tlen -= len;
*cp = '\0';
cp += tlen; /* points to cookie storage */
tl2 = (u_int32_t *)cp;
uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
uiop->uio_offset += (tlen + NFSX_HYPER);
} else {
error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
if (error)
goto nfsmout;
}
nfhp = NULL;
if (nd->nd_flag & ND_NFSV3) {
NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
ncookie.lval[0] = *tl++;
ncookie.lval[1] = *tl++;
attrflag = fxdr_unsigned(int, *tl);
if (attrflag) {
error = nfsm_loadattr(nd, &nfsva);
if (error)
goto nfsmout;
}
NFSM_DISSECT(tl,u_int32_t *,NFSX_UNSIGNED);
if (*tl) {
error = nfsm_getfh(nd, &nfhp);
if (error)
goto nfsmout;
}
if (!attrflag && nfhp != NULL) {
FREE((caddr_t)nfhp, M_NFSFH);
nfhp = NULL;
}
} else {
rderr = 0;
nfsva.na_mntonfileno = 0xffffffff;
error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
NULL, NULL, &rderr, p, cred);
if (error)
goto nfsmout;
}
if (bigenough) {
if (nd->nd_flag & ND_NFSV4) {
if (rderr) {
dp->d_fileno = 0;
} else if (gotmnton) {
if (nfsva.na_mntonfileno != 0xffffffff)
dp->d_fileno = nfsva.na_mntonfileno;
else
dp->d_fileno = nfsva.na_fileid;
} else if (nfsva.na_filesid[0] ==
dnp->n_vattr.na_filesid[0] &&
nfsva.na_filesid[1] ==
dnp->n_vattr.na_filesid[1]) {
dp->d_fileno = nfsva.na_fileid;
} else {
do {
fakefileno--;
} while (fakefileno ==
nfsva.na_fileid);
dp->d_fileno = fakefileno;
}
} else {
dp->d_fileno = fileno;
}
*tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
ncookie.lval[0];
*tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
ncookie.lval[1];
if (nfhp != NULL) {
if (NFSRV_CMPFH(nfhp->nfh_fh, nfhp->nfh_len,
dnp->n_fhp->nfh_fh, dnp->n_fhp->nfh_len)) {
VREF(vp);
newvp = vp;
unlocknewvp = 0;
FREE((caddr_t)nfhp, M_NFSFH);
np = dnp;
} else {
error = nfscl_nget(vnode_mount(vp), vp,
nfhp, cnp, p, &np, NULL);
if (!error) {
newvp = NFSTOV(np);
unlocknewvp = 1;
}
}
nfhp = NULL;
if (newvp != NULLVP) {
error = nfscl_loadattrcache(&newvp,
&nfsva, NULL, NULL, 0, 0);
if (error) {
if (unlocknewvp)
vput(newvp);
else
vrele(newvp);
goto nfsmout;
}
dp->d_type =
vtonfs_dtype(np->n_vattr.na_type);
ndp->ni_vp = newvp;
NFSCNHASH(cnp, HASHINIT);
if (cnp->cn_namelen <= NCHNAMLEN) {
np->n_ctime = np->n_vattr.na_ctime;
cache_enter(ndp->ni_dvp,ndp->ni_vp,cnp);
}
if (unlocknewvp)
vput(newvp);
else
vrele(newvp);
newvp = NULLVP;
}
}
} else if (nfhp != NULL) {
FREE((caddr_t)nfhp, M_NFSFH);
}
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
more_dirs = fxdr_unsigned(int, *tl);
}
/*
* If at end of rpc data, get the eof boolean
*/
if (!more_dirs) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
eof = fxdr_unsigned(int, *tl);
if (tryformoredirs)
more_dirs = !eof;
if (nd->nd_flag & ND_NFSV4) {
error = nfscl_postop_attr(nd, nap, attrflagp,
stuff);
if (error)
goto nfsmout;
}
}
mbuf_freem(nd->nd_mrep);
nd->nd_mrep = NULL;
}
/*
* Fill last record, iff any, out to a multiple of DIRBLKSIZ
* by increasing d_reclen for the last record.
*/
if (blksiz > 0) {
left = DIRBLKSIZ - blksiz;
dp->d_reclen += left;
uio_iov_base_add(uiop, left);
uio_iov_len_add(uiop, -(left));
uio_uio_resid_add(uiop, -(left));
uiop->uio_offset += left;
}
/*
* If returning no data, assume end of file.
* If not bigenough, return not end of file, since you aren't
* returning all the data
* Otherwise, return the eof flag from the server.
*/
if (eofp != NULL) {
if (tresid == uio_uio_resid(uiop))
*eofp = 1;
else if (!bigenough)
*eofp = 0;
else
*eofp = eof;
}
/*
* Add extra empty records to any remaining DIRBLKSIZ chunks.
*/
while (uio_uio_resid(uiop) > 0 && uio_uio_resid(uiop) != tresid) {
dp = (struct dirent *)uio_iov_base(uiop);
dp->d_type = DT_UNKNOWN;
dp->d_fileno = 0;
dp->d_namlen = 0;
dp->d_name[0] = '\0';
tl = (u_int32_t *)&dp->d_name[4];
*tl++ = cookie.lval[0];
*tl = cookie.lval[1];
dp->d_reclen = DIRBLKSIZ;
uio_iov_base_add(uiop, DIRBLKSIZ);
uio_iov_len_add(uiop, -(DIRBLKSIZ));
uio_uio_resid_add(uiop, -(DIRBLKSIZ));
uiop->uio_offset += DIRBLKSIZ;
}
nfsmout:
if (nd->nd_mrep != NULL)
mbuf_freem(nd->nd_mrep);
return (error);
}
#endif /* !APPLE */
/*
* Nfs commit rpc
*/
APPLESTATIC int
nfsrpc_commit(vnode_t vp, u_quad_t offset, int cnt, struct ucred *cred,
NFSPROC_T *p, u_char *verfp, struct nfsvattr *nap, int *attrflagp,
void *stuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
nfsattrbit_t attrbits;
int error;
*attrflagp = 0;
NFSCL_REQSTART(nd, NFSPROC_COMMIT, vp);
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
txdr_hyper(offset, tl);
tl += 2;
*tl = txdr_unsigned(cnt);
if (nd->nd_flag & ND_NFSV4) {
/*
* And do a Getattr op.
*/
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETATTR);
NFSGETATTR_ATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
}
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
error = nfscl_wcc_data(nd, vp, nap, attrflagp, NULL, stuff);
if (!error && !nd->nd_repstat) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
NFSBCOPY((caddr_t)tl, verfp, NFSX_VERF);
if (nd->nd_flag & ND_NFSV4)
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
}
nfsmout:
if (!error && nd->nd_repstat)
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* NFS byte range lock rpc.
* (Mostly just calls one of the three lower level RPC routines.)
*/
APPLESTATIC int
nfsrpc_advlock(vnode_t vp, off_t size, int op, struct flock *fl,
int reclaim, struct ucred *cred, NFSPROC_T *p)
{
struct nfscllockowner *lp;
struct nfsclclient *clp;
struct nfsfh *nfhp;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
u_int64_t off, len;
off_t start, end;
u_int32_t clidrev = 0;
int error = 0, newone = 0, expireret = 0, retrycnt, donelocally;
int callcnt, dorpc;
/*
* Convert the flock structure into a start and end and do POSIX
* bounds checking.
*/
switch (fl->l_whence) {
case SEEK_SET:
case SEEK_CUR:
/*
* Caller is responsible for adding any necessary offset
* when SEEK_CUR is used.
*/
start = fl->l_start;
off = fl->l_start;
break;
case SEEK_END:
start = size + fl->l_start;
off = size + fl->l_start;
break;
default:
return (EINVAL);
};
if (start < 0)
return (EINVAL);
if (fl->l_len != 0) {
end = start + fl->l_len - 1;
if (end < start)
return (EINVAL);
}
len = fl->l_len;
if (len == 0)
len = NFS64BITSSET;
retrycnt = 0;
do {
nd->nd_repstat = 0;
if (op == F_GETLK) {
error = nfscl_getcl(vp, cred, p, &clp);
if (error)
return (error);
error = nfscl_lockt(vp, clp, off, len, fl, p);
if (!error) {
clidrev = clp->nfsc_clientidrev;
error = nfsrpc_lockt(nd, vp, clp, off, len, fl, cred,
p);
} else if (error == -1) {
error = 0;
}
nfscl_clientrelease(clp);
} else if (op == F_UNLCK && fl->l_type == F_UNLCK) {
/*
* We must loop around for all lockowner cases.
*/
callcnt = 0;
error = nfscl_getcl(vp, cred, p, &clp);
if (error)
return (error);
do {
error = nfscl_relbytelock(vp, off, len, cred, p, callcnt,
clp, &lp, &dorpc);
/*
* If it returns a NULL lp, we're done.
*/
if (lp == NULL) {
if (callcnt == 0)
nfscl_clientrelease(clp);
else
nfscl_releasealllocks(clp, vp, p);
return (error);
}
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
else
clidrev = 0;
/*
* If the server doesn't support Posix lock semantics,
* only allow locks on the entire file, since it won't
* handle overlapping byte ranges.
* There might still be a problem when a lock
* upgrade/downgrade (read<->write) occurs, since the
* server "might" expect an unlock first?
*/
if (dorpc && (lp->nfsl_open->nfso_posixlock ||
(off == 0 && len == NFS64BITSSET))) {
/*
* Since the lock records will go away, we must
* wait for grace and delay here.
*/
do {
error = nfsrpc_locku(nd, nmp, lp, off, len,
NFSV4LOCKT_READ, cred, p, 0);
if ((nd->nd_repstat == NFSERR_GRACE ||
nd->nd_repstat == NFSERR_DELAY) &&
error == 0)
(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
"nfs_advlock");
} while ((nd->nd_repstat == NFSERR_GRACE ||
nd->nd_repstat == NFSERR_DELAY) && error == 0);
}
callcnt++;
} while (error == 0 && nd->nd_repstat == 0);
nfscl_releasealllocks(clp, vp, p);
} else if (op == F_SETLK) {
error = nfscl_getbytelock(vp, off, len, fl->l_type, cred, p,
NULL, 0, NULL, NULL, &lp, &newone, &donelocally);
if (error || donelocally) {
return (error);
}
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
else
clidrev = 0;
nfhp = VTONFS(vp)->n_fhp;
if (!lp->nfsl_open->nfso_posixlock &&
(off != 0 || len != NFS64BITSSET)) {
error = EINVAL;
} else {
error = nfsrpc_lock(nd, nmp, vp, nfhp->nfh_fh,
nfhp->nfh_len, lp, newone, reclaim, off,
len, fl->l_type, cred, p, 0);
}
if (!error)
error = nd->nd_repstat;
nfscl_lockrelease(lp, error, newone);
} else {
error = EINVAL;
}
if (!error)
error = nd->nd_repstat;
if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
error == NFSERR_STALEDONTRECOVER ||
error == NFSERR_STALECLIENTID || error == NFSERR_DELAY) {
(void) nfs_catnap(PZERO, error, "nfs_advlock");
} else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
&& clidrev != 0) {
expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
retrycnt++;
}
} while (error == NFSERR_GRACE ||
error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
error == NFSERR_STALEDONTRECOVER || error == NFSERR_STALESTATEID ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
expireret == 0 && clidrev != 0 && retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
return (error);
}
/*
* The lower level routine for the LockT case.
*/
APPLESTATIC int
nfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
struct nfsclclient *clp, u_int64_t off, u_int64_t len, struct flock *fl,
struct ucred *cred, NFSPROC_T *p)
{
u_int32_t *tl;
int error, type, size;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
NFSCL_REQSTART(nd, NFSPROC_LOCKT, vp);
NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
if (fl->l_type == F_RDLCK)
*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
else
*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
txdr_hyper(off, tl);
tl += 2;
txdr_hyper(len, tl);
tl += 2;
*tl++ = clp->nfsc_clientid.lval[0];
*tl = clp->nfsc_clientid.lval[1];
nfscl_filllockowner(p, own);
(void) nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN);
error = nfscl_request(nd, vp, p, cred, NULL);
if (error)
return (error);
if (nd->nd_repstat == 0) {
fl->l_type = F_UNLCK;
} else if (nd->nd_repstat == NFSERR_DENIED) {
nd->nd_repstat = 0;
fl->l_whence = SEEK_SET;
NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
fl->l_start = fxdr_hyper(tl);
tl += 2;
len = fxdr_hyper(tl);
tl += 2;
if (len == NFS64BITSSET)
fl->l_len = 0;
else
fl->l_len = len;
type = fxdr_unsigned(int, *tl++);
if (type == NFSV4LOCKT_WRITE)
fl->l_type = F_WRLCK;
else
fl->l_type = F_RDLCK;
/*
* XXX For now, I have no idea what to do with the
* conflicting lock_owner, so I'll just set the pid == 0
* and skip over the lock_owner.
*/
fl->l_pid = (pid_t)0;
tl += 2;
size = fxdr_unsigned(int, *tl);
if (size < 0 || size > NFSV4_OPAQUELIMIT)
error = EBADRPC;
if (!error)
error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
} else if (nd->nd_repstat == NFSERR_STALECLIENTID)
nfscl_initiate_recovery(clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* Lower level function that performs the LockU RPC.
*/
static int
nfsrpc_locku(struct nfsrv_descript *nd, struct nfsmount *nmp,
struct nfscllockowner *lp, u_int64_t off, u_int64_t len,
u_int32_t type, struct ucred *cred, NFSPROC_T *p, int syscred)
{
u_int32_t *tl;
int error;
nfscl_reqstart(nd, NFSPROC_LOCKU, nmp, lp->nfsl_open->nfso_fh,
lp->nfsl_open->nfso_fhlen, NULL);
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(type);
*tl = txdr_unsigned(lp->nfsl_seqid);
if (nfstest_outofseq &&
(arc4random() % nfstest_outofseq) == 0)
*tl = txdr_unsigned(lp->nfsl_seqid + 1);
tl++;
*tl++ = lp->nfsl_stateid.seqid;
*tl++ = lp->nfsl_stateid.other[0];
*tl++ = lp->nfsl_stateid.other[1];
*tl++ = lp->nfsl_stateid.other[2];
txdr_hyper(off, tl);
tl += 2;
txdr_hyper(len, tl);
if (syscred)
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
if (error)
return (error);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
lp->nfsl_stateid.seqid = *tl++;
lp->nfsl_stateid.other[0] = *tl++;
lp->nfsl_stateid.other[1] = *tl++;
lp->nfsl_stateid.other[2] = *tl;
} else if (nd->nd_repstat == NFSERR_STALESTATEID)
nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* The actual Lock RPC.
*/
APPLESTATIC int
nfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
u_int8_t *nfhp, int fhlen, struct nfscllockowner *lp, int newone,
int reclaim, u_int64_t off, u_int64_t len, short type, struct ucred *cred,
NFSPROC_T *p, int syscred)
{
u_int32_t *tl;
int error, size;
nfscl_reqstart(nd, NFSPROC_LOCK, nmp, nfhp, fhlen, NULL);
NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
if (type == F_RDLCK)
*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
else
*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
*tl++ = txdr_unsigned(reclaim);
txdr_hyper(off, tl);
tl += 2;
txdr_hyper(len, tl);
tl += 2;
if (newone) {
*tl = newnfs_true;
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
2 * NFSX_UNSIGNED + NFSX_HYPER);
*tl++ = txdr_unsigned(lp->nfsl_open->nfso_own->nfsow_seqid);
*tl++ = lp->nfsl_open->nfso_stateid.seqid;
*tl++ = lp->nfsl_open->nfso_stateid.other[0];
*tl++ = lp->nfsl_open->nfso_stateid.other[1];
*tl++ = lp->nfsl_open->nfso_stateid.other[2];
*tl++ = txdr_unsigned(lp->nfsl_seqid);
*tl++ = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[0];
*tl = lp->nfsl_open->nfso_own->nfsow_clp->nfsc_clientid.lval[1];
(void) nfsm_strtom(nd, lp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
} else {
*tl = newnfs_false;
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
*tl++ = lp->nfsl_stateid.seqid;
*tl++ = lp->nfsl_stateid.other[0];
*tl++ = lp->nfsl_stateid.other[1];
*tl++ = lp->nfsl_stateid.other[2];
*tl = txdr_unsigned(lp->nfsl_seqid);
if (nfstest_outofseq &&
(arc4random() % nfstest_outofseq) == 0)
*tl = txdr_unsigned(lp->nfsl_seqid + 1);
}
if (syscred)
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
if (newone)
NFSCL_INCRSEQID(lp->nfsl_open->nfso_own->nfsow_seqid, nd);
NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
lp->nfsl_stateid.seqid = *tl++;
lp->nfsl_stateid.other[0] = *tl++;
lp->nfsl_stateid.other[1] = *tl++;
lp->nfsl_stateid.other[2] = *tl;
} else if (nd->nd_repstat == NFSERR_DENIED) {
NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
size = fxdr_unsigned(int, *(tl + 7));
if (size < 0 || size > NFSV4_OPAQUELIMIT)
error = EBADRPC;
if (!error)
error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
} else if (nd->nd_repstat == NFSERR_STALESTATEID)
nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs statfs rpc
* (always called with the vp for the mount point)
*/
APPLESTATIC int
nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
void *stuff)
{
u_int32_t *tl = NULL;
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
nfsattrbit_t attrbits;
int error;
*attrflagp = 0;
nmp = VFSTONFS(vnode_mount(vp));
if (NFSHASNFSV4(nmp)) {
/*
* For V4, you actually do a getattr.
*/
NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
NFSSTATFS_GETATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
nd->nd_flag |= ND_USEGSSNAME;
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, sbp, fsp, NULL, 0, NULL, NULL, NULL, p,
cred);
if (!error) {
nmp->nm_fsid[0] = nap->na_filesid[0];
nmp->nm_fsid[1] = nap->na_filesid[1];
NFSSETHASSETFSID(nmp);
*attrflagp = 1;
}
} else {
error = nd->nd_repstat;
}
if (error)
goto nfsmout;
} else {
NFSCL_REQSTART(nd, NFSPROC_FSSTAT, vp);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_flag & ND_NFSV3) {
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (error)
goto nfsmout;
}
if (nd->nd_repstat) {
error = nd->nd_repstat;
goto nfsmout;
}
NFSM_DISSECT(tl, u_int32_t *,
NFSX_STATFS(nd->nd_flag & ND_NFSV3));
}
if (NFSHASNFSV3(nmp)) {
sbp->sf_tbytes = fxdr_hyper(tl); tl += 2;
sbp->sf_fbytes = fxdr_hyper(tl); tl += 2;
sbp->sf_abytes = fxdr_hyper(tl); tl += 2;
sbp->sf_tfiles = fxdr_hyper(tl); tl += 2;
sbp->sf_ffiles = fxdr_hyper(tl); tl += 2;
sbp->sf_afiles = fxdr_hyper(tl); tl += 2;
sbp->sf_invarsec = fxdr_unsigned(u_int32_t, *tl);
} else if (NFSHASNFSV4(nmp) == 0) {
sbp->sf_tsize = fxdr_unsigned(u_int32_t, *tl++);
sbp->sf_bsize = fxdr_unsigned(u_int32_t, *tl++);
sbp->sf_blocks = fxdr_unsigned(u_int32_t, *tl++);
sbp->sf_bfree = fxdr_unsigned(u_int32_t, *tl++);
sbp->sf_bavail = fxdr_unsigned(u_int32_t, *tl);
}
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs pathconf rpc
*/
APPLESTATIC int
nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc,
struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
void *stuff)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
u_int32_t *tl;
nfsattrbit_t attrbits;
int error;
*attrflagp = 0;
nmp = VFSTONFS(vnode_mount(vp));
if (NFSHASNFSV4(nmp)) {
/*
* For V4, you actually do a getattr.
*/
NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
NFSPATHCONF_GETATTRBIT(&attrbits);
(void) nfsrv_putattrbit(nd, &attrbits);
nd->nd_flag |= ND_USEGSSNAME;
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p,
cred);
if (!error)
*attrflagp = 1;
} else {
error = nd->nd_repstat;
}
} else {
NFSCL_REQSTART(nd, NFSPROC_PATHCONF, vp);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
if (!error) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V3PATHCONF);
pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl++);
pc->pc_namemax = fxdr_unsigned(u_int32_t, *tl++);
pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl++);
pc->pc_chownrestricted =
fxdr_unsigned(u_int32_t, *tl++);
pc->pc_caseinsensitive =
fxdr_unsigned(u_int32_t, *tl++);
pc->pc_casepreserving = fxdr_unsigned(u_int32_t, *tl);
}
}
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs version 3 fsinfo rpc call
*/
APPLESTATIC int
nfsrpc_fsinfo(vnode_t vp, struct nfsfsinfo *fsp, struct ucred *cred,
NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
{
u_int32_t *tl;
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
*attrflagp = 0;
NFSCL_REQSTART(nd, NFSPROC_FSINFO, vp);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
if (nd->nd_repstat && !error)
error = nd->nd_repstat;
if (!error) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V3FSINFO);
fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *tl++);
fsp->fs_rtpref = fxdr_unsigned(u_int32_t, *tl++);
fsp->fs_rtmult = fxdr_unsigned(u_int32_t, *tl++);
fsp->fs_wtmax = fxdr_unsigned(u_int32_t, *tl++);
fsp->fs_wtpref = fxdr_unsigned(u_int32_t, *tl++);
fsp->fs_wtmult = fxdr_unsigned(u_int32_t, *tl++);
fsp->fs_dtpref = fxdr_unsigned(u_int32_t, *tl++);
fsp->fs_maxfilesize = fxdr_hyper(tl);
tl += 2;
fxdr_nfsv3time(tl, &fsp->fs_timedelta);
tl += 2;
fsp->fs_properties = fxdr_unsigned(u_int32_t, *tl);
}
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* This function performs the Renew RPC.
*/
APPLESTATIC int
nfsrpc_renew(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
{
u_int32_t *tl;
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
struct nfsmount *nmp;
int error;
nmp = clp->nfsc_nmp;
if (nmp == NULL)
return (0);
nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = clp->nfsc_clientid.lval[0];
*tl = clp->nfsc_clientid.lval[1];
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* This function performs the Releaselockowner RPC.
*/
APPLESTATIC int
nfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
struct ucred *cred, NFSPROC_T *p)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
u_int32_t *tl;
int error;
nfscl_reqstart(nd, NFSPROC_RELEASELCKOWN, nmp, NULL, 0, NULL);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = nmp->nm_clp->nfsc_clientid.lval[0];
*tl = nmp->nm_clp->nfsc_clientid.lval[1];
(void) nfsm_strtom(nd, lp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* This function performs the Compound to get the mount pt FH.
*/
APPLESTATIC int
nfsrpc_getdirpath(struct nfsmount *nmp, u_char *dirpath, struct ucred *cred,
NFSPROC_T *p)
{
u_int32_t *tl;
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
u_char *cp, *cp2;
int error, cnt, len, setnil;
u_int32_t *opcntp;
nfscl_reqstart(nd, NFSPROC_PUTROOTFH, nmp, NULL, 0, &opcntp);
cp = dirpath;
cnt = 0;
do {
setnil = 0;
while (*cp == '/')
cp++;
cp2 = cp;
while (*cp2 != '\0' && *cp2 != '/')
cp2++;
if (*cp2 == '/') {
setnil = 1;
*cp2 = '\0';
}
if (cp2 != cp) {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_LOOKUP);
nfsm_strtom(nd, cp, strlen(cp));
cnt++;
}
if (setnil)
*cp2++ = '/';
cp = cp2;
} while (*cp != '\0');
*opcntp = txdr_unsigned(2 + cnt);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_GETFH);
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, (3 + 2 * cnt) * NFSX_UNSIGNED);
tl += (2 + 2 * cnt);
if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
len > NFSX_FHMAX) {
nd->nd_repstat = NFSERR_BADXDR;
} else {
nd->nd_repstat = nfsrv_mtostr(nd, nmp->nm_fh, len);
if (nd->nd_repstat == 0)
nmp->nm_fhsize = len;
}
}
error = nd->nd_repstat;
nfsmout:
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* This function performs the Delegreturn RPC.
*/
APPLESTATIC int
nfsrpc_delegreturn(struct nfscldeleg *dp, struct ucred *cred,
struct nfsmount *nmp, NFSPROC_T *p, int syscred)
{
u_int32_t *tl;
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
int error;
nfscl_reqstart(nd, NFSPROC_DELEGRETURN, nmp, dp->nfsdl_fh,
dp->nfsdl_fhlen, NULL);
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
*tl++ = dp->nfsdl_stateid.seqid;
*tl++ = dp->nfsdl_stateid.other[0];
*tl++ = dp->nfsdl_stateid.other[1];
*tl = dp->nfsdl_stateid.other[2];
if (syscred)
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL);
if (error)
return (error);
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs getacl call.
*/
APPLESTATIC int
nfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
struct acl *aclp, void *stuff)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
nfsattrbit_t attrbits;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
return (EOPNOTSUPP);
NFSCL_REQSTART(nd, NFSPROC_GETACL, vp);
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
(void) nfsrv_putattrbit(nd, &attrbits);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
if (!nd->nd_repstat)
error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, p, cred);
else
error = nd->nd_repstat;
mbuf_freem(nd->nd_mrep);
return (error);
}
/*
* nfs setacl call.
*/
APPLESTATIC int
nfsrpc_setacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
struct acl *aclp, void *stuff)
{
int error;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
return (EOPNOTSUPP);
error = nfsrpc_setattr(vp, NULL, aclp, cred, p, NULL, NULL, stuff);
return (error);
}
/*
* nfs setacl call.
*/
static int
nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
struct acl *aclp, nfsv4stateid_t *stateidp, void *stuff)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
nfsattrbit_t attrbits;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
if (!NFSHASNFSV4(nmp))
return (EOPNOTSUPP);
NFSCL_REQSTART(nd, NFSPROC_SETACL, vp);
nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
(void) nfsv4_fillattr(nd, vnode_mount(vp), vp, aclp, NULL, NULL, 0,
- &attrbits, NULL, NULL, 0, 0, 0, (uint64_t)0);
+ &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0);
error = nfscl_request(nd, vp, p, cred, stuff);
if (error)
return (error);
/* Don't care about the pre/postop attributes */
mbuf_freem(nd->nd_mrep);
return (nd->nd_repstat);
}
Index: stable/8/sys/fs/nfsclient/nfs_clstate.c
===================================================================
--- stable/8/sys/fs/nfsclient/nfs_clstate.c (revision 221216)
+++ stable/8/sys/fs/nfsclient/nfs_clstate.c (revision 221217)
@@ -1,4201 +1,4201 @@
/*-
* Copyright (c) 2009 Rick Macklem, University of Guelph
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$");
/*
* These functions implement the client side state handling for NFSv4.
* NFSv4 state handling:
* - A lockowner is used to determine lock contention, so it
* corresponds directly to a Posix pid. (1 to 1 mapping)
* - The correct granularity of an OpenOwner is not nearly so
* obvious. An OpenOwner does the following:
* - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
* - is used to check for Open/Share contention (not applicable to
* this client, since all Opens are Deny_None)
* As such, I considered both extreme.
* 1 OpenOwner per ClientID - Simple to manage, but fully serializes
* all Open, Close and Lock (with a new lockowner) Ops.
* 1 OpenOwner for each Open - This one results in an OpenConfirm for
* every Open, for most servers.
* So, I chose to use the same mapping as I did for LockOwnwers.
* The main concern here is that you can end up with multiple Opens
* for the same File Handle, but on different OpenOwners (opens
* inherited from parents, grandparents...) and you do not know
* which of these the vnodeop close applies to. This is handled by
* delaying the Close Op(s) until all of the Opens have been closed.
* (It is not yet obvious if this is the correct granularity.)
* - How the code handles serialization:
* - For the ClientId, it uses an exclusive lock while getting its
* SetClientId and during recovery. Otherwise, it uses a shared
* lock via a reference count.
* - For the rest of the data structures, it uses an SMP mutex
* (once the nfs client is SMP safe) and doesn't sleep while
* manipulating the linked lists.
* - The serialization of Open/Close/Lock/LockU falls out in the
* "wash", since OpenOwners and LockOwners are both mapped from
* Posix pid. In other words, there is only one Posix pid using
* any given owner, so that owner is serialized. (If you change
* the granularity of the OpenOwner, then code must be added to
* serialize Ops on the OpenOwner.)
* - When to get rid of OpenOwners and LockOwners.
* - When a process exits, it calls nfscl_cleanup(), which goes
* through the client list looking for all Open and Lock Owners.
* When one is found, it is marked "defunct" or in the case of
* an OpenOwner without any Opens, freed.
* The renew thread scans for defunct Owners and gets rid of them,
* if it can. The LockOwners will also be deleted when the
* associated Open is closed.
* - If the LockU or Close Op(s) fail during close in a way
* that could be recovered upon retry, they are relinked to the
* ClientId's defunct open list and retried by the renew thread
* until they succeed or an unmount/recovery occurs.
* (Since we are done with them, they do not need to be recovered.)
*/
#ifndef APPLEKEXT
#include <fs/nfs/nfsport.h>
/*
* Global variables
*/
extern struct nfsstats newnfsstats;
extern struct nfsreqhead nfsd_reqq;
NFSREQSPINLOCK;
NFSCLSTATEMUTEX;
int nfscl_inited = 0;
struct nfsclhead nfsclhead; /* Head of clientid list */
int nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
#endif /* !APPLEKEXT */
static int nfscl_delegcnt = 0;
static int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
NFSPROC_T *, u_int32_t, struct nfsclowner **, struct nfsclopen **);
static void nfscl_clrelease(struct nfsclclient *);
static void nfscl_cleanclient(struct nfsclclient *);
static void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
struct ucred *, NFSPROC_T *);
static int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
struct nfsmount *, struct ucred *, NFSPROC_T *);
static void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
static void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
struct nfscllock *, int);
static int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
struct nfscllock **, int);
static void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *);
static u_int32_t nfscl_nextcbident(void);
static mount_t nfscl_getmnt(u_int32_t);
static struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
int);
static int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
u_int8_t *, struct nfscllock **);
static void nfscl_freelockowner(struct nfscllockowner *, int);
static void nfscl_freealllocks(struct nfscllockownerhead *, int);
static int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
static void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *);
static int nfscl_moveopen(vnode_t , struct nfsclclient *,
struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
struct nfscldeleg *, struct ucred *, NFSPROC_T *);
static void nfscl_totalrecall(struct nfsclclient *);
static int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
static int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
static int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
struct ucred *, NFSPROC_T *);
static int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
static int nfscl_errmap(struct nfsrv_descript *);
static void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int);
static void nfscl_freeopenowner(struct nfsclowner *, int);
static void nfscl_cleandeleg(struct nfscldeleg *);
static int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
struct nfsmount *, NFSPROC_T *);
static short nfscberr_null[] = {
0,
0,
};
static short nfscberr_getattr[] = {
NFSERR_RESOURCE,
NFSERR_BADHANDLE,
NFSERR_BADXDR,
NFSERR_RESOURCE,
NFSERR_SERVERFAULT,
0,
};
static short nfscberr_recall[] = {
NFSERR_RESOURCE,
NFSERR_BADHANDLE,
NFSERR_BADSTATEID,
NFSERR_BADXDR,
NFSERR_RESOURCE,
NFSERR_SERVERFAULT,
0,
};
static short *nfscl_cberrmap[] = {
nfscberr_null,
nfscberr_null,
nfscberr_null,
nfscberr_getattr,
nfscberr_recall
};
#define NETFAMILY(clp) \
(((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
/*
* Called for an open operation.
* If the nfhp argument is NULL, just get an openowner.
*/
APPLESTATIC int
nfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
struct nfsclopen **opp, int *newonep, int *retp, int lockit)
{
struct nfsclclient *clp;
struct nfsclowner *owp, *nowp;
struct nfsclopen *op = NULL, *nop = NULL;
struct nfscldeleg *dp;
struct nfsclownerhead *ohp;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
int ret;
if (newonep != NULL)
*newonep = 0;
if (opp != NULL)
*opp = NULL;
if (owpp != NULL)
*owpp = NULL;
/*
* Might need one or both of these, so MALLOC them now, to
* avoid a tsleep() in MALLOC later.
*/
MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner),
M_NFSCLOWNER, M_WAITOK);
if (nfhp != NULL)
MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
fhlen - 1, M_NFSCLOPEN, M_WAITOK);
ret = nfscl_getcl(vp, cred, p, &clp);
if (ret != 0) {
FREE((caddr_t)nowp, M_NFSCLOWNER);
if (nop != NULL)
FREE((caddr_t)nop, M_NFSCLOPEN);
return (ret);
}
/*
* Get the Open iff it already exists.
* If none found, add the new one or return error, depending upon
* "create".
*/
nfscl_filllockowner(p, own);
NFSLOCKCLSTATE();
dp = NULL;
/* First check the delegation list */
if (nfhp != NULL && usedeleg) {
LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
if (dp->nfsdl_fhlen == fhlen &&
!NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
(dp->nfsdl_flags & NFSCLDL_WRITE))
break;
dp = NULL;
break;
}
}
}
if (dp != NULL)
ohp = &dp->nfsdl_owner;
else
ohp = &clp->nfsc_owner;
/* Now, search for an openowner */
LIST_FOREACH(owp, ohp, nfsow_list) {
if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
break;
}
/*
* Create a new open, as required.
*/
nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
newonep);
/*
* Serialize modifications to the open owner for multiple threads
* within the same process using a read/write sleep lock.
*/
if (lockit)
nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
NFSUNLOCKCLSTATE();
if (nowp != NULL)
FREE((caddr_t)nowp, M_NFSCLOWNER);
if (nop != NULL)
FREE((caddr_t)nop, M_NFSCLOPEN);
if (owpp != NULL)
*owpp = owp;
if (opp != NULL)
*opp = op;
if (retp != NULL) {
if (nfhp != NULL && dp != NULL && nop == NULL)
/* new local open on delegation */
*retp = NFSCLOPEN_SETCRED;
else
*retp = NFSCLOPEN_OK;
}
/*
* Now, check the mode on the open and return the appropriate
* value.
*/
if (op != NULL && (amode & ~(op->nfso_mode))) {
op->nfso_mode |= amode;
if (retp != NULL && dp == NULL)
*retp = NFSCLOPEN_DOOPEN;
}
return (0);
}
/*
* Create a new open, as required.
*/
static void
nfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
int *newonep)
{
struct nfsclowner *owp = *owpp, *nowp;
struct nfsclopen *op, *nop;
if (nowpp != NULL)
nowp = *nowpp;
else
nowp = NULL;
if (nopp != NULL)
nop = *nopp;
else
nop = NULL;
if (owp == NULL && nowp != NULL) {
NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
LIST_INIT(&nowp->nfsow_open);
nowp->nfsow_clp = clp;
nowp->nfsow_seqid = 0;
nowp->nfsow_defunct = 0;
nfscl_lockinit(&nowp->nfsow_rwlock);
if (dp != NULL) {
newnfsstats.cllocalopenowners++;
LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
} else {
newnfsstats.clopenowners++;
LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
}
owp = *owpp = nowp;
*nowpp = NULL;
if (newonep != NULL)
*newonep = 1;
}
/* If an fhp has been specified, create an Open as well. */
if (fhp != NULL) {
/* and look for the correct open, based upon FH */
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == fhlen &&
!NFSBCMP(op->nfso_fh, fhp, fhlen))
break;
}
if (op == NULL && nop != NULL) {
nop->nfso_own = owp;
nop->nfso_mode = 0;
nop->nfso_opencnt = 0;
nop->nfso_posixlock = 1;
nop->nfso_fhlen = fhlen;
NFSBCOPY(fhp, nop->nfso_fh, fhlen);
LIST_INIT(&nop->nfso_lock);
nop->nfso_stateid.seqid = 0;
nop->nfso_stateid.other[0] = 0;
nop->nfso_stateid.other[1] = 0;
nop->nfso_stateid.other[2] = 0;
if (dp != NULL) {
TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
nfsdl_list);
dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
newnfsstats.cllocalopens++;
} else {
newnfsstats.clopens++;
}
LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
*opp = nop;
*nopp = NULL;
if (newonep != NULL)
*newonep = 1;
} else {
*opp = op;
}
}
}
/*
* Called to find/add a delegation to a client.
*/
APPLESTATIC int
nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp)
{
struct nfscldeleg *dp = *dpp, *tdp;
/*
* First, if we have received a Read delegation for a file on a
* read/write file system, just return it, because they aren't
* useful, imho.
*/
if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) &&
(dp->nfsdl_flags & NFSCLDL_READ)) {
(void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p);
FREE((caddr_t)dp, M_NFSCLDELEG);
*dpp = NULL;
return (0);
}
/* Look for the correct deleg, based upon FH */
NFSLOCKCLSTATE();
tdp = nfscl_finddeleg(clp, nfhp, fhlen);
if (tdp == NULL) {
if (dp == NULL) {
NFSUNLOCKCLSTATE();
return (NFSERR_BADSTATEID);
}
*dpp = NULL;
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
nfsdl_hash);
dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
newnfsstats.cldelegates++;
nfscl_delegcnt++;
} else {
/*
* Delegation already exists, what do we do if a new one??
*/
if (dp != NULL) {
printf("Deleg already exists!\n");
FREE((caddr_t)dp, M_NFSCLDELEG);
*dpp = NULL;
} else {
*dpp = tdp;
}
}
NFSUNLOCKCLSTATE();
return (0);
}
/*
* Find a delegation for this file handle. Return NULL upon failure.
*/
static struct nfscldeleg *
nfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
{
struct nfscldeleg *dp;
LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
if (dp->nfsdl_fhlen == fhlen &&
!NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
break;
}
return (dp);
}
/*
* Get a stateid for an I/O operation. First, look for an open and iff
* found, return either a lockowner stateid or the open stateid.
* If no Open is found, just return error and the special stateid of all zeros.
*/
APPLESTATIC int
nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
void **lckpp)
{
struct nfsclclient *clp;
struct nfsclowner *owp;
struct nfsclopen *op = NULL;
struct nfscllockowner *lp;
struct nfscldeleg *dp;
struct nfsnode *np;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
int error, done;
*lckpp = NULL;
/*
* Initially, just set the special stateid of all zeros.
*/
stateidp->seqid = 0;
stateidp->other[0] = 0;
stateidp->other[1] = 0;
stateidp->other[2] = 0;
if (vnode_vtype(vp) != VREG)
return (EISDIR);
np = VTONFS(vp);
NFSLOCKCLSTATE();
clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (EACCES);
}
/*
* Wait for recovery to complete.
*/
while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG))
(void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR,
PZERO, "nfsrecvr", NULL);
/*
* First, look for a delegation.
*/
LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
if (dp->nfsdl_fhlen == fhlen &&
!NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
(dp->nfsdl_flags & NFSCLDL_WRITE)) {
stateidp->seqid = dp->nfsdl_stateid.seqid;
stateidp->other[0] = dp->nfsdl_stateid.other[0];
stateidp->other[1] = dp->nfsdl_stateid.other[1];
stateidp->other[2] = dp->nfsdl_stateid.other[2];
if (!(np->n_flag & NDELEGRECALL)) {
TAILQ_REMOVE(&clp->nfsc_deleg, dp,
nfsdl_list);
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
nfsdl_list);
dp->nfsdl_timestamp = NFSD_MONOSEC +
120;
dp->nfsdl_rwlock.nfslock_usecnt++;
*lckpp = (void *)&dp->nfsdl_rwlock;
}
NFSUNLOCKCLSTATE();
return (0);
}
break;
}
}
if (p != NULL) {
/*
* If p != NULL, we want to search the parentage tree
* for a matching OpenOwner and use that.
*/
nfscl_filllockowner(p, own);
error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, NULL, p,
mode, NULL, &op);
if (error == 0) {
/* now look for a lockowner */
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN)) {
stateidp->seqid =
lp->nfsl_stateid.seqid;
stateidp->other[0] =
lp->nfsl_stateid.other[0];
stateidp->other[1] =
lp->nfsl_stateid.other[1];
stateidp->other[2] =
lp->nfsl_stateid.other[2];
NFSUNLOCKCLSTATE();
return (0);
}
}
}
}
if (op == NULL) {
/* If not found, just look for any OpenOwner that will work. */
done = 0;
owp = LIST_FIRST(&clp->nfsc_owner);
while (!done && owp != NULL) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == fhlen &&
!NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
(mode & op->nfso_mode) == mode) {
done = 1;
break;
}
}
if (!done)
owp = LIST_NEXT(owp, nfsow_list);
}
if (!done) {
NFSUNLOCKCLSTATE();
return (ENOENT);
}
/* for read aheads or write behinds, use the open cred */
newnfs_copycred(&op->nfso_cred, cred);
}
/*
* No lock stateid, so return the open stateid.
*/
stateidp->seqid = op->nfso_stateid.seqid;
stateidp->other[0] = op->nfso_stateid.other[0];
stateidp->other[1] = op->nfso_stateid.other[1];
stateidp->other[2] = op->nfso_stateid.other[2];
NFSUNLOCKCLSTATE();
return (0);
}
/*
* Get an existing open. Search up the parentage tree for a match and
* return with the first one found.
*/
static int
nfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
u_int8_t *rown, NFSPROC_T *p, u_int32_t mode, struct nfsclowner **owpp,
struct nfsclopen **opp)
{
struct nfsclowner *owp = NULL;
struct nfsclopen *op;
NFSPROC_T *nproc;
u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
nproc = p;
op = NULL;
while (op == NULL && (nproc != NULL || rown != NULL)) {
if (nproc != NULL) {
nfscl_filllockowner(nproc, own);
ownp = own;
} else {
ownp = rown;
}
/* Search the client list */
LIST_FOREACH(owp, ohp, nfsow_list) {
if (!NFSBCMP(owp->nfsow_owner, ownp,
NFSV4CL_LOCKNAMELEN))
break;
}
if (owp != NULL) {
/* and look for the correct open */
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == fhlen &&
!NFSBCMP(op->nfso_fh, nfhp, fhlen)
&& (op->nfso_mode & mode) == mode) {
break;
}
}
}
if (rown != NULL)
break;
if (op == NULL)
nproc = nfscl_getparent(nproc);
}
if (op == NULL) {
return (EBADF);
}
if (owpp)
*owpp = owp;
*opp = op;
return (0);
}
/*
* Release use of an open owner. Called when open operations are done
* with the open owner.
*/
APPLESTATIC void
nfscl_ownerrelease(struct nfsclowner *owp, __unused int error,
__unused int candelete, int unlocked)
{
if (owp == NULL)
return;
NFSLOCKCLSTATE();
if (!unlocked)
nfscl_lockunlock(&owp->nfsow_rwlock);
nfscl_clrelease(owp->nfsow_clp);
NFSUNLOCKCLSTATE();
}
/*
* Release use of an open structure under an open owner.
*/
APPLESTATIC void
nfscl_openrelease(struct nfsclopen *op, int error, int candelete)
{
struct nfsclclient *clp;
struct nfsclowner *owp;
if (op == NULL)
return;
NFSLOCKCLSTATE();
owp = op->nfso_own;
nfscl_lockunlock(&owp->nfsow_rwlock);
clp = owp->nfsow_clp;
if (error && candelete && op->nfso_opencnt == 0)
nfscl_freeopen(op, 0);
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
}
/*
* Called to get a clientid structure. It will optionally lock the
* client data structures to do the SetClientId/SetClientId_confirm,
* but will release that lock and return the clientid with a refernce
* count on it.
* If the "cred" argument is NULL, a new clientid should not be created.
* If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
* be done.
* It always clpp with a reference count on it, unless returning an error.
*/
APPLESTATIC int
nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
struct nfsclclient **clpp)
{
struct nfsclclient *clp;
struct nfsclclient *newclp = NULL;
struct nfscllockowner *lp, *nlp;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
char uuid[HOSTUUIDLEN];
int igotlock = 0, error, trystalecnt, clidinusedelay, i;
u_int16_t idlen = 0;
if (cred != NULL) {
getcredhostuuid(cred, uuid, sizeof uuid);
idlen = strlen(uuid);
if (idlen > 0)
idlen += sizeof (u_int64_t);
else
idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
MALLOC(newclp, struct nfsclclient *,
sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
M_WAITOK);
}
NFSLOCKCLSTATE();
clp = nmp->nm_clp;
if (clp == NULL) {
if (newclp == NULL) {
NFSUNLOCKCLSTATE();
return (EACCES);
}
clp = newclp;
NFSBZERO((caddr_t)clp, sizeof(struct nfsclclient) + idlen - 1);
clp->nfsc_idlen = idlen;
LIST_INIT(&clp->nfsc_owner);
TAILQ_INIT(&clp->nfsc_deleg);
for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
LIST_INIT(&clp->nfsc_deleghash[i]);
LIST_INIT(&clp->nfsc_defunctlockowner);
clp->nfsc_flags = NFSCLFLAGS_INITED;
clp->nfsc_clientidrev = 1;
clp->nfsc_cbident = nfscl_nextcbident();
nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
clp->nfsc_idlen);
LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
nmp->nm_clp = clp;
clp->nfsc_nmp = nmp;
NFSUNLOCKCLSTATE();
nfscl_start_renewthread(clp);
} else {
NFSUNLOCKCLSTATE();
if (newclp != NULL)
FREE((caddr_t)newclp, M_NFSCLCLIENT);
}
NFSLOCKCLSTATE();
while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock)
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
NFSCLSTATEMUTEXPTR);
if (!igotlock)
nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR);
NFSUNLOCKCLSTATE();
/*
* If it needs a clientid, do the setclientid now.
*/
if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
if (!igotlock)
panic("nfscl_clget");
if (p == NULL || cred == NULL) {
NFSLOCKCLSTATE();
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
return (EACCES);
}
/* get rid of defunct lockowners */
LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list,
nlp) {
nfscl_freelockowner(lp, 0);
}
/*
* If RFC3530 Sec. 14.2.33 is taken literally,
* NFSERR_CLIDINUSE will be returned persistently for the
* case where a new mount of the same file system is using
* a different principal. In practice, NFSERR_CLIDINUSE is
* only returned when there is outstanding unexpired state
* on the clientid. As such, try for twice the lease
* interval, if we know what that is. Otherwise, make a
* wild ass guess.
* The case of returning NFSERR_STALECLIENTID is far less
* likely, but might occur if there is a significant delay
* between doing the SetClientID and SetClientIDConfirm Ops,
* such that the server throws away the clientid before
* receiving the SetClientIDConfirm.
*/
if (clp->nfsc_renew > 0)
clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
else
clidinusedelay = 120;
trystalecnt = 3;
do {
error = nfsrpc_setclient(VFSTONFS(vnode_mount(vp)),
clp, cred, p);
if (error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER ||
error == NFSERR_CLIDINUSE) {
(void) nfs_catnap(PZERO, error, "nfs_setcl");
}
} while (((error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
(error == NFSERR_CLIDINUSE && --clidinusedelay > 0));
if (error) {
NFSLOCKCLSTATE();
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
return (error);
}
clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
}
if (igotlock) {
NFSLOCKCLSTATE();
nfsv4_unlock(&clp->nfsc_lock, 1);
NFSUNLOCKCLSTATE();
}
*clpp = clp;
return (0);
}
/*
* Get a reference to a clientid and return it, if valid.
*/
APPLESTATIC struct nfsclclient *
nfscl_findcl(struct nfsmount *nmp)
{
struct nfsclclient *clp;
clp = nmp->nm_clp;
if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
return (NULL);
return (clp);
}
/*
* Release the clientid structure. It may be locked or reference counted.
*/
static void
nfscl_clrelease(struct nfsclclient *clp)
{
if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
nfsv4_unlock(&clp->nfsc_lock, 0);
else
nfsv4_relref(&clp->nfsc_lock);
}
/*
* External call for nfscl_clrelease.
*/
APPLESTATIC void
nfscl_clientrelease(struct nfsclclient *clp)
{
NFSLOCKCLSTATE();
if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
nfsv4_unlock(&clp->nfsc_lock, 0);
else
nfsv4_relref(&clp->nfsc_lock);
NFSUNLOCKCLSTATE();
}
/*
* Called when wanting to lock a byte region.
*/
APPLESTATIC int
nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
int recovery, u_int8_t *rownp, u_int8_t *ropenownp,
struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
{
struct nfscllockowner *lp;
struct nfsclopen *op;
struct nfsclclient *clp;
struct nfscllockowner *nlp;
struct nfscllock *nlop, *otherlop;
struct nfscldeleg *dp = NULL, *ldp = NULL;
struct nfscllockownerhead *lhp = NULL;
struct nfsnode *np;
u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
int error = 0, ret, donelocally = 0;
u_int32_t mode;
if (type == F_WRLCK)
mode = NFSV4OPEN_ACCESSWRITE;
else
mode = NFSV4OPEN_ACCESSREAD;
np = VTONFS(vp);
*lpp = NULL;
*newonep = 0;
*donelocallyp = 0;
/*
* Might need these, so MALLOC them now, to
* avoid a tsleep() in MALLOC later.
*/
MALLOC(nlp, struct nfscllockowner *,
sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
MALLOC(otherlop, struct nfscllock *,
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
MALLOC(nlop, struct nfscllock *,
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
nlop->nfslo_type = type;
nlop->nfslo_first = off;
if (len == NFS64BITSSET) {
nlop->nfslo_end = NFS64BITSSET;
} else {
nlop->nfslo_end = off + len;
if (nlop->nfslo_end <= nlop->nfslo_first)
error = NFSERR_INVAL;
}
if (!error) {
if (recovery)
clp = rclp;
else
error = nfscl_getcl(vp, cred, p, &clp);
}
if (error) {
FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
FREE((caddr_t)otherlop, M_NFSCLLOCK);
FREE((caddr_t)nlop, M_NFSCLLOCK);
return (error);
}
op = NULL;
if (recovery) {
ownp = rownp;
} else {
nfscl_filllockowner(p, own);
ownp = own;
}
if (!recovery) {
NFSLOCKCLSTATE();
/*
* First, search for a delegation. If one exists for this file,
* the lock can be done locally against it, so long as there
* isn't a local lock conflict.
*/
ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len);
/* Just sanity check for correct type of delegation */
if (dp != NULL && ((dp->nfsdl_flags &
(NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 ||
(type == F_WRLCK &&
(dp->nfsdl_flags & NFSCLDL_WRITE) == 0)))
dp = NULL;
}
if (dp != NULL) {
/* Now, find the associated open to get the correct openowner */
ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len, NULL, p, mode, NULL, &op);
if (ret)
ret = nfscl_getopen(&clp->nfsc_owner,
np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
mode, NULL, &op);
if (!ret) {
lhp = &dp->nfsdl_lock;
TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
donelocally = 1;
} else {
dp = NULL;
}
}
if (!donelocally) {
/*
* Get the related Open.
*/
if (recovery)
error = nfscl_getopen(&clp->nfsc_owner,
np->n_fhp->nfh_fh, np->n_fhp->nfh_len, ropenownp,
NULL, mode, NULL, &op);
else
error = nfscl_getopen(&clp->nfsc_owner,
np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
mode, NULL, &op);
if (!error)
lhp = &op->nfso_lock;
}
if (!error && !recovery)
error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
if (error) {
if (!recovery) {
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
}
FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
FREE((caddr_t)otherlop, M_NFSCLLOCK);
FREE((caddr_t)nlop, M_NFSCLLOCK);
return (error);
}
/*
* Ok, see if a lockowner exists and create one, as required.
*/
LIST_FOREACH(lp, lhp, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
break;
}
if (lp == NULL) {
NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
if (recovery)
NFSBCOPY(ropenownp, nlp->nfsl_openowner,
NFSV4CL_LOCKNAMELEN);
else
NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
NFSV4CL_LOCKNAMELEN);
nlp->nfsl_seqid = 0;
nlp->nfsl_defunct = 0;
nlp->nfsl_inprog = NULL;
nfscl_lockinit(&nlp->nfsl_rwlock);
LIST_INIT(&nlp->nfsl_lock);
if (donelocally) {
nlp->nfsl_open = NULL;
newnfsstats.cllocallockowners++;
} else {
nlp->nfsl_open = op;
newnfsstats.cllockowners++;
}
LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
lp = nlp;
nlp = NULL;
*newonep = 1;
}
/*
* Now, update the byte ranges for locks.
*/
ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
if (!ret)
donelocally = 1;
if (donelocally) {
*donelocallyp = 1;
if (!recovery)
nfscl_clrelease(clp);
} else {
/*
* Serial modifications on the lock owner for multiple threads
* for the same process using a read/write lock.
*/
if (!recovery)
nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
}
if (!recovery)
NFSUNLOCKCLSTATE();
if (nlp)
FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
if (nlop)
FREE((caddr_t)nlop, M_NFSCLLOCK);
if (otherlop)
FREE((caddr_t)otherlop, M_NFSCLLOCK);
*lpp = lp;
return (0);
}
/*
* Called to unlock a byte range, for LockU.
*/
APPLESTATIC int
nfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
__unused struct ucred *cred, NFSPROC_T *p, int callcnt,
struct nfsclclient *clp, struct nfscllockowner **lpp, int *dorpcp)
{
struct nfscllockowner *lp;
struct nfsclowner *owp;
struct nfsclopen *op;
struct nfscllock *nlop, *other_lop = NULL;
struct nfscldeleg *dp;
struct nfsnode *np;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
int ret = 0, fnd;
np = VTONFS(vp);
*lpp = NULL;
*dorpcp = 0;
/*
* Might need these, so MALLOC them now, to
* avoid a tsleep() in MALLOC later.
*/
MALLOC(nlop, struct nfscllock *,
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
nlop->nfslo_type = F_UNLCK;
nlop->nfslo_first = off;
if (len == NFS64BITSSET) {
nlop->nfslo_end = NFS64BITSSET;
} else {
nlop->nfslo_end = off + len;
if (nlop->nfslo_end <= nlop->nfslo_first) {
FREE((caddr_t)nlop, M_NFSCLLOCK);
return (NFSERR_INVAL);
}
}
if (callcnt == 0) {
MALLOC(other_lop, struct nfscllock *,
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
*other_lop = *nlop;
}
nfscl_filllockowner(p, own);
dp = NULL;
NFSLOCKCLSTATE();
if (callcnt == 0)
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len);
/*
* First, unlock any local regions on a delegation.
*/
if (dp != NULL) {
/* Look for this lockowner. */
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN))
break;
}
if (lp != NULL)
/* Use other_lop, so nlop is still available */
(void)nfscl_updatelock(lp, &other_lop, NULL, 1);
}
/*
* Now, find a matching open/lockowner that hasn't already been done,
* as marked by nfsl_inprog.
*/
lp = NULL;
fnd = 0;
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lp->nfsl_inprog == NULL &&
!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN)) {
fnd = 1;
break;
}
}
if (fnd)
break;
}
}
if (fnd)
break;
}
if (lp != NULL) {
ret = nfscl_updatelock(lp, &nlop, NULL, 0);
if (ret)
*dorpcp = 1;
/*
* Serial modifications on the lock owner for multiple
* threads for the same process using a read/write lock.
*/
lp->nfsl_inprog = p;
nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
*lpp = lp;
}
NFSUNLOCKCLSTATE();
if (nlop)
FREE((caddr_t)nlop, M_NFSCLLOCK);
if (other_lop)
FREE((caddr_t)other_lop, M_NFSCLLOCK);
return (0);
}
/*
* Release all lockowners marked in progess for this process and file.
*/
APPLESTATIC void
nfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p)
{
struct nfsclowner *owp;
struct nfsclopen *op;
struct nfscllockowner *lp;
struct nfsnode *np;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
np = VTONFS(vp);
nfscl_filllockowner(p, own);
NFSLOCKCLSTATE();
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lp->nfsl_inprog == p &&
!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN)) {
lp->nfsl_inprog = NULL;
nfscl_lockunlock(&lp->nfsl_rwlock);
}
}
}
}
}
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
}
/*
* Called to find out if any bytes within the byte range specified are
* write locked by the calling process. Used to determine if flushing
* is required before a LockU.
* If in doubt, return 1, so the flush will occur.
*/
APPLESTATIC int
nfscl_checkwritelocked(vnode_t vp, struct flock *fl,
struct ucred *cred, NFSPROC_T *p)
{
struct nfsclowner *owp;
struct nfscllockowner *lp;
struct nfsclopen *op;
struct nfsclclient *clp;
struct nfscllock *lop;
struct nfscldeleg *dp;
struct nfsnode *np;
u_int64_t off, end;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
int error = 0;
np = VTONFS(vp);
switch (fl->l_whence) {
case SEEK_SET:
case SEEK_CUR:
/*
* Caller is responsible for adding any necessary offset
* when SEEK_CUR is used.
*/
off = fl->l_start;
break;
case SEEK_END:
off = np->n_size + fl->l_start;
break;
default:
return (1);
};
if (fl->l_len != 0) {
end = off + fl->l_len;
if (end < off)
return (1);
} else {
end = NFS64BITSSET;
}
error = nfscl_getcl(vp, cred, p, &clp);
if (error)
return (1);
nfscl_filllockowner(p, own);
NFSLOCKCLSTATE();
/*
* First check the delegation locks.
*/
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
if (dp != NULL) {
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN))
break;
}
if (lp != NULL) {
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
if (lop->nfslo_first >= end)
break;
if (lop->nfslo_end <= off)
continue;
if (lop->nfslo_type == F_WRLCK) {
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
return (1);
}
}
}
}
/*
* Now, check state against the server.
*/
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN))
break;
}
if (lp != NULL) {
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
if (lop->nfslo_first >= end)
break;
if (lop->nfslo_end <= off)
continue;
if (lop->nfslo_type == F_WRLCK) {
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
return (1);
}
}
}
}
}
}
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
return (0);
}
/*
* Release a byte range lock owner structure.
*/
APPLESTATIC void
nfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
{
struct nfsclclient *clp;
if (lp == NULL)
return;
NFSLOCKCLSTATE();
clp = lp->nfsl_open->nfso_own->nfsow_clp;
if (error != 0 && candelete &&
(lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
nfscl_freelockowner(lp, 0);
else
nfscl_lockunlock(&lp->nfsl_rwlock);
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
}
/*
* Free up an open structure and any associated byte range lock structures.
*/
APPLESTATIC void
nfscl_freeopen(struct nfsclopen *op, int local)
{
LIST_REMOVE(op, nfso_list);
nfscl_freealllocks(&op->nfso_lock, local);
FREE((caddr_t)op, M_NFSCLOPEN);
if (local)
newnfsstats.cllocalopens--;
else
newnfsstats.clopens--;
}
/*
* Free up all lock owners and associated locks.
*/
static void
nfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
{
struct nfscllockowner *lp, *nlp;
LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
panic("nfscllckw");
nfscl_freelockowner(lp, local);
}
}
/*
* Called for an Open when NFSERR_EXPIRED is received from the server.
* If there are no byte range locks nor a Share Deny lost, try to do a
* fresh Open. Otherwise, free the open.
*/
static int
nfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
{
struct nfscllockowner *lp;
struct nfscldeleg *dp;
int mustdelete = 0, error;
/*
* Look for any byte range lock(s).
*/
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (!LIST_EMPTY(&lp->nfsl_lock)) {
mustdelete = 1;
break;
}
}
/*
* If no byte range lock(s) nor a Share deny, try to re-open.
*/
if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
newnfs_copycred(&op->nfso_cred, cred);
dp = NULL;
error = nfsrpc_reopen(nmp, op->nfso_fh,
op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
if (error) {
mustdelete = 1;
if (dp != NULL) {
FREE((caddr_t)dp, M_NFSCLDELEG);
dp = NULL;
}
}
if (dp != NULL)
nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
op->nfso_fhlen, cred, p, &dp);
}
/*
* If a byte range lock or Share deny or couldn't re-open, free it.
*/
if (mustdelete)
nfscl_freeopen(op, 0);
return (mustdelete);
}
/*
* Free up an open owner structure.
*/
static void
nfscl_freeopenowner(struct nfsclowner *owp, int local)
{
LIST_REMOVE(owp, nfsow_list);
FREE((caddr_t)owp, M_NFSCLOWNER);
if (local)
newnfsstats.cllocalopenowners--;
else
newnfsstats.clopenowners--;
}
/*
* Free up a byte range lock owner structure.
*/
static void
nfscl_freelockowner(struct nfscllockowner *lp, int local)
{
struct nfscllock *lop, *nlop;
LIST_REMOVE(lp, nfsl_list);
LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
nfscl_freelock(lop, local);
}
FREE((caddr_t)lp, M_NFSCLLOCKOWNER);
if (local)
newnfsstats.cllocallockowners--;
else
newnfsstats.cllockowners--;
}
/*
* Free up a byte range lock structure.
*/
APPLESTATIC void
nfscl_freelock(struct nfscllock *lop, int local)
{
LIST_REMOVE(lop, nfslo_list);
FREE((caddr_t)lop, M_NFSCLLOCK);
if (local)
newnfsstats.cllocallocks--;
else
newnfsstats.cllocks--;
}
/*
* Clean out the state related to a delegation.
*/
static void
nfscl_cleandeleg(struct nfscldeleg *dp)
{
struct nfsclowner *owp, *nowp;
struct nfsclopen *op;
LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
op = LIST_FIRST(&owp->nfsow_open);
if (op != NULL) {
if (LIST_NEXT(op, nfso_list) != NULL)
panic("nfscleandel");
nfscl_freeopen(op, 1);
}
nfscl_freeopenowner(owp, 1);
}
nfscl_freealllocks(&dp->nfsdl_lock, 1);
}
/*
* Free a delegation.
*/
static void
nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
{
TAILQ_REMOVE(hdp, dp, nfsdl_list);
LIST_REMOVE(dp, nfsdl_hash);
FREE((caddr_t)dp, M_NFSCLDELEG);
newnfsstats.cldelegates--;
nfscl_delegcnt--;
}
/*
* Free up all state related to this client structure.
*/
static void
nfscl_cleanclient(struct nfsclclient *clp)
{
struct nfsclowner *owp, *nowp;
struct nfsclopen *op, *nop;
struct nfscllockowner *lp, *nlp;
/* get rid of defunct lockowners */
LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) {
nfscl_freelockowner(lp, 0);
}
/* Now, all the OpenOwners, etc. */
LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
nfscl_freeopen(op, 0);
}
nfscl_freeopenowner(owp, 0);
}
}
/*
* Called when an NFSERR_EXPIRED is received from the server.
*/
static void
nfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
struct ucred *cred, NFSPROC_T *p)
{
struct nfsclowner *owp, *nowp, *towp;
struct nfsclopen *op, *nop, *top;
struct nfscldeleg *dp, *ndp;
int ret, printed = 0;
/*
* First, merge locally issued Opens into the list for the server.
*/
dp = TAILQ_FIRST(&clp->nfsc_deleg);
while (dp != NULL) {
ndp = TAILQ_NEXT(dp, nfsdl_list);
owp = LIST_FIRST(&dp->nfsdl_owner);
while (owp != NULL) {
nowp = LIST_NEXT(owp, nfsow_list);
op = LIST_FIRST(&owp->nfsow_open);
if (op != NULL) {
if (LIST_NEXT(op, nfso_list) != NULL)
panic("nfsclexp");
LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
NFSV4CL_LOCKNAMELEN))
break;
}
if (towp != NULL) {
/* Merge opens in */
LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
if (top->nfso_fhlen == op->nfso_fhlen &&
!NFSBCMP(top->nfso_fh, op->nfso_fh,
op->nfso_fhlen)) {
top->nfso_mode |= op->nfso_mode;
top->nfso_opencnt += op->nfso_opencnt;
break;
}
}
if (top == NULL) {
/* Just add the open to the owner list */
LIST_REMOVE(op, nfso_list);
op->nfso_own = towp;
LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
newnfsstats.cllocalopens--;
newnfsstats.clopens++;
}
} else {
/* Just add the openowner to the client list */
LIST_REMOVE(owp, nfsow_list);
owp->nfsow_clp = clp;
LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
newnfsstats.cllocalopenowners--;
newnfsstats.clopenowners++;
newnfsstats.cllocalopens--;
newnfsstats.clopens++;
}
}
owp = nowp;
}
if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
printed = 1;
printf("nfsv4 expired locks lost\n");
}
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
dp = ndp;
}
if (!TAILQ_EMPTY(&clp->nfsc_deleg))
panic("nfsclexp");
/*
* Now, try and reopen against the server.
*/
LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
owp->nfsow_seqid = 0;
LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
ret = nfscl_expireopen(clp, op, nmp, cred, p);
if (ret && !printed) {
printed = 1;
printf("nfsv4 expired locks lost\n");
}
}
if (LIST_EMPTY(&owp->nfsow_open))
nfscl_freeopenowner(owp, 0);
}
}
#ifndef __FreeBSD__
/*
* Called from exit() upon process termination.
*/
APPLESTATIC void
nfscl_cleanup(NFSPROC_T *p)
{
struct nfsclclient *clp;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
if (!nfscl_inited)
return;
nfscl_filllockowner(p, own);
NFSLOCKCLSTATE();
/*
* Loop through all the clientids, looking for the OpenOwners.
*/
LIST_FOREACH(clp, &nfsclhead, nfsc_list)
nfscl_cleanup_common(clp, own);
NFSUNLOCKCLSTATE();
}
#endif /* !__FreeBSD__ */
/*
* Common code used by nfscl_cleanup() and nfscl_cleanupkext().
* Must be called with CLSTATE lock held.
*/
static void
nfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
{
struct nfsclowner *owp, *nowp;
struct nfsclopen *op;
struct nfscllockowner *lp, *nlp;
struct nfscldeleg *dp;
/* First, get rid of local locks on delegations. */
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
panic("nfscllckw");
nfscl_freelockowner(lp, 1);
}
}
}
owp = LIST_FIRST(&clp->nfsc_owner);
while (owp != NULL) {
nowp = LIST_NEXT(owp, nfsow_list);
if (!NFSBCMP(owp->nfsow_owner, own,
NFSV4CL_LOCKNAMELEN)) {
/*
* If there are children that haven't closed the
* file descriptors yet, the opens will still be
* here. For that case, let the renew thread clear
* out the OpenOwner later.
*/
if (LIST_EMPTY(&owp->nfsow_open))
nfscl_freeopenowner(owp, 0);
else
owp->nfsow_defunct = 1;
} else {
/* look for lockowners on other opens */
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN))
lp->nfsl_defunct = 1;
}
}
}
owp = nowp;
}
/* and check the defunct list */
LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN))
lp->nfsl_defunct = 1;
}
}
#if defined(APPLEKEXT) || defined(__FreeBSD__)
/*
* Simulate the call nfscl_cleanup() by looking for open owners associated
* with processes that no longer exist, since a call to nfscl_cleanup()
* can't be patched into exit().
*/
static void
nfscl_cleanupkext(struct nfsclclient *clp)
{
struct nfsclowner *owp, *nowp;
struct nfscllockowner *lp;
NFSPROCLISTLOCK();
NFSLOCKCLSTATE();
LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
if (nfscl_procdoesntexist(owp->nfsow_owner))
nfscl_cleanup_common(clp, owp->nfsow_owner);
}
/* and check the defunct list */
LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) {
if (nfscl_procdoesntexist(lp->nfsl_owner))
lp->nfsl_defunct = 1;
}
NFSUNLOCKCLSTATE();
NFSPROCLISTUNLOCK();
}
#endif /* APPLEKEXT || __FreeBSD__ */
/*
* Called from nfs umount to free up the clientid.
*/
APPLESTATIC void
nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
{
struct nfsclclient *clp;
struct ucred *cred;
int igotlock;
clp = nmp->nm_clp;
if (clp != NULL) {
if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
panic("nfscl umount");
/*
* First, handshake with the nfscl renew thread, to terminate
* it.
*/
clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
(void) tsleep((caddr_t)clp, PWAIT, "nfsclumnt", hz);
NFSLOCKCLSTATE();
do {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
NFSCLSTATEMUTEXPTR);
} while (!igotlock);
NFSUNLOCKCLSTATE();
/*
* Free up all the state. It will expire on the server, but
* maybe we should do a SetClientId/SetClientIdConfirm so
* the server throws it away?
*/
LIST_REMOVE(clp, nfsc_list);
nfscl_delegreturnall(clp, p);
cred = newnfs_getcred();
(void) nfsrpc_setclient(nmp, clp, cred, p);
nfscl_cleanclient(clp);
nmp->nm_clp = NULL;
NFSFREECRED(cred);
FREE((caddr_t)clp, M_NFSCLCLIENT);
}
}
/*
* This function is called when a server replies with NFSERR_STALECLIENTID
* or NFSERR_STALESTATEID. It traverses the clientid lists, doing Opens
* and Locks with reclaim. If these fail, it deletes the corresponding state.
*/
static void
nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
{
struct nfsclowner *owp, *nowp;
struct nfsclopen *op, *nop;
struct nfscllockowner *lp, *nlp;
struct nfscllock *lop, *nlop;
struct nfscldeleg *dp, *ndp, *tdp;
struct nfsmount *nmp;
struct ucred *tcred;
struct nfsclopenhead extra_open;
struct nfscldeleghead extra_deleg;
struct nfsreq *rep;
u_int64_t len;
u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
int igotlock = 0, error, trycnt, firstlock, s;
/*
* First, lock the client structure, so everyone else will
* block when trying to use state.
*/
NFSLOCKCLSTATE();
clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
do {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
NFSCLSTATEMUTEXPTR);
} while (!igotlock);
NFSUNLOCKCLSTATE();
nmp = clp->nfsc_nmp;
if (nmp == NULL)
panic("nfscl recover");
trycnt = 5;
do {
error = nfsrpc_setclient(nmp, clp, cred, p);
} while ((error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
if (error) {
nfscl_cleanclient(clp);
NFSLOCKCLSTATE();
clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG);
wakeup(&clp->nfsc_flags);
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
return;
}
clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
/*
* Mark requests already queued on the server, so that they don't
* initiate another recovery cycle. Any requests already in the
* queue that handle state information will have the old stale
* clientid/stateid and will get a NFSERR_STALESTATEID or
* NFSERR_STALECLIENTID reply from the server. This will be
* translated to NFSERR_STALEDONTRECOVER when R_DONTRECOVER is set.
*/
s = splsoftclock();
NFSLOCKREQ();
TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
if (rep->r_nmp == nmp)
rep->r_flags |= R_DONTRECOVER;
}
NFSUNLOCKREQ();
splx(s);
/* get rid of defunct lockowners */
LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) {
nfscl_freelockowner(lp, 0);
}
/*
* Now, mark all delegations "need reclaim".
*/
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
TAILQ_INIT(&extra_deleg);
LIST_INIT(&extra_open);
/*
* Now traverse the state lists, doing Open and Lock Reclaims.
*/
tcred = newnfs_getcred();
owp = LIST_FIRST(&clp->nfsc_owner);
while (owp != NULL) {
nowp = LIST_NEXT(owp, nfsow_list);
owp->nfsow_seqid = 0;
op = LIST_FIRST(&owp->nfsow_open);
while (op != NULL) {
nop = LIST_NEXT(op, nfso_list);
if (error != NFSERR_NOGRACE) {
/* Search for a delegation to reclaim with the open */
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
continue;
if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
mode = NFSV4OPEN_ACCESSWRITE;
delegtype = NFSV4OPEN_DELEGATEWRITE;
} else {
mode = NFSV4OPEN_ACCESSREAD;
delegtype = NFSV4OPEN_DELEGATEREAD;
}
if ((op->nfso_mode & mode) == mode &&
op->nfso_fhlen == dp->nfsdl_fhlen &&
!NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
break;
}
ndp = dp;
if (dp == NULL)
delegtype = NFSV4OPEN_DELEGATENONE;
newnfs_copycred(&op->nfso_cred, tcred);
error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
tcred, p);
if (!error) {
/* Handle any replied delegation */
if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
|| NFSMNT_RDONLY(nmp->nm_mountp))) {
if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
mode = NFSV4OPEN_ACCESSWRITE;
else
mode = NFSV4OPEN_ACCESSREAD;
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
continue;
if ((op->nfso_mode & mode) == mode &&
op->nfso_fhlen == dp->nfsdl_fhlen &&
!NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
op->nfso_fhlen)) {
dp->nfsdl_stateid = ndp->nfsdl_stateid;
dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
dp->nfsdl_ace = ndp->nfsdl_ace;
dp->nfsdl_change = ndp->nfsdl_change;
dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
dp->nfsdl_flags |= NFSCLDL_RECALL;
FREE((caddr_t)ndp, M_NFSCLDELEG);
ndp = NULL;
break;
}
}
}
if (ndp != NULL)
TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
/* and reclaim all byte range locks */
lp = LIST_FIRST(&op->nfso_lock);
while (lp != NULL) {
nlp = LIST_NEXT(lp, nfsl_list);
lp->nfsl_seqid = 0;
firstlock = 1;
lop = LIST_FIRST(&lp->nfsl_lock);
while (lop != NULL) {
nlop = LIST_NEXT(lop, nfslo_list);
if (lop->nfslo_end == NFS64BITSSET)
len = NFS64BITSSET;
else
len = lop->nfslo_end - lop->nfslo_first;
if (error != NFSERR_NOGRACE)
error = nfscl_trylock(nmp, NULL,
op->nfso_fh, op->nfso_fhlen, lp,
firstlock, 1, lop->nfslo_first, len,
lop->nfslo_type, tcred, p);
if (error != 0)
nfscl_freelock(lop, 0);
else
firstlock = 0;
lop = nlop;
}
/* If no locks, but a lockowner, just delete it. */
if (LIST_EMPTY(&lp->nfsl_lock))
nfscl_freelockowner(lp, 0);
lp = nlp;
}
} else {
nfscl_freeopen(op, 0);
}
}
op = nop;
}
owp = nowp;
}
/*
* Now, try and get any delegations not yet reclaimed by cobbling
* to-gether an appropriate open.
*/
nowp = NULL;
dp = TAILQ_FIRST(&clp->nfsc_deleg);
while (dp != NULL) {
ndp = TAILQ_NEXT(dp, nfsdl_list);
if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
if (nowp == NULL) {
MALLOC(nowp, struct nfsclowner *,
sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
/*
* Name must be as long an largest possible
* NFSV4CL_LOCKNAMELEN. 12 for now.
*/
NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
NFSV4CL_LOCKNAMELEN);
LIST_INIT(&nowp->nfsow_open);
nowp->nfsow_clp = clp;
nowp->nfsow_seqid = 0;
nowp->nfsow_defunct = 0;
nfscl_lockinit(&nowp->nfsow_rwlock);
}
nop = NULL;
if (error != NFSERR_NOGRACE) {
MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
nop->nfso_own = nowp;
if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
delegtype = NFSV4OPEN_DELEGATEWRITE;
} else {
nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
delegtype = NFSV4OPEN_DELEGATEREAD;
}
nop->nfso_opencnt = 0;
nop->nfso_posixlock = 1;
nop->nfso_fhlen = dp->nfsdl_fhlen;
NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
LIST_INIT(&nop->nfso_lock);
nop->nfso_stateid.seqid = 0;
nop->nfso_stateid.other[0] = 0;
nop->nfso_stateid.other[1] = 0;
nop->nfso_stateid.other[2] = 0;
newnfs_copycred(&dp->nfsdl_cred, tcred);
newnfs_copyincred(tcred, &nop->nfso_cred);
tdp = NULL;
error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
nop->nfso_mode, nop, NULL, 0, &tdp, 1,
delegtype, tcred, p);
if (tdp != NULL) {
if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
mode = NFSV4OPEN_ACCESSWRITE;
else
mode = NFSV4OPEN_ACCESSREAD;
if ((nop->nfso_mode & mode) == mode &&
nop->nfso_fhlen == tdp->nfsdl_fhlen &&
!NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
nop->nfso_fhlen)) {
dp->nfsdl_stateid = tdp->nfsdl_stateid;
dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
dp->nfsdl_ace = tdp->nfsdl_ace;
dp->nfsdl_change = tdp->nfsdl_change;
dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
dp->nfsdl_flags |= NFSCLDL_RECALL;
FREE((caddr_t)tdp, M_NFSCLDELEG);
} else {
TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
}
}
}
if (error) {
if (nop != NULL)
FREE((caddr_t)nop, M_NFSCLOPEN);
/*
* Couldn't reclaim it, so throw the state
* away. Ouch!!
*/
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
} else {
LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
}
}
dp = ndp;
}
/*
* Now, get rid of extra Opens and Delegations.
*/
LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
do {
newnfs_copycred(&op->nfso_cred, tcred);
error = nfscl_tryclose(op, tcred, nmp, p);
if (error == NFSERR_GRACE)
(void) nfs_catnap(PZERO, error, "nfsexcls");
} while (error == NFSERR_GRACE);
LIST_REMOVE(op, nfso_list);
FREE((caddr_t)op, M_NFSCLOPEN);
}
if (nowp != NULL)
FREE((caddr_t)nowp, M_NFSCLOWNER);
TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
do {
newnfs_copycred(&dp->nfsdl_cred, tcred);
error = nfscl_trydelegreturn(dp, tcred, nmp, p);
if (error == NFSERR_GRACE)
(void) nfs_catnap(PZERO, error, "nfsexdlg");
} while (error == NFSERR_GRACE);
TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
FREE((caddr_t)dp, M_NFSCLDELEG);
}
NFSLOCKCLSTATE();
clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
wakeup(&clp->nfsc_flags);
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
NFSFREECRED(tcred);
}
/*
* This function is called when a server replies with NFSERR_EXPIRED.
* It deletes all state for the client and does a fresh SetClientId/confirm.
* XXX Someday it should post a signal to the process(es) that hold the
* state, so they know that lock state has been lost.
*/
APPLESTATIC int
nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
{
struct nfscllockowner *lp, *nlp;
struct nfsmount *nmp;
struct ucred *cred;
int igotlock = 0, error, trycnt;
/*
* If the clientid has gone away or a new SetClientid has already
* been done, just return ok.
*/
if (clp == NULL || clidrev != clp->nfsc_clientidrev)
return (0);
/*
* First, lock the client structure, so everyone else will
* block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
* that only one thread does the work.
*/
NFSLOCKCLSTATE();
clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
do {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
NFSCLSTATEMUTEXPTR);
} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
if (igotlock)
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
return (0);
}
clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
NFSUNLOCKCLSTATE();
nmp = clp->nfsc_nmp;
if (nmp == NULL)
panic("nfscl expired");
cred = newnfs_getcred();
trycnt = 5;
do {
error = nfsrpc_setclient(nmp, clp, cred, p);
} while ((error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
if (error) {
/*
* Clear out any state.
*/
nfscl_cleanclient(clp);
NFSLOCKCLSTATE();
clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
NFSCLFLAGS_RECOVER);
} else {
/* get rid of defunct lockowners */
LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list,
nlp) {
nfscl_freelockowner(lp, 0);
}
/*
* Expire the state for the client.
*/
nfscl_expireclient(clp, nmp, cred, p);
NFSLOCKCLSTATE();
clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
}
clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG);
wakeup(&clp->nfsc_flags);
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
NFSFREECRED(cred);
return (error);
}
/*
* This function inserts a lock in the list after insert_lop.
*/
static void
nfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
struct nfscllock *insert_lop, int local)
{
if ((struct nfscllockowner *)insert_lop == lp)
LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
else
LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
if (local)
newnfsstats.cllocallocks++;
else
newnfsstats.cllocks++;
}
/*
* This function updates the locking for a lock owner and given file. It
* maintains a list of lock ranges ordered on increasing file offset that
* are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
* It always adds new_lop to the list and sometimes uses the one pointed
* at by other_lopp.
* Returns 1 if the locks were modified, 0 otherwise.
*/
static int
nfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
struct nfscllock **other_lopp, int local)
{
struct nfscllock *new_lop = *new_lopp;
struct nfscllock *lop, *tlop, *ilop;
struct nfscllock *other_lop;
int unlock = 0, modified = 0;
u_int64_t tmp;
/*
* Work down the list until the lock is merged.
*/
if (new_lop->nfslo_type == F_UNLCK)
unlock = 1;
ilop = (struct nfscllock *)lp;
lop = LIST_FIRST(&lp->nfsl_lock);
while (lop != NULL) {
/*
* Only check locks for this file that aren't before the start of
* new lock's range.
*/
if (lop->nfslo_end >= new_lop->nfslo_first) {
if (new_lop->nfslo_end < lop->nfslo_first) {
/*
* If the new lock ends before the start of the
* current lock's range, no merge, just insert
* the new lock.
*/
break;
}
if (new_lop->nfslo_type == lop->nfslo_type ||
(new_lop->nfslo_first <= lop->nfslo_first &&
new_lop->nfslo_end >= lop->nfslo_end)) {
/*
* This lock can be absorbed by the new lock/unlock.
* This happens when it covers the entire range
* of the old lock or is contiguous
* with the old lock and is of the same type or an
* unlock.
*/
if (new_lop->nfslo_type != lop->nfslo_type ||
new_lop->nfslo_first != lop->nfslo_first ||
new_lop->nfslo_end != lop->nfslo_end)
modified = 1;
if (lop->nfslo_first < new_lop->nfslo_first)
new_lop->nfslo_first = lop->nfslo_first;
if (lop->nfslo_end > new_lop->nfslo_end)
new_lop->nfslo_end = lop->nfslo_end;
tlop = lop;
lop = LIST_NEXT(lop, nfslo_list);
nfscl_freelock(tlop, local);
continue;
}
/*
* All these cases are for contiguous locks that are not the
* same type, so they can't be merged.
*/
if (new_lop->nfslo_first <= lop->nfslo_first) {
/*
* This case is where the new lock overlaps with the
* first part of the old lock. Move the start of the
* old lock to just past the end of the new lock. The
* new lock will be inserted in front of the old, since
* ilop hasn't been updated. (We are done now.)
*/
if (lop->nfslo_first != new_lop->nfslo_end) {
lop->nfslo_first = new_lop->nfslo_end;
modified = 1;
}
break;
}
if (new_lop->nfslo_end >= lop->nfslo_end) {
/*
* This case is where the new lock overlaps with the
* end of the old lock's range. Move the old lock's
* end to just before the new lock's first and insert
* the new lock after the old lock.
* Might not be done yet, since the new lock could
* overlap further locks with higher ranges.
*/
if (lop->nfslo_end != new_lop->nfslo_first) {
lop->nfslo_end = new_lop->nfslo_first;
modified = 1;
}
ilop = lop;
lop = LIST_NEXT(lop, nfslo_list);
continue;
}
/*
* The final case is where the new lock's range is in the
* middle of the current lock's and splits the current lock
* up. Use *other_lopp to handle the second part of the
* split old lock range. (We are done now.)
* For unlock, we use new_lop as other_lop and tmp, since
* other_lop and new_lop are the same for this case.
* We noted the unlock case above, so we don't need
* new_lop->nfslo_type any longer.
*/
tmp = new_lop->nfslo_first;
if (unlock) {
other_lop = new_lop;
*new_lopp = NULL;
} else {
other_lop = *other_lopp;
*other_lopp = NULL;
}
other_lop->nfslo_first = new_lop->nfslo_end;
other_lop->nfslo_end = lop->nfslo_end;
other_lop->nfslo_type = lop->nfslo_type;
lop->nfslo_end = tmp;
nfscl_insertlock(lp, other_lop, lop, local);
ilop = lop;
modified = 1;
break;
}
ilop = lop;
lop = LIST_NEXT(lop, nfslo_list);
if (lop == NULL)
break;
}
/*
* Insert the new lock in the list at the appropriate place.
*/
if (!unlock) {
nfscl_insertlock(lp, new_lop, ilop, local);
*new_lopp = NULL;
modified = 1;
}
return (modified);
}
/*
* This function must be run as a kernel thread.
* It does Renew Ops and recovery, when required.
*/
APPLESTATIC void
nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
{
struct nfsclowner *owp, *nowp;
struct nfsclopen *op;
struct nfscllockowner *lp, *nlp, *olp;
struct nfscldeleghead dh;
struct nfscllockownerhead lh;
struct nfscldeleg *dp, *ndp;
struct ucred *cred;
u_int32_t clidrev;
int error, cbpathdown, islept, igotlock, ret, clearok;
uint32_t recover_done_time = 0;
cred = newnfs_getcred();
NFSLOCKCLSTATE();
clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
NFSUNLOCKCLSTATE();
for(;;) {
newnfs_setroot(cred);
cbpathdown = 0;
if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
/*
* Only allow one recover within 1/2 of the lease
* duration (nfsc_renew).
*/
if (recover_done_time < NFSD_MONOSEC) {
recover_done_time = NFSD_MONOSEC +
clp->nfsc_renew;
nfscl_recover(clp, cred, p);
} else {
NFSLOCKCLSTATE();
clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
NFSUNLOCKCLSTATE();
}
}
if (clp->nfsc_expire <= NFSD_MONOSEC &&
(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
clidrev = clp->nfsc_clientidrev;
error = nfsrpc_renew(clp, cred, p);
if (error == NFSERR_CBPATHDOWN)
cbpathdown = 1;
else if (error == NFSERR_STALECLIENTID) {
NFSLOCKCLSTATE();
clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
NFSUNLOCKCLSTATE();
} else if (error == NFSERR_EXPIRED)
(void) nfscl_hasexpired(clp, clidrev, p);
}
LIST_INIT(&lh);
TAILQ_INIT(&dh);
NFSLOCKCLSTATE();
if (cbpathdown)
/* It's a Total Recall! */
nfscl_totalrecall(clp);
/*
* Now, handle defunct owners.
*/
owp = LIST_FIRST(&clp->nfsc_owner);
while (owp != NULL) {
nowp = LIST_NEXT(owp, nfsow_list);
if (LIST_EMPTY(&owp->nfsow_open)) {
if (owp->nfsow_defunct)
nfscl_freeopenowner(owp, 0);
} else {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
lp = LIST_FIRST(&op->nfso_lock);
while (lp != NULL) {
nlp = LIST_NEXT(lp, nfsl_list);
if (lp->nfsl_defunct &&
LIST_EMPTY(&lp->nfsl_lock)) {
LIST_FOREACH(olp, &lh, nfsl_list) {
if (!NFSBCMP(olp->nfsl_owner,
lp->nfsl_owner,NFSV4CL_LOCKNAMELEN))
break;
}
if (olp == NULL) {
LIST_REMOVE(lp, nfsl_list);
LIST_INSERT_HEAD(&lh, lp, nfsl_list);
} else {
nfscl_freelockowner(lp, 0);
}
}
lp = nlp;
}
}
}
owp = nowp;
}
/* also search the defunct list */
lp = LIST_FIRST(&clp->nfsc_defunctlockowner);
while (lp != NULL) {
nlp = LIST_NEXT(lp, nfsl_list);
if (lp->nfsl_defunct) {
LIST_FOREACH(olp, &lh, nfsl_list) {
if (!NFSBCMP(olp->nfsl_owner, lp->nfsl_owner,
NFSV4CL_LOCKNAMELEN))
break;
}
if (olp == NULL) {
LIST_REMOVE(lp, nfsl_list);
LIST_INSERT_HEAD(&lh, lp, nfsl_list);
} else {
nfscl_freelockowner(lp, 0);
}
}
lp = nlp;
}
/* and release defunct lock owners */
LIST_FOREACH_SAFE(lp, &lh, nfsl_list, nlp) {
nfscl_freelockowner(lp, 0);
}
/*
* Do the recall on any delegations. To avoid trouble, always
* come back up here after having slept.
*/
igotlock = 0;
tryagain:
dp = TAILQ_FIRST(&clp->nfsc_deleg);
while (dp != NULL) {
ndp = TAILQ_NEXT(dp, nfsdl_list);
if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
/*
* Wait for outstanding I/O ops to be done.
*/
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
if (igotlock) {
nfsv4_unlock(&clp->nfsc_lock, 0);
igotlock = 0;
}
dp->nfsdl_rwlock.nfslock_lock |=
NFSV4LOCK_WANTED;
(void) nfsmsleep(&dp->nfsdl_rwlock,
NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
NULL);
goto tryagain;
}
while (!igotlock) {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
&islept, NFSCLSTATEMUTEXPTR);
if (islept)
goto tryagain;
}
NFSUNLOCKCLSTATE();
newnfs_copycred(&dp->nfsdl_cred, cred);
ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
NULL, cred, p, 1);
if (!ret) {
nfscl_cleandeleg(dp);
TAILQ_REMOVE(&clp->nfsc_deleg, dp,
nfsdl_list);
LIST_REMOVE(dp, nfsdl_hash);
TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
nfscl_delegcnt--;
newnfsstats.cldelegates--;
}
NFSLOCKCLSTATE();
}
dp = ndp;
}
/*
* Clear out old delegations, if we are above the high water
* mark. Only clear out ones with no state related to them.
* The tailq list is in LRU order.
*/
dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
dp->nfsdl_rwlock.nfslock_lock == 0 &&
dp->nfsdl_timestamp < NFSD_MONOSEC &&
(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
clearok = 1;
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
op = LIST_FIRST(&owp->nfsow_open);
if (op != NULL) {
clearok = 0;
break;
}
}
if (clearok) {
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
if (!LIST_EMPTY(&lp->nfsl_lock)) {
clearok = 0;
break;
}
}
}
if (clearok) {
TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
LIST_REMOVE(dp, nfsdl_hash);
TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
nfscl_delegcnt--;
newnfsstats.cldelegates--;
}
}
dp = ndp;
}
if (igotlock)
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
/*
* Delegreturn any delegations cleaned out or recalled.
*/
TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
newnfs_copycred(&dp->nfsdl_cred, cred);
(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
TAILQ_REMOVE(&dh, dp, nfsdl_list);
FREE((caddr_t)dp, M_NFSCLDELEG);
}
#if defined(APPLEKEXT) || defined(__FreeBSD__)
/*
* Simulate the calls to nfscl_cleanup() when a process
* exits, since the call can't be patched into exit().
*/
{
struct timespec mytime;
static time_t prevsec = 0;
NFSGETNANOTIME(&mytime);
if (prevsec != mytime.tv_sec) {
prevsec = mytime.tv_sec;
nfscl_cleanupkext(clp);
}
}
#endif /* APPLEKEXT || __FreeBSD__ */
if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
(void) tsleep((caddr_t)clp, PWAIT, "nfscl", hz);
if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
NFSFREECRED(cred);
clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
wakeup((caddr_t)clp);
return;
}
}
}
/*
* Initiate state recovery. Called when NFSERR_STALECLIENTID or
* NFSERR_STALESTATEID is received.
*/
APPLESTATIC void
nfscl_initiate_recovery(struct nfsclclient *clp)
{
if (clp == NULL)
return;
NFSLOCKCLSTATE();
clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
NFSUNLOCKCLSTATE();
wakeup((caddr_t)clp);
}
/*
* Dump out the state stuff for debugging.
*/
APPLESTATIC void
nfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
int lockowner, int locks)
{
struct nfsclclient *clp;
struct nfsclowner *owp;
struct nfsclopen *op;
struct nfscllockowner *lp;
struct nfscllock *lop;
struct nfscldeleg *dp;
clp = nmp->nm_clp;
if (clp == NULL) {
printf("nfscl dumpstate NULL clp\n");
return;
}
NFSLOCKCLSTATE();
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
if (openowner && !LIST_EMPTY(&owp->nfsow_open))
printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
owp->nfsow_owner[0], owp->nfsow_owner[1],
owp->nfsow_owner[2], owp->nfsow_owner[3],
owp->nfsow_seqid);
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (opens)
printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
op->nfso_stateid.other[0], op->nfso_stateid.other[1],
op->nfso_stateid.other[2], op->nfso_opencnt,
op->nfso_fh[12]);
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lockowner)
printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
lp->nfsl_owner[0], lp->nfsl_owner[1],
lp->nfsl_owner[2], lp->nfsl_owner[3],
lp->nfsl_seqid,
lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
lp->nfsl_stateid.other[2]);
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
if (locks)
#ifdef __FreeBSD__
printf("lck typ=%d fst=%ju end=%ju\n",
lop->nfslo_type, (intmax_t)lop->nfslo_first,
(intmax_t)lop->nfslo_end);
#else
printf("lck typ=%d fst=%qd end=%qd\n",
lop->nfslo_type, lop->nfslo_first,
lop->nfslo_end);
#endif
}
}
}
}
}
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
if (openowner && !LIST_EMPTY(&owp->nfsow_open))
printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
owp->nfsow_owner[0], owp->nfsow_owner[1],
owp->nfsow_owner[2], owp->nfsow_owner[3],
owp->nfsow_seqid);
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (opens)
printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
op->nfso_stateid.other[0], op->nfso_stateid.other[1],
op->nfso_stateid.other[2], op->nfso_opencnt,
op->nfso_fh[12]);
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lockowner)
printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
lp->nfsl_owner[0], lp->nfsl_owner[1],
lp->nfsl_owner[2], lp->nfsl_owner[3],
lp->nfsl_seqid,
lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
lp->nfsl_stateid.other[2]);
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
if (locks)
#ifdef __FreeBSD__
printf("lck typ=%d fst=%ju end=%ju\n",
lop->nfslo_type, (intmax_t)lop->nfslo_first,
(intmax_t)lop->nfslo_end);
#else
printf("lck typ=%d fst=%qd end=%qd\n",
lop->nfslo_type, lop->nfslo_first,
lop->nfslo_end);
#endif
}
}
}
}
NFSUNLOCKCLSTATE();
}
/*
* Check for duplicate open owners and opens.
* (Only used as a diagnostic aid.)
*/
APPLESTATIC void
nfscl_dupopen(vnode_t vp, int dupopens)
{
struct nfsclclient *clp;
struct nfsclowner *owp, *owp2;
struct nfsclopen *op, *op2;
struct nfsfh *nfhp;
clp = VFSTONFS(vnode_mount(vp))->nm_clp;
if (clp == NULL) {
printf("nfscl dupopen NULL clp\n");
return;
}
nfhp = VTONFS(vp)->n_fhp;
NFSLOCKCLSTATE();
/*
* First, search for duplicate owners.
* These should never happen!
*/
LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
if (owp != owp2 &&
!NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
NFSV4CL_LOCKNAMELEN)) {
NFSUNLOCKCLSTATE();
printf("DUP OWNER\n");
nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
return;
}
}
}
/*
* Now, search for duplicate stateids.
* These shouldn't happen, either.
*/
LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op != op2 &&
(op->nfso_stateid.other[0] != 0 ||
op->nfso_stateid.other[1] != 0 ||
op->nfso_stateid.other[2] != 0) &&
op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
NFSUNLOCKCLSTATE();
printf("DUP STATEID\n");
nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
0);
return;
}
}
}
}
}
/*
* Now search for duplicate opens.
* Duplicate opens for the same owner
* should never occur. Other duplicates are
* possible and are checked for if "dupopens"
* is true.
*/
LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
if (nfhp->nfh_len == op2->nfso_fhlen &&
!NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
(!NFSBCMP(op->nfso_own->nfsow_owner,
op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
dupopens)) {
if (!NFSBCMP(op->nfso_own->nfsow_owner,
op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
NFSUNLOCKCLSTATE();
printf("BADDUP OPEN\n");
} else {
NFSUNLOCKCLSTATE();
printf("DUP OPEN\n");
}
nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
0, 0);
return;
}
}
}
}
}
}
NFSUNLOCKCLSTATE();
}
/*
* During close, find an open that needs to be dereferenced and
* dereference it. If there are no more opens for this file,
* log a message to that effect.
* Opens aren't actually Close'd until VOP_INACTIVE() is performed
* on the file's vnode.
* This is the safe way, since it is difficult to identify
* which open the close is for and I/O can be performed after the
* close(2) system call when a file is mmap'd.
* If it returns 0 for success, there will be a referenced
* clp returned via clpp.
*/
APPLESTATIC int
nfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
{
struct nfsclclient *clp;
struct nfsclowner *owp;
struct nfsclopen *op;
struct nfscldeleg *dp;
struct nfsfh *nfhp;
int error, notdecr;
error = nfscl_getcl(vp, NULL, NULL, &clp);
if (error)
return (error);
*clpp = clp;
nfhp = VTONFS(vp)->n_fhp;
notdecr = 1;
NFSLOCKCLSTATE();
/*
* First, look for one under a delegation that was locally issued
* and just decrement the opencnt for it. Since all my Opens against
* the server are DENY_NONE, I don't see a problem with hanging
* onto them. (It is much easier to use one of the extant Opens
* that I already have on the server when a Delegation is recalled
* than to do fresh Opens.) Someday, I might need to rethink this, but.
*/
dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
if (dp != NULL) {
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
op = LIST_FIRST(&owp->nfsow_open);
if (op != NULL) {
/*
* Since a delegation is for a file, there
* should never be more than one open for
* each openowner.
*/
if (LIST_NEXT(op, nfso_list) != NULL)
panic("nfscdeleg opens");
if (notdecr && op->nfso_opencnt > 0) {
notdecr = 0;
op->nfso_opencnt--;
break;
}
}
}
}
/* Now process the opens against the server. */
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == nfhp->nfh_len &&
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
nfhp->nfh_len)) {
/* Found an open, decrement cnt if possible */
if (notdecr && op->nfso_opencnt > 0) {
notdecr = 0;
op->nfso_opencnt--;
}
/*
* There are more opens, so just return.
*/
if (op->nfso_opencnt > 0) {
NFSUNLOCKCLSTATE();
return (0);
}
}
}
}
NFSUNLOCKCLSTATE();
if (notdecr)
printf("nfscl: never fnd open\n");
return (0);
}
APPLESTATIC int
nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
{
struct nfsclclient *clp;
struct nfsclowner *owp, *nowp;
struct nfsclopen *op;
struct nfscldeleg *dp;
struct nfsfh *nfhp;
int error;
error = nfscl_getcl(vp, NULL, NULL, &clp);
if (error)
return (error);
*clpp = clp;
nfhp = VTONFS(vp)->n_fhp;
NFSLOCKCLSTATE();
/*
* First get rid of the local Open structures, which should be no
* longer in use.
*/
dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
if (dp != NULL) {
LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
op = LIST_FIRST(&owp->nfsow_open);
if (op != NULL) {
KASSERT((op->nfso_opencnt == 0),
("nfscl: bad open cnt on deleg"));
nfscl_freeopen(op, 1);
}
nfscl_freeopenowner(owp, 1);
}
}
/* Now process the opens against the server. */
lookformore:
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
op = LIST_FIRST(&owp->nfsow_open);
while (op != NULL) {
if (op->nfso_fhlen == nfhp->nfh_len &&
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
nfhp->nfh_len)) {
/* Found an open, close it. */
KASSERT((op->nfso_opencnt == 0),
("nfscl: bad open cnt on server"));
NFSUNLOCKCLSTATE();
nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
p);
NFSLOCKCLSTATE();
goto lookformore;
}
op = LIST_NEXT(op, nfso_list);
}
}
NFSUNLOCKCLSTATE();
return (0);
}
/*
* Return all delegations on this client.
* (Must be called with client sleep lock.)
*/
static void
nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
{
struct nfscldeleg *dp, *ndp;
struct ucred *cred;
cred = newnfs_getcred();
TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
nfscl_cleandeleg(dp);
(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
}
NFSFREECRED(cred);
}
/*
* Do a callback RPC.
*/
APPLESTATIC void
nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
{
int i, op;
u_int32_t *tl;
struct nfsclclient *clp;
struct nfscldeleg *dp = NULL;
int numops, taglen = -1, error = 0, trunc, ret = 0;
u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident;
u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
vnode_t vp = NULL;
struct nfsnode *np;
struct vattr va;
struct nfsfh *nfhp;
mount_t mp;
nfsattrbit_t attrbits, rattrbits;
nfsv4stateid_t stateid;
nfsrvd_rephead(nd);
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
taglen = fxdr_unsigned(int, *tl);
if (taglen < 0) {
error = EBADRPC;
goto nfsmout;
}
if (taglen <= NFSV4_SMALLSTR)
tagstr = tag;
else
tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
error = nfsrv_mtostr(nd, tagstr, taglen);
if (error) {
if (taglen > NFSV4_SMALLSTR)
free(tagstr, M_TEMP);
taglen = -1;
goto nfsmout;
}
(void) nfsm_strtom(nd, tag, taglen);
if (taglen > NFSV4_SMALLSTR) {
free(tagstr, M_TEMP);
}
NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
minorvers = fxdr_unsigned(u_int32_t, *tl++);
if (minorvers != NFSV4_MINORVERSION)
nd->nd_repstat = NFSERR_MINORVERMISMATCH;
cbident = fxdr_unsigned(u_int32_t, *tl++);
if (nd->nd_repstat)
numops = 0;
else
numops = fxdr_unsigned(int, *tl);
/*
* Loop around doing the sub ops.
*/
for (i = 0; i < numops; i++) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
*repp++ = *tl;
op = fxdr_unsigned(int, *tl);
if (op < NFSV4OP_CBGETATTR || op > NFSV4OP_CBRECALL) {
nd->nd_repstat = NFSERR_OPILLEGAL;
*repp = nfscl_errmap(nd);
retops++;
break;
}
nd->nd_procnum = op;
newnfsstats.cbrpccnt[nd->nd_procnum]++;
switch (op) {
case NFSV4OP_CBGETATTR:
clp = NULL;
error = nfsm_getfh(nd, &nfhp);
if (!error)
error = nfsrv_getattrbits(nd, &attrbits,
NULL, NULL);
if (!error) {
mp = nfscl_getmnt(cbident);
if (mp == NULL)
error = NFSERR_SERVERFAULT;
}
if (!error) {
dp = NULL;
NFSLOCKCLSTATE();
clp = nfscl_findcl(VFSTONFS(mp));
if (clp != NULL)
dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
nfhp->nfh_len);
NFSUNLOCKCLSTATE();
if (dp == NULL)
error = NFSERR_SERVERFAULT;
}
if (!error) {
ret = nfscl_ngetreopen(mp, nfhp->nfh_fh,
nfhp->nfh_len, p, &np);
if (!ret)
vp = NFSTOV(np);
}
if (nfhp != NULL)
FREE((caddr_t)nfhp, M_NFSFH);
if (!error) {
NFSZERO_ATTRBIT(&rattrbits);
if (NFSISSET_ATTRBIT(&attrbits,
NFSATTRBIT_SIZE)) {
if (!ret)
va.va_size = np->n_size;
else
va.va_size = dp->nfsdl_size;
NFSSETBIT_ATTRBIT(&rattrbits,
NFSATTRBIT_SIZE);
}
if (NFSISSET_ATTRBIT(&attrbits,
NFSATTRBIT_CHANGE)) {
va.va_filerev = dp->nfsdl_change;
if (ret || (np->n_flag & NDELEGMOD))
va.va_filerev++;
NFSSETBIT_ATTRBIT(&rattrbits,
NFSATTRBIT_CHANGE);
}
(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
- NULL, 0, &rattrbits, NULL, NULL, 0, 0, 0,
+ NULL, 0, &rattrbits, NULL, NULL, 0, 0, 0, 0,
(uint64_t)0);
if (!ret)
vrele(vp);
}
break;
case NFSV4OP_CBRECALL:
clp = NULL;
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
NFSX_UNSIGNED);
stateid.seqid = *tl++;
NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
NFSX_STATEIDOTHER);
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
trunc = fxdr_unsigned(int, *tl);
error = nfsm_getfh(nd, &nfhp);
if (!error) {
mp = nfscl_getmnt(cbident);
if (mp == NULL)
error = NFSERR_SERVERFAULT;
}
if (!error) {
NFSLOCKCLSTATE();
clp = nfscl_findcl(VFSTONFS(mp));
if (clp != NULL) {
dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
nfhp->nfh_len);
if (dp != NULL && (dp->nfsdl_flags &
NFSCLDL_DELEGRET) == 0) {
dp->nfsdl_flags |=
NFSCLDL_RECALL;
wakeup((caddr_t)clp);
}
} else {
error = NFSERR_SERVERFAULT;
}
NFSUNLOCKCLSTATE();
}
if (nfhp != NULL)
FREE((caddr_t)nfhp, M_NFSFH);
break;
};
if (error) {
if (error == EBADRPC || error == NFSERR_BADXDR) {
nd->nd_repstat = NFSERR_BADXDR;
} else {
nd->nd_repstat = error;
}
error = 0;
}
retops++;
if (nd->nd_repstat) {
*repp = nfscl_errmap(nd);
break;
} else
*repp = 0; /* NFS4_OK */
}
nfsmout:
if (error) {
if (error == EBADRPC || error == NFSERR_BADXDR)
nd->nd_repstat = NFSERR_BADXDR;
else
printf("nfsv4 comperr1=%d\n", error);
}
if (taglen == -1) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = 0;
*tl = 0;
} else {
*retopsp = txdr_unsigned(retops);
}
*nd->nd_errp = nfscl_errmap(nd);
}
/*
* Generate the next cbident value. Basically just increment a static value
* and then check that it isn't already in the list, if it has wrapped around.
*/
static u_int32_t
nfscl_nextcbident(void)
{
struct nfsclclient *clp;
int matched;
static u_int32_t nextcbident = 0;
static int haswrapped = 0;
nextcbident++;
if (nextcbident == 0)
haswrapped = 1;
if (haswrapped) {
/*
* Search the clientid list for one already using this cbident.
*/
do {
matched = 0;
NFSLOCKCLSTATE();
LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
if (clp->nfsc_cbident == nextcbident) {
matched = 1;
break;
}
}
NFSUNLOCKCLSTATE();
if (matched == 1)
nextcbident++;
} while (matched);
}
return (nextcbident);
}
/*
* Get the mount point related to a given cbident.
*/
static mount_t
nfscl_getmnt(u_int32_t cbident)
{
struct nfsclclient *clp;
struct nfsmount *nmp;
NFSLOCKCLSTATE();
LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
if (clp->nfsc_cbident == cbident)
break;
}
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (NULL);
}
nmp = clp->nfsc_nmp;
NFSUNLOCKCLSTATE();
return (nmp->nm_mountp);
}
/*
* Search for a lock conflict locally on the client. A conflict occurs if
* - not same owner and overlapping byte range and at least one of them is
* a write lock or this is an unlock.
*/
static int
nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
struct nfscllock **lopp)
{
struct nfsclowner *owp;
struct nfsclopen *op;
int ret;
if (dp != NULL) {
ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
if (ret)
return (ret);
}
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == fhlen &&
!NFSBCMP(op->nfso_fh, fhp, fhlen)) {
ret = nfscl_checkconflict(&op->nfso_lock, nlop,
own, lopp);
if (ret)
return (ret);
}
}
}
return (0);
}
static int
nfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
u_int8_t *own, struct nfscllock **lopp)
{
struct nfscllockowner *lp;
struct nfscllock *lop;
LIST_FOREACH(lp, lhp, nfsl_list) {
if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
if (lop->nfslo_first >= nlop->nfslo_end)
break;
if (lop->nfslo_end <= nlop->nfslo_first)
continue;
if (lop->nfslo_type == F_WRLCK ||
nlop->nfslo_type == F_WRLCK ||
nlop->nfslo_type == F_UNLCK) {
if (lopp != NULL)
*lopp = lop;
return (NFSERR_DENIED);
}
}
}
}
return (0);
}
/*
* Check for a local conflicting lock.
*/
APPLESTATIC int
nfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
u_int64_t len, struct flock *fl, NFSPROC_T *p)
{
struct nfscllock *lop, nlck;
struct nfscldeleg *dp;
struct nfsnode *np;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
int error;
nlck.nfslo_type = fl->l_type;
nlck.nfslo_first = off;
if (len == NFS64BITSSET) {
nlck.nfslo_end = NFS64BITSSET;
} else {
nlck.nfslo_end = off + len;
if (nlck.nfslo_end <= nlck.nfslo_first)
return (NFSERR_INVAL);
}
np = VTONFS(vp);
nfscl_filllockowner(p, own);
NFSLOCKCLSTATE();
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
&nlck, own, dp, &lop);
if (error != 0) {
fl->l_whence = SEEK_SET;
fl->l_start = lop->nfslo_first;
if (lop->nfslo_end == NFS64BITSSET)
fl->l_len = 0;
else
fl->l_len = lop->nfslo_end - lop->nfslo_first;
fl->l_pid = (pid_t)0;
fl->l_type = lop->nfslo_type;
error = -1; /* no RPC required */
} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
fl->l_type == F_RDLCK)) {
/*
* The delegation ensures that there isn't a conflicting
* lock on the server, so return -1 to indicate an RPC
* isn't required.
*/
fl->l_type = F_UNLCK;
error = -1;
}
NFSUNLOCKCLSTATE();
return (error);
}
/*
* Handle Recall of a delegation.
* The clp must be exclusive locked when this is called.
*/
static int
nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
int called_from_renewthread)
{
struct nfsclowner *owp, *lowp, *nowp;
struct nfsclopen *op, *lop;
struct nfscllockowner *lp;
struct nfscllock *lckp;
struct nfsnode *np;
int error = 0, ret, gotvp = 0;
if (vp == NULL) {
/*
* First, get a vnode for the file. This is needed to do RPCs.
*/
ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
dp->nfsdl_fhlen, p, &np);
if (ret) {
/*
* File isn't open, so nothing to move over to the
* server.
*/
return (0);
}
vp = NFSTOV(np);
gotvp = 1;
} else {
np = VTONFS(vp);
}
dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
/*
* Ok, if it's a write delegation, flush data to the server, so
* that close/open consistency is retained.
*/
ret = 0;
NFSLOCKNODE(np);
if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
np->n_flag |= NDELEGRECALL;
NFSUNLOCKNODE(np);
ret = ncl_flush(vp, MNT_WAIT, cred, p, 1,
called_from_renewthread);
NFSLOCKNODE(np);
np->n_flag &= ~NDELEGRECALL;
}
NFSINVALATTRCACHE(np);
NFSUNLOCKNODE(np);
if (ret == EIO && called_from_renewthread != 0) {
/*
* If the flush failed with EIO for the renew thread,
* return now, so that the dirty buffer will be flushed
* later.
*/
if (gotvp != 0)
vrele(vp);
return (ret);
}
/*
* Now, for each openowner with opens issued locally, move them
* over to state against the server.
*/
LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
lop = LIST_FIRST(&lowp->nfsow_open);
if (lop != NULL) {
if (LIST_NEXT(lop, nfso_list) != NULL)
panic("nfsdlg mult opens");
/*
* Look for the same openowner against the server.
*/
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
if (!NFSBCMP(lowp->nfsow_owner,
owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
newnfs_copycred(&dp->nfsdl_cred, cred);
ret = nfscl_moveopen(vp, clp, nmp, lop,
owp, dp, cred, p);
if (ret == NFSERR_STALECLIENTID ||
ret == NFSERR_STALEDONTRECOVER) {
if (gotvp)
vrele(vp);
return (ret);
}
if (ret) {
nfscl_freeopen(lop, 1);
if (!error)
error = ret;
}
break;
}
}
/*
* If no openowner found, create one and get an open
* for it.
*/
if (owp == NULL) {
MALLOC(nowp, struct nfsclowner *,
sizeof (struct nfsclowner), M_NFSCLOWNER,
M_WAITOK);
nfscl_newopen(clp, NULL, &owp, &nowp, &op,
NULL, lowp->nfsow_owner, dp->nfsdl_fh,
dp->nfsdl_fhlen, NULL);
newnfs_copycred(&dp->nfsdl_cred, cred);
ret = nfscl_moveopen(vp, clp, nmp, lop,
owp, dp, cred, p);
if (ret) {
nfscl_freeopenowner(owp, 0);
if (ret == NFSERR_STALECLIENTID ||
ret == NFSERR_STALEDONTRECOVER) {
if (gotvp)
vrele(vp);
return (ret);
}
if (ret) {
nfscl_freeopen(lop, 1);
if (!error)
error = ret;
}
}
}
}
}
/*
* Now, get byte range locks for any locks done locally.
*/
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
newnfs_copycred(&dp->nfsdl_cred, cred);
ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
if (ret == NFSERR_STALESTATEID ||
ret == NFSERR_STALEDONTRECOVER ||
ret == NFSERR_STALECLIENTID) {
if (gotvp)
vrele(vp);
return (ret);
}
if (ret && !error)
error = ret;
}
}
if (gotvp)
vrele(vp);
return (error);
}
/*
* Move a locally issued open over to an owner on the state list.
* SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
* returns with it unlocked.
*/
static int
nfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
struct ucred *cred, NFSPROC_T *p)
{
struct nfsclopen *op, *nop;
struct nfscldeleg *ndp;
struct nfsnode *np;
int error = 0, newone;
/*
* First, look for an appropriate open, If found, just increment the
* opencnt in it.
*/
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
op->nfso_fhlen == lop->nfso_fhlen &&
!NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
op->nfso_opencnt += lop->nfso_opencnt;
nfscl_freeopen(lop, 1);
return (0);
}
}
/* No appropriate open, so we have to do one against the server. */
np = VTONFS(vp);
MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
newone = 0;
nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
lop->nfso_fh, lop->nfso_fhlen, &newone);
ndp = dp;
error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
if (error) {
if (newone)
nfscl_freeopen(op, 0);
} else {
if (newone)
newnfs_copyincred(cred, &op->nfso_cred);
op->nfso_mode |= lop->nfso_mode;
op->nfso_opencnt += lop->nfso_opencnt;
nfscl_freeopen(lop, 1);
}
if (nop != NULL)
FREE((caddr_t)nop, M_NFSCLOPEN);
if (ndp != NULL) {
/*
* What should I do with the returned delegation, since the
* delegation is being recalled? For now, just printf and
* through it away.
*/
printf("Moveopen returned deleg\n");
FREE((caddr_t)ndp, M_NFSCLDELEG);
}
return (error);
}
/*
* Recall all delegations on this client.
*/
static void
nfscl_totalrecall(struct nfsclclient *clp)
{
struct nfscldeleg *dp;
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
dp->nfsdl_flags |= NFSCLDL_RECALL;
}
}
/*
* Relock byte ranges. Called for delegation recall and state expiry.
*/
static int
nfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
NFSPROC_T *p)
{
struct nfscllockowner *nlp;
struct nfsfh *nfhp;
u_int64_t off, len;
u_int32_t clidrev = 0;
int error, newone, donelocally;
off = lop->nfslo_first;
len = lop->nfslo_end - lop->nfslo_first;
error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
clp, 1, lp->nfsl_owner, lp->nfsl_openowner, &nlp, &newone,
&donelocally);
if (error || donelocally)
return (error);
if (nmp->nm_clp != NULL)
clidrev = nmp->nm_clp->nfsc_clientidrev;
else
clidrev = 0;
nfhp = VTONFS(vp)->n_fhp;
error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
nfhp->nfh_len, nlp, newone, 0, off,
len, lop->nfslo_type, cred, p);
if (error)
nfscl_freelockowner(nlp, 0);
return (error);
}
/*
* Called to re-open a file. Basically get a vnode for the file handle
* and then call nfsrpc_openrpc() to do the rest.
*/
static int
nfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
struct ucred *cred, NFSPROC_T *p)
{
struct nfsnode *np;
vnode_t vp;
int error;
error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
if (error)
return (error);
vp = NFSTOV(np);
if (np->n_v4 != NULL) {
error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
cred, p);
} else {
error = EINVAL;
}
vrele(vp);
return (error);
}
/*
* Try an open against the server. Just call nfsrpc_openrpc(), retrying while
* NFSERR_DELAY. Also, try system credentials, if the passed in credentials
* fail.
*/
static int
nfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
{
int error;
do {
error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
0, 0);
if (error == NFSERR_DELAY)
(void) nfs_catnap(PZERO, error, "nfstryop");
} while (error == NFSERR_DELAY);
if (error == EAUTH || error == EACCES) {
/* Try again using system credentials */
newnfs_setroot(cred);
do {
error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
newfhlen, mode, op, name, namelen, ndpp, reclaim,
delegtype, cred, p, 1, 0);
if (error == NFSERR_DELAY)
(void) nfs_catnap(PZERO, error, "nfstryop");
} while (error == NFSERR_DELAY);
}
return (error);
}
/*
* Try a byte range lock. Just loop on nfsrpc_lock() while it returns
* NFSERR_DELAY. Also, retry with system credentials, if the provided
* cred don't work.
*/
static int
nfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
do {
error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
reclaim, off, len, type, cred, p, 0);
if (!error && nd->nd_repstat == NFSERR_DELAY)
(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
"nfstrylck");
} while (!error && nd->nd_repstat == NFSERR_DELAY);
if (!error)
error = nd->nd_repstat;
if (error == EAUTH || error == EACCES) {
/* Try again using root credentials */
newnfs_setroot(cred);
do {
error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
newone, reclaim, off, len, type, cred, p, 1);
if (!error && nd->nd_repstat == NFSERR_DELAY)
(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
"nfstrylck");
} while (!error && nd->nd_repstat == NFSERR_DELAY);
if (!error)
error = nd->nd_repstat;
}
return (error);
}
/*
* Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
* retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
* credentials fail.
*/
static int
nfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
struct nfsmount *nmp, NFSPROC_T *p)
{
int error;
do {
error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
if (error == NFSERR_DELAY)
(void) nfs_catnap(PZERO, error, "nfstrydp");
} while (error == NFSERR_DELAY);
if (error == EAUTH || error == EACCES) {
/* Try again using system credentials */
newnfs_setroot(cred);
do {
error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
if (error == NFSERR_DELAY)
(void) nfs_catnap(PZERO, error, "nfstrydp");
} while (error == NFSERR_DELAY);
}
return (error);
}
/*
* Try a close against the server. Just call nfsrpc_closerpc(),
* retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
* credentials fail.
*/
APPLESTATIC int
nfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
struct nfsmount *nmp, NFSPROC_T *p)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
int error;
do {
error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
if (error == NFSERR_DELAY)
(void) nfs_catnap(PZERO, error, "nfstrycl");
} while (error == NFSERR_DELAY);
if (error == EAUTH || error == EACCES) {
/* Try again using system credentials */
newnfs_setroot(cred);
do {
error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
if (error == NFSERR_DELAY)
(void) nfs_catnap(PZERO, error, "nfstrycl");
} while (error == NFSERR_DELAY);
}
return (error);
}
/*
* Decide if a delegation on a file permits close without flushing writes
* to the server. This might be a big performance win in some environments.
* (Not useful until the client does caching on local stable storage.)
*/
APPLESTATIC int
nfscl_mustflush(vnode_t vp)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsnode *np;
struct nfsmount *nmp;
np = VTONFS(vp);
nmp = VFSTONFS(vnode_mount(vp));
if (!NFSHASNFSV4(nmp))
return (1);
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (1);
}
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
if (dp != NULL && (dp->nfsdl_flags &
(NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
NFSCLDL_WRITE &&
(dp->nfsdl_sizelimit >= np->n_size ||
!NFSHASSTRICT3530(nmp))) {
NFSUNLOCKCLSTATE();
return (0);
}
NFSUNLOCKCLSTATE();
return (1);
}
/*
* See if a (write) delegation exists for this file.
*/
APPLESTATIC int
nfscl_nodeleg(vnode_t vp, int writedeleg)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsnode *np;
struct nfsmount *nmp;
np = VTONFS(vp);
nmp = VFSTONFS(vnode_mount(vp));
if (!NFSHASNFSV4(nmp))
return (1);
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (1);
}
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
if (dp != NULL &&
(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
(writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
NFSCLDL_WRITE)) {
NFSUNLOCKCLSTATE();
return (0);
}
NFSUNLOCKCLSTATE();
return (1);
}
/*
* Look for an associated delegation that should be DelegReturned.
*/
APPLESTATIC int
nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsclowner *owp;
struct nfscllockowner *lp;
struct nfsmount *nmp;
struct ucred *cred;
struct nfsnode *np;
int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
nmp = VFSTONFS(vnode_mount(vp));
np = VTONFS(vp);
NFSLOCKCLSTATE();
/*
* Loop around waiting for:
* - outstanding I/O operations on delegations to complete
* - for a delegation on vp that has state, lock the client and
* do a recall
* - return delegation with no state
*/
while (1) {
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (retcnt);
}
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len);
if (dp != NULL) {
/*
* Wait for outstanding I/O ops to be done.
*/
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
if (igotlock) {
nfsv4_unlock(&clp->nfsc_lock, 0);
igotlock = 0;
}
dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
(void) nfsmsleep(&dp->nfsdl_rwlock,
NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
continue;
}
needsrecall = 0;
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
if (!LIST_EMPTY(&owp->nfsow_open)) {
needsrecall = 1;
break;
}
}
if (!needsrecall) {
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
if (!LIST_EMPTY(&lp->nfsl_lock)) {
needsrecall = 1;
break;
}
}
}
if (needsrecall && !triedrecall) {
dp->nfsdl_flags |= NFSCLDL_DELEGRET;
islept = 0;
while (!igotlock) {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
&islept, NFSCLSTATEMUTEXPTR);
if (islept)
break;
}
if (islept)
continue;
NFSUNLOCKCLSTATE();
cred = newnfs_getcred();
newnfs_copycred(&dp->nfsdl_cred, cred);
(void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0);
NFSFREECRED(cred);
triedrecall = 1;
NFSLOCKCLSTATE();
nfsv4_unlock(&clp->nfsc_lock, 0);
igotlock = 0;
continue;
}
*stp = dp->nfsdl_stateid;
retcnt = 1;
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
}
if (igotlock)
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
return (retcnt);
}
}
/*
* Look for associated delegation(s) that should be DelegReturned.
*/
APPLESTATIC int
nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsclowner *owp;
struct nfscllockowner *lp;
struct nfsmount *nmp;
struct ucred *cred;
struct nfsnode *np;
int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
nmp = VFSTONFS(vnode_mount(fvp));
*gotfdp = 0;
*gottdp = 0;
NFSLOCKCLSTATE();
/*
* Loop around waiting for:
* - outstanding I/O operations on delegations to complete
* - for a delegation on fvp that has state, lock the client and
* do a recall
* - return delegation(s) with no state.
*/
while (1) {
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (retcnt);
}
np = VTONFS(fvp);
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len);
if (dp != NULL && *gotfdp == 0) {
/*
* Wait for outstanding I/O ops to be done.
*/
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
if (igotlock) {
nfsv4_unlock(&clp->nfsc_lock, 0);
igotlock = 0;
}
dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
(void) nfsmsleep(&dp->nfsdl_rwlock,
NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
continue;
}
needsrecall = 0;
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
if (!LIST_EMPTY(&owp->nfsow_open)) {
needsrecall = 1;
break;
}
}
if (!needsrecall) {
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
if (!LIST_EMPTY(&lp->nfsl_lock)) {
needsrecall = 1;
break;
}
}
}
if (needsrecall && !triedrecall) {
dp->nfsdl_flags |= NFSCLDL_DELEGRET;
islept = 0;
while (!igotlock) {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
&islept, NFSCLSTATEMUTEXPTR);
if (islept)
break;
}
if (islept)
continue;
NFSUNLOCKCLSTATE();
cred = newnfs_getcred();
newnfs_copycred(&dp->nfsdl_cred, cred);
(void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0);
NFSFREECRED(cred);
triedrecall = 1;
NFSLOCKCLSTATE();
nfsv4_unlock(&clp->nfsc_lock, 0);
igotlock = 0;
continue;
}
*fstp = dp->nfsdl_stateid;
retcnt++;
*gotfdp = 1;
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
}
if (igotlock) {
nfsv4_unlock(&clp->nfsc_lock, 0);
igotlock = 0;
}
if (tvp != NULL) {
np = VTONFS(tvp);
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len);
if (dp != NULL && *gottdp == 0) {
/*
* Wait for outstanding I/O ops to be done.
*/
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
(void) nfsmsleep(&dp->nfsdl_rwlock,
NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
continue;
}
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
if (!LIST_EMPTY(&owp->nfsow_open)) {
NFSUNLOCKCLSTATE();
return (retcnt);
}
}
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
if (!LIST_EMPTY(&lp->nfsl_lock)) {
NFSUNLOCKCLSTATE();
return (retcnt);
}
}
*tstp = dp->nfsdl_stateid;
retcnt++;
*gottdp = 1;
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
}
}
NFSUNLOCKCLSTATE();
return (retcnt);
}
}
/*
* Get a reference on the clientid associated with the mount point.
* Return 1 if success, 0 otherwise.
*/
APPLESTATIC int
nfscl_getref(struct nfsmount *nmp)
{
struct nfsclclient *clp;
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (0);
}
nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR);
NFSUNLOCKCLSTATE();
return (1);
}
/*
* Release a reference on a clientid acquired with the above call.
*/
APPLESTATIC void
nfscl_relref(struct nfsmount *nmp)
{
struct nfsclclient *clp;
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return;
}
nfsv4_relref(&clp->nfsc_lock);
NFSUNLOCKCLSTATE();
}
/*
* Save the size attribute in the delegation, since the nfsnode
* is going away.
*/
APPLESTATIC void
nfscl_reclaimnode(vnode_t vp)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp;
nmp = VFSTONFS(vnode_mount(vp));
if (!NFSHASNFSV4(nmp))
return;
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return;
}
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
dp->nfsdl_size = np->n_size;
NFSUNLOCKCLSTATE();
}
/*
* Get the saved size attribute in the delegation, since it is a
* newly allocated nfsnode.
*/
APPLESTATIC void
nfscl_newnode(vnode_t vp)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp;
nmp = VFSTONFS(vnode_mount(vp));
if (!NFSHASNFSV4(nmp))
return;
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return;
}
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
np->n_size = dp->nfsdl_size;
NFSUNLOCKCLSTATE();
}
/*
* If there is a valid write delegation for this file, set the modtime
* to the local clock time.
*/
APPLESTATIC void
nfscl_delegmodtime(vnode_t vp)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp;
nmp = VFSTONFS(vnode_mount(vp));
if (!NFSHASNFSV4(nmp))
return;
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return;
}
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
NFSGETNANOTIME(&dp->nfsdl_modtime);
dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
}
NFSUNLOCKCLSTATE();
}
/*
* If there is a valid write delegation for this file with a modtime set,
* put that modtime in mtime.
*/
APPLESTATIC void
nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp;
nmp = VFSTONFS(vnode_mount(vp));
if (!NFSHASNFSV4(nmp))
return;
NFSLOCKCLSTATE();
clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return;
}
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
if (dp != NULL &&
(dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
(NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
*mtime = dp->nfsdl_modtime;
NFSUNLOCKCLSTATE();
}
static int
nfscl_errmap(struct nfsrv_descript *nd)
{
short *defaulterrp, *errp;
if (!nd->nd_repstat)
return (0);
if (nd->nd_procnum == NFSPROC_NOOP)
return (txdr_unsigned(nd->nd_repstat & 0xffff));
if (nd->nd_repstat == EBADRPC)
return (txdr_unsigned(NFSERR_BADXDR));
if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
nd->nd_repstat == NFSERR_OPILLEGAL)
return (txdr_unsigned(nd->nd_repstat));
errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
while (*++errp)
if (*errp == (short)nd->nd_repstat)
return (txdr_unsigned(nd->nd_repstat));
return (txdr_unsigned(*defaulterrp));
}
Index: stable/8/sys/fs/nfsserver/nfs_nfsdport.c
===================================================================
--- stable/8/sys/fs/nfsserver/nfs_nfsdport.c (revision 221216)
+++ stable/8/sys/fs/nfsserver/nfs_nfsdport.c (revision 221217)
@@ -1,3199 +1,3206 @@
/*-
* 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.
* 4. 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$");
/*
* Functions that perform the vfs operations required by the routines in
* nfsd_serv.c. It is hoped that this change will make the server more
* portable.
*/
#include <fs/nfs/nfsport.h>
#include <sys/hash.h>
#include <sys/sysctl.h>
#include <nlm/nlm_prot.h>
#include <nlm/nlm.h>
extern u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
extern int nfsrv_useacl;
extern int newnfs_numnfsd;
extern struct mount nfsv4root_mnt;
extern struct nfsrv_stablefirst nfsrv_stablefirst;
extern void (*nfsd_call_servertimer)(void);
extern SVCPOOL *nfsrvd_pool;
struct vfsoptlist nfsv4root_opt, nfsv4root_newopt;
NFSDLOCKMUTEX;
struct mtx nfs_cache_mutex;
struct mtx nfs_v4root_mutex;
struct nfsrvfh nfs_rootfh, nfs_pubfh;
int nfs_pubfhset = 0, nfs_rootfhset = 0;
struct proc *nfsd_master_proc = NULL;
static pid_t nfsd_master_pid = (pid_t)-1;
static char nfsd_master_comm[MAXCOMLEN + 1];
static struct timeval nfsd_master_start;
static uint32_t nfsv4_sysid = 0;
static int nfssvc_srvcall(struct thread *, struct nfssvc_args *,
struct ucred *);
int nfsrv_enable_crossmntpt = 1;
static int nfs_commit_blks;
static int nfs_commit_miss;
extern int nfsrv_issuedelegs;
extern int nfsrv_dolocallocks;
SYSCTL_DECL(_vfs_newnfs);
SYSCTL_INT(_vfs_newnfs, OID_AUTO, mirrormnt, CTLFLAG_RW,
&nfsrv_enable_crossmntpt, 0, "Enable nfsd to cross mount points");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, commit_blks, CTLFLAG_RW, &nfs_commit_blks,
0, "");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, commit_miss, CTLFLAG_RW, &nfs_commit_miss,
0, "");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, issue_delegations, CTLFLAG_RW,
&nfsrv_issuedelegs, 0, "Enable nfsd to issue delegations");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, enable_locallocks, CTLFLAG_RW,
&nfsrv_dolocallocks, 0, "Enable nfsd to acquire local locks on files");
#define NUM_HEURISTIC 1017
#define NHUSE_INIT 64
#define NHUSE_INC 16
#define NHUSE_MAX 2048
static struct nfsheur {
struct vnode *nh_vp; /* vp to match (unreferenced pointer) */
off_t nh_nextr; /* next offset for sequential detection */
int nh_use; /* use count for selection */
int nh_seqcount; /* heuristic */
} nfsheur[NUM_HEURISTIC];
/*
* Get attributes into nfsvattr structure.
*/
int
nfsvno_getattr(struct vnode *vp, struct nfsvattr *nvap, struct ucred *cred,
struct thread *p, int vpislocked)
{
int error, lockedit = 0;
if (vpislocked == 0) {
/*
* When vpislocked == 0, the vnode is either exclusively
* locked by this thread or not locked by this thread.
* As such, shared lock it, if not exclusively locked.
*/
if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) {
lockedit = 1;
vn_lock(vp, LK_SHARED | LK_RETRY);
}
}
error = VOP_GETATTR(vp, &nvap->na_vattr, cred);
if (lockedit != 0)
VOP_UNLOCK(vp, 0);
return (error);
}
/*
* Get a file handle for a vnode.
*/
int
nfsvno_getfh(struct vnode *vp, fhandle_t *fhp, struct thread *p)
{
int error;
NFSBZERO((caddr_t)fhp, sizeof(fhandle_t));
fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
error = VOP_VPTOFH(vp, &fhp->fh_fid);
return (error);
}
/*
* Perform access checking for vnodes obtained from file handles that would
* refer to files already opened by a Unix client. You cannot just use
* vn_writechk() and VOP_ACCESSX() for two reasons.
* 1 - You must check for exported rdonly as well as MNT_RDONLY for the write
* case.
* 2 - The owner is to be given access irrespective of mode bits for some
* operations, so that processes that chmod after opening a file don't
* break.
*/
int
nfsvno_accchk(struct vnode *vp, accmode_t accmode, struct ucred *cred,
struct nfsexstuff *exp, struct thread *p, int override, int vpislocked,
u_int32_t *supportedtypep)
{
struct vattr vattr;
int error = 0, getret = 0;
if (vpislocked == 0) {
if (vn_lock(vp, LK_SHARED) != 0)
return (EPERM);
}
if (accmode & VWRITE) {
/* Just vn_writechk() changed to check rdonly */
/*
* Disallow write attempts on read-only file systems;
* unless the file is a socket or a block or character
* device resident on the file system.
*/
if (NFSVNO_EXRDONLY(exp) ||
(vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG:
case VDIR:
case VLNK:
error = EROFS;
default:
break;
}
}
/*
* If there's shared text associated with
* the inode, try to free it up once. If
* we fail, we can't allow writing.
*/
if ((vp->v_vflag & VV_TEXT) != 0 && error == 0)
error = ETXTBSY;
}
if (error != 0) {
if (vpislocked == 0)
VOP_UNLOCK(vp, 0);
return (error);
}
/*
* Should the override still be applied when ACLs are enabled?
*/
error = VOP_ACCESSX(vp, accmode, cred, p);
if (error != 0 && (accmode & (VDELETE | VDELETE_CHILD))) {
/*
* Try again with VEXPLICIT_DENY, to see if the test for
* deletion is supported.
*/
error = VOP_ACCESSX(vp, accmode | VEXPLICIT_DENY, cred, p);
if (error == 0) {
if (vp->v_type == VDIR) {
accmode &= ~(VDELETE | VDELETE_CHILD);
accmode |= VWRITE;
error = VOP_ACCESSX(vp, accmode, cred, p);
} else if (supportedtypep != NULL) {
*supportedtypep &= ~NFSACCESS_DELETE;
}
}
}
/*
* Allow certain operations for the owner (reads and writes
* on files that are already open).
*/
if (override != NFSACCCHK_NOOVERRIDE &&
(error == EPERM || error == EACCES)) {
if (cred->cr_uid == 0 && (override & NFSACCCHK_ALLOWROOT))
error = 0;
else if (override & NFSACCCHK_ALLOWOWNER) {
getret = VOP_GETATTR(vp, &vattr, cred);
if (getret == 0 && cred->cr_uid == vattr.va_uid)
error = 0;
}
}
if (vpislocked == 0)
VOP_UNLOCK(vp, 0);
return (error);
}
/*
* Set attribute(s) vnop.
*/
int
nfsvno_setattr(struct vnode *vp, struct nfsvattr *nvap, struct ucred *cred,
struct thread *p, struct nfsexstuff *exp)
{
int error;
error = VOP_SETATTR(vp, &nvap->na_vattr, cred);
return (error);
}
/*
* Set up nameidata for a lookup() call and do it
* For the cases where we are crossing mount points
* (looking up the public fh path or the v4 root path when
* not using a pseudo-root fs), set/release the Giant lock,
* as required.
*/
int
nfsvno_namei(struct nfsrv_descript *nd, struct nameidata *ndp,
struct vnode *dp, int islocked, struct nfsexstuff *exp, struct thread *p,
struct vnode **retdirp)
{
struct componentname *cnp = &ndp->ni_cnd;
int i;
struct iovec aiov;
struct uio auio;
int lockleaf = (cnp->cn_flags & LOCKLEAF) != 0, linklen;
int error = 0, crossmnt;
char *cp;
*retdirp = NULL;
cnp->cn_nameptr = cnp->cn_pnbuf;
/*
* Extract and set starting directory.
*/
if (dp->v_type != VDIR) {
if (islocked)
vput(dp);
else
vrele(dp);
nfsvno_relpathbuf(ndp);
return (ENOTDIR);
}
if (islocked)
NFSVOPUNLOCK(dp, 0, p);
VREF(dp);
*retdirp = dp;
if (NFSVNO_EXRDONLY(exp))
cnp->cn_flags |= RDONLY;
ndp->ni_segflg = UIO_SYSSPACE;
crossmnt = 1;
if (nd->nd_flag & ND_PUBLOOKUP) {
ndp->ni_loopcnt = 0;
if (cnp->cn_pnbuf[0] == '/') {
vrele(dp);
/*
* Check for degenerate pathnames here, since lookup()
* panics on them.
*/
for (i = 1; i < ndp->ni_pathlen; i++)
if (cnp->cn_pnbuf[i] != '/')
break;
if (i == ndp->ni_pathlen) {
error = NFSERR_ACCES;
goto out;
}
dp = rootvnode;
VREF(dp);
}
} else if ((nfsrv_enable_crossmntpt == 0 && NFSVNO_EXPORTED(exp)) ||
(nd->nd_flag & ND_NFSV4) == 0) {
/*
* Only cross mount points for NFSv4 when doing a
* mount while traversing the file system above
* the mount point, unless nfsrv_enable_crossmntpt is set.
*/
cnp->cn_flags |= NOCROSSMOUNT;
crossmnt = 0;
}
/*
* Initialize for scan, set ni_startdir and bump ref on dp again
* becuase lookup() will dereference ni_startdir.
*/
cnp->cn_thread = p;
ndp->ni_startdir = dp;
ndp->ni_rootdir = rootvnode;
if (!lockleaf)
cnp->cn_flags |= LOCKLEAF;
for (;;) {
cnp->cn_nameptr = cnp->cn_pnbuf;
/*
* Call lookup() to do the real work. If an error occurs,
* ndp->ni_vp and ni_dvp are left uninitialized or NULL and
* we do not have to dereference anything before returning.
* In either case ni_startdir will be dereferenced and NULLed
* out.
*/
error = lookup(ndp);
if (error)
break;
/*
* Check for encountering a symbolic link. Trivial
* termination occurs if no symlink encountered.
*/
if ((cnp->cn_flags & ISSYMLINK) == 0) {
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
nfsvno_relpathbuf(ndp);
if (ndp->ni_vp && !lockleaf)
NFSVOPUNLOCK(ndp->ni_vp, 0, p);
break;
}
/*
* Validate symlink
*/
if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
NFSVOPUNLOCK(ndp->ni_dvp, 0, p);
if (!(nd->nd_flag & ND_PUBLOOKUP)) {
error = EINVAL;
goto badlink2;
}
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
error = ELOOP;
goto badlink2;
}
if (ndp->ni_pathlen > 1)
cp = uma_zalloc(namei_zone, M_WAITOK);
else
cp = cnp->cn_pnbuf;
aiov.iov_base = cp;
aiov.iov_len = MAXPATHLEN;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_rw = UIO_READ;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_td = NULL;
auio.uio_resid = MAXPATHLEN;
error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
if (error) {
badlink1:
if (ndp->ni_pathlen > 1)
uma_zfree(namei_zone, cp);
badlink2:
vrele(ndp->ni_dvp);
vput(ndp->ni_vp);
break;
}
linklen = MAXPATHLEN - auio.uio_resid;
if (linklen == 0) {
error = ENOENT;
goto badlink1;
}
if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
error = ENAMETOOLONG;
goto badlink1;
}
/*
* Adjust or replace path
*/
if (ndp->ni_pathlen > 1) {
NFSBCOPY(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
uma_zfree(namei_zone, cnp->cn_pnbuf);
cnp->cn_pnbuf = cp;
} else
cnp->cn_pnbuf[linklen] = '\0';
ndp->ni_pathlen += linklen;
/*
* Cleanup refs for next loop and check if root directory
* should replace current directory. Normally ni_dvp
* becomes the new base directory and is cleaned up when
* we loop. Explicitly null pointers after invalidation
* to clarify operation.
*/
vput(ndp->ni_vp);
ndp->ni_vp = NULL;
if (cnp->cn_pnbuf[0] == '/') {
vrele(ndp->ni_dvp);
ndp->ni_dvp = ndp->ni_rootdir;
VREF(ndp->ni_dvp);
}
ndp->ni_startdir = ndp->ni_dvp;
ndp->ni_dvp = NULL;
}
if (!lockleaf)
cnp->cn_flags &= ~LOCKLEAF;
out:
if (error) {
uma_zfree(namei_zone, cnp->cn_pnbuf);
ndp->ni_vp = NULL;
ndp->ni_dvp = NULL;
ndp->ni_startdir = NULL;
cnp->cn_flags &= ~HASBUF;
} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
ndp->ni_dvp = NULL;
}
return (error);
}
/*
* Set up a pathname buffer and return a pointer to it and, optionally
* set a hash pointer.
*/
void
nfsvno_setpathbuf(struct nameidata *ndp, char **bufpp, u_long **hashpp)
{
struct componentname *cnp = &ndp->ni_cnd;
cnp->cn_flags |= (NOMACCHECK | HASBUF);
cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
if (hashpp != NULL)
*hashpp = NULL;
*bufpp = cnp->cn_pnbuf;
}
/*
* Release the above path buffer, if not released by nfsvno_namei().
*/
void
nfsvno_relpathbuf(struct nameidata *ndp)
{
if ((ndp->ni_cnd.cn_flags & HASBUF) == 0)
panic("nfsrelpath");
uma_zfree(namei_zone, ndp->ni_cnd.cn_pnbuf);
ndp->ni_cnd.cn_flags &= ~HASBUF;
}
/*
* Readlink vnode op into an mbuf list.
*/
int
nfsvno_readlink(struct vnode *vp, struct ucred *cred, struct thread *p,
struct mbuf **mpp, struct mbuf **mpendp, int *lenp)
{
struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN];
struct iovec *ivp = iv;
struct uio io, *uiop = &io;
struct mbuf *mp, *mp2 = NULL, *mp3 = NULL;
int i, len, tlen, error;
len = 0;
i = 0;
while (len < NFS_MAXPATHLEN) {
NFSMGET(mp);
MCLGET(mp, M_WAIT);
mp->m_len = NFSMSIZ(mp);
if (len == 0) {
mp3 = mp2 = mp;
} else {
mp2->m_next = mp;
mp2 = mp;
}
if ((len + mp->m_len) > NFS_MAXPATHLEN) {
mp->m_len = NFS_MAXPATHLEN - len;
len = NFS_MAXPATHLEN;
} else {
len += mp->m_len;
}
ivp->iov_base = mtod(mp, caddr_t);
ivp->iov_len = mp->m_len;
i++;
ivp++;
}
uiop->uio_iov = iv;
uiop->uio_iovcnt = i;
uiop->uio_offset = 0;
uiop->uio_resid = len;
uiop->uio_rw = UIO_READ;
uiop->uio_segflg = UIO_SYSSPACE;
uiop->uio_td = NULL;
error = VOP_READLINK(vp, uiop, cred);
if (error) {
m_freem(mp3);
*lenp = 0;
return (error);
}
if (uiop->uio_resid > 0) {
len -= uiop->uio_resid;
tlen = NFSM_RNDUP(len);
nfsrv_adj(mp3, NFS_MAXPATHLEN - tlen, tlen - len);
}
*lenp = len;
*mpp = mp3;
*mpendp = mp;
return (0);
}
/*
* Read vnode op call into mbuf list.
*/
int
nfsvno_read(struct vnode *vp, off_t off, int cnt, struct ucred *cred,
struct thread *p, struct mbuf **mpp, struct mbuf **mpendp)
{
struct mbuf *m;
int i;
struct iovec *iv;
struct iovec *iv2;
int error = 0, len, left, siz, tlen, ioflag = 0, hi, try = 32;
struct mbuf *m2 = NULL, *m3;
struct uio io, *uiop = &io;
struct nfsheur *nh;
/*
* Calculate seqcount for heuristic
*/
/*
* Locate best candidate
*/
hi = ((int)(vm_offset_t)vp / sizeof(struct vnode)) % NUM_HEURISTIC;
nh = &nfsheur[hi];
while (try--) {
if (nfsheur[hi].nh_vp == vp) {
nh = &nfsheur[hi];
break;
}
if (nfsheur[hi].nh_use > 0)
--nfsheur[hi].nh_use;
hi = (hi + 1) % NUM_HEURISTIC;
if (nfsheur[hi].nh_use < nh->nh_use)
nh = &nfsheur[hi];
}
if (nh->nh_vp != vp) {
nh->nh_vp = vp;
nh->nh_nextr = off;
nh->nh_use = NHUSE_INIT;
if (off == 0)
nh->nh_seqcount = 4;
else
nh->nh_seqcount = 1;
}
/*
* Calculate heuristic
*/
if ((off == 0 && nh->nh_seqcount > 0) || off == nh->nh_nextr) {
if (++nh->nh_seqcount > IO_SEQMAX)
nh->nh_seqcount = IO_SEQMAX;
} else if (nh->nh_seqcount > 1) {
nh->nh_seqcount = 1;
} else {
nh->nh_seqcount = 0;
}
nh->nh_use += NHUSE_INC;
if (nh->nh_use > NHUSE_MAX)
nh->nh_use = NHUSE_MAX;
ioflag |= nh->nh_seqcount << IO_SEQSHIFT;
len = left = NFSM_RNDUP(cnt);
m3 = NULL;
/*
* Generate the mbuf list with the uio_iov ref. to it.
*/
i = 0;
while (left > 0) {
NFSMGET(m);
MCLGET(m, M_WAIT);
m->m_len = 0;
siz = min(M_TRAILINGSPACE(m), left);
left -= siz;
i++;
if (m3)
m2->m_next = m;
else
m3 = m;
m2 = m;
}
MALLOC(iv, struct iovec *, i * sizeof (struct iovec),
M_TEMP, M_WAITOK);
uiop->uio_iov = iv2 = iv;
m = m3;
left = len;
i = 0;
while (left > 0) {
if (m == NULL)
panic("nfsvno_read iov");
siz = min(M_TRAILINGSPACE(m), left);
if (siz > 0) {
iv->iov_base = mtod(m, caddr_t) + m->m_len;
iv->iov_len = siz;
m->m_len += siz;
left -= siz;
iv++;
i++;
}
m = m->m_next;
}
uiop->uio_iovcnt = i;
uiop->uio_offset = off;
uiop->uio_resid = len;
uiop->uio_rw = UIO_READ;
uiop->uio_segflg = UIO_SYSSPACE;
error = VOP_READ(vp, uiop, IO_NODELOCKED | ioflag, cred);
FREE((caddr_t)iv2, M_TEMP);
if (error) {
m_freem(m3);
*mpp = NULL;
return (error);
}
tlen = len - uiop->uio_resid;
cnt = cnt < tlen ? cnt : tlen;
tlen = NFSM_RNDUP(cnt);
if (tlen == 0) {
m_freem(m3);
m3 = NULL;
} else if (len != tlen || tlen != cnt)
nfsrv_adj(m3, len - tlen, tlen - cnt);
*mpp = m3;
*mpendp = m2;
return (0);
}
/*
* Write vnode op from an mbuf list.
*/
int
nfsvno_write(struct vnode *vp, off_t off, int retlen, int cnt, int stable,
struct mbuf *mp, char *cp, struct ucred *cred, struct thread *p)
{
struct iovec *ivp;
int i, len;
struct iovec *iv;
int ioflags, error;
struct uio io, *uiop = &io;
MALLOC(ivp, struct iovec *, cnt * sizeof (struct iovec), M_TEMP,
M_WAITOK);
uiop->uio_iov = iv = ivp;
uiop->uio_iovcnt = cnt;
i = mtod(mp, caddr_t) + mp->m_len - cp;
len = retlen;
while (len > 0) {
if (mp == NULL)
panic("nfsvno_write");
if (i > 0) {
i = min(i, len);
ivp->iov_base = cp;
ivp->iov_len = i;
ivp++;
len -= i;
}
mp = mp->m_next;
if (mp) {
i = mp->m_len;
cp = mtod(mp, caddr_t);
}
}
if (stable == NFSWRITE_UNSTABLE)
ioflags = IO_NODELOCKED;
else
ioflags = (IO_SYNC | IO_NODELOCKED);
uiop->uio_resid = retlen;
uiop->uio_rw = UIO_WRITE;
uiop->uio_segflg = UIO_SYSSPACE;
NFSUIOPROC(uiop, p);
uiop->uio_offset = off;
error = VOP_WRITE(vp, uiop, ioflags, cred);
FREE((caddr_t)iv, M_TEMP);
return (error);
}
/*
* Common code for creating a regular file (plus special files for V2).
*/
int
nfsvno_createsub(struct nfsrv_descript *nd, struct nameidata *ndp,
struct vnode **vpp, struct nfsvattr *nvap, int *exclusive_flagp,
int32_t *cverf, NFSDEV_T rdev, struct thread *p, struct nfsexstuff *exp)
{
u_quad_t tempsize;
int error;
error = nd->nd_repstat;
if (!error && ndp->ni_vp == NULL) {
if (nvap->na_type == VREG || nvap->na_type == VSOCK) {
vrele(ndp->ni_startdir);
error = VOP_CREATE(ndp->ni_dvp,
&ndp->ni_vp, &ndp->ni_cnd, &nvap->na_vattr);
vput(ndp->ni_dvp);
nfsvno_relpathbuf(ndp);
if (!error) {
if (*exclusive_flagp) {
*exclusive_flagp = 0;
NFSVNO_ATTRINIT(nvap);
nvap->na_atime.tv_sec = cverf[0];
nvap->na_atime.tv_nsec = cverf[1];
error = VOP_SETATTR(ndp->ni_vp,
&nvap->na_vattr, nd->nd_cred);
}
}
/*
* NFS V2 Only. nfsrvd_mknod() does this for V3.
* (This implies, just get out on an error.)
*/
} else if (nvap->na_type == VCHR || nvap->na_type == VBLK ||
nvap->na_type == VFIFO) {
if (nvap->na_type == VCHR && rdev == 0xffffffff)
nvap->na_type = VFIFO;
if (nvap->na_type != VFIFO &&
(error = priv_check_cred(nd->nd_cred,
PRIV_VFS_MKNOD_DEV, 0))) {
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
vput(ndp->ni_dvp);
return (error);
}
nvap->na_rdev = rdev;
error = VOP_MKNOD(ndp->ni_dvp, &ndp->ni_vp,
&ndp->ni_cnd, &nvap->na_vattr);
vput(ndp->ni_dvp);
nfsvno_relpathbuf(ndp);
vrele(ndp->ni_startdir);
if (error)
return (error);
} else {
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
vput(ndp->ni_dvp);
return (ENXIO);
}
*vpp = ndp->ni_vp;
} else {
/*
* Handle cases where error is already set and/or
* the file exists.
* 1 - clean up the lookup
* 2 - iff !error and na_size set, truncate it
*/
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
*vpp = ndp->ni_vp;
if (ndp->ni_dvp == *vpp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
if (!error && nvap->na_size != VNOVAL) {
error = nfsvno_accchk(*vpp, VWRITE,
nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
NFSACCCHK_VPISLOCKED, NULL);
if (!error) {
tempsize = nvap->na_size;
NFSVNO_ATTRINIT(nvap);
nvap->na_size = tempsize;
error = VOP_SETATTR(*vpp,
&nvap->na_vattr, nd->nd_cred);
}
}
if (error)
vput(*vpp);
}
return (error);
}
/*
* Do a mknod vnode op.
*/
int
nfsvno_mknod(struct nameidata *ndp, struct nfsvattr *nvap, struct ucred *cred,
struct thread *p)
{
int error = 0;
enum vtype vtyp;
vtyp = nvap->na_type;
/*
* Iff doesn't exist, create it.
*/
if (ndp->ni_vp) {
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
vput(ndp->ni_dvp);
vrele(ndp->ni_vp);
return (EEXIST);
}
if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) {
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
vput(ndp->ni_dvp);
return (NFSERR_BADTYPE);
}
if (vtyp == VSOCK) {
vrele(ndp->ni_startdir);
error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp,
&ndp->ni_cnd, &nvap->na_vattr);
vput(ndp->ni_dvp);
nfsvno_relpathbuf(ndp);
} else {
if (nvap->na_type != VFIFO &&
(error = priv_check_cred(cred, PRIV_VFS_MKNOD_DEV, 0))) {
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
vput(ndp->ni_dvp);
return (error);
}
error = VOP_MKNOD(ndp->ni_dvp, &ndp->ni_vp,
&ndp->ni_cnd, &nvap->na_vattr);
vput(ndp->ni_dvp);
nfsvno_relpathbuf(ndp);
vrele(ndp->ni_startdir);
/*
* Since VOP_MKNOD returns the ni_vp, I can't
* see any reason to do the lookup.
*/
}
return (error);
}
/*
* Mkdir vnode op.
*/
int
nfsvno_mkdir(struct nameidata *ndp, struct nfsvattr *nvap, uid_t saved_uid,
struct ucred *cred, struct thread *p, struct nfsexstuff *exp)
{
int error = 0;
if (ndp->ni_vp != NULL) {
if (ndp->ni_dvp == ndp->ni_vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
vrele(ndp->ni_vp);
nfsvno_relpathbuf(ndp);
return (EEXIST);
}
error = VOP_MKDIR(ndp->ni_dvp, &ndp->ni_vp, &ndp->ni_cnd,
&nvap->na_vattr);
vput(ndp->ni_dvp);
nfsvno_relpathbuf(ndp);
return (error);
}
/*
* symlink vnode op.
*/
int
nfsvno_symlink(struct nameidata *ndp, struct nfsvattr *nvap, char *pathcp,
int pathlen, int not_v2, uid_t saved_uid, struct ucred *cred, struct thread *p,
struct nfsexstuff *exp)
{
int error = 0;
if (ndp->ni_vp) {
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
if (ndp->ni_dvp == ndp->ni_vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
vrele(ndp->ni_vp);
return (EEXIST);
}
error = VOP_SYMLINK(ndp->ni_dvp, &ndp->ni_vp, &ndp->ni_cnd,
&nvap->na_vattr, pathcp);
vput(ndp->ni_dvp);
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
/*
* Although FreeBSD still had the lookup code in
* it for 7/current, there doesn't seem to be any
* point, since VOP_SYMLINK() returns the ni_vp.
* Just vput it for v2.
*/
if (!not_v2 && !error)
vput(ndp->ni_vp);
return (error);
}
/*
* Parse symbolic link arguments.
* This function has an ugly side effect. It will MALLOC() an area for
* the symlink and set iov_base to point to it, only if it succeeds.
* So, if it returns with uiop->uio_iov->iov_base != NULL, that must
* be FREE'd later.
*/
int
nfsvno_getsymlink(struct nfsrv_descript *nd, struct nfsvattr *nvap,
struct thread *p, char **pathcpp, int *lenp)
{
u_int32_t *tl;
char *pathcp = NULL;
int error = 0, len;
struct nfsv2_sattr *sp;
*pathcpp = NULL;
*lenp = 0;
if ((nd->nd_flag & ND_NFSV3) &&
(error = nfsrv_sattr(nd, nvap, NULL, NULL, p)))
goto nfsmout;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
len = fxdr_unsigned(int, *tl);
if (len > NFS_MAXPATHLEN || len <= 0) {
error = EBADRPC;
goto nfsmout;
}
MALLOC(pathcp, caddr_t, len + 1, M_TEMP, M_WAITOK);
error = nfsrv_mtostr(nd, pathcp, len);
if (error)
goto nfsmout;
if (nd->nd_flag & ND_NFSV2) {
NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
nvap->na_mode = fxdr_unsigned(u_int16_t, sp->sa_mode);
}
*pathcpp = pathcp;
*lenp = len;
return (0);
nfsmout:
if (pathcp)
free(pathcp, M_TEMP);
return (error);
}
/*
* Remove a non-directory object.
*/
int
nfsvno_removesub(struct nameidata *ndp, int is_v4, struct ucred *cred,
struct thread *p, struct nfsexstuff *exp)
{
struct vnode *vp;
int error = 0;
vp = ndp->ni_vp;
if (vp->v_type == VDIR)
error = NFSERR_ISDIR;
else if (is_v4)
error = nfsrv_checkremove(vp, 1, p);
if (!error)
error = VOP_REMOVE(ndp->ni_dvp, vp, &ndp->ni_cnd);
if (ndp->ni_dvp == vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
vput(vp);
return (error);
}
/*
* Remove a directory.
*/
int
nfsvno_rmdirsub(struct nameidata *ndp, int is_v4, struct ucred *cred,
struct thread *p, struct nfsexstuff *exp)
{
struct vnode *vp;
int error = 0;
vp = ndp->ni_vp;
if (vp->v_type != VDIR) {
error = ENOTDIR;
goto out;
}
/*
* No rmdir "." please.
*/
if (ndp->ni_dvp == vp) {
error = EINVAL;
goto out;
}
/*
* The root of a mounted filesystem cannot be deleted.
*/
if (vp->v_vflag & VV_ROOT)
error = EBUSY;
out:
if (!error)
error = VOP_RMDIR(ndp->ni_dvp, vp, &ndp->ni_cnd);
if (ndp->ni_dvp == vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
vput(vp);
return (error);
}
/*
* Rename vnode op.
*/
int
nfsvno_rename(struct nameidata *fromndp, struct nameidata *tondp,
u_int32_t ndstat, u_int32_t ndflag, struct ucred *cred, struct thread *p)
{
struct vnode *fvp, *tvp, *tdvp;
int error = 0;
fvp = fromndp->ni_vp;
if (ndstat) {
vrele(fromndp->ni_dvp);
vrele(fvp);
error = ndstat;
goto out1;
}
tdvp = tondp->ni_dvp;
tvp = tondp->ni_vp;
if (tvp != NULL) {
if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
error = (ndflag & ND_NFSV2) ? EISDIR : EEXIST;
goto out;
} else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
error = (ndflag & ND_NFSV2) ? ENOTDIR : EEXIST;
goto out;
}
if (tvp->v_type == VDIR && tvp->v_mountedhere) {
error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
goto out;
}
/*
* A rename to '.' or '..' results in a prematurely
* unlocked vnode on FreeBSD5, so I'm just going to fail that
* here.
*/
if ((tondp->ni_cnd.cn_namelen == 1 &&
tondp->ni_cnd.cn_nameptr[0] == '.') ||
(tondp->ni_cnd.cn_namelen == 2 &&
tondp->ni_cnd.cn_nameptr[0] == '.' &&
tondp->ni_cnd.cn_nameptr[1] == '.')) {
error = EINVAL;
goto out;
}
}
if (fvp->v_type == VDIR && fvp->v_mountedhere) {
error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
goto out;
}
if (fvp->v_mount != tdvp->v_mount) {
error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
goto out;
}
if (fvp == tdvp) {
error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EINVAL;
goto out;
}
if (fvp == tvp) {
/*
* If source and destination are the same, there is nothing to
* do. Set error to -1 to indicate this.
*/
error = -1;
goto out;
}
if (ndflag & ND_NFSV4) {
if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
error = nfsrv_checkremove(fvp, 0, p);
VOP_UNLOCK(fvp, 0);
} else
error = EPERM;
if (tvp && !error)
error = nfsrv_checkremove(tvp, 1, p);
} else {
/*
* For NFSv2 and NFSv3, try to get rid of the delegation, so
* that the NFSv4 client won't be confused by the rename.
* Since nfsd_recalldelegation() can only be called on an
* unlocked vnode at this point and fvp is the file that will
* still exist after the rename, just do fvp.
*/
nfsd_recalldelegation(fvp, p);
}
out:
if (!error) {
error = VOP_RENAME(fromndp->ni_dvp, fromndp->ni_vp,
&fromndp->ni_cnd, tondp->ni_dvp, tondp->ni_vp,
&tondp->ni_cnd);
} else {
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vput(tvp);
vrele(fromndp->ni_dvp);
vrele(fvp);
if (error == -1)
error = 0;
}
vrele(tondp->ni_startdir);
nfsvno_relpathbuf(tondp);
out1:
vrele(fromndp->ni_startdir);
nfsvno_relpathbuf(fromndp);
return (error);
}
/*
* Link vnode op.
*/
int
nfsvno_link(struct nameidata *ndp, struct vnode *vp, struct ucred *cred,
struct thread *p, struct nfsexstuff *exp)
{
struct vnode *xp;
int error = 0;
xp = ndp->ni_vp;
if (xp != NULL) {
error = EEXIST;
} else {
xp = ndp->ni_dvp;
if (vp->v_mount != xp->v_mount)
error = EXDEV;
}
if (!error) {
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if ((vp->v_iflag & VI_DOOMED) == 0)
error = VOP_LINK(ndp->ni_dvp, vp, &ndp->ni_cnd);
else
error = EPERM;
if (ndp->ni_dvp == vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
VOP_UNLOCK(vp, 0);
} else {
if (ndp->ni_dvp == ndp->ni_vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
if (ndp->ni_vp)
vrele(ndp->ni_vp);
}
nfsvno_relpathbuf(ndp);
return (error);
}
/*
* Do the fsync() appropriate for the commit.
*/
int
nfsvno_fsync(struct vnode *vp, u_int64_t off, int cnt, struct ucred *cred,
struct thread *td)
{
int error = 0;
if (cnt > MAX_COMMIT_COUNT) {
/*
* Give up and do the whole thing
*/
if (vp->v_object &&
(vp->v_object->flags & OBJ_MIGHTBEDIRTY)) {
VM_OBJECT_LOCK(vp->v_object);
vm_object_page_clean(vp->v_object, 0, 0, OBJPC_SYNC);
VM_OBJECT_UNLOCK(vp->v_object);
}
error = VOP_FSYNC(vp, MNT_WAIT, td);
} else {
/*
* Locate and synchronously write any buffers that fall
* into the requested range. Note: we are assuming that
* f_iosize is a power of 2.
*/
int iosize = vp->v_mount->mnt_stat.f_iosize;
int iomask = iosize - 1;
struct bufobj *bo;
daddr_t lblkno;
/*
* Align to iosize boundry, super-align to page boundry.
*/
if (off & iomask) {
cnt += off & iomask;
off &= ~(u_quad_t)iomask;
}
if (off & PAGE_MASK) {
cnt += off & PAGE_MASK;
off &= ~(u_quad_t)PAGE_MASK;
}
lblkno = off / iosize;
if (vp->v_object &&
(vp->v_object->flags & OBJ_MIGHTBEDIRTY)) {
VM_OBJECT_LOCK(vp->v_object);
vm_object_page_clean(vp->v_object, off / PAGE_SIZE, (cnt + PAGE_MASK) / PAGE_SIZE, OBJPC_SYNC);
VM_OBJECT_UNLOCK(vp->v_object);
}
bo = &vp->v_bufobj;
BO_LOCK(bo);
while (cnt > 0) {
struct buf *bp;
/*
* If we have a buffer and it is marked B_DELWRI we
* have to lock and write it. Otherwise the prior
* write is assumed to have already been committed.
*
* gbincore() can return invalid buffers now so we
* have to check that bit as well (though B_DELWRI
* should not be set if B_INVAL is set there could be
* a race here since we haven't locked the buffer).
*/
if ((bp = gbincore(&vp->v_bufobj, lblkno)) != NULL) {
if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL |
LK_INTERLOCK, BO_MTX(bo)) == ENOLCK) {
BO_LOCK(bo);
continue; /* retry */
}
if ((bp->b_flags & (B_DELWRI|B_INVAL)) ==
B_DELWRI) {
bremfree(bp);
bp->b_flags &= ~B_ASYNC;
bwrite(bp);
++nfs_commit_miss;
} else
BUF_UNLOCK(bp);
BO_LOCK(bo);
}
++nfs_commit_blks;
if (cnt < iosize)
break;
cnt -= iosize;
++lblkno;
}
BO_UNLOCK(bo);
}
return (error);
}
/*
* Statfs vnode op.
*/
int
nfsvno_statfs(struct vnode *vp, struct statfs *sf)
{
return (VFS_STATFS(vp->v_mount, sf));
}
/*
* Do the vnode op stuff for Open. Similar to nfsvno_createsub(), but
* must handle nfsrv_opencheck() calls after any other access checks.
*/
void
nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
nfsquad_t clientid, nfsv4stateid_t *stateidp, struct nfsstate *stp,
int *exclusive_flagp, struct nfsvattr *nvap, int32_t *cverf, int create,
NFSACL_T *aclp, nfsattrbit_t *attrbitp, struct ucred *cred, struct thread *p,
struct nfsexstuff *exp, struct vnode **vpp)
{
struct vnode *vp = NULL;
u_quad_t tempsize;
struct nfsexstuff nes;
if (ndp->ni_vp == NULL)
nd->nd_repstat = nfsrv_opencheck(clientid,
stateidp, stp, NULL, nd, p, nd->nd_repstat);
if (!nd->nd_repstat) {
if (ndp->ni_vp == NULL) {
vrele(ndp->ni_startdir);
nd->nd_repstat = VOP_CREATE(ndp->ni_dvp,
&ndp->ni_vp, &ndp->ni_cnd, &nvap->na_vattr);
vput(ndp->ni_dvp);
nfsvno_relpathbuf(ndp);
if (!nd->nd_repstat) {
if (*exclusive_flagp) {
*exclusive_flagp = 0;
NFSVNO_ATTRINIT(nvap);
nvap->na_atime.tv_sec = cverf[0];
nvap->na_atime.tv_nsec = cverf[1];
nd->nd_repstat = VOP_SETATTR(ndp->ni_vp,
&nvap->na_vattr, cred);
} else {
nfsrv_fixattr(nd, ndp->ni_vp, nvap,
aclp, p, attrbitp, exp);
}
}
vp = ndp->ni_vp;
} else {
if (ndp->ni_startdir)
vrele(ndp->ni_startdir);
nfsvno_relpathbuf(ndp);
vp = ndp->ni_vp;
if (create == NFSV4OPEN_CREATE) {
if (ndp->ni_dvp == vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
}
if (NFSVNO_ISSETSIZE(nvap) && vp->v_type == VREG) {
if (ndp->ni_cnd.cn_flags & RDONLY)
NFSVNO_SETEXRDONLY(&nes);
else
NFSVNO_EXINIT(&nes);
nd->nd_repstat = nfsvno_accchk(vp,
VWRITE, cred, &nes, p,
NFSACCCHK_NOOVERRIDE,
NFSACCCHK_VPISLOCKED, NULL);
nd->nd_repstat = nfsrv_opencheck(clientid,
stateidp, stp, vp, nd, p, nd->nd_repstat);
if (!nd->nd_repstat) {
tempsize = nvap->na_size;
NFSVNO_ATTRINIT(nvap);
nvap->na_size = tempsize;
nd->nd_repstat = VOP_SETATTR(vp,
&nvap->na_vattr, cred);
}
} else if (vp->v_type == VREG) {
nd->nd_repstat = nfsrv_opencheck(clientid,
stateidp, stp, vp, nd, p, nd->nd_repstat);
}
}
} else {
if (ndp->ni_cnd.cn_flags & HASBUF)
nfsvno_relpathbuf(ndp);
if (ndp->ni_startdir && create == NFSV4OPEN_CREATE) {
vrele(ndp->ni_startdir);
if (ndp->ni_dvp == ndp->ni_vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
if (ndp->ni_vp)
vput(ndp->ni_vp);
}
}
*vpp = vp;
}
/*
* Updates the file rev and sets the mtime and ctime
* to the current clock time, returning the va_filerev and va_Xtime
* values.
*/
void
nfsvno_updfilerev(struct vnode *vp, struct nfsvattr *nvap,
struct ucred *cred, struct thread *p)
{
struct vattr va;
VATTR_NULL(&va);
getnanotime(&va.va_mtime);
(void) VOP_SETATTR(vp, &va, cred);
(void) nfsvno_getattr(vp, nvap, cred, p, 1);
}
/*
* Glue routine to nfsv4_fillattr().
*/
int
nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp,
struct nfsvattr *nvap, fhandle_t *fhp, int rderror, nfsattrbit_t *attrbitp,
- struct ucred *cred, struct thread *p, int isdgram, int reterr, int at_root,
- uint64_t mounted_on_fileno)
+ struct ucred *cred, struct thread *p, int isdgram, int reterr,
+ int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
{
int error;
error = nfsv4_fillattr(nd, mp, vp, NULL, &nvap->na_vattr, fhp, rderror,
- attrbitp, cred, p, isdgram, reterr, at_root, mounted_on_fileno);
+ attrbitp, cred, p, isdgram, reterr, supports_nfsv4acls, at_root,
+ mounted_on_fileno);
return (error);
}
/* Since the Readdir vnode ops vary, put the entire functions in here. */
/*
* nfs readdir service
* - mallocs what it thinks is enough to read
* count rounded up to a multiple of DIRBLKSIZ <= NFS_MAXREADDIR
* - calls VOP_READDIR()
* - loops around building the reply
* if the output generated exceeds count break out of loop
* The NFSM_CLGET macro is used here so that the reply will be packed
* tightly in mbuf clusters.
* - it trims out records with d_fileno == 0
* this doesn't matter for Unix clients, but they might confuse clients
* for other os'.
* - it trims out records with d_type == DT_WHT
* these cannot be seen through NFS (unless we extend the protocol)
* The alternate call nfsrvd_readdirplus() does lookups as well.
* PS: The NFS protocol spec. does not clarify what the "count" byte
* argument is a count of.. just name strings and file id's or the
* entire reply rpc or ...
* I tried just file name and id sizes and it confused the Sun client,
* so I am using the full rpc size now. The "paranoia.." comment refers
* to including the status longwords that are not a part of the dir.
* "entry" structures, but are in the rpc.
*/
int
nfsrvd_readdir(struct nfsrv_descript *nd, int isdgram,
struct vnode *vp, struct thread *p, struct nfsexstuff *exp)
{
struct dirent *dp;
u_int32_t *tl;
int dirlen;
char *cpos, *cend, *rbuf;
struct nfsvattr at;
int nlen, error = 0, getret = 1;
int siz, cnt, fullsiz, eofflag, ncookies;
u_int64_t off, toff, verf;
u_long *cookies = NULL, *cookiep;
struct uio io;
struct iovec iv;
int not_zfs;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
return (0);
}
if (nd->nd_flag & ND_NFSV2) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
off = fxdr_unsigned(u_quad_t, *tl++);
} else {
NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
off = fxdr_hyper(tl);
tl += 2;
verf = fxdr_hyper(tl);
tl += 2;
}
toff = off;
cnt = fxdr_unsigned(int, *tl);
if (cnt > NFS_SRVMAXDATA(nd) || cnt < 0)
cnt = NFS_SRVMAXDATA(nd);
siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
fullsiz = siz;
if (nd->nd_flag & ND_NFSV3) {
nd->nd_repstat = getret = nfsvno_getattr(vp, &at, nd->nd_cred,
p, 1);
#if 0
/*
* va_filerev is not sufficient as a cookie verifier,
* since it is not supposed to change when entries are
* removed/added unless that offset cookies returned to
* the client are no longer valid.
*/
if (!nd->nd_repstat && toff && verf != at.na_filerev)
nd->nd_repstat = NFSERR_BAD_COOKIE;
#endif
}
if (nd->nd_repstat == 0 && cnt == 0) {
if (nd->nd_flag & ND_NFSV2)
/* NFSv2 does not have NFSERR_TOOSMALL */
nd->nd_repstat = EPERM;
else
nd->nd_repstat = NFSERR_TOOSMALL;
}
if (!nd->nd_repstat)
nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
NFSACCCHK_VPISLOCKED, NULL);
if (nd->nd_repstat) {
vput(vp);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
return (0);
}
not_zfs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "zfs");
MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
again:
eofflag = 0;
if (cookies) {
free((caddr_t)cookies, M_TEMP);
cookies = NULL;
}
iv.iov_base = rbuf;
iv.iov_len = siz;
io.uio_iov = &iv;
io.uio_iovcnt = 1;
io.uio_offset = (off_t)off;
io.uio_resid = siz;
io.uio_segflg = UIO_SYSSPACE;
io.uio_rw = UIO_READ;
io.uio_td = NULL;
nd->nd_repstat = VOP_READDIR(vp, &io, nd->nd_cred, &eofflag, &ncookies,
&cookies);
off = (u_int64_t)io.uio_offset;
if (io.uio_resid)
siz -= io.uio_resid;
if (!cookies && !nd->nd_repstat)
nd->nd_repstat = NFSERR_PERM;
if (nd->nd_flag & ND_NFSV3) {
getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
if (!nd->nd_repstat)
nd->nd_repstat = getret;
}
/*
* Handles the failed cases. nd->nd_repstat == 0 past here.
*/
if (nd->nd_repstat) {
vput(vp);
free((caddr_t)rbuf, M_TEMP);
if (cookies)
free((caddr_t)cookies, M_TEMP);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
return (0);
}
/*
* If nothing read, return eof
* rpc reply
*/
if (siz == 0) {
vput(vp);
if (nd->nd_flag & ND_NFSV2) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
} else {
nfsrv_postopattr(nd, getret, &at);
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
txdr_hyper(at.na_filerev, tl);
tl += 2;
}
*tl++ = newnfs_false;
*tl = newnfs_true;
FREE((caddr_t)rbuf, M_TEMP);
FREE((caddr_t)cookies, M_TEMP);
return (0);
}
/*
* Check for degenerate cases of nothing useful read.
* If so go try again
*/
cpos = rbuf;
cend = rbuf + siz;
dp = (struct dirent *)cpos;
cookiep = cookies;
/*
* For some reason FreeBSD's ufs_readdir() chooses to back the
* directory offset up to a block boundary, so it is necessary to
* skip over the records that precede the requested offset. This
* requires the assumption that file offset cookies monotonically
* increase.
* Since the offset cookies don't monotonically increase for ZFS,
* this is not done when ZFS is the file system.
*/
while (cpos < cend && ncookies > 0 &&
(dp->d_fileno == 0 || dp->d_type == DT_WHT ||
(not_zfs != 0 && ((u_quad_t)(*cookiep)) <= toff))) {
cpos += dp->d_reclen;
dp = (struct dirent *)cpos;
cookiep++;
ncookies--;
}
if (cpos >= cend || ncookies == 0) {
siz = fullsiz;
toff = off;
goto again;
}
vput(vp);
/*
* dirlen is the size of the reply, including all XDR and must
* not exceed cnt. For NFSv2, RFC1094 didn't clearly indicate
* if the XDR should be included in "count", but to be safe, we do.
* (Include the two booleans at the end of the reply in dirlen now.)
*/
if (nd->nd_flag & ND_NFSV3) {
nfsrv_postopattr(nd, getret, &at);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
txdr_hyper(at.na_filerev, tl);
dirlen = NFSX_V3POSTOPATTR + NFSX_VERF + 2 * NFSX_UNSIGNED;
} else {
dirlen = 2 * NFSX_UNSIGNED;
}
/* Loop through the records and build reply */
while (cpos < cend && ncookies > 0) {
nlen = dp->d_namlen;
if (dp->d_fileno != 0 && dp->d_type != DT_WHT &&
nlen <= NFS_MAXNAMLEN) {
if (nd->nd_flag & ND_NFSV3)
dirlen += (6*NFSX_UNSIGNED + NFSM_RNDUP(nlen));
else
dirlen += (4*NFSX_UNSIGNED + NFSM_RNDUP(nlen));
if (dirlen > cnt) {
eofflag = 0;
break;
}
/*
* Build the directory record xdr from
* the dirent entry.
*/
if (nd->nd_flag & ND_NFSV3) {
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
*tl++ = 0;
} else {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
}
*tl = txdr_unsigned(dp->d_fileno);
(void) nfsm_strtom(nd, dp->d_name, nlen);
if (nd->nd_flag & ND_NFSV3) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = 0;
} else
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(*cookiep);
}
cpos += dp->d_reclen;
dp = (struct dirent *)cpos;
cookiep++;
ncookies--;
}
if (cpos < cend)
eofflag = 0;
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = newnfs_false;
if (eofflag)
*tl = newnfs_true;
else
*tl = newnfs_false;
FREE((caddr_t)rbuf, M_TEMP);
FREE((caddr_t)cookies, M_TEMP);
return (0);
nfsmout:
vput(vp);
return (error);
}
/*
* Readdirplus for V3 and Readdir for V4.
*/
int
nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
struct vnode *vp, struct thread *p, struct nfsexstuff *exp)
{
struct dirent *dp;
u_int32_t *tl;
int dirlen;
char *cpos, *cend, *rbuf;
struct vnode *nvp;
fhandle_t nfh;
struct nfsvattr nva, at, *nvap = &nva;
struct mbuf *mb0, *mb1;
struct nfsreferral *refp;
int nlen, r, error = 0, getret = 1, usevget = 1;
int siz, cnt, fullsiz, eofflag, ncookies, entrycnt;
caddr_t bpos0, bpos1;
u_int64_t off, toff, verf;
u_long *cookies = NULL, *cookiep;
nfsattrbit_t attrbits, rderrbits, savbits;
struct uio io;
struct iovec iv;
struct componentname cn;
- int at_root, needs_unbusy, not_zfs;
+ int at_root, needs_unbusy, not_zfs, supports_nfsv4acls;
struct mount *mp, *new_mp;
uint64_t mounted_on_fileno;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
return (0);
}
NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
off = fxdr_hyper(tl);
toff = off;
tl += 2;
verf = fxdr_hyper(tl);
tl += 2;
siz = fxdr_unsigned(int, *tl++);
cnt = fxdr_unsigned(int, *tl);
/*
* Use the server's maximum data transfer size as the upper bound
* on reply datalen.
*/
if (cnt > NFS_SRVMAXDATA(nd) || cnt < 0)
cnt = NFS_SRVMAXDATA(nd);
/*
* siz is a "hint" of how much directory information (name, fileid,
* cookie) should be in the reply. At least one client "hints" 0,
* so I set it to cnt for that case. I also round it up to the
* next multiple of DIRBLKSIZ.
*/
if (siz <= 0)
siz = cnt;
siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
if (nd->nd_flag & ND_NFSV4) {
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
if (error)
goto nfsmout;
NFSSET_ATTRBIT(&savbits, &attrbits);
NFSCLRNOTFILLABLE_ATTRBIT(&attrbits);
NFSZERO_ATTRBIT(&rderrbits);
NFSSETBIT_ATTRBIT(&rderrbits, NFSATTRBIT_RDATTRERROR);
} else {
NFSZERO_ATTRBIT(&attrbits);
}
fullsiz = siz;
nd->nd_repstat = getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
if (!nd->nd_repstat) {
if (off && verf != at.na_filerev) {
/*
* va_filerev is not sufficient as a cookie verifier,
* since it is not supposed to change when entries are
* removed/added unless that offset cookies returned to
* the client are no longer valid.
*/
#if 0
if (nd->nd_flag & ND_NFSV4) {
nd->nd_repstat = NFSERR_NOTSAME;
} else {
nd->nd_repstat = NFSERR_BAD_COOKIE;
}
#endif
} else if ((nd->nd_flag & ND_NFSV4) && off == 0 && verf != 0) {
nd->nd_repstat = NFSERR_BAD_COOKIE;
}
}
if (!nd->nd_repstat && vp->v_type != VDIR)
nd->nd_repstat = NFSERR_NOTDIR;
if (!nd->nd_repstat && cnt == 0)
nd->nd_repstat = NFSERR_TOOSMALL;
if (!nd->nd_repstat)
nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
NFSACCCHK_VPISLOCKED, NULL);
if (nd->nd_repstat) {
vput(vp);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
return (0);
}
not_zfs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "zfs");
MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
again:
eofflag = 0;
if (cookies) {
free((caddr_t)cookies, M_TEMP);
cookies = NULL;
}
iv.iov_base = rbuf;
iv.iov_len = siz;
io.uio_iov = &iv;
io.uio_iovcnt = 1;
io.uio_offset = (off_t)off;
io.uio_resid = siz;
io.uio_segflg = UIO_SYSSPACE;
io.uio_rw = UIO_READ;
io.uio_td = NULL;
nd->nd_repstat = VOP_READDIR(vp, &io, nd->nd_cred, &eofflag, &ncookies,
&cookies);
off = (u_int64_t)io.uio_offset;
if (io.uio_resid)
siz -= io.uio_resid;
getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
if (!cookies && !nd->nd_repstat)
nd->nd_repstat = NFSERR_PERM;
if (!nd->nd_repstat)
nd->nd_repstat = getret;
if (nd->nd_repstat) {
vput(vp);
if (cookies)
free((caddr_t)cookies, M_TEMP);
free((caddr_t)rbuf, M_TEMP);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
return (0);
}
/*
* If nothing read, return eof
* rpc reply
*/
if (siz == 0) {
vput(vp);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
txdr_hyper(at.na_filerev, tl);
tl += 2;
*tl++ = newnfs_false;
*tl = newnfs_true;
free((caddr_t)cookies, M_TEMP);
free((caddr_t)rbuf, M_TEMP);
return (0);
}
/*
* Check for degenerate cases of nothing useful read.
* If so go try again
*/
cpos = rbuf;
cend = rbuf + siz;
dp = (struct dirent *)cpos;
cookiep = cookies;
/*
* For some reason FreeBSD's ufs_readdir() chooses to back the
* directory offset up to a block boundary, so it is necessary to
* skip over the records that precede the requested offset. This
* requires the assumption that file offset cookies monotonically
* increase.
* Since the offset cookies don't monotonically increase for ZFS,
* this is not done when ZFS is the file system.
*/
while (cpos < cend && ncookies > 0 &&
(dp->d_fileno == 0 || dp->d_type == DT_WHT ||
(not_zfs != 0 && ((u_quad_t)(*cookiep)) <= toff) ||
((nd->nd_flag & ND_NFSV4) &&
((dp->d_namlen == 1 && dp->d_name[0] == '.') ||
(dp->d_namlen==2 && dp->d_name[0]=='.' && dp->d_name[1]=='.'))))) {
cpos += dp->d_reclen;
dp = (struct dirent *)cpos;
cookiep++;
ncookies--;
}
if (cpos >= cend || ncookies == 0) {
siz = fullsiz;
toff = off;
goto again;
}
/*
* Busy the file system so that the mount point won't go away
* and, as such, VFS_VGET() can be used safely.
*/
mp = vp->v_mount;
vfs_ref(mp);
VOP_UNLOCK(vp, 0);
nd->nd_repstat = vfs_busy(mp, 0);
vfs_rel(mp);
if (nd->nd_repstat != 0) {
vrele(vp);
free(cookies, M_TEMP);
free(rbuf, M_TEMP);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
return (0);
}
/*
* Save this position, in case there is an error before one entry
* is created.
*/
mb0 = nd->nd_mb;
bpos0 = nd->nd_bpos;
/*
* Fill in the first part of the reply.
* dirlen is the reply length in bytes and cannot exceed cnt.
* (Include the two booleans at the end of the reply in dirlen now,
* so we recognize when we have exceeded cnt.)
*/
if (nd->nd_flag & ND_NFSV3) {
dirlen = NFSX_V3POSTOPATTR + NFSX_VERF + 2 * NFSX_UNSIGNED;
nfsrv_postopattr(nd, getret, &at);
} else {
dirlen = NFSX_VERF + 2 * NFSX_UNSIGNED;
}
NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
txdr_hyper(at.na_filerev, tl);
/*
* Save this position, in case there is an empty reply needed.
*/
mb1 = nd->nd_mb;
bpos1 = nd->nd_bpos;
/* Loop through the records and build reply */
entrycnt = 0;
while (cpos < cend && ncookies > 0 && dirlen < cnt) {
nlen = dp->d_namlen;
if (dp->d_fileno != 0 && dp->d_type != DT_WHT &&
nlen <= NFS_MAXNAMLEN &&
((nd->nd_flag & ND_NFSV3) || nlen > 2 ||
(nlen==2 && (dp->d_name[0]!='.' || dp->d_name[1]!='.'))
|| (nlen == 1 && dp->d_name[0] != '.'))) {
/*
* Save the current position in the reply, in case
* this entry exceeds cnt.
*/
mb1 = nd->nd_mb;
bpos1 = nd->nd_bpos;
/*
* For readdir_and_lookup get the vnode using
* the file number.
*/
nvp = NULL;
refp = NULL;
r = 0;
at_root = 0;
needs_unbusy = 0;
new_mp = mp;
mounted_on_fileno = (uint64_t)dp->d_fileno;
if ((nd->nd_flag & ND_NFSV3) ||
NFSNONZERO_ATTRBIT(&savbits)) {
if (nd->nd_flag & ND_NFSV4)
refp = nfsv4root_getreferral(NULL,
vp, dp->d_fileno);
if (refp == NULL) {
if (usevget)
r = VFS_VGET(mp, dp->d_fileno,
LK_SHARED, &nvp);
else
r = EOPNOTSUPP;
if (r == EOPNOTSUPP) {
if (usevget) {
usevget = 0;
cn.cn_nameiop = LOOKUP;
cn.cn_lkflags =
LK_SHARED |
LK_RETRY;
cn.cn_cred =
nd->nd_cred;
cn.cn_thread = p;
}
cn.cn_nameptr = dp->d_name;
cn.cn_namelen = nlen;
cn.cn_flags = ISLASTCN |
NOFOLLOW | LOCKLEAF |
MPSAFE;
if (nlen == 2 &&
dp->d_name[0] == '.' &&
dp->d_name[1] == '.')
cn.cn_flags |=
ISDOTDOT;
if (vn_lock(vp, LK_SHARED)
!= 0) {
nd->nd_repstat = EPERM;
break;
}
if ((vp->v_vflag & VV_ROOT) != 0
&& (cn.cn_flags & ISDOTDOT)
!= 0) {
vref(vp);
nvp = vp;
r = 0;
} else {
r = VOP_LOOKUP(vp, &nvp,
&cn);
if (vp != nvp)
VOP_UNLOCK(vp,
0);
}
}
/*
* For NFSv4, check to see if nvp is
* a mount point and get the mount
* point vnode, as required.
*/
if (r == 0 &&
nfsrv_enable_crossmntpt != 0 &&
(nd->nd_flag & ND_NFSV4) != 0 &&
nvp->v_type == VDIR &&
nvp->v_mountedhere != NULL) {
new_mp = nvp->v_mountedhere;
r = vfs_busy(new_mp, 0);
vput(nvp);
nvp = NULL;
if (r == 0) {
r = VFS_ROOT(new_mp,
LK_SHARED, &nvp);
needs_unbusy = 1;
if (r == 0)
at_root = 1;
}
}
}
if (!r) {
if (refp == NULL &&
((nd->nd_flag & ND_NFSV3) ||
NFSNONZERO_ATTRBIT(&attrbits))) {
r = nfsvno_getfh(nvp, &nfh, p);
if (!r)
r = nfsvno_getattr(nvp, nvap,
nd->nd_cred, p, 1);
}
} else {
nvp = NULL;
}
if (r) {
if (!NFSISSET_ATTRBIT(&attrbits,
NFSATTRBIT_RDATTRERROR)) {
if (nvp != NULL)
vput(nvp);
if (needs_unbusy != 0)
vfs_unbusy(new_mp);
nd->nd_repstat = r;
break;
}
}
}
/*
* Build the directory record xdr
*/
if (nd->nd_flag & ND_NFSV3) {
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
*tl++ = 0;
*tl = txdr_unsigned(dp->d_fileno);
dirlen += nfsm_strtom(nd, dp->d_name, nlen);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = 0;
*tl = txdr_unsigned(*cookiep);
nfsrv_postopattr(nd, 0, nvap);
dirlen += nfsm_fhtom(nd,(u_int8_t *)&nfh,0,1);
dirlen += (5*NFSX_UNSIGNED+NFSX_V3POSTOPATTR);
if (nvp != NULL)
vput(nvp);
} else {
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = newnfs_true;
*tl++ = 0;
*tl = txdr_unsigned(*cookiep);
dirlen += nfsm_strtom(nd, dp->d_name, nlen);
- if (nvp != NULL)
+ if (nvp != NULL) {
+ supports_nfsv4acls =
+ nfs_supportsnfsv4acls(nvp);
VOP_UNLOCK(nvp, 0);
+ } else
+ supports_nfsv4acls = 0;
if (refp != NULL) {
dirlen += nfsrv_putreferralattr(nd,
&savbits, refp, 0,
&nd->nd_repstat);
if (nd->nd_repstat) {
if (nvp != NULL)
vrele(nvp);
if (needs_unbusy != 0)
vfs_unbusy(new_mp);
break;
}
} else if (r) {
dirlen += nfsvno_fillattr(nd, new_mp,
nvp, nvap, &nfh, r, &rderrbits,
- nd->nd_cred, p, isdgram, 0, at_root,
+ nd->nd_cred, p, isdgram, 0,
+ supports_nfsv4acls, at_root,
mounted_on_fileno);
} else {
dirlen += nfsvno_fillattr(nd, new_mp,
nvp, nvap, &nfh, r, &attrbits,
- nd->nd_cred, p, isdgram, 0, at_root,
+ nd->nd_cred, p, isdgram, 0,
+ supports_nfsv4acls, at_root,
mounted_on_fileno);
}
if (nvp != NULL)
vrele(nvp);
dirlen += (3 * NFSX_UNSIGNED);
}
if (needs_unbusy != 0)
vfs_unbusy(new_mp);
if (dirlen <= cnt)
entrycnt++;
}
cpos += dp->d_reclen;
dp = (struct dirent *)cpos;
cookiep++;
ncookies--;
}
vrele(vp);
vfs_unbusy(mp);
/*
* If dirlen > cnt, we must strip off the last entry. If that
* results in an empty reply, report NFSERR_TOOSMALL.
*/
if (dirlen > cnt || nd->nd_repstat) {
if (!nd->nd_repstat && entrycnt == 0)
nd->nd_repstat = NFSERR_TOOSMALL;
if (nd->nd_repstat)
newnfs_trimtrailing(nd, mb0, bpos0);
else
newnfs_trimtrailing(nd, mb1, bpos1);
eofflag = 0;
} else if (cpos < cend)
eofflag = 0;
if (!nd->nd_repstat) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = newnfs_false;
if (eofflag)
*tl = newnfs_true;
else
*tl = newnfs_false;
}
FREE((caddr_t)cookies, M_TEMP);
FREE((caddr_t)rbuf, M_TEMP);
return (0);
nfsmout:
vput(vp);
return (error);
}
/*
* Get the settable attributes out of the mbuf list.
* (Return 0 or EBADRPC)
*/
int
nfsrv_sattr(struct nfsrv_descript *nd, struct nfsvattr *nvap,
nfsattrbit_t *attrbitp, NFSACL_T *aclp, struct thread *p)
{
u_int32_t *tl;
struct nfsv2_sattr *sp;
struct timeval curtime;
int error = 0, toclient = 0;
switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
case ND_NFSV2:
NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
/*
* Some old clients didn't fill in the high order 16bits.
* --> check the low order 2 bytes for 0xffff
*/
if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
nvap->na_mode = nfstov_mode(sp->sa_mode);
if (sp->sa_uid != newnfs_xdrneg1)
nvap->na_uid = fxdr_unsigned(uid_t, sp->sa_uid);
if (sp->sa_gid != newnfs_xdrneg1)
nvap->na_gid = fxdr_unsigned(gid_t, sp->sa_gid);
if (sp->sa_size != newnfs_xdrneg1)
nvap->na_size = fxdr_unsigned(u_quad_t, sp->sa_size);
if (sp->sa_atime.nfsv2_sec != newnfs_xdrneg1) {
#ifdef notyet
fxdr_nfsv2time(&sp->sa_atime, &nvap->na_atime);
#else
nvap->na_atime.tv_sec =
fxdr_unsigned(u_int32_t,sp->sa_atime.nfsv2_sec);
nvap->na_atime.tv_nsec = 0;
#endif
}
if (sp->sa_mtime.nfsv2_sec != newnfs_xdrneg1)
fxdr_nfsv2time(&sp->sa_mtime, &nvap->na_mtime);
break;
case ND_NFSV3:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (*tl == newnfs_true) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
nvap->na_mode = nfstov_mode(*tl);
}
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (*tl == newnfs_true) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
nvap->na_uid = fxdr_unsigned(uid_t, *tl);
}
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (*tl == newnfs_true) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
nvap->na_gid = fxdr_unsigned(gid_t, *tl);
}
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (*tl == newnfs_true) {
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
nvap->na_size = fxdr_hyper(tl);
}
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
switch (fxdr_unsigned(int, *tl)) {
case NFSV3SATTRTIME_TOCLIENT:
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
fxdr_nfsv3time(tl, &nvap->na_atime);
toclient = 1;
break;
case NFSV3SATTRTIME_TOSERVER:
NFSGETTIME(&curtime);
nvap->na_atime.tv_sec = curtime.tv_sec;
nvap->na_atime.tv_nsec = curtime.tv_usec * 1000;
nvap->na_vaflags |= VA_UTIMES_NULL;
break;
};
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
switch (fxdr_unsigned(int, *tl)) {
case NFSV3SATTRTIME_TOCLIENT:
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
fxdr_nfsv3time(tl, &nvap->na_mtime);
nvap->na_vaflags &= ~VA_UTIMES_NULL;
break;
case NFSV3SATTRTIME_TOSERVER:
NFSGETTIME(&curtime);
nvap->na_mtime.tv_sec = curtime.tv_sec;
nvap->na_mtime.tv_nsec = curtime.tv_usec * 1000;
if (!toclient)
nvap->na_vaflags |= VA_UTIMES_NULL;
break;
};
break;
case ND_NFSV4:
error = nfsv4_sattr(nd, nvap, attrbitp, aclp, p);
};
nfsmout:
return (error);
}
/*
* Handle the setable attributes for V4.
* Returns NFSERR_BADXDR if it can't be parsed, 0 otherwise.
*/
int
nfsv4_sattr(struct nfsrv_descript *nd, struct nfsvattr *nvap,
nfsattrbit_t *attrbitp, NFSACL_T *aclp, struct thread *p)
{
u_int32_t *tl;
int attrsum = 0;
int i, j;
int error, attrsize, bitpos, aclsize, aceerr, retnotsup = 0;
int toclient = 0;
u_char *cp, namestr[NFSV4_SMALLSTR + 1];
uid_t uid;
gid_t gid;
struct timeval curtime;
error = nfsrv_getattrbits(nd, attrbitp, NULL, &retnotsup);
if (error)
return (error);
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
attrsize = fxdr_unsigned(int, *tl);
/*
* Loop around getting the setable attributes. If an unsupported
* one is found, set nd_repstat == NFSERR_ATTRNOTSUPP and return.
*/
if (retnotsup) {
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
bitpos = NFSATTRBIT_MAX;
} else {
bitpos = 0;
}
for (; bitpos < NFSATTRBIT_MAX; bitpos++) {
if (attrsum > attrsize) {
error = NFSERR_BADXDR;
goto nfsmout;
}
if (NFSISSET_ATTRBIT(attrbitp, bitpos))
switch (bitpos) {
case NFSATTRBIT_SIZE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
nvap->na_size = fxdr_hyper(tl);
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_ACL:
error = nfsrv_dissectacl(nd, aclp, &aceerr, &aclsize,
p);
if (error)
goto nfsmout;
if (aceerr && !nd->nd_repstat)
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
attrsum += aclsize;
break;
case NFSATTRBIT_ARCHIVE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (!nd->nd_repstat)
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HIDDEN:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (!nd->nd_repstat)
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MIMETYPE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
i = fxdr_unsigned(int, *tl);
error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
if (error)
goto nfsmout;
if (!nd->nd_repstat)
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
break;
case NFSATTRBIT_MODE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
nvap->na_mode = nfstov_mode(*tl);
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_OWNER:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
j = fxdr_unsigned(int, *tl);
if (j < 0)
return (NFSERR_BADXDR);
if (j > NFSV4_SMALLSTR)
cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
else
cp = namestr;
error = nfsrv_mtostr(nd, cp, j);
if (error) {
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
return (error);
}
if (!nd->nd_repstat) {
nd->nd_repstat = nfsv4_strtouid(cp,j,&uid,p);
if (!nd->nd_repstat)
nvap->na_uid = uid;
}
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
break;
case NFSATTRBIT_OWNERGROUP:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
j = fxdr_unsigned(int, *tl);
if (j < 0)
return (NFSERR_BADXDR);
if (j > NFSV4_SMALLSTR)
cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
else
cp = namestr;
error = nfsrv_mtostr(nd, cp, j);
if (error) {
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
return (error);
}
if (!nd->nd_repstat) {
nd->nd_repstat = nfsv4_strtogid(cp,j,&gid,p);
if (!nd->nd_repstat)
nvap->na_gid = gid;
}
if (j > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
break;
case NFSATTRBIT_SYSTEM:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (!nd->nd_repstat)
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESSSET:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
attrsum += NFSX_UNSIGNED;
if (fxdr_unsigned(int, *tl)==NFSV4SATTRTIME_TOCLIENT) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
fxdr_nfsv4time(tl, &nvap->na_atime);
toclient = 1;
attrsum += NFSX_V4TIME;
} else {
NFSGETTIME(&curtime);
nvap->na_atime.tv_sec = curtime.tv_sec;
nvap->na_atime.tv_nsec = curtime.tv_usec * 1000;
nvap->na_vaflags |= VA_UTIMES_NULL;
}
break;
case NFSATTRBIT_TIMEBACKUP:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
if (!nd->nd_repstat)
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMECREATE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
if (!nd->nd_repstat)
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMODIFYSET:
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
attrsum += NFSX_UNSIGNED;
if (fxdr_unsigned(int, *tl)==NFSV4SATTRTIME_TOCLIENT) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
fxdr_nfsv4time(tl, &nvap->na_mtime);
nvap->na_vaflags &= ~VA_UTIMES_NULL;
attrsum += NFSX_V4TIME;
} else {
NFSGETTIME(&curtime);
nvap->na_mtime.tv_sec = curtime.tv_sec;
nvap->na_mtime.tv_nsec = curtime.tv_usec * 1000;
if (!toclient)
nvap->na_vaflags |= VA_UTIMES_NULL;
}
break;
default:
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
/*
* set bitpos so we drop out of the loop.
*/
bitpos = NFSATTRBIT_MAX;
break;
};
}
/*
* some clients pad the attrlist, so we need to skip over the
* padding.
*/
if (attrsum > attrsize) {
error = NFSERR_BADXDR;
} else {
attrsize = NFSM_RNDUP(attrsize);
if (attrsum < attrsize)
error = nfsm_advance(nd, attrsize - attrsum, -1);
}
nfsmout:
return (error);
}
/*
* Check/setup export credentials.
*/
int
nfsd_excred(struct nfsrv_descript *nd, struct nfsexstuff *exp,
struct ucred *credanon)
{
int error = 0;
/*
* Check/setup credentials.
*/
if (nd->nd_flag & ND_GSS)
exp->nes_exflag &= ~MNT_EXPORTANON;
/*
* Check to see if the operation is allowed for this security flavor.
* RFC2623 suggests that the NFSv3 Fsinfo RPC be allowed to
* AUTH_NONE or AUTH_SYS for file systems requiring RPCSEC_GSS.
* Also, allow Secinfo, so that it can acquire the correct flavor(s).
*/
if (nfsvno_testexp(nd, exp) &&
nd->nd_procnum != NFSV4OP_SECINFO &&
nd->nd_procnum != NFSPROC_FSINFO) {
if (nd->nd_flag & ND_NFSV4)
error = NFSERR_WRONGSEC;
else
error = (NFSERR_AUTHERR | AUTH_TOOWEAK);
return (error);
}
/*
* Check to see if the file system is exported V4 only.
*/
if (NFSVNO_EXV4ONLY(exp) && !(nd->nd_flag & ND_NFSV4))
return (NFSERR_PROGNOTV4);
/*
* Now, map the user credentials.
* (Note that ND_AUTHNONE will only be set for an NFSv3
* Fsinfo RPC. If set for anything else, this code might need
* to change.)
*/
if (NFSVNO_EXPORTED(exp) &&
((!(nd->nd_flag & ND_GSS) && nd->nd_cred->cr_uid == 0) ||
NFSVNO_EXPORTANON(exp) ||
(nd->nd_flag & ND_AUTHNONE))) {
nd->nd_cred->cr_uid = credanon->cr_uid;
nd->nd_cred->cr_gid = credanon->cr_gid;
crsetgroups(nd->nd_cred, credanon->cr_ngroups,
credanon->cr_groups);
}
return (0);
}
/*
* Check exports.
*/
int
nfsvno_checkexp(struct mount *mp, struct sockaddr *nam, struct nfsexstuff *exp,
struct ucred **credp)
{
int i, error, *secflavors;
error = VFS_CHECKEXP(mp, nam, &exp->nes_exflag, credp,
&exp->nes_numsecflavor, &secflavors);
if (error) {
if (nfs_rootfhset) {
exp->nes_exflag = 0;
exp->nes_numsecflavor = 0;
error = 0;
}
} else {
/* Copy the security flavors. */
for (i = 0; i < exp->nes_numsecflavor; i++)
exp->nes_secflavors[i] = secflavors[i];
}
return (error);
}
/*
* Get a vnode for a file handle and export stuff.
*/
int
nfsvno_fhtovp(struct mount *mp, fhandle_t *fhp, struct sockaddr *nam,
int lktype, struct vnode **vpp, struct nfsexstuff *exp,
struct ucred **credp)
{
int i, error, *secflavors;
*credp = NULL;
exp->nes_numsecflavor = 0;
if (VFS_NEEDSGIANT(mp))
error = ESTALE;
else
error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
if (error != 0)
/* Make sure the server replies ESTALE to the client. */
error = ESTALE;
if (nam && !error) {
error = VFS_CHECKEXP(mp, nam, &exp->nes_exflag, credp,
&exp->nes_numsecflavor, &secflavors);
if (error) {
if (nfs_rootfhset) {
exp->nes_exflag = 0;
exp->nes_numsecflavor = 0;
error = 0;
} else {
vput(*vpp);
}
} else {
/* Copy the security flavors. */
for (i = 0; i < exp->nes_numsecflavor; i++)
exp->nes_secflavors[i] = secflavors[i];
}
}
if (error == 0 && lktype == LK_SHARED)
/*
* It would be much better to pass lktype to VFS_FHTOVP(),
* but this will have to do until VFS_FHTOVP() has a lock
* type argument like VFS_VGET().
*/
vn_lock(*vpp, LK_DOWNGRADE | LK_RETRY);
return (error);
}
/*
* Do the pathconf vnode op.
*/
int
nfsvno_pathconf(struct vnode *vp, int flag, register_t *retf,
struct ucred *cred, struct thread *p)
{
int error;
error = VOP_PATHCONF(vp, flag, retf);
return (error);
}
/*
* nfsd_fhtovp() - convert a fh to a vnode ptr
* - look up fsid in mount list (if not found ret error)
* - get vp and export rights by calling nfsvno_fhtovp()
* - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
* for AUTH_SYS
* - if mpp != NULL, return the mount point so that it can
* be used for vn_finished_write() by the caller
*/
void
nfsd_fhtovp(struct nfsrv_descript *nd, struct nfsrvfh *nfp, int lktype,
struct vnode **vpp, struct nfsexstuff *exp,
struct mount **mpp, int startwrite, struct thread *p)
{
struct mount *mp;
struct ucred *credanon;
fhandle_t *fhp;
fhp = (fhandle_t *)nfp->nfsrvfh_data;
/*
* Check for the special case of the nfsv4root_fh.
*/
mp = vfs_busyfs(&fhp->fh_fsid);
if (mpp != NULL)
*mpp = mp;
if (mp == NULL) {
*vpp = NULL;
nd->nd_repstat = ESTALE;
return;
}
if (startwrite)
vn_start_write(NULL, mpp, V_WAIT);
nd->nd_repstat = nfsvno_fhtovp(mp, fhp, nd->nd_nam, lktype, vpp, exp,
&credanon);
vfs_unbusy(mp);
/*
* For NFSv4 without a pseudo root fs, unexported file handles
* can be returned, so that Lookup works everywhere.
*/
if (!nd->nd_repstat && exp->nes_exflag == 0 &&
!(nd->nd_flag & ND_NFSV4)) {
vput(*vpp);
nd->nd_repstat = EACCES;
}
/*
* Personally, I've never seen any point in requiring a
* reserved port#, since only in the rare case where the
* clients are all boxes with secure system priviledges,
* does it provide any enhanced security, but... some people
* believe it to be useful and keep putting this code back in.
* (There is also some "security checker" out there that
* complains if the nfs server doesn't enforce this.)
* However, note the following:
* RFC3530 (NFSv4) specifies that a reserved port# not be
* required.
* RFC2623 recommends that, if a reserved port# is checked for,
* that there be a way to turn that off--> ifdef'd.
*/
#ifdef NFS_REQRSVPORT
if (!nd->nd_repstat) {
struct sockaddr_in *saddr;
struct sockaddr_in6 *saddr6;
saddr = NFSSOCKADDR(nd->nd_nam, struct sockaddr_in *);
saddr6 = NFSSOCKADDR(nd->nd_nam, struct sockaddr_in6 *);
if (!(nd->nd_flag & ND_NFSV4) &&
((saddr->sin_family == AF_INET &&
ntohs(saddr->sin_port) >= IPPORT_RESERVED) ||
(saddr6->sin6_family == AF_INET6 &&
ntohs(saddr6->sin6_port) >= IPPORT_RESERVED))) {
vput(*vpp);
nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK);
}
}
#endif /* NFS_REQRSVPORT */
/*
* Check/setup credentials.
*/
if (!nd->nd_repstat) {
nd->nd_saveduid = nd->nd_cred->cr_uid;
nd->nd_repstat = nfsd_excred(nd, exp, credanon);
if (nd->nd_repstat)
vput(*vpp);
}
if (credanon != NULL)
crfree(credanon);
if (nd->nd_repstat) {
if (startwrite)
vn_finished_write(mp);
*vpp = NULL;
if (mpp != NULL)
*mpp = NULL;
}
}
/*
* glue for fp.
*/
int
fp_getfvp(struct thread *p, int fd, struct file **fpp, struct vnode **vpp)
{
struct filedesc *fdp;
struct file *fp;
fdp = p->td_proc->p_fd;
if (fd >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[fd]) == NULL)
return (EBADF);
*fpp = fp;
return (0);
}
/*
* Called from nfssvc() to update the exports list. Just call
* vfs_export(). This has to be done, since the v4 root fake fs isn't
* in the mount list.
*/
int
nfsrv_v4rootexport(void *argp, struct ucred *cred, struct thread *p)
{
struct nfsex_args *nfsexargp = (struct nfsex_args *)argp;
int error;
struct nameidata nd;
fhandle_t fh;
error = vfs_export(&nfsv4root_mnt, &nfsexargp->export);
if ((nfsexargp->export.ex_flags & MNT_DELEXPORT) != 0)
nfs_rootfhset = 0;
else if (error == 0) {
if (nfsexargp->fspec == NULL)
return (EPERM);
/*
* If fspec != NULL, this is the v4root path.
*/
NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_USERSPACE,
nfsexargp->fspec, p);
if ((error = namei(&nd)) != 0)
return (error);
error = nfsvno_getfh(nd.ni_vp, &fh, p);
vrele(nd.ni_vp);
if (!error) {
nfs_rootfh.nfsrvfh_len = NFSX_MYFH;
NFSBCOPY((caddr_t)&fh,
nfs_rootfh.nfsrvfh_data,
sizeof (fhandle_t));
nfs_rootfhset = 1;
}
}
return (error);
}
/*
* Get the tcp socket sequence numbers we need.
* (Maybe this should be moved to the tcp sources?)
*/
int
nfsrv_getsocksndseq(struct socket *so, tcp_seq *maxp, tcp_seq *unap)
{
struct inpcb *inp;
struct tcpcb *tp;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("nfsrv_getsocksndseq: inp == NULL"));
INP_RLOCK(inp);
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
INP_RUNLOCK(inp);
return (EPIPE);
}
tp = intotcpcb(inp);
if (tp->t_state != TCPS_ESTABLISHED) {
INP_RUNLOCK(inp);
return (EPIPE);
}
*maxp = tp->snd_max;
*unap = tp->snd_una;
INP_RUNLOCK(inp);
return (0);
}
/*
* This function needs to test to see if the system is near its limit
* for memory allocation via malloc() or mget() and return True iff
* either of these resources are near their limit.
* XXX (For now, this is just a stub.)
*/
int nfsrv_testmalloclimit = 0;
int
nfsrv_mallocmget_limit(void)
{
static int printmesg = 0;
static int testval = 1;
if (nfsrv_testmalloclimit && (testval++ % 1000) == 0) {
if ((printmesg++ % 100) == 0)
printf("nfsd: malloc/mget near limit\n");
return (1);
}
return (0);
}
/*
* BSD specific initialization of a mount point.
*/
void
nfsd_mntinit(void)
{
static int inited = 0;
if (inited)
return;
inited = 1;
nfsv4root_mnt.mnt_flag = (MNT_RDONLY | MNT_EXPORTED);
TAILQ_INIT(&nfsv4root_mnt.mnt_nvnodelist);
nfsv4root_mnt.mnt_export = NULL;
TAILQ_INIT(&nfsv4root_opt);
TAILQ_INIT(&nfsv4root_newopt);
nfsv4root_mnt.mnt_opt = &nfsv4root_opt;
nfsv4root_mnt.mnt_optnew = &nfsv4root_newopt;
nfsv4root_mnt.mnt_nvnodelistsize = 0;
}
/*
* Get a vnode for a file handle, without checking exports, etc.
*/
struct vnode *
nfsvno_getvp(fhandle_t *fhp)
{
struct mount *mp;
struct vnode *vp;
int error;
mp = vfs_busyfs(&fhp->fh_fsid);
if (mp == NULL)
return (NULL);
error = VFS_FHTOVP(mp, &fhp->fh_fid, &vp);
vfs_unbusy(mp);
if (error)
return (NULL);
return (vp);
}
/*
* Do a local VOP_ADVLOCK().
*/
int
nfsvno_advlock(struct vnode *vp, int ftype, u_int64_t first,
u_int64_t end, struct thread *td)
{
int error;
struct flock fl;
u_int64_t tlen;
if (nfsrv_dolocallocks == 0)
return (0);
/* Check for VI_DOOMED here, so that VOP_ADVLOCK() isn't performed. */
if ((vp->v_iflag & VI_DOOMED) != 0)
return (EPERM);
fl.l_whence = SEEK_SET;
fl.l_type = ftype;
fl.l_start = (off_t)first;
if (end == NFS64BITSSET) {
fl.l_len = 0;
} else {
tlen = end - first;
fl.l_len = (off_t)tlen;
}
/*
* For FreeBSD8, the l_pid and l_sysid must be set to the same
* values for all calls, so that all locks will be held by the
* nfsd server. (The nfsd server handles conflicts between the
* various clients.)
* Since an NFSv4 lockowner is a ClientID plus an array of up to 1024
* bytes, so it can't be put in l_sysid.
*/
if (nfsv4_sysid == 0)
nfsv4_sysid = nlm_acquire_next_sysid();
fl.l_pid = (pid_t)0;
fl.l_sysid = (int)nfsv4_sysid;
NFSVOPUNLOCK(vp, 0, td);
if (ftype == F_UNLCK)
error = VOP_ADVLOCK(vp, (caddr_t)td->td_proc, F_UNLCK, &fl,
(F_POSIX | F_REMOTE));
else
error = VOP_ADVLOCK(vp, (caddr_t)td->td_proc, F_SETLK, &fl,
(F_POSIX | F_REMOTE));
NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY, td);
return (error);
}
/*
* Check the nfsv4 root exports.
*/
int
nfsvno_v4rootexport(struct nfsrv_descript *nd)
{
struct ucred *credanon;
int exflags, error, numsecflavor, *secflavors, i;
error = vfs_stdcheckexp(&nfsv4root_mnt, nd->nd_nam, &exflags,
&credanon, &numsecflavor, &secflavors);
if (error)
return (NFSERR_PROGUNAVAIL);
if (credanon != NULL)
crfree(credanon);
for (i = 0; i < numsecflavor; i++) {
if (secflavors[i] == AUTH_SYS)
nd->nd_flag |= ND_EXAUTHSYS;
else if (secflavors[i] == RPCSEC_GSS_KRB5)
nd->nd_flag |= ND_EXGSS;
else if (secflavors[i] == RPCSEC_GSS_KRB5I)
nd->nd_flag |= ND_EXGSSINTEGRITY;
else if (secflavors[i] == RPCSEC_GSS_KRB5P)
nd->nd_flag |= ND_EXGSSPRIVACY;
}
return (0);
}
/*
* Nfs server psuedo system call for the nfsd's
*/
/*
* MPSAFE
*/
static int
nfssvc_nfsd(struct thread *td, struct nfssvc_args *uap)
{
struct file *fp;
struct nfsd_addsock_args sockarg;
struct nfsd_nfsd_args nfsdarg;
int error;
if (uap->flag & NFSSVC_NFSDADDSOCK) {
error = copyin(uap->argp, (caddr_t)&sockarg, sizeof (sockarg));
if (error)
return (error);
if ((error = fget(td, sockarg.sock, &fp)) != 0) {
return (error);
}
if (fp->f_type != DTYPE_SOCKET) {
fdrop(fp, td);
return (EPERM);
}
error = nfsrvd_addsock(fp);
fdrop(fp, td);
} else if (uap->flag & NFSSVC_NFSDNFSD) {
if (uap->argp == NULL)
return (EINVAL);
error = copyin(uap->argp, (caddr_t)&nfsdarg,
sizeof (nfsdarg));
if (error)
return (error);
error = nfsrvd_nfsd(td, &nfsdarg);
} else {
error = nfssvc_srvcall(td, uap, td->td_ucred);
}
return (error);
}
static int
nfssvc_srvcall(struct thread *p, struct nfssvc_args *uap, struct ucred *cred)
{
struct nfsex_args export;
struct file *fp = NULL;
int stablefd, len;
struct nfsd_clid adminrevoke;
struct nfsd_dumplist dumplist;
struct nfsd_dumpclients *dumpclients;
struct nfsd_dumplocklist dumplocklist;
struct nfsd_dumplocks *dumplocks;
struct nameidata nd;
vnode_t vp;
int error = EINVAL;
struct proc *procp;
if (uap->flag & NFSSVC_PUBLICFH) {
NFSBZERO((caddr_t)&nfs_pubfh.nfsrvfh_data,
sizeof (fhandle_t));
error = copyin(uap->argp,
&nfs_pubfh.nfsrvfh_data, sizeof (fhandle_t));
if (!error)
nfs_pubfhset = 1;
} else if (uap->flag & NFSSVC_V4ROOTEXPORT) {
error = copyin(uap->argp,(caddr_t)&export,
sizeof (struct nfsex_args));
if (!error)
error = nfsrv_v4rootexport(&export, cred, p);
} else if (uap->flag & NFSSVC_NOPUBLICFH) {
nfs_pubfhset = 0;
error = 0;
} else if (uap->flag & NFSSVC_STABLERESTART) {
error = copyin(uap->argp, (caddr_t)&stablefd,
sizeof (int));
if (!error)
error = fp_getfvp(p, stablefd, &fp, &vp);
if (!error && (NFSFPFLAG(fp) & (FREAD | FWRITE)) != (FREAD | FWRITE))
error = EBADF;
if (!error && newnfs_numnfsd != 0)
error = EPERM;
if (!error) {
nfsrv_stablefirst.nsf_fp = fp;
nfsrv_setupstable(p);
}
} else if (uap->flag & NFSSVC_ADMINREVOKE) {
error = copyin(uap->argp, (caddr_t)&adminrevoke,
sizeof (struct nfsd_clid));
if (!error)
error = nfsrv_adminrevoke(&adminrevoke, p);
} else if (uap->flag & NFSSVC_DUMPCLIENTS) {
error = copyin(uap->argp, (caddr_t)&dumplist,
sizeof (struct nfsd_dumplist));
if (!error && (dumplist.ndl_size < 1 ||
dumplist.ndl_size > NFSRV_MAXDUMPLIST))
error = EPERM;
if (!error) {
len = sizeof (struct nfsd_dumpclients) * dumplist.ndl_size;
dumpclients = (struct nfsd_dumpclients *)malloc(len,
M_TEMP, M_WAITOK);
nfsrv_dumpclients(dumpclients, dumplist.ndl_size);
error = copyout(dumpclients,
CAST_USER_ADDR_T(dumplist.ndl_list), len);
free((caddr_t)dumpclients, M_TEMP);
}
} else if (uap->flag & NFSSVC_DUMPLOCKS) {
error = copyin(uap->argp, (caddr_t)&dumplocklist,
sizeof (struct nfsd_dumplocklist));
if (!error && (dumplocklist.ndllck_size < 1 ||
dumplocklist.ndllck_size > NFSRV_MAXDUMPLIST))
error = EPERM;
if (!error)
error = nfsrv_lookupfilename(&nd,
dumplocklist.ndllck_fname, p);
if (!error) {
len = sizeof (struct nfsd_dumplocks) *
dumplocklist.ndllck_size;
dumplocks = (struct nfsd_dumplocks *)malloc(len,
M_TEMP, M_WAITOK);
nfsrv_dumplocks(nd.ni_vp, dumplocks,
dumplocklist.ndllck_size, p);
vput(nd.ni_vp);
error = copyout(dumplocks,
CAST_USER_ADDR_T(dumplocklist.ndllck_list), len);
free((caddr_t)dumplocks, M_TEMP);
}
} else if (uap->flag & NFSSVC_BACKUPSTABLE) {
procp = p->td_proc;
PROC_LOCK(procp);
nfsd_master_pid = procp->p_pid;
bcopy(procp->p_comm, nfsd_master_comm, MAXCOMLEN + 1);
nfsd_master_start = procp->p_stats->p_start;
nfsd_master_proc = procp;
PROC_UNLOCK(procp);
}
return (error);
}
/*
* Check exports.
* Returns 0 if ok, 1 otherwise.
*/
int
nfsvno_testexp(struct nfsrv_descript *nd, struct nfsexstuff *exp)
{
int i;
/*
* This seems odd, but allow the case where the security flavor
* list is empty. This happens when NFSv4 is traversing non-exported
* file systems. Exported file systems should always have a non-empty
* security flavor list.
*/
if (exp->nes_numsecflavor == 0)
return (0);
for (i = 0; i < exp->nes_numsecflavor; i++) {
/*
* The tests for privacy and integrity must be first,
* since ND_GSS is set for everything but AUTH_SYS.
*/
if (exp->nes_secflavors[i] == RPCSEC_GSS_KRB5P &&
(nd->nd_flag & ND_GSSPRIVACY))
return (0);
if (exp->nes_secflavors[i] == RPCSEC_GSS_KRB5I &&
(nd->nd_flag & ND_GSSINTEGRITY))
return (0);
if (exp->nes_secflavors[i] == RPCSEC_GSS_KRB5 &&
(nd->nd_flag & ND_GSS))
return (0);
if (exp->nes_secflavors[i] == AUTH_SYS &&
(nd->nd_flag & ND_GSS) == 0)
return (0);
}
return (1);
}
/*
* Calculate a hash value for the fid in a file handle.
*/
uint32_t
nfsrv_hashfh(fhandle_t *fhp)
{
uint32_t hashval;
hashval = hash32_buf(&fhp->fh_fid, sizeof(struct fid), 0);
return (hashval);
}
/*
* Signal the userland master nfsd to backup the stable restart file.
*/
void
nfsrv_backupstable(void)
{
struct proc *procp;
if (nfsd_master_proc != NULL) {
procp = pfind(nfsd_master_pid);
/* Try to make sure it is the correct process. */
if (procp == nfsd_master_proc &&
procp->p_stats->p_start.tv_sec ==
nfsd_master_start.tv_sec &&
procp->p_stats->p_start.tv_usec ==
nfsd_master_start.tv_usec &&
strcmp(procp->p_comm, nfsd_master_comm) == 0)
psignal(procp, SIGUSR2);
else
nfsd_master_proc = NULL;
if (procp != NULL)
PROC_UNLOCK(procp);
}
}
extern int (*nfsd_call_nfsd)(struct thread *, struct nfssvc_args *);
/*
* Called once to initialize data structures...
*/
static int
nfsd_modevent(module_t mod, int type, void *data)
{
int error = 0;
static int loaded = 0;
switch (type) {
case MOD_LOAD:
if (loaded)
return (0);
newnfs_portinit();
mtx_init(&nfs_cache_mutex, "nfs_cache_mutex", NULL, MTX_DEF);
mtx_init(&nfs_v4root_mutex, "nfs_v4root_mutex", NULL, MTX_DEF);
mtx_init(&nfsv4root_mnt.mnt_mtx, "struct mount mtx", NULL,
MTX_DEF);
lockinit(&nfsv4root_mnt.mnt_explock, PVFS, "explock", 0, 0);
nfsrvd_initcache();
nfsd_init();
NFSD_LOCK();
nfsrvd_init(0);
NFSD_UNLOCK();
nfsd_mntinit();
#ifdef VV_DISABLEDELEG
vn_deleg_ops.vndeleg_recall = nfsd_recalldelegation;
vn_deleg_ops.vndeleg_disable = nfsd_disabledelegation;
#endif
nfsd_call_servertimer = nfsrv_servertimer;
nfsd_call_nfsd = nfssvc_nfsd;
loaded = 1;
break;
case MOD_UNLOAD:
if (newnfs_numnfsd != 0) {
error = EBUSY;
break;
}
#ifdef VV_DISABLEDELEG
vn_deleg_ops.vndeleg_recall = NULL;
vn_deleg_ops.vndeleg_disable = NULL;
#endif
nfsd_call_servertimer = NULL;
nfsd_call_nfsd = NULL;
/* Clean out all NFSv4 state. */
nfsrv_throwawayallstate(curthread);
/* Clean the NFS server reply cache */
nfsrvd_cleancache();
/* Free up the krpc server pool. */
if (nfsrvd_pool != NULL)
svcpool_destroy(nfsrvd_pool);
/* and get rid of the locks */
mtx_destroy(&nfs_cache_mutex);
mtx_destroy(&nfs_v4root_mutex);
mtx_destroy(&nfsv4root_mnt.mnt_mtx);
lockdestroy(&nfsv4root_mnt.mnt_explock);
loaded = 0;
break;
default:
error = EOPNOTSUPP;
break;
}
return error;
}
static moduledata_t nfsd_mod = {
"nfsd",
nfsd_modevent,
NULL,
};
DECLARE_MODULE(nfsd, nfsd_mod, SI_SUB_VFS, SI_ORDER_ANY);
/* So that loader and kldload(2) can find us, wherever we are.. */
MODULE_VERSION(nfsd, 1);
MODULE_DEPEND(nfsd, nfscommon, 1, 1, 1);
MODULE_DEPEND(nfsd, nfslock, 1, 1, 1);
MODULE_DEPEND(nfsd, nfslockd, 1, 1, 1);
MODULE_DEPEND(nfsd, krpc, 1, 1, 1);
MODULE_DEPEND(nfsd, nfssvc, 1, 1, 1);
Index: stable/8/sys/fs/nfsserver/nfs_nfsdserv.c
===================================================================
--- stable/8/sys/fs/nfsserver/nfs_nfsdserv.c (revision 221216)
+++ stable/8/sys/fs/nfsserver/nfs_nfsdserv.c (revision 221217)
@@ -1,3448 +1,3449 @@
/*-
* 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.
* 4. 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$");
/*
* 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.
*/
#ifndef APPLEKEXT
#include <fs/nfs/nfsport.h>
/* Global vars */
extern u_int32_t newnfs_false, newnfs_true;
extern enum vtype nv34tov_type[8];
extern struct timeval nfsboottime;
extern int nfs_rootfhset;
extern int nfsrv_enable_crossmntpt;
#endif /* !APPLEKEXT */
/*
* 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)
*/
APPLESTATIC int
nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
{
u_int32_t *tl;
int getret, error = 0;
struct nfsvattr nva;
u_int32_t testmode, nfsmode, supported = 0;
accmode_t deletebit;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, 1, &nva);
return (0);
}
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))) {
nd->nd_repstat = NFSERR_INVAL;
vput(vp);
return (0);
}
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_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->nd_cred, p, 1);
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);
return (0);
nfsmout:
vput(vp);
return (error);
}
/*
* nfs getattr service
*/
APPLESTATIC int
nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
struct nfsvattr nva;
fhandle_t fh;
- int at_root = 0, error = 0;
+ int at_root = 0, error = 0, supports_nfsv4acls;
struct nfsreferral *refp;
nfsattrbit_t attrbits;
struct mount *mp;
struct vnode *tvp = NULL;
struct vattr va;
uint64_t mounted_on_fileno = 0;
if (nd->nd_repstat)
return (0);
if (nd->nd_flag & ND_NFSV4) {
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
if (error) {
vput(vp);
return (error);
}
/*
* 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);
return (0);
}
if (!nd->nd_repstat)
nd->nd_repstat = nfsvno_accchk(vp,
VREAD_ATTRIBUTES,
nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
NFSACCCHK_VPISLOCKED, NULL);
}
if (!nd->nd_repstat)
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
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, nd->nd_cred, 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);
VOP_UNLOCK(vp, 0);
if (at_root != 0) {
if ((nd->nd_repstat =
vn_lock(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, at_root,
- mounted_on_fileno);
+ 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);
}
return (0);
}
/*
* nfs setattr service
*/
APPLESTATIC int
nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
{
struct nfsvattr nva, nva2;
u_int32_t *tl;
int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
struct timespec guard = { 0, 0 };
nfsattrbit_t attrbits, retbits;
nfsv4stateid_t stateid;
NFSACL_T *aclp = NULL;
if (nd->nd_repstat) {
nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
return (0);
}
#ifdef NFS4_ACL_EXTATTR_NAME
aclp = acl_alloc(M_WAITOK);
aclp->acl_cnt = 0;
#endif
NFSVNO_ATTRINIT(&nva);
NFSZERO_ATTRBIT(&retbits);
if (nd->nd_flag & ND_NFSV4) {
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);
}
error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
if (error)
goto nfsmout;
preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p, 1);
if (!nd->nd_repstat)
nd->nd_repstat = preat_ret;
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);
return (0);
}
} 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(vnode_mount(vp)) & 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);
}
}
if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
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_MODE)) {
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)
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
}
#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->nd_cred, p, 1);
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);
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);
}
return (error);
}
/*
* nfs lookup rpc
* (Also performs lookup parent for v4)
*/
APPLESTATIC int
nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
__unused struct nfsexstuff *exp)
{
struct nameidata named;
vnode_t vp, dirp = NULL;
int error, dattr_ret = 1;
struct nfsvattr nva, dattr;
char *bufp;
u_long *hashp;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, dattr_ret, &dattr);
return (0);
}
/*
* 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);
return (0);
}
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);
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 (nd->nd_repstat) {
if (dirp) {
if (nd->nd_flag & ND_NFSV3)
dattr_ret = nfsvno_getattr(dirp, &dattr,
nd->nd_cred, p, 0);
vrele(dirp);
}
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, dattr_ret, &dattr);
return (0);
}
if (named.ni_startdir)
vrele(named.ni_startdir);
nfsvno_relpathbuf(&named);
vp = named.ni_vp;
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->nd_cred, p, 1);
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->nd_cred,
p, 0);
vrele(dirp);
}
if (nd->nd_repstat) {
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, dattr_ret, &dattr);
return (0);
}
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);
}
return (0);
}
/*
* nfs readlink service
*/
APPLESTATIC int
nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
mbuf_t mp = NULL, mpend = NULL;
int getret = 1, len;
struct nfsvattr nva;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &nva);
return (0);
}
if (vnode_vtype(vp) != VLNK) {
if (nd->nd_flag & ND_NFSV2)
nd->nd_repstat = ENXIO;
else
nd->nd_repstat = EINVAL;
}
if (!nd->nd_repstat)
nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p,
&mp, &mpend, &len);
if (nd->nd_flag & ND_NFSV3)
getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
vput(vp);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &nva);
if (nd->nd_repstat)
return (0);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(len);
mbuf_setnext(nd->nd_mb, mp);
nd->nd_mb = mpend;
nd->nd_bpos = NFSMTOD(mpend, caddr_t) + mbuf_len(mpend);
return (0);
}
/*
* nfs read service
*/
APPLESTATIC int
nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
{
u_int32_t *tl;
int error = 0, cnt, len, getret = 1, reqlen, eof = 0;
mbuf_t 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;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &nva);
return (0);
}
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;
}
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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
stp->ls_stateid.other[2] = *tl++;
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->nd_cred, p, 1);
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);
}
if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
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);
return (0);
}
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;
else
cnt = reqlen;
len = NFSM_RNDUP(cnt);
m3 = NULL;
if (cnt > 0) {
nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p,
&m3, &m2);
if (!(nd->nd_flag & ND_NFSV4)) {
getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
if (!nd->nd_repstat)
nd->nd_repstat = getret;
}
if (nd->nd_repstat) {
vput(vp);
if (m3)
mbuf_freem(m3);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &nva);
return (0);
}
}
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 (len < reqlen || eof)
*tl++ = newnfs_true;
else
*tl++ = newnfs_false;
}
*tl = txdr_unsigned(cnt);
if (m3) {
mbuf_setnext(nd->nd_mb, m3);
nd->nd_mb = m2;
nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
}
return (0);
nfsmout:
vput(vp);
return (error);
}
/*
* nfs write service
*/
APPLESTATIC int
nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
{
int i, cnt;
u_int32_t *tl;
mbuf_t mp;
struct nfsvattr nva, forat;
int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
int stable = NFSWRITE_FILESYNC;
off_t off;
struct nfsstate st, *stp = &st;
struct nfslock lo, *lop = &lo;
nfsv4stateid_t stateid;
nfsquad_t clientid;
if (nd->nd_repstat) {
nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
return (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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
stp->ls_stateid.other[2] = *tl++;
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;
}
/*
* Loop through the mbuf chain, counting how many mbufs are a
* part of this write operation, so the iovec size is known.
*/
cnt = 0;
mp = nd->nd_md;
i = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - nd->nd_dpos;
while (len > 0) {
if (i > 0) {
len -= i;
cnt++;
}
mp = mbuf_next(mp);
if (!mp) {
if (len > 0) {
error = EBADRPC;
goto nfsmout;
}
} else
i = mbuf_len(mp);
}
if (retlen > NFS_MAXDATA || 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;
}
forat_ret = nfsvno_getattr(vp, &forat, nd->nd_cred, p, 1);
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);
if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
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);
return (0);
}
/*
* 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, cnt, stable,
nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
if (error)
panic("nfsrv_write mbuf");
}
if (nd->nd_flag & ND_NFSV4)
aftat_ret = 0;
else
aftat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
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)
return (0);
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(retlen);
if (stable == NFSWRITE_UNSTABLE)
*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);
return (0);
nfsmout:
vput(vp);
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.
*/
APPLESTATIC int
nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, NFSPROC_T *p, 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 };
if (nd->nd_repstat) {
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
LOCKPARENT | LOCKLEAF | SAVESTART);
nfsvno_setpathbuf(&named, &bufp, &hashp);
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
if (error) {
vput(dp);
nfsvno_relpathbuf(&named);
return (error);
}
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, &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->nd_cred,
p, 1);
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
&diraft);
}
vput(dp);
return (0);
}
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->nd_cred,
p, 0);
}
}
if (nd->nd_repstat) {
if (nd->nd_flag & ND_NFSV3)
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
&diraft);
if (dirp)
vrele(dirp);
return (0);
}
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, p, 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->nd_cred,
p, 1);
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->nd_cred, p, 0);
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);
}
return (0);
nfsmout:
vput(dp);
nfsvno_relpathbuf(&named);
return (error);
}
/*
* nfs v3 mknod service (and v4 create)
*/
APPLESTATIC int
nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
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;
NFSVNO_ATTRINIT(&nva);
cnflags = (LOCKPARENT | SAVESTART);
if (nd->nd_repstat) {
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
#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) {
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
return (error);
}
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
return (0);
};
}
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags);
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
nfsvno_relpathbuf(&named);
if (pathcp)
FREE(pathcp, M_TEMP);
return (error);
}
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, &nva, &attrbits, aclp, p);
if (error) {
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
nfsvno_relpathbuf(&named);
if (pathcp)
FREE(pathcp, M_TEMP);
return (error);
}
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->nd_cred, p, 0);
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);
return (0);
}
/*
* 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->nd_cred, p, 0);
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);
return (0);
}
if (dirp)
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
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
return (0);
} 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);
return (0);
}
}
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->nd_cred,
p, 1);
if (vpp != NULL && nd->nd_repstat == 0) {
VOP_UNLOCK(vp, 0);
*vpp = vp;
} else
vput(vp);
}
diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
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
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);
return (error);
}
/*
* nfs remove service
*/
APPLESTATIC int
nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
{
struct nameidata named;
u_int32_t *tl;
int error, dirfor_ret = 1, diraft_ret = 1;
vnode_t dirp = NULL;
struct nfsvattr dirfor, diraft;
char *bufp;
u_long *hashp;
if (nd->nd_repstat) {
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
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);
return (error);
}
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->nd_cred, p, 0);
} 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->nd_cred,
p, 0);
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);
}
}
return (0);
}
/*
* nfs rename service
*/
APPLESTATIC int
nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
vnode_t dp, vnode_t todp, NFSPROC_T *p, struct nfsexstuff *exp,
struct nfsexstuff *toexp)
{
u_int32_t *tl;
int error, 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;
if (nd->nd_repstat) {
nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
return (0);
}
if (!(nd->nd_flag & ND_NFSV2))
fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd->nd_cred, p, 1);
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);
return (error);
}
if (nd->nd_flag & ND_NFSV4) {
tdp = todp;
tnes = *toexp;
tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, p, 0);
} else {
error = nfsrv_mtofh(nd, &tfh);
if (error) {
vput(dp);
/* todp is always NULL except NFSv4 */
nfsvno_relpathbuf(&fromnd);
return (error);
}
nd->nd_cred->cr_uid = nd->nd_saveduid;
nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL, 0, p);
if (tdp) {
tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
p, 1);
NFSVOPUNLOCK(tdp, 0, p);
}
}
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);
vput(dp);
nfsvno_relpathbuf(&fromnd);
nfsvno_relpathbuf(&tond);
return (error);
}
}
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);
vput(dp);
nfsvno_relpathbuf(&fromnd);
nfsvno_relpathbuf(&tond);
return (0);
}
/*
* Done parsing, now down to business.
*/
nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 1, 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);
return (0);
}
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->nd_cred, p,
0);
if (tdirp)
tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd->nd_cred, p,
0);
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);
}
return (0);
}
/*
* nfs link service
*/
APPLESTATIC int
nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
vnode_t vp, vnode_t tovp, NFSPROC_T *p, 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;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
NFSVOPUNLOCK(vp, 0, p);
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);
} else if (vnode_vtype(vp) == VLNK) {
if (nd->nd_flag & ND_NFSV2)
nd->nd_repstat = NFSERR_INVAL;
else
nd->nd_repstat = NFSERR_NOTSUPP;
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 */
return (error);
}
nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0,
p);
if (dp)
NFSVOPUNLOCK(dp, 0, p);
}
}
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
LOCKPARENT | SAVENAME);
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);
return (error);
}
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->nd_cred, p, 0);
}
}
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->nd_cred, p, 0);
if (dirp) {
diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
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);
}
return (0);
}
/*
* nfs symbolic link service
*/
APPLESTATIC int
nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
struct nfsexstuff *exp)
{
struct nfsvattr nva, dirfor, diraft;
struct nameidata named;
int error, dirfor_ret = 1, diraft_ret = 1, pathlen;
vnode_t dirp = NULL;
char *bufp, *pathcp = NULL;
u_long *hashp;
if (nd->nd_repstat) {
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
if (vpp)
*vpp = NULL;
NFSVNO_ATTRINIT(&nva);
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
LOCKPARENT | SAVESTART);
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);
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 (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->nd_cred,
p, 0);
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->nd_cred, p, 0);
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);
}
return (0);
}
/*
* 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->nd_cred, p, 1);
}
if (vpp != NULL && nd->nd_repstat == 0) {
VOP_UNLOCK(ndp->ni_vp, 0);
*vpp = ndp->ni_vp;
} else
vput(ndp->ni_vp);
}
if (dirp) {
*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
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);
}
}
/*
* nfs mkdir service
*/
APPLESTATIC int
nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
struct nfsexstuff *exp)
{
struct nfsvattr nva, dirfor, diraft;
struct nameidata named;
u_int32_t *tl;
int error, dirfor_ret = 1, diraft_ret = 1;
vnode_t dirp = NULL;
char *bufp;
u_long *hashp;
if (nd->nd_repstat) {
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
LOCKPARENT | SAVENAME);
nfsvno_setpathbuf(&named, &bufp, &hashp);
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
if (error) {
vrele(dp);
nfsvno_relpathbuf(&named);
return (error);
}
if (!nd->nd_repstat) {
NFSVNO_ATTRINIT(&nva);
if (nd->nd_flag & ND_NFSV3) {
error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
if (error) {
vrele(dp);
nfsvno_relpathbuf(&named);
return (error);
}
} 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->nd_cred,
p, 0);
vrele(dirp);
}
if (nd->nd_flag & ND_NFSV3)
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
&diraft);
return (0);
}
if (dirp != NULL)
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
/*
* 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);
}
return (0);
nfsmout:
vrele(dp);
nfsvno_relpathbuf(&named);
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->nd_cred,
p, 1);
if (vpp && !nd->nd_repstat) {
NFSVOPUNLOCK(vp, 0, p);
*vpp = vp;
} else {
vput(vp);
}
}
if (dirp) {
*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
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);
}
}
/*
* nfs commit service
*/
APPLESTATIC int
nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __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;
if (nd->nd_repstat) {
nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
return (0);
}
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->nd_cred, p, 1);
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->nd_cred, p, 1);
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);
}
return (0);
nfsmout:
vput(vp);
return (error);
}
/*
* nfs statfs service
*/
APPLESTATIC int
nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
struct statfs *sf;
u_int32_t *tl;
int getret = 1;
struct nfsvattr at;
struct statfs sfs;
u_quad_t tval;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
return (0);
}
sf = &sfs;
nd->nd_repstat = nfsvno_statfs(vp, sf);
getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
vput(vp);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
if (nd->nd_repstat)
return (0);
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;
}
return (0);
}
/*
* nfs fsinfo service
*/
APPLESTATIC int
nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
struct nfsfsinfo fs;
int getret = 1;
struct nfsvattr at;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
return (0);
}
getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
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);
return (0);
}
/*
* nfs pathconf service
*/
APPLESTATIC int
nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
struct nfsv3_pathconf *pc;
int getret = 1;
register_t linkmax, namemax, chownres, notrunc;
struct nfsvattr at;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
return (0);
}
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->nd_cred, p, 1);
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;
}
return (0);
}
/*
* nfsv4 lock service
*/
APPLESTATIC int
nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, 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;
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;
}
MALLOC(stp, struct nfsstate *, 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);
stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
clientid.lval[0] = *tl++;
clientid.lval[1] = *tl++;
if (nd->nd_flag & ND_IMPLIEDCLID) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
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);
MALLOC(stp, struct nfsstate *, 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);
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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
}
MALLOC(lop, struct nfslock *, 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((caddr_t)lop, M_NFSDLOCK);
if (stp)
FREE((caddr_t)stp, M_NFSDSTATE);
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);
} 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);
return (0);
nfsmout:
vput(vp);
if (stp)
free((caddr_t)stp, M_NFSDSTATE);
return (error);
}
/*
* nfsv4 lock test service
*/
APPLESTATIC int
nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, 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;
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;
}
MALLOC(stp, struct nfsstate *, 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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
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 (stp)
FREE((caddr_t)stp, M_NFSDSTATE);
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);
return (0);
nfsmout:
vput(vp);
if (stp)
free((caddr_t)stp, M_NFSDSTATE);
return (error);
}
/*
* nfsv4 unlock service
*/
APPLESTATIC int
nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, 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;
NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
M_NFSDSTATE, M_WAITOK);
MALLOC(lop, struct nfslock *, 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);
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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
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((caddr_t)stp, M_NFSDSTATE);
if (lop)
free((caddr_t)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);
return (error);
}
/*
* nfsv4 open service
*/
APPLESTATIC int
nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
struct nfsexstuff *exp)
{
u_int32_t *tl;
int i;
struct nfsstate *stp = NULL;
int error = 0, create, claim, exclusive_flag = 0;
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;
#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;
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
return (0);
}
MALLOC(stp, struct nfsstate *, 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++);
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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
if (error) {
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
FREE((caddr_t)stp, M_NFSDSTATE);
return (error);
}
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->nd_cred, p, 0);
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, &nva, &attrbits, aclp, p);
if (error) {
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
FREE((caddr_t)stp, M_NFSDSTATE);
return (error);
}
/*
* 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;
default:
nd->nd_repstat = NFSERR_BADXDR;
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
FREE((caddr_t)stp, M_NFSDSTATE);
return (0);
};
} else if (create != NFSV4OPEN_NOCREATE) {
nd->nd_repstat = NFSERR_BADXDR;
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
FREE((caddr_t)stp, M_NFSDSTATE);
return (0);
}
/*
* 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) {
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) {
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);
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
FREE((caddr_t)stp, M_NFSDSTATE);
return (0);
}
if (create == NFSV4OPEN_CREATE)
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
LOCKPARENT | LOCKLEAF | SAVESTART);
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((caddr_t)stp, M_NFSDSTATE);
nfsvno_relpathbuf(&named);
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;
};
}
nfsvno_open(nd, &named, clientid, &stateid, stp,
&exclusive_flag, &nva, cverf, create, aclp, &attrbits,
nd->nd_cred, p, exp, &vp);
} else 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;
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
FREE((caddr_t)stp, M_NFSDSTATE);
return (0);
};
stp->ls_flags |= NFSLCK_RECLAIM;
vp = dp;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if ((vp->v_iflag & VI_DOOMED) == 0)
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;
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
FREE((caddr_t)stp, M_NFSDSTATE);
return (0);
}
/*
* Do basic access checking.
*/
if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
if (vnode_vtype(vp) == VDIR)
nd->nd_repstat = NFSERR_ISDIR;
else if (vnode_vtype(vp) == VLNK)
nd->nd_repstat = NFSERR_SYMLINK;
else
nd->nd_repstat = NFSERR_INVAL;
}
if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
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);
}
if (!nd->nd_repstat) {
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
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, 0, p);
if (stp)
FREE((caddr_t)stp, M_NFSDSTATE);
if (!nd->nd_repstat && dirp)
nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p,
0);
if (!nd->nd_repstat) {
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
*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
return (0);
nfsmout:
vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
acl_free(aclp);
#endif
if (stp)
FREE((caddr_t)stp, M_NFSDSTATE);
return (error);
}
/*
* nfsv4 close service
*/
APPLESTATIC int
nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
struct nfsstate st, *stp = &st;
int error = 0;
nfsv4stateid_t stateid;
nfsquad_t clientid;
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);
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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
vput(vp);
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);
}
return (0);
nfsmout:
vput(vp);
return (error);
}
/*
* nfsv4 delegpurge service
*/
APPLESTATIC int
nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
__unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
int error = 0;
nfsquad_t clientid;
if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
nd->nd_repstat = NFSERR_WRONGSEC;
return (0);
}
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
clientid.lval[0] = *tl++;
clientid.lval[1] = *tl;
if (nd->nd_flag & ND_IMPLIEDCLID) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
nd->nd_repstat = nfsrv_delegupdate(clientid, NULL, NULL,
NFSV4OP_DELEGPURGE, nd->nd_cred, p);
nfsmout:
return (error);
}
/*
* nfsv4 delegreturn service
*/
APPLESTATIC int
nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
int error = 0;
nfsv4stateid_t stateid;
nfsquad_t clientid;
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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
nd->nd_repstat = nfsrv_delegupdate(clientid, &stateid, vp,
NFSV4OP_DELEGRETURN, nd->nd_cred, p);
nfsmout:
vput(vp);
return (error);
}
/*
* nfsv4 get file handle service
*/
APPLESTATIC int
nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
fhandle_t fh;
nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
vput(vp);
if (!nd->nd_repstat)
(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
return (0);
}
/*
* nfsv4 open confirm service
*/
APPLESTATIC int
nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
struct nfsstate st, *stp = &st;
int error = 0;
nfsv4stateid_t stateid;
nfsquad_t clientid;
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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
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);
return (error);
}
/*
* nfsv4 open downgrade service
*/
APPLESTATIC int
nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
int i;
struct nfsstate st, *stp = &st;
int error = 0;
nfsv4stateid_t stateid;
nfsquad_t clientid;
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);
stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
i = fxdr_unsigned(int, *tl++);
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_BADXDR;
};
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_BADXDR;
};
clientid.lval[0] = stp->ls_stateid.other[0];
clientid.lval[1] = stp->ls_stateid.other[1];
if (nd->nd_flag & ND_IMPLIEDCLID) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
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);
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);
return (error);
}
/*
* nfsv4 renew lease service
*/
APPLESTATIC int
nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
__unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
int error = 0;
nfsquad_t clientid;
if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
nd->nd_repstat = NFSERR_WRONGSEC;
return (0);
}
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
clientid.lval[0] = *tl++;
clientid.lval[1] = *tl;
if (nd->nd_flag & ND_IMPLIEDCLID) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
nd->nd_flag |= ND_IMPLIEDCLID;
nd->nd_clientid.qval = clientid.qval;
}
nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
NULL, (nfsquad_t)((u_quad_t)0), nd, p);
nfsmout:
return (error);
}
/*
* nfsv4 security info service
*/
APPLESTATIC int
nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
vnode_t dp, NFSPROC_T *p, 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, savflag, i;
char *bufp;
u_long *hashp;
/*
* 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);
return (error);
}
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)
return (0);
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) {
nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0, p);
if (vp)
vput(vp);
}
nd->nd_flag = savflag;
if (nd->nd_repstat)
return (0);
/*
* Finally have the export flags for name, so we can create
* the security info.
*/
len = 0;
NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
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);
return (0);
}
/*
* nfsv4 set client id service
*/
APPLESTATIC int
nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
__unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
int i;
int error = 0, idlen;
struct nfsclient *clp = NULL;
struct sockaddr_in *rad;
u_char *verf, *ucp, *ucp2, addrbuf[24];
nfsquad_t clientid, confirm;
if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
nd->nd_repstat = NFSERR_WRONGSEC;
return (0);
}
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;
return (error);
}
idlen = i;
if (nd->nd_flag & ND_GSS)
i += nd->nd_princlen;
MALLOC(clp, struct nfsclient *, sizeof (struct nfsclient) + i,
M_NFSDCLIENT, M_WAITOK);
NFSBZERO((caddr_t)clp, sizeof (struct nfsclient) + i);
NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
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;
}
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) {
if (clp->lc_flags & LCL_TCPCALLBACK)
(void) nfsm_strtom(nd, "tcp", 3);
else
(void) nfsm_strtom(nd, "udp", 3);
rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *);
ucp = (u_char *)&rad->sin_addr.s_addr;
ucp2 = (u_char *)&rad->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);
(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
}
if (clp) {
NFSSOCKADDRFREE(clp->lc_req.nr_nam);
NFSFREEMUTEX(&clp->lc_req.nr_mtx);
free((caddr_t)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];
}
return (0);
nfsmout:
if (clp) {
NFSSOCKADDRFREE(clp->lc_req.nr_nam);
NFSFREEMUTEX(&clp->lc_req.nr_mtx);
free((caddr_t)clp, M_NFSDCLIENT);
}
return (error);
}
/*
* nfsv4 set client id confirm service
*/
APPLESTATIC int
nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
__unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
__unused struct nfsexstuff *exp)
{
u_int32_t *tl;
int error = 0;
nfsquad_t clientid, confirm;
if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
nd->nd_repstat = NFSERR_WRONGSEC;
return (0);
}
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, confirm, nd, p);
nfsmout:
return (error);
}
/*
* nfsv4 verify service
*/
APPLESTATIC int
nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
int error = 0, ret, fhsize = NFSX_MYFH;
struct nfsvattr nva;
struct statfs sf;
struct nfsfsinfo fs;
fhandle_t fh;
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
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);
return (error);
}
/*
* nfs openattr rpc
*/
APPLESTATIC int
nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
__unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
int error = 0, createdir;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
createdir = fxdr_unsigned(int, *tl);
nd->nd_repstat = NFSERR_NOTSUPP;
nfsmout:
vrele(dp);
return (error);
}
/*
* nfsv4 release lock owner service
*/
APPLESTATIC int
nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
__unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
u_int32_t *tl;
struct nfsstate *stp = NULL;
int error = 0, len;
nfsquad_t clientid;
if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
nd->nd_repstat = NFSERR_WRONGSEC;
return (0);
}
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;
return (0);
}
MALLOC(stp, struct nfsstate *, 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) {
if (nd->nd_clientid.qval != clientid.qval)
printf("EEK! multiple clids\n");
} else {
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((caddr_t)stp, M_NFSDSTATE);
return (0);
nfsmout:
if (stp)
free((caddr_t)stp, M_NFSDSTATE);
return (error);
}
Index: stable/8/sys
===================================================================
--- stable/8/sys (revision 221216)
+++ stable/8/sys (revision 221217)
Property changes on: stable/8/sys
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys:r220648
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Jul 23, 6:47 AM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35352195
Default Alt Text
(632 KB)
Attached To
Mode
rS FreeBSD src repository - subversion
Attached
Detach File
Event Timeline
Log In to Comment