Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F166187267
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
399 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 221211)
+++ stable/8/sys/amd64/include/xen (revision 221212)
Property changes on: stable/8/sys/amd64/include/xen
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/amd64/include/xen:r200069
Index: stable/8/sys/cddl/contrib/opensolaris
===================================================================
--- stable/8/sys/cddl/contrib/opensolaris (revision 221211)
+++ stable/8/sys/cddl/contrib/opensolaris (revision 221212)
Property changes on: stable/8/sys/cddl/contrib/opensolaris
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/cddl/contrib/opensolaris:r200069
Index: stable/8/sys/contrib/dev/acpica
===================================================================
--- stable/8/sys/contrib/dev/acpica (revision 221211)
+++ stable/8/sys/contrib/dev/acpica (revision 221212)
Property changes on: stable/8/sys/contrib/dev/acpica
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/contrib/dev/acpica:r200069
Index: stable/8/sys/contrib/pf
===================================================================
--- stable/8/sys/contrib/pf (revision 221211)
+++ stable/8/sys/contrib/pf (revision 221212)
Property changes on: stable/8/sys/contrib/pf
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys/contrib/pf:r200069
Index: stable/8/sys/fs/nfs/nfs_commonacl.c
===================================================================
--- stable/8/sys/fs/nfs/nfs_commonacl.c (revision 221211)
+++ stable/8/sys/fs/nfs/nfs_commonacl.c (revision 221212)
@@ -1,682 +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);
-#if defined(NFS4_ACL_EXTATTR_NAME)
/*
* 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);
}
-#else
-/*
- * 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;
- 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;
- 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;
- flag &= ~NFSV4ACE_IDENTIFIERGROUP;
- }
- } else if (len == 9 && !NFSBCMP(name, "EVERYONE@", 9)) {
- acep->ae_tag = ACL_OTHER;
- acep->ae_id = ACL_UNDEFINED_ID;
- gotid = 1;
- }
- if (!gotid) {
- if (flag & NFSV4ACE_IDENTIFIERGROUP) {
- flag &= ~NFSV4ACE_IDENTIFIERGROUP;
- acep->ae_tag = ACL_GROUP;
- aceerr = nfsv4_strtogid(name, len, &gid, p);
- if (!aceerr)
- acep->ae_id = (uid_t)gid;
- } else {
- acep->ae_tag = ACL_USER;
- aceerr = nfsv4_strtouid(name, len, &uid, p);
- if (!aceerr)
- acep->ae_id = uid;
- }
- }
- if (len > NFSV4_SMALLSTR)
- free(name, M_NFSSTRING);
-
- /*
- * Now, check for unsupported types or flag bits.
- */
- if (!aceerr && ((acetype != NFSV4ACE_ALLOWEDTYPE &&
- acetype != NFSV4ACE_AUDITTYPE && acetype != NFSV4ACE_ALARMTYPE
- && acetype != NFSV4ACE_DENIEDTYPE) || flag))
- aceerr = NFSERR_ATTRNOTSUPP;
-
- /*
- * And turn the mask into perm bits.
- */
- if (!aceerr)
- 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 (acetype != NFSV4ACE_ALLOWEDTYPE && acetype != NFSV4ACE_DENIEDTYPE){
- if (mask & ~NFSV4ACE_AUDITMASK)
- return (NFSERR_ATTRNOTSUPP);
- }
- if (mask & NFSV4ACE_DELETE) {
- return (NFSERR_ATTRNOTSUPP);
- }
- if (acetype == NFSV4ACE_DENIEDTYPE) {
- if (mask & NFSV4ACE_ALLFILESMASK) {
- return (NFSERR_ATTRNOTSUPP);
- }
- if (owner) {
- if (mask & NFSV4ACE_OWNERMASK) {
- return (NFSERR_ATTRNOTSUPP);
- }
- } else {
- if ((mask & NFSV4ACE_OWNERMASK) != NFSV4ACE_OWNERMASK) {
- return (NFSERR_ATTRNOTSUPP);
- }
- mask &= ~NFSV4ACE_OWNERMASK;
- }
- } else if (acetype == NFSV4ACE_ALLOWEDTYPE) {
- if ((mask & NFSV4ACE_ALLFILESMASK) != NFSV4ACE_ALLFILESMASK) {
- return (NFSERR_ATTRNOTSUPP);
- }
- mask &= ~NFSV4ACE_ALLFILESMASK;
- if (owner) {
- if ((mask & NFSV4ACE_OWNERMASK) != NFSV4ACE_OWNERMASK) {
- return (NFSERR_ATTRNOTSUPP);
- }
- mask &= ~NFSV4ACE_OWNERMASK;
- } else if (mask & NFSV4ACE_OWNERMASK) {
- return (NFSERR_ATTRNOTSUPP);
- }
- }
- if (type == VDIR) {
- if ((mask & NFSV4ACE_DIRREADMASK) == NFSV4ACE_DIRREADMASK) {
- perm |= ACL_READ;
- mask &= ~NFSV4ACE_DIRREADMASK;
- }
- if ((mask & NFSV4ACE_DIRWRITEMASK) == NFSV4ACE_DIRWRITEMASK) {
- perm |= ACL_WRITE;
- mask &= ~NFSV4ACE_DIRWRITEMASK;
- }
- if ((mask & NFSV4ACE_DIREXECUTEMASK)==NFSV4ACE_DIREXECUTEMASK){
- perm |= ACL_EXECUTE;
- mask &= ~NFSV4ACE_DIREXECUTEMASK;
- }
- } else {
- if (acetype == NFSV4ACE_DENIEDTYPE &&
- (mask & NFSV4ACE_SYNCHRONIZE)) {
- return (NFSERR_ATTRNOTSUPP);
- }
- mask &= ~(NFSV4ACE_SYNCHRONIZE | NFSV4ACE_DELETECHILD);
- if ((mask & NFSV4ACE_READMASK) == NFSV4ACE_READMASK) {
- perm |= ACL_READ;
- mask &= ~NFSV4ACE_READMASK;
- }
- if ((mask & NFSV4ACE_WRITEMASK) == NFSV4ACE_WRITEMASK) {
- perm |= ACL_WRITE;
- mask &= ~NFSV4ACE_WRITEMASK;
- }
- if ((mask & NFSV4ACE_EXECUTEMASK) == NFSV4ACE_EXECUTEMASK) {
- perm |= ACL_EXECUTE;
- mask &= ~NFSV4ACE_EXECUTEMASK;
- }
- }
- if (mask) {
- return (NFSERR_ATTRNOTSUPP);
- }
- *permp = perm;
- return (0);
-}
-#endif /* !NFS4_ACL_EXTATTR_NAME */
-
-#ifdef NFS4_ACL_EXTATTR_NAME
/* 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)))
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);
}
-
-#endif /* NFS4_ACL_EXTATTR_NAME */
Index: stable/8/sys/fs/nfs/nfs_commonport.c
===================================================================
--- stable/8/sys/fs/nfs/nfs_commonport.c (revision 221211)
+++ stable/8/sys/fs/nfs/nfs_commonport.c (revision 221212)
@@ -1,521 +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);
}
-#ifdef NFS4_ACL_EXTATTR_NAME
/*
* Determine if the file system supports NFSv4 ACLs.
* Return 1 if it does, 0 otherwise.
*/
int
nfs_supportsnfsv4acls(struct mount *mp)
{
if (mp->mnt_stat.f_fstypename == NULL)
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 */
return (1);
}
return (0);
}
-#endif /* NFS4_ACL_EXTATTR_NAME */
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 221211)
+++ stable/8/sys/fs/nfs/nfs_commonsubs.c (revision 221212)
@@ -1,3434 +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;
-#ifdef NFS4_ACL_EXTATTR_NAME
if (aclp)
aclp->acl_cnt = 0;
-#endif
/*
* 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);
-#ifdef NFS4_ACL_EXTATTR_NAME
if (acecnt > ACL_MAX_ENTRIES)
aceerr = 1;
-#endif
if (nfsrv_useacl == 0)
aceerr = 1;
for (i = 0; i < acecnt; i++) {
-#ifdef NFS4_ACL_EXTATTR_NAME
if (aclp && !aceerr)
error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
&aceerr, &acesize, p);
else
-#endif
error = nfsrv_skipace(nd, &acesize);
if (error)
return (error);
aclsize += acesize;
}
-#ifdef NFS4_ACL_EXTATTR_NAME
if (aclp && !aceerr)
aclp->acl_cnt = acecnt;
-#endif
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)) {
-#ifdef NFS4_ACL_EXTATTR_NAME
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
-#endif
- {
+ } 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, 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 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;
long fid;
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);
-#ifdef NFS4_ACL_EXTATTR_NAME
naclp = acl_alloc(M_WAITOK);
-#endif
aclp = naclp;
}
nfsvno_getfs(&fsinf, isdgram);
#ifndef APPLE
/*
* Get the VFS_STATFS(), since some attributes need them.
*/
if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
error = VFS_STATFS(vnode_mount(vp), &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)
-#ifdef NFS4_ACL_EXTATTR_NAME
- && (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
- !NFSHASNFS4ACL(vnode_mount(vp))))
-#endif
- ) {
+ if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
+ (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
+ !NFSHASNFS4ACL(vnode_mount(vp))))) {
NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
}
if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
-#ifdef NFS4_ACL_EXTATTR_NAME
if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
!NFSHASNFS4ACL(vnode_mount(vp)))) {
-#endif
NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
-#ifdef NFS4_ACL_EXTATTR_NAME
} 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);
}
}
-#endif
}
/*
* 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);
-#ifdef NFS4_ACL_EXTATTR_NAME
if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
- && !NFSHASNFS4ACL(vnode_mount(vp))))
-#endif
- {
+ && !NFSHASNFS4ACL(vnode_mount(vp)))) {
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(vfs_statfs(vnode_mount(vp))->f_fsid.val[0]);
*tl++ = 0;
*tl=txdr_unsigned(vfs_statfs(vnode_mount(vp))->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.)
*/
-#ifdef NFS4_ACL_EXTATTR_NAME
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;
-#endif
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(vnode_mount(vp),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(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 */
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(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 */
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(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 */
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);
*tl++ = 0;
if (nfsrv_atroot(vp, &fid))
*tl = txdr_unsigned(fid);
else
*tl = txdr_unsigned(vap->va_fileid);
retnum += NFSX_HYPER;
break;
default:
printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
};
}
}
-#ifdef NFS4_ACL_EXTATTR_NAME
if (naclp != NULL)
acl_free(naclp);
-#endif
*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 221211)
+++ stable/8/sys/fs/nfs/nfs_var.h (revision 221212)
@@ -1,602 +1,600 @@
/*-
* 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 *, vnode_t, NFSACL_T *,
struct vattr *, fhandle_t *, int, nfsattrbit_t *,
struct ucred *, NFSPROC_T *, int, int);
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 *);
/* nfs_commonacl.c */
int nfsrv_dissectace(struct nfsrv_descript *, struct acl_entry *,
int *, int *, NFSPROC_T *);
-#ifdef NFS4_ACL_EXTATTR_NAME
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 *);
-#endif
/* 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 *, vnode_t,
struct nfsvattr *, fhandle_t *, int, nfsattrbit_t *,
struct ucred *, NFSPROC_T *, int, int);
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 221211)
+++ stable/8/sys/fs/nfs/nfsport.h (revision 221212)
@@ -1,918 +1,914 @@
/*-
* 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
-#ifdef NFS4_ACL_EXTATTR_NAME
#define NFSACL_T struct acl
-#else
-#define NFSACL_T void
-#endif
/*
* 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_clrpcops.c
===================================================================
--- stable/8/sys/fs/nfsclient/nfs_clrpcops.c (revision 221211)
+++ stable/8/sys/fs/nfsclient/nfs_clrpcops.c (revision 221212)
@@ -1,4197 +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);
-#ifdef NFS4_ACL_EXTATTR_NAME
static int nfsrpc_setaclrpc(vnode_t, struct ucred *, NFSPROC_T *,
struct acl *, nfsv4stateid_t *, void *);
-#endif
/*
* 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);
-#ifdef NFS4_ACL_EXTATTR_NAME
else
error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
stuff);
-#else
- else
- error = EOPNOTSUPP;
-#endif
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);
}
-#ifdef NFS4_ACL_EXTATTR_NAME
/*
* 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, vp, aclp, NULL, NULL, 0, &attrbits,
NULL, NULL, 0, 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);
}
-
-#endif /* NFS4_ACL_EXTATTR_NAME */
Index: stable/8/sys/fs/nfsclient/nfs_clvnops.c
===================================================================
--- stable/8/sys/fs/nfsclient/nfs_clvnops.c (revision 221211)
+++ stable/8/sys/fs/nfsclient/nfs_clvnops.c (revision 221212)
@@ -1,3364 +1,3350 @@
/*-
* 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.
*
* from nfs_vnops.c 8.16 (Berkeley) 5/27/95
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* vnode op calls for Sun NFS version 2, 3 and 4
*/
#include "opt_inet.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/jail.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/namei.h>
#include <sys/socket.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/signalvar.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_extern.h>
#include <vm/vm_object.h>
#include <fs/nfs/nfsport.h>
#include <fs/nfsclient/nfsnode.h>
#include <fs/nfsclient/nfsmount.h>
#include <fs/nfsclient/nfs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <nfs/nfs_lock.h>
/* Defs */
#define TRUE 1
#define FALSE 0
extern struct nfsstats newnfsstats;
extern int nfsrv_useacl;
MALLOC_DECLARE(M_NEWNFSREQ);
/*
* Ifdef for FreeBSD-current merged buffer cache. It is unfortunate that these
* calls are not in getblk() and brelse() so that they would not be necessary
* here.
*/
#ifndef B_VMIO
#define vfs_busy_pages(bp, f)
#endif
static vop_read_t nfsfifo_read;
static vop_write_t nfsfifo_write;
static vop_close_t nfsfifo_close;
static int nfs_setattrrpc(struct vnode *, struct vattr *, struct ucred *,
struct thread *);
static vop_lookup_t nfs_lookup;
static vop_create_t nfs_create;
static vop_mknod_t nfs_mknod;
static vop_open_t nfs_open;
static vop_pathconf_t nfs_pathconf;
static vop_close_t nfs_close;
static vop_access_t nfs_access;
static vop_getattr_t nfs_getattr;
static vop_setattr_t nfs_setattr;
static vop_read_t nfs_read;
static vop_fsync_t nfs_fsync;
static vop_remove_t nfs_remove;
static vop_link_t nfs_link;
static vop_rename_t nfs_rename;
static vop_mkdir_t nfs_mkdir;
static vop_rmdir_t nfs_rmdir;
static vop_symlink_t nfs_symlink;
static vop_readdir_t nfs_readdir;
static vop_strategy_t nfs_strategy;
static vop_lock1_t nfs_lock1;
static int nfs_lookitup(struct vnode *, char *, int,
struct ucred *, struct thread *, struct nfsnode **);
static int nfs_sillyrename(struct vnode *, struct vnode *,
struct componentname *);
static vop_access_t nfsspec_access;
static vop_readlink_t nfs_readlink;
static vop_print_t nfs_print;
static vop_advlock_t nfs_advlock;
static vop_advlockasync_t nfs_advlockasync;
-#ifdef NFS4_ACL_EXTATTR_NAME
static vop_getacl_t nfs_getacl;
static vop_setacl_t nfs_setacl;
-#endif
/*
* Global vfs data structures for nfs
*/
struct vop_vector newnfs_vnodeops = {
.vop_default = &default_vnodeops,
.vop_access = nfs_access,
.vop_advlock = nfs_advlock,
.vop_advlockasync = nfs_advlockasync,
.vop_close = nfs_close,
.vop_create = nfs_create,
.vop_fsync = nfs_fsync,
.vop_getattr = nfs_getattr,
.vop_getpages = ncl_getpages,
.vop_putpages = ncl_putpages,
.vop_inactive = ncl_inactive,
.vop_link = nfs_link,
.vop_lock1 = nfs_lock1,
.vop_lookup = nfs_lookup,
.vop_mkdir = nfs_mkdir,
.vop_mknod = nfs_mknod,
.vop_open = nfs_open,
.vop_pathconf = nfs_pathconf,
.vop_print = nfs_print,
.vop_read = nfs_read,
.vop_readdir = nfs_readdir,
.vop_readlink = nfs_readlink,
.vop_reclaim = ncl_reclaim,
.vop_remove = nfs_remove,
.vop_rename = nfs_rename,
.vop_rmdir = nfs_rmdir,
.vop_setattr = nfs_setattr,
.vop_strategy = nfs_strategy,
.vop_symlink = nfs_symlink,
.vop_write = ncl_write,
-#ifdef NFS4_ACL_EXTATTR_NAME
.vop_getacl = nfs_getacl,
.vop_setacl = nfs_setacl,
-#endif
};
struct vop_vector newnfs_fifoops = {
.vop_default = &fifo_specops,
.vop_access = nfsspec_access,
.vop_close = nfsfifo_close,
.vop_fsync = nfs_fsync,
.vop_getattr = nfs_getattr,
.vop_inactive = ncl_inactive,
.vop_print = nfs_print,
.vop_read = nfsfifo_read,
.vop_reclaim = ncl_reclaim,
.vop_setattr = nfs_setattr,
.vop_write = nfsfifo_write,
};
static int nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vattr *vap);
static int nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name,
int namelen, struct ucred *cred, struct thread *td);
static int nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp,
char *fnameptr, int fnamelen, struct vnode *tdvp, struct vnode *tvp,
char *tnameptr, int tnamelen, struct ucred *cred, struct thread *td);
static int nfs_renameit(struct vnode *sdvp, struct vnode *svp,
struct componentname *scnp, struct sillyrename *sp);
/*
* Global variables
*/
#define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
SYSCTL_DECL(_vfs_newnfs);
static int nfsaccess_cache_timeout = NFS_MAXATTRTIMO;
SYSCTL_INT(_vfs_newnfs, OID_AUTO, access_cache_timeout, CTLFLAG_RW,
&nfsaccess_cache_timeout, 0, "NFS ACCESS cache timeout");
static int nfs_prime_access_cache = 0;
SYSCTL_INT(_vfs_newnfs, OID_AUTO, prime_access_cache, CTLFLAG_RW,
&nfs_prime_access_cache, 0,
"Prime NFS ACCESS cache when fetching attributes");
static int newnfs_commit_on_close = 0;
SYSCTL_INT(_vfs_newnfs, OID_AUTO, commit_on_close, CTLFLAG_RW,
&newnfs_commit_on_close, 0, "write+commit on close, else only write");
static int nfs_clean_pages_on_close = 1;
SYSCTL_INT(_vfs_newnfs, OID_AUTO, clean_pages_on_close, CTLFLAG_RW,
&nfs_clean_pages_on_close, 0, "NFS clean dirty pages on close");
int newnfs_directio_enable = 0;
SYSCTL_INT(_vfs_newnfs, OID_AUTO, directio_enable, CTLFLAG_RW,
&newnfs_directio_enable, 0, "Enable NFS directio");
/*
* This sysctl allows other processes to mmap a file that has been opened
* O_DIRECT by a process. In general, having processes mmap the file while
* Direct IO is in progress can lead to Data Inconsistencies. But, we allow
* this by default to prevent DoS attacks - to prevent a malicious user from
* opening up files O_DIRECT preventing other users from mmap'ing these
* files. "Protected" environments where stricter consistency guarantees are
* required can disable this knob. The process that opened the file O_DIRECT
* cannot mmap() the file, because mmap'ed IO on an O_DIRECT open() is not
* meaningful.
*/
int newnfs_directio_allow_mmap = 1;
SYSCTL_INT(_vfs_newnfs, OID_AUTO, directio_allow_mmap, CTLFLAG_RW,
&newnfs_directio_allow_mmap, 0, "Enable mmaped IO on file with O_DIRECT opens");
#if 0
SYSCTL_INT(_vfs_newnfs, OID_AUTO, access_cache_hits, CTLFLAG_RD,
&newnfsstats.accesscache_hits, 0, "NFS ACCESS cache hit count");
SYSCTL_INT(_vfs_newnfs, OID_AUTO, access_cache_misses, CTLFLAG_RD,
&newnfsstats.accesscache_misses, 0, "NFS ACCESS cache miss count");
#endif
#define NFSACCESS_ALL (NFSACCESS_READ | NFSACCESS_MODIFY \
| NFSACCESS_EXTEND | NFSACCESS_EXECUTE \
| NFSACCESS_DELETE | NFSACCESS_LOOKUP)
/*
* SMP Locking Note :
* The list of locks after the description of the lock is the ordering
* of other locks acquired with the lock held.
* np->n_mtx : Protects the fields in the nfsnode.
VM Object Lock
VI_MTX (acquired indirectly)
* nmp->nm_mtx : Protects the fields in the nfsmount.
rep->r_mtx
* ncl_iod_mutex : Global lock, protects shared nfsiod state.
* nfs_reqq_mtx : Global lock, protects the nfs_reqq list.
nmp->nm_mtx
rep->r_mtx
* rep->r_mtx : Protects the fields in an nfsreq.
*/
static int
nfs34_access_otw(struct vnode *vp, int wmode, struct thread *td,
struct ucred *cred, u_int32_t *retmode)
{
int error = 0, attrflag, i, lrupos;
u_int32_t rmode;
struct nfsnode *np = VTONFS(vp);
struct nfsvattr nfsva;
error = nfsrpc_accessrpc(vp, wmode, cred, td, &nfsva, &attrflag,
&rmode, NULL);
if (attrflag)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
if (!error) {
lrupos = 0;
mtx_lock(&np->n_mtx);
for (i = 0; i < NFS_ACCESSCACHESIZE; i++) {
if (np->n_accesscache[i].uid == cred->cr_uid) {
np->n_accesscache[i].mode = rmode;
np->n_accesscache[i].stamp = time_second;
break;
}
if (i > 0 && np->n_accesscache[i].stamp <
np->n_accesscache[lrupos].stamp)
lrupos = i;
}
if (i == NFS_ACCESSCACHESIZE) {
np->n_accesscache[lrupos].uid = cred->cr_uid;
np->n_accesscache[lrupos].mode = rmode;
np->n_accesscache[lrupos].stamp = time_second;
}
mtx_unlock(&np->n_mtx);
if (retmode != NULL)
*retmode = rmode;
} else if (NFS_ISV4(vp)) {
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
}
return (error);
}
/*
* nfs access vnode op.
* For nfs version 2, just return ok. File accesses may fail later.
* For nfs version 3, use the access rpc to check accessibility. If file modes
* are changed on the server, accesses might still fail later.
*/
static int
nfs_access(struct vop_access_args *ap)
{
struct vnode *vp = ap->a_vp;
int error = 0, i, gotahit;
u_int32_t mode, wmode, rmode;
int v34 = NFS_ISV34(vp);
struct nfsnode *np = VTONFS(vp);
/*
* Disallow write attempts on filesystems mounted read-only;
* unless the file is a socket, fifo, or a block or character
* device resident on the filesystem.
*/
- if ((ap->a_accmode & (VWRITE | VAPPEND
-#ifdef NFS4_ACL_EXTATTR_NAME
- | VWRITE_NAMED_ATTRS | VDELETE_CHILD | VWRITE_ATTRIBUTES |
- VDELETE | VWRITE_ACL | VWRITE_OWNER
-#endif
- )) != 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
+ if ((ap->a_accmode & (VWRITE | VAPPEND | VWRITE_NAMED_ATTRS |
+ VDELETE_CHILD | VWRITE_ATTRIBUTES | VDELETE | VWRITE_ACL |
+ VWRITE_OWNER)) != 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
switch (vp->v_type) {
case VREG:
case VDIR:
case VLNK:
return (EROFS);
default:
break;
}
}
/*
* For nfs v3 or v4, check to see if we have done this recently, and if
* so return our cached result instead of making an ACCESS call.
* If not, do an access rpc, otherwise you are stuck emulating
* ufs_access() locally using the vattr. This may not be correct,
* since the server may apply other access criteria such as
* client uid-->server uid mapping that we do not know about.
*/
if (v34) {
if (ap->a_accmode & VREAD)
mode = NFSACCESS_READ;
else
mode = 0;
if (vp->v_type != VDIR) {
if (ap->a_accmode & VWRITE)
mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
if (ap->a_accmode & VAPPEND)
mode |= NFSACCESS_EXTEND;
if (ap->a_accmode & VEXEC)
mode |= NFSACCESS_EXECUTE;
-#ifdef NFS4_ACL_EXTATTR_NAME
if (ap->a_accmode & VDELETE)
mode |= NFSACCESS_DELETE;
-#endif
} else {
if (ap->a_accmode & VWRITE)
mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
if (ap->a_accmode & VAPPEND)
mode |= NFSACCESS_EXTEND;
if (ap->a_accmode & VEXEC)
mode |= NFSACCESS_LOOKUP;
-#ifdef NFS4_ACL_EXTATTR_NAME
if (ap->a_accmode & VDELETE)
mode |= NFSACCESS_DELETE;
if (ap->a_accmode & VDELETE_CHILD)
mode |= NFSACCESS_MODIFY;
-#endif
}
/* XXX safety belt, only make blanket request if caching */
if (nfsaccess_cache_timeout > 0) {
wmode = NFSACCESS_READ | NFSACCESS_MODIFY |
NFSACCESS_EXTEND | NFSACCESS_EXECUTE |
NFSACCESS_DELETE | NFSACCESS_LOOKUP;
} else {
wmode = mode;
}
/*
* Does our cached result allow us to give a definite yes to
* this request?
*/
gotahit = 0;
mtx_lock(&np->n_mtx);
for (i = 0; i < NFS_ACCESSCACHESIZE; i++) {
if (ap->a_cred->cr_uid == np->n_accesscache[i].uid) {
if (time_second < (np->n_accesscache[i].stamp
+ nfsaccess_cache_timeout) &&
(np->n_accesscache[i].mode & mode) == mode) {
NFSINCRGLOBAL(newnfsstats.accesscache_hits);
gotahit = 1;
}
break;
}
}
mtx_unlock(&np->n_mtx);
if (gotahit == 0) {
/*
* Either a no, or a don't know. Go to the wire.
*/
NFSINCRGLOBAL(newnfsstats.accesscache_misses);
error = nfs34_access_otw(vp, wmode, ap->a_td,
ap->a_cred, &rmode);
if (!error &&
(rmode & mode) != mode)
error = EACCES;
}
return (error);
} else {
if ((error = nfsspec_access(ap)) != 0) {
return (error);
}
/*
* Attempt to prevent a mapped root from accessing a file
* which it shouldn't. We try to read a byte from the file
* if the user is root and the file is not zero length.
* After calling nfsspec_access, we should have the correct
* file size cached.
*/
mtx_lock(&np->n_mtx);
if (ap->a_cred->cr_uid == 0 && (ap->a_accmode & VREAD)
&& VTONFS(vp)->n_size > 0) {
struct iovec aiov;
struct uio auio;
char buf[1];
mtx_unlock(&np->n_mtx);
aiov.iov_base = buf;
aiov.iov_len = 1;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_resid = 1;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_td = ap->a_td;
if (vp->v_type == VREG)
error = ncl_readrpc(vp, &auio, ap->a_cred);
else if (vp->v_type == VDIR) {
char* bp;
bp = malloc(NFS_DIRBLKSIZ, M_TEMP, M_WAITOK);
aiov.iov_base = bp;
aiov.iov_len = auio.uio_resid = NFS_DIRBLKSIZ;
error = ncl_readdirrpc(vp, &auio, ap->a_cred,
ap->a_td);
free(bp, M_TEMP);
} else if (vp->v_type == VLNK)
error = ncl_readlinkrpc(vp, &auio, ap->a_cred);
else
error = EACCES;
} else
mtx_unlock(&np->n_mtx);
return (error);
}
}
/*
* nfs open vnode op
* Check to see if the type is ok
* and that deletion is not in progress.
* For paged in text files, you will need to flush the page cache
* if consistency is lost.
*/
/* ARGSUSED */
static int
nfs_open(struct vop_open_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct vattr vattr;
int error;
int fmode = ap->a_mode;
if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
return (EOPNOTSUPP);
/*
* For NFSv4, we need to do the Open Op before cache validation,
* so that we conform to RFC3530 Sec. 9.3.1.
*/
if (NFS_ISV4(vp)) {
error = nfsrpc_open(vp, fmode, ap->a_cred, ap->a_td);
if (error) {
error = nfscl_maperr(ap->a_td, error, (uid_t)0,
(gid_t)0);
return (error);
}
}
/*
* Now, if this Open will be doing reading, re-validate/flush the
* cache, so that Close/Open coherency is maintained.
*/
mtx_lock(&np->n_mtx);
if (np->n_flag & NMODIFIED) {
mtx_unlock(&np->n_mtx);
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
if (error == EINTR || error == EIO) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
return (error);
}
mtx_lock(&np->n_mtx);
np->n_attrstamp = 0;
if (vp->v_type == VDIR)
np->n_direofoffset = 0;
mtx_unlock(&np->n_mtx);
error = VOP_GETATTR(vp, &vattr, ap->a_cred);
if (error) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
return (error);
}
mtx_lock(&np->n_mtx);
np->n_mtime = vattr.va_mtime;
if (NFS_ISV4(vp))
np->n_change = vattr.va_filerev;
} else {
mtx_unlock(&np->n_mtx);
error = VOP_GETATTR(vp, &vattr, ap->a_cred);
if (error) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
return (error);
}
mtx_lock(&np->n_mtx);
if ((NFS_ISV4(vp) && np->n_change != vattr.va_filerev) ||
NFS_TIMESPEC_COMPARE(&np->n_mtime, &vattr.va_mtime)) {
if (vp->v_type == VDIR)
np->n_direofoffset = 0;
mtx_unlock(&np->n_mtx);
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
if (error == EINTR || error == EIO) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
return (error);
}
mtx_lock(&np->n_mtx);
np->n_mtime = vattr.va_mtime;
if (NFS_ISV4(vp))
np->n_change = vattr.va_filerev;
}
}
/*
* If the object has >= 1 O_DIRECT active opens, we disable caching.
*/
if (newnfs_directio_enable && (fmode & O_DIRECT) &&
(vp->v_type == VREG)) {
if (np->n_directio_opens == 0) {
mtx_unlock(&np->n_mtx);
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
if (error) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
return (error);
}
mtx_lock(&np->n_mtx);
np->n_flag |= NNONCACHE;
}
np->n_directio_opens++;
}
mtx_unlock(&np->n_mtx);
vnode_create_vobject(vp, vattr.va_size, ap->a_td);
return (0);
}
/*
* nfs close vnode op
* What an NFS client should do upon close after writing is a debatable issue.
* Most NFS clients push delayed writes to the server upon close, basically for
* two reasons:
* 1 - So that any write errors may be reported back to the client process
* doing the close system call. By far the two most likely errors are
* NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure.
* 2 - To put a worst case upper bound on cache inconsistency between
* multiple clients for the file.
* There is also a consistency problem for Version 2 of the protocol w.r.t.
* not being able to tell if other clients are writing a file concurrently,
* since there is no way of knowing if the changed modify time in the reply
* is only due to the write for this client.
* (NFS Version 3 provides weak cache consistency data in the reply that
* should be sufficient to detect and handle this case.)
*
* The current code does the following:
* for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
* for NFS Version 3 - flush dirty buffers to the server but don't invalidate
* or commit them (this satisfies 1 and 2 except for the
* case where the server crashes after this close but
* before the commit RPC, which is felt to be "good
* enough". Changing the last argument to ncl_flush() to
* a 1 would force a commit operation, if it is felt a
* commit is necessary now.
* for NFS Version 4 - flush the dirty buffers and commit them, if
* nfscl_mustflush() says this is necessary.
* It is necessary if there is no write delegation held,
* in order to satisfy open/close coherency.
* If the file isn't cached on local stable storage,
* it may be necessary in order to detect "out of space"
* errors from the server, if the write delegation
* issued by the server doesn't allow the file to grow.
*/
/* ARGSUSED */
static int
nfs_close(struct vop_close_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsvattr nfsva;
struct ucred *cred;
int error = 0, ret, localcred = 0;
int fmode = ap->a_fflag;
if ((vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF))
return (0);
/*
* During shutdown, a_cred isn't valid, so just use root.
*/
if (ap->a_cred == NOCRED) {
cred = newnfs_getcred();
localcred = 1;
} else {
cred = ap->a_cred;
}
if (vp->v_type == VREG) {
/*
* Examine and clean dirty pages, regardless of NMODIFIED.
* This closes a major hole in close-to-open consistency.
* We want to push out all dirty pages (and buffers) on
* close, regardless of whether they were dirtied by
* mmap'ed writes or via write().
*/
if (nfs_clean_pages_on_close && vp->v_object) {
VM_OBJECT_LOCK(vp->v_object);
vm_object_page_clean(vp->v_object, 0, 0, 0);
VM_OBJECT_UNLOCK(vp->v_object);
}
mtx_lock(&np->n_mtx);
if (np->n_flag & NMODIFIED) {
mtx_unlock(&np->n_mtx);
if (NFS_ISV3(vp)) {
/*
* Under NFSv3 we have dirty buffers to dispose of. We
* must flush them to the NFS server. We have the option
* of waiting all the way through the commit rpc or just
* waiting for the initial write. The default is to only
* wait through the initial write so the data is in the
* server's cache, which is roughly similar to the state
* a standard disk subsystem leaves the file in on close().
*
* We cannot clear the NMODIFIED bit in np->n_flag due to
* potential races with other processes, and certainly
* cannot clear it if we don't commit.
* These races occur when there is no longer the old
* traditional vnode locking implemented for Vnode Ops.
*/
int cm = newnfs_commit_on_close ? 1 : 0;
error = ncl_flush(vp, MNT_WAIT, cred, ap->a_td, cm, 0);
/* np->n_flag &= ~NMODIFIED; */
} else if (NFS_ISV4(vp)) {
if (nfscl_mustflush(vp) != 0) {
int cm = newnfs_commit_on_close ? 1 : 0;
error = ncl_flush(vp, MNT_WAIT, cred, ap->a_td,
cm, 0);
/*
* as above w.r.t races when clearing
* NMODIFIED.
* np->n_flag &= ~NMODIFIED;
*/
}
} else
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
mtx_lock(&np->n_mtx);
}
/*
* Invalidate the attribute cache in all cases.
* An open is going to fetch fresh attrs any way, other procs
* on this node that have file open will be forced to do an
* otw attr fetch, but this is safe.
* --> A user found that their RPC count dropped by 20% when
* this was commented out and I can't see any requirement
* for it, so I've disabled it when negative lookups are
* enabled. (What does this have to do with negative lookup
* caching? Well nothing, except it was reported by the
* same user that needed negative lookup caching and I wanted
* there to be a way to disable it to see if it
* is the cause of some caching/coherency issue that might
* crop up.)
*/
if (VFSTONFS(vp->v_mount)->nm_negnametimeo == 0)
np->n_attrstamp = 0;
if (np->n_flag & NWRITEERR) {
np->n_flag &= ~NWRITEERR;
error = np->n_error;
}
mtx_unlock(&np->n_mtx);
}
if (NFS_ISV4(vp)) {
/*
* Get attributes so "change" is up to date.
*/
if (error == 0 && nfscl_mustflush(vp) != 0) {
ret = nfsrpc_getattr(vp, cred, ap->a_td, &nfsva,
NULL);
if (!ret) {
np->n_change = nfsva.na_filerev;
(void) nfscl_loadattrcache(&vp, &nfsva, NULL,
NULL, 0, 0);
}
}
/*
* and do the close.
*/
ret = nfsrpc_close(vp, 0, ap->a_td);
if (!error && ret)
error = ret;
if (error)
error = nfscl_maperr(ap->a_td, error, (uid_t)0,
(gid_t)0);
}
if (newnfs_directio_enable)
KASSERT((np->n_directio_asyncwr == 0),
("nfs_close: dirty unflushed (%d) directio buffers\n",
np->n_directio_asyncwr));
if (newnfs_directio_enable && (fmode & O_DIRECT) && (vp->v_type == VREG)) {
mtx_lock(&np->n_mtx);
KASSERT((np->n_directio_opens > 0),
("nfs_close: unexpectedly value (0) of n_directio_opens\n"));
np->n_directio_opens--;
if (np->n_directio_opens == 0)
np->n_flag &= ~NNONCACHE;
mtx_unlock(&np->n_mtx);
}
if (localcred)
NFSFREECRED(cred);
return (error);
}
/*
* nfs getattr call from vfs.
*/
static int
nfs_getattr(struct vop_getattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct thread *td = curthread; /* XXX */
struct nfsnode *np = VTONFS(vp);
int error = 0;
struct nfsvattr nfsva;
struct vattr *vap = ap->a_vap;
struct vattr vattr;
/*
* Update local times for special files.
*/
mtx_lock(&np->n_mtx);
if (np->n_flag & (NACC | NUPD))
np->n_flag |= NCHG;
mtx_unlock(&np->n_mtx);
/*
* First look in the cache.
*/
if (ncl_getattrcache(vp, &vattr) == 0) {
vap->va_type = vattr.va_type;
vap->va_mode = vattr.va_mode;
vap->va_nlink = vattr.va_nlink;
vap->va_uid = vattr.va_uid;
vap->va_gid = vattr.va_gid;
vap->va_fsid = vattr.va_fsid;
vap->va_fileid = vattr.va_fileid;
vap->va_size = vattr.va_size;
vap->va_blocksize = vattr.va_blocksize;
vap->va_atime = vattr.va_atime;
vap->va_mtime = vattr.va_mtime;
vap->va_ctime = vattr.va_ctime;
vap->va_gen = vattr.va_gen;
vap->va_flags = vattr.va_flags;
vap->va_rdev = vattr.va_rdev;
vap->va_bytes = vattr.va_bytes;
vap->va_filerev = vattr.va_filerev;
/*
* Get the local modify time for the case of a write
* delegation.
*/
nfscl_deleggetmodtime(vp, &vap->va_mtime);
return (0);
}
if (NFS_ISV34(vp) && nfs_prime_access_cache &&
nfsaccess_cache_timeout > 0) {
NFSINCRGLOBAL(newnfsstats.accesscache_misses);
nfs34_access_otw(vp, NFSACCESS_ALL, td, ap->a_cred, NULL);
if (ncl_getattrcache(vp, ap->a_vap) == 0) {
nfscl_deleggetmodtime(vp, &ap->a_vap->va_mtime);
return (0);
}
}
error = nfsrpc_getattr(vp, ap->a_cred, td, &nfsva, NULL);
if (!error)
error = nfscl_loadattrcache(&vp, &nfsva, vap, NULL, 0, 0);
if (!error) {
/*
* Get the local modify time for the case of a write
* delegation.
*/
nfscl_deleggetmodtime(vp, &vap->va_mtime);
} else if (NFS_ISV4(vp)) {
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
}
return (error);
}
/*
* nfs setattr call.
*/
static int
nfs_setattr(struct vop_setattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct thread *td = curthread; /* XXX */
struct vattr *vap = ap->a_vap;
int error = 0;
u_quad_t tsize;
#ifndef nolint
tsize = (u_quad_t)0;
#endif
/*
* Setting of flags and marking of atimes are not supported.
*/
if (vap->va_flags != VNOVAL)
return (EOPNOTSUPP);
/*
* Disallow write attempts if the filesystem is mounted read-only.
*/
if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
(vp->v_mount->mnt_flag & MNT_RDONLY))
return (EROFS);
if (vap->va_size != VNOVAL) {
switch (vp->v_type) {
case VDIR:
return (EISDIR);
case VCHR:
case VBLK:
case VSOCK:
case VFIFO:
if (vap->va_mtime.tv_sec == VNOVAL &&
vap->va_atime.tv_sec == VNOVAL &&
vap->va_mode == (mode_t)VNOVAL &&
vap->va_uid == (uid_t)VNOVAL &&
vap->va_gid == (gid_t)VNOVAL)
return (0);
vap->va_size = VNOVAL;
break;
default:
/*
* Disallow write attempts if the filesystem is
* mounted read-only.
*/
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
/*
* We run vnode_pager_setsize() early (why?),
* we must set np->n_size now to avoid vinvalbuf
* V_SAVE races that might setsize a lower
* value.
*/
mtx_lock(&np->n_mtx);
tsize = np->n_size;
mtx_unlock(&np->n_mtx);
error = ncl_meta_setsize(vp, ap->a_cred, td,
vap->va_size);
mtx_lock(&np->n_mtx);
if (np->n_flag & NMODIFIED) {
tsize = np->n_size;
mtx_unlock(&np->n_mtx);
if (vap->va_size == 0)
error = ncl_vinvalbuf(vp, 0, td, 1);
else
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error) {
vnode_pager_setsize(vp, tsize);
return (error);
}
/*
* Call nfscl_delegmodtime() to set the modify time
* locally, as required.
*/
nfscl_delegmodtime(vp);
} else
mtx_unlock(&np->n_mtx);
/*
* np->n_size has already been set to vap->va_size
* in ncl_meta_setsize(). We must set it again since
* nfs_loadattrcache() could be called through
* ncl_meta_setsize() and could modify np->n_size.
*/
mtx_lock(&np->n_mtx);
np->n_vattr.na_size = np->n_size = vap->va_size;
mtx_unlock(&np->n_mtx);
};
} else {
mtx_lock(&np->n_mtx);
if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) &&
(np->n_flag & NMODIFIED) && vp->v_type == VREG) {
mtx_unlock(&np->n_mtx);
if ((error = ncl_vinvalbuf(vp, V_SAVE, td, 1)) != 0 &&
(error == EINTR || error == EIO))
return (error);
} else
mtx_unlock(&np->n_mtx);
}
error = nfs_setattrrpc(vp, vap, ap->a_cred, td);
if (error && vap->va_size != VNOVAL) {
mtx_lock(&np->n_mtx);
np->n_size = np->n_vattr.na_size = tsize;
vnode_pager_setsize(vp, tsize);
mtx_unlock(&np->n_mtx);
}
return (error);
}
/*
* Do an nfs setattr rpc.
*/
static int
nfs_setattrrpc(struct vnode *vp, struct vattr *vap, struct ucred *cred,
struct thread *td)
{
struct nfsnode *np = VTONFS(vp);
int error, ret, attrflag, i;
struct nfsvattr nfsva;
if (NFS_ISV34(vp)) {
mtx_lock(&np->n_mtx);
for (i = 0; i < NFS_ACCESSCACHESIZE; i++)
np->n_accesscache[i].stamp = 0;
np->n_flag |= NDELEGMOD;
mtx_unlock(&np->n_mtx);
}
error = nfsrpc_setattr(vp, vap, NULL, cred, td, &nfsva, &attrflag,
NULL);
if (attrflag) {
ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
if (ret && !error)
error = ret;
}
if (error && NFS_ISV4(vp))
error = nfscl_maperr(td, error, vap->va_uid, vap->va_gid);
return (error);
}
/*
* nfs lookup call, one step at a time...
* First look in cache
* If not found, unlock the directory nfsnode and do the rpc
*/
static int
nfs_lookup(struct vop_lookup_args *ap)
{
struct componentname *cnp = ap->a_cnp;
struct vnode *dvp = ap->a_dvp;
struct vnode **vpp = ap->a_vpp;
struct mount *mp = dvp->v_mount;
int flags = cnp->cn_flags;
struct vnode *newvp;
struct nfsmount *nmp;
struct nfsnode *np, *newnp;
int error = 0, attrflag, dattrflag, ltype;
struct thread *td = cnp->cn_thread;
struct nfsfh *nfhp;
struct nfsvattr dnfsva, nfsva;
struct vattr vattr;
struct timespec dmtime;
*vpp = NULLVP;
if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
return (EROFS);
if (dvp->v_type != VDIR)
return (ENOTDIR);
nmp = VFSTONFS(mp);
np = VTONFS(dvp);
/* For NFSv4, wait until any remove is done. */
mtx_lock(&np->n_mtx);
while (NFSHASNFSV4(nmp) && (np->n_flag & NREMOVEINPROG)) {
np->n_flag |= NREMOVEWANT;
(void) msleep((caddr_t)np, &np->n_mtx, PZERO, "nfslkup", 0);
}
mtx_unlock(&np->n_mtx);
if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
return (error);
error = cache_lookup(dvp, vpp, cnp);
if (error > 0 && error != ENOENT)
return (error);
if (error == -1) {
/*
* We only accept a positive hit in the cache if the
* change time of the file matches our cached copy.
* Otherwise, we discard the cache entry and fallback
* to doing a lookup RPC.
*
* To better handle stale file handles and attributes,
* clear the attribute cache of this node if it is a
* leaf component, part of an open() call, and not
* locally modified before fetching the attributes.
* This should allow stale file handles to be detected
* here where we can fall back to a LOOKUP RPC to
* recover rather than having nfs_open() detect the
* stale file handle and failing open(2) with ESTALE.
*/
newvp = *vpp;
newnp = VTONFS(newvp);
if ((flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN) &&
!(newnp->n_flag & NMODIFIED)) {
mtx_lock(&newnp->n_mtx);
newnp->n_attrstamp = 0;
mtx_unlock(&newnp->n_mtx);
}
if (nfscl_nodeleg(newvp, 0) == 0 ||
(VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 &&
timespeccmp(&vattr.va_ctime, &newnp->n_ctime, ==))) {
NFSINCRGLOBAL(newnfsstats.lookupcache_hits);
if (cnp->cn_nameiop != LOOKUP &&
(flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
return (0);
}
cache_purge(newvp);
if (dvp != newvp)
vput(newvp);
else
vrele(newvp);
*vpp = NULLVP;
} else if (error == ENOENT) {
if (dvp->v_iflag & VI_DOOMED)
return (ENOENT);
/*
* We only accept a negative hit in the cache if the
* modification time of the parent directory matches
* our cached copy. Otherwise, we discard all of the
* negative cache entries for this directory. We also
* only trust -ve cache entries for less than
* nm_negative_namecache_timeout seconds.
*/
if ((u_int)(ticks - np->n_dmtime_ticks) <
(nmp->nm_negnametimeo * hz) &&
VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0 &&
timespeccmp(&vattr.va_mtime, &np->n_dmtime, ==)) {
NFSINCRGLOBAL(newnfsstats.lookupcache_hits);
return (ENOENT);
}
cache_purge_negative(dvp);
mtx_lock(&np->n_mtx);
timespecclear(&np->n_dmtime);
mtx_unlock(&np->n_mtx);
}
/*
* Cache the modification time of the parent directory in case
* the lookup fails and results in adding the first negative
* name cache entry for the directory. Since this is reading
* a single time_t, don't bother with locking. The
* modification time may be a bit stale, but it must be read
* before performing the lookup RPC to prevent a race where
* another lookup updates the timestamp on the directory after
* the lookup RPC has been performed on the server but before
* n_dmtime is set at the end of this function.
*/
dmtime = np->n_vattr.na_mtime;
error = 0;
newvp = NULLVP;
NFSINCRGLOBAL(newnfsstats.lookupcache_misses);
error = nfsrpc_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_cred, td, &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
NULL);
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
if (error) {
if (newvp != NULLVP) {
vput(newvp);
*vpp = NULLVP;
}
if (error != ENOENT) {
if (NFS_ISV4(dvp))
error = nfscl_maperr(td, error, (uid_t)0,
(gid_t)0);
return (error);
}
/* The requested file was not found. */
if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
(flags & ISLASTCN)) {
/*
* XXX: UFS does a full VOP_ACCESS(dvp,
* VWRITE) here instead of just checking
* MNT_RDONLY.
*/
if (mp->mnt_flag & MNT_RDONLY)
return (EROFS);
cnp->cn_flags |= SAVENAME;
return (EJUSTRETURN);
}
if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) {
/*
* Maintain n_dmtime as the modification time
* of the parent directory when the oldest -ve
* name cache entry for this directory was
* added. If a -ve cache entry has already
* been added with a newer modification time
* by a concurrent lookup, then don't bother
* adding a cache entry. The modification
* time of the directory might have changed
* due to the file this lookup failed to find
* being created. In that case a subsequent
* lookup would incorrectly use the entry
* added here instead of doing an extra
* lookup.
*/
mtx_lock(&np->n_mtx);
if (timespeccmp(&np->n_dmtime, &dmtime, <=)) {
if (!timespecisset(&np->n_dmtime)) {
np->n_dmtime = dmtime;
np->n_dmtime_ticks = ticks;
}
mtx_unlock(&np->n_mtx);
cache_enter(dvp, NULL, cnp);
} else
mtx_unlock(&np->n_mtx);
}
return (ENOENT);
}
/*
* Handle RENAME case...
*/
if (cnp->cn_nameiop == RENAME && (flags & ISLASTCN)) {
if (NFS_CMPFH(np, nfhp->nfh_fh, nfhp->nfh_len)) {
FREE((caddr_t)nfhp, M_NFSFH);
return (EISDIR);
}
error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL);
if (error)
return (error);
newvp = NFSTOV(np);
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
*vpp = newvp;
cnp->cn_flags |= SAVENAME;
return (0);
}
if (flags & ISDOTDOT) {
ltype = VOP_ISLOCKED(dvp);
error = vfs_busy(mp, MBF_NOWAIT);
if (error != 0) {
vfs_ref(mp);
VOP_UNLOCK(dvp, 0);
error = vfs_busy(mp, 0);
vn_lock(dvp, ltype | LK_RETRY);
vfs_rel(mp);
if (error == 0 && (dvp->v_iflag & VI_DOOMED)) {
vfs_unbusy(mp);
error = ENOENT;
}
if (error != 0)
return (error);
}
VOP_UNLOCK(dvp, 0);
error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL);
if (error == 0)
newvp = NFSTOV(np);
vfs_unbusy(mp);
if (newvp != dvp)
vn_lock(dvp, ltype | LK_RETRY);
if (dvp->v_iflag & VI_DOOMED) {
if (error == 0) {
if (newvp == dvp)
vrele(newvp);
else
vput(newvp);
}
error = ENOENT;
}
if (error != 0)
return (error);
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
} else if (NFS_CMPFH(np, nfhp->nfh_fh, nfhp->nfh_len)) {
FREE((caddr_t)nfhp, M_NFSFH);
VREF(dvp);
newvp = dvp;
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
} else {
error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL);
if (error)
return (error);
newvp = NFSTOV(np);
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
else if ((flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN) &&
!(np->n_flag & NMODIFIED)) {
/*
* Flush the attribute cache when opening a
* leaf node to ensure that fresh attributes
* are fetched in nfs_open() since we did not
* fetch attributes from the LOOKUP reply.
*/
mtx_lock(&np->n_mtx);
np->n_attrstamp = 0;
mtx_unlock(&np->n_mtx);
}
}
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
if ((cnp->cn_flags & MAKEENTRY) &&
(cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
np->n_ctime = np->n_vattr.na_vattr.va_ctime;
cache_enter(dvp, newvp, cnp);
}
*vpp = newvp;
return (0);
}
/*
* nfs read call.
* Just call ncl_bioread() to do the work.
*/
static int
nfs_read(struct vop_read_args *ap)
{
struct vnode *vp = ap->a_vp;
switch (vp->v_type) {
case VREG:
return (ncl_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred));
case VDIR:
return (EISDIR);
default:
return (EOPNOTSUPP);
}
}
/*
* nfs readlink call
*/
static int
nfs_readlink(struct vop_readlink_args *ap)
{
struct vnode *vp = ap->a_vp;
if (vp->v_type != VLNK)
return (EINVAL);
return (ncl_bioread(vp, ap->a_uio, 0, ap->a_cred));
}
/*
* Do a readlink rpc.
* Called by ncl_doio() from below the buffer cache.
*/
int
ncl_readlinkrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred)
{
int error, ret, attrflag;
struct nfsvattr nfsva;
error = nfsrpc_readlink(vp, uiop, cred, uiop->uio_td, &nfsva,
&attrflag, NULL);
if (attrflag) {
ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
if (ret && !error)
error = ret;
}
if (error && NFS_ISV4(vp))
error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0);
return (error);
}
/*
* nfs read rpc call
* Ditto above
*/
int
ncl_readrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred)
{
int error, ret, attrflag;
struct nfsvattr nfsva;
error = nfsrpc_read(vp, uiop, cred, uiop->uio_td, &nfsva, &attrflag,
NULL);
if (attrflag) {
ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
if (ret && !error)
error = ret;
}
if (error && NFS_ISV4(vp))
error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0);
return (error);
}
/*
* nfs write call
*/
int
ncl_writerpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
int *iomode, int *must_commit, int called_from_strategy)
{
struct nfsvattr nfsva;
int error = 0, attrflag, ret;
u_char verf[NFSX_VERF];
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
*must_commit = 0;
error = nfsrpc_write(vp, uiop, iomode, verf, cred,
uiop->uio_td, &nfsva, &attrflag, NULL, called_from_strategy);
NFSLOCKMNT(nmp);
if (!error && NFSHASWRITEVERF(nmp) &&
NFSBCMP(verf, nmp->nm_verf, NFSX_VERF)) {
*must_commit = 1;
NFSBCOPY(verf, nmp->nm_verf, NFSX_VERF);
}
NFSUNLOCKMNT(nmp);
if (attrflag) {
if (VTONFS(vp)->n_flag & ND_NFSV4)
ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 1,
1);
else
ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
1);
if (ret && !error)
error = ret;
}
if (vp->v_mount->mnt_kern_flag & MNTK_ASYNC)
*iomode = NFSWRITE_FILESYNC;
if (error && NFS_ISV4(vp))
error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0);
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.
*/
static int
nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
struct vattr *vap)
{
struct nfsvattr nfsva, dnfsva;
struct vnode *newvp = NULL;
struct nfsnode *np = NULL, *dnp;
struct nfsfh *nfhp;
struct vattr vattr;
int error = 0, attrflag, dattrflag;
u_int32_t rdev;
if (vap->va_type == VCHR || vap->va_type == VBLK)
rdev = vap->va_rdev;
else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
rdev = 0xffffffff;
else
return (EOPNOTSUPP);
if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
return (error);
error = nfsrpc_mknod(dvp, cnp->cn_nameptr, cnp->cn_namelen, vap,
rdev, vap->va_type, cnp->cn_cred, cnp->cn_thread, &dnfsva,
&nfsva, &nfhp, &attrflag, &dattrflag, NULL);
if (!error) {
if (!nfhp)
(void) nfsrpc_lookup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread,
&dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
NULL);
if (nfhp)
error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp,
cnp->cn_thread, &np, NULL);
}
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
if (!error) {
newvp = NFSTOV(np);
if (attrflag)
error = nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
}
if (!error) {
if ((cnp->cn_flags & MAKEENTRY))
cache_enter(dvp, newvp, cnp);
*vpp = newvp;
} else if (NFS_ISV4(dvp)) {
error = nfscl_maperr(cnp->cn_thread, error, vap->va_uid,
vap->va_gid);
}
dnp = VTONFS(dvp);
mtx_lock(&dnp->n_mtx);
dnp->n_flag |= NMODIFIED;
if (!dattrflag)
dnp->n_attrstamp = 0;
mtx_unlock(&dnp->n_mtx);
return (error);
}
/*
* nfs mknod vop
* just call nfs_mknodrpc() to do the work.
*/
/* ARGSUSED */
static int
nfs_mknod(struct vop_mknod_args *ap)
{
return (nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap));
}
static u_long create_verf;
/*
* nfs file create call
*/
static int
nfs_create(struct vop_create_args *ap)
{
struct vnode *dvp = ap->a_dvp;
struct vattr *vap = ap->a_vap;
struct componentname *cnp = ap->a_cnp;
struct nfsnode *np = NULL, *dnp;
struct vnode *newvp = NULL;
struct nfsmount *nmp;
struct nfsvattr dnfsva, nfsva;
struct nfsfh *nfhp;
nfsquad_t cverf;
int error = 0, attrflag, dattrflag, fmode = 0;
struct vattr vattr;
/*
* Oops, not for me..
*/
if (vap->va_type == VSOCK)
return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap));
if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
return (error);
if (vap->va_vaflags & VA_EXCLUSIVE)
fmode |= O_EXCL;
dnp = VTONFS(dvp);
nmp = VFSTONFS(vnode_mount(dvp));
again:
/* For NFSv4, wait until any remove is done. */
mtx_lock(&dnp->n_mtx);
while (NFSHASNFSV4(nmp) && (dnp->n_flag & NREMOVEINPROG)) {
dnp->n_flag |= NREMOVEWANT;
(void) msleep((caddr_t)dnp, &dnp->n_mtx, PZERO, "nfscrt", 0);
}
mtx_unlock(&dnp->n_mtx);
#ifdef INET
CURVNET_SET(CRED_TO_VNET(cnp->cn_cred));
IN_IFADDR_RLOCK();
if (!TAILQ_EMPTY(&V_in_ifaddrhead))
cverf.lval[0] = IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->sin_addr.s_addr;
else
#endif
cverf.lval[0] = create_verf;
#ifdef INET
IN_IFADDR_RUNLOCK();
CURVNET_RESTORE();
#endif
cverf.lval[1] = ++create_verf;
error = nfsrpc_create(dvp, cnp->cn_nameptr, cnp->cn_namelen,
vap, cverf, fmode, cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva,
&nfhp, &attrflag, &dattrflag, NULL);
if (!error) {
if (nfhp == NULL)
(void) nfsrpc_lookup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread,
&dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
NULL);
if (nfhp != NULL)
error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp,
cnp->cn_thread, &np, NULL);
}
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
if (!error) {
newvp = NFSTOV(np);
if (attrflag)
error = nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
}
if (error) {
if (newvp != NULL) {
vrele(newvp);
newvp = NULL;
}
if (NFS_ISV34(dvp) && (fmode & O_EXCL) &&
error == NFSERR_NOTSUPP) {
fmode &= ~O_EXCL;
goto again;
}
} else if (NFS_ISV34(dvp) && (fmode & O_EXCL)) {
if (nfscl_checksattr(vap, &nfsva)) {
error = nfsrpc_setattr(newvp, vap, NULL, cnp->cn_cred,
cnp->cn_thread, &nfsva, &attrflag, NULL);
if (error && (vap->va_uid != (uid_t)VNOVAL ||
vap->va_gid != (gid_t)VNOVAL)) {
/* try again without setting uid/gid */
vap->va_uid = (uid_t)VNOVAL;
vap->va_gid = (uid_t)VNOVAL;
error = nfsrpc_setattr(newvp, vap, NULL,
cnp->cn_cred, cnp->cn_thread, &nfsva,
&attrflag, NULL);
}
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL,
NULL, 0, 1);
}
}
if (!error) {
if (cnp->cn_flags & MAKEENTRY)
cache_enter(dvp, newvp, cnp);
*ap->a_vpp = newvp;
} else if (NFS_ISV4(dvp)) {
error = nfscl_maperr(cnp->cn_thread, error, vap->va_uid,
vap->va_gid);
}
mtx_lock(&dnp->n_mtx);
dnp->n_flag |= NMODIFIED;
if (!dattrflag)
dnp->n_attrstamp = 0;
mtx_unlock(&dnp->n_mtx);
return (error);
}
/*
* nfs file remove call
* To try and make nfs semantics closer to ufs semantics, a file that has
* other processes using the vnode is renamed instead of removed and then
* removed later on the last close.
* - If v_usecount > 1
* If a rename is not already in the works
* call nfs_sillyrename() to set it up
* else
* do the remove rpc
*/
static int
nfs_remove(struct vop_remove_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
struct nfsnode *np = VTONFS(vp);
int error = 0;
struct vattr vattr;
KASSERT((cnp->cn_flags & HASBUF) != 0, ("nfs_remove: no name"));
KASSERT(vrefcnt(vp) > 0, ("nfs_remove: bad v_usecount"));
if (vp->v_type == VDIR)
error = EPERM;
else if (vrefcnt(vp) == 1 || (np->n_sillyrename &&
VOP_GETATTR(vp, &vattr, cnp->cn_cred) == 0 &&
vattr.va_nlink > 1)) {
/*
* Purge the name cache so that the chance of a lookup for
* the name succeeding while the remove is in progress is
* minimized. Without node locking it can still happen, such
* that an I/O op returns ESTALE, but since you get this if
* another host removes the file..
*/
cache_purge(vp);
/*
* throw away biocache buffers, mainly to avoid
* unnecessary delayed writes later.
*/
error = ncl_vinvalbuf(vp, 0, cnp->cn_thread, 1);
/* Do the rpc */
if (error != EINTR && error != EIO)
error = nfs_removerpc(dvp, vp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread);
/*
* Kludge City: If the first reply to the remove rpc is lost..
* the reply to the retransmitted request will be ENOENT
* since the file was in fact removed
* Therefore, we cheat and return success.
*/
if (error == ENOENT)
error = 0;
} else if (!np->n_sillyrename)
error = nfs_sillyrename(dvp, vp, cnp);
np->n_attrstamp = 0;
return (error);
}
/*
* nfs file remove rpc called from nfs_inactive
*/
int
ncl_removeit(struct sillyrename *sp, struct vnode *vp)
{
/*
* Make sure that the directory vnode is still valid.
* XXX we should lock sp->s_dvp here.
*/
if (sp->s_dvp->v_type == VBAD)
return (0);
return (nfs_removerpc(sp->s_dvp, vp, sp->s_name, sp->s_namlen,
sp->s_cred, NULL));
}
/*
* Nfs remove rpc, called from nfs_remove() and ncl_removeit().
*/
static int
nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name,
int namelen, struct ucred *cred, struct thread *td)
{
struct nfsvattr dnfsva;
struct nfsnode *dnp = VTONFS(dvp);
int error = 0, dattrflag;
mtx_lock(&dnp->n_mtx);
dnp->n_flag |= NREMOVEINPROG;
mtx_unlock(&dnp->n_mtx);
error = nfsrpc_remove(dvp, name, namelen, vp, cred, td, &dnfsva,
&dattrflag, NULL);
mtx_lock(&dnp->n_mtx);
if ((dnp->n_flag & NREMOVEWANT)) {
dnp->n_flag &= ~(NREMOVEWANT | NREMOVEINPROG);
mtx_unlock(&dnp->n_mtx);
wakeup((caddr_t)dnp);
} else {
dnp->n_flag &= ~NREMOVEINPROG;
mtx_unlock(&dnp->n_mtx);
}
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
mtx_lock(&dnp->n_mtx);
dnp->n_flag |= NMODIFIED;
if (!dattrflag)
dnp->n_attrstamp = 0;
mtx_unlock(&dnp->n_mtx);
if (error && NFS_ISV4(dvp))
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
return (error);
}
/*
* nfs file rename call
*/
static int
nfs_rename(struct vop_rename_args *ap)
{
struct vnode *fvp = ap->a_fvp;
struct vnode *tvp = ap->a_tvp;
struct vnode *fdvp = ap->a_fdvp;
struct vnode *tdvp = ap->a_tdvp;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
struct nfsnode *fnp = VTONFS(ap->a_fvp);
struct nfsnode *tdnp = VTONFS(ap->a_tdvp);
struct nfsv4node *newv4 = NULL;
int error;
KASSERT((tcnp->cn_flags & HASBUF) != 0 &&
(fcnp->cn_flags & HASBUF) != 0, ("nfs_rename: no name"));
/* Check for cross-device rename */
if ((fvp->v_mount != tdvp->v_mount) ||
(tvp && (fvp->v_mount != tvp->v_mount))) {
error = EXDEV;
goto out;
}
if (fvp == tvp) {
ncl_printf("nfs_rename: fvp == tvp (can't happen)\n");
error = 0;
goto out;
}
if ((error = vn_lock(fvp, LK_EXCLUSIVE)))
goto out;
/*
* We have to flush B_DELWRI data prior to renaming
* the file. If we don't, the delayed-write buffers
* can be flushed out later after the file has gone stale
* under NFSV3. NFSV2 does not have this problem because
* ( as far as I can tell ) it flushes dirty buffers more
* often.
*
* Skip the rename operation if the fsync fails, this can happen
* due to the server's volume being full, when we pushed out data
* that was written back to our cache earlier. Not checking for
* this condition can result in potential (silent) data loss.
*/
error = VOP_FSYNC(fvp, MNT_WAIT, fcnp->cn_thread);
VOP_UNLOCK(fvp, 0);
if (!error && tvp)
error = VOP_FSYNC(tvp, MNT_WAIT, tcnp->cn_thread);
if (error)
goto out;
/*
* If the tvp exists and is in use, sillyrename it before doing the
* rename of the new file over it.
* XXX Can't sillyrename a directory.
*/
if (tvp && vrefcnt(tvp) > 1 && !VTONFS(tvp)->n_sillyrename &&
tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) {
vput(tvp);
tvp = NULL;
}
error = nfs_renamerpc(fdvp, fvp, fcnp->cn_nameptr, fcnp->cn_namelen,
tdvp, tvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred,
tcnp->cn_thread);
if (!error) {
/*
* For NFSv4, check to see if it is the same name and
* replace the name, if it is different.
*/
MALLOC(newv4, struct nfsv4node *,
sizeof (struct nfsv4node) +
tdnp->n_fhp->nfh_len + tcnp->cn_namelen - 1,
M_NFSV4NODE, M_WAITOK);
mtx_lock(&tdnp->n_mtx);
mtx_lock(&fnp->n_mtx);
if (fnp->n_v4 != NULL && fvp->v_type == VREG &&
(fnp->n_v4->n4_namelen != tcnp->cn_namelen ||
NFSBCMP(tcnp->cn_nameptr, NFS4NODENAME(fnp->n_v4),
tcnp->cn_namelen) ||
tdnp->n_fhp->nfh_len != fnp->n_v4->n4_fhlen ||
NFSBCMP(tdnp->n_fhp->nfh_fh, fnp->n_v4->n4_data,
tdnp->n_fhp->nfh_len))) {
#ifdef notdef
{ char nnn[100]; int nnnl;
nnnl = (tcnp->cn_namelen < 100) ? tcnp->cn_namelen : 99;
bcopy(tcnp->cn_nameptr, nnn, nnnl);
nnn[nnnl] = '\0';
printf("ren replace=%s\n",nnn);
}
#endif
FREE((caddr_t)fnp->n_v4, M_NFSV4NODE);
fnp->n_v4 = newv4;
newv4 = NULL;
fnp->n_v4->n4_fhlen = tdnp->n_fhp->nfh_len;
fnp->n_v4->n4_namelen = tcnp->cn_namelen;
NFSBCOPY(tdnp->n_fhp->nfh_fh, fnp->n_v4->n4_data,
tdnp->n_fhp->nfh_len);
NFSBCOPY(tcnp->cn_nameptr,
NFS4NODENAME(fnp->n_v4), tcnp->cn_namelen);
}
mtx_unlock(&tdnp->n_mtx);
mtx_unlock(&fnp->n_mtx);
if (newv4 != NULL)
FREE((caddr_t)newv4, M_NFSV4NODE);
}
if (fvp->v_type == VDIR) {
if (tvp != NULL && tvp->v_type == VDIR)
cache_purge(tdvp);
cache_purge(fdvp);
}
out:
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vput(tvp);
vrele(fdvp);
vrele(fvp);
/*
* Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
*/
if (error == ENOENT)
error = 0;
return (error);
}
/*
* nfs file rename rpc called from nfs_remove() above
*/
static int
nfs_renameit(struct vnode *sdvp, struct vnode *svp, struct componentname *scnp,
struct sillyrename *sp)
{
return (nfs_renamerpc(sdvp, svp, scnp->cn_nameptr, scnp->cn_namelen,
sdvp, NULL, sp->s_name, sp->s_namlen, scnp->cn_cred,
scnp->cn_thread));
}
/*
* Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit().
*/
static int
nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, char *fnameptr,
int fnamelen, struct vnode *tdvp, struct vnode *tvp, char *tnameptr,
int tnamelen, struct ucred *cred, struct thread *td)
{
struct nfsvattr fnfsva, tnfsva;
struct nfsnode *fdnp = VTONFS(fdvp);
struct nfsnode *tdnp = VTONFS(tdvp);
int error = 0, fattrflag, tattrflag;
error = nfsrpc_rename(fdvp, fvp, fnameptr, fnamelen, tdvp, tvp,
tnameptr, tnamelen, cred, td, &fnfsva, &tnfsva, &fattrflag,
&tattrflag, NULL, NULL);
mtx_lock(&fdnp->n_mtx);
fdnp->n_flag |= NMODIFIED;
mtx_unlock(&fdnp->n_mtx);
mtx_lock(&tdnp->n_mtx);
tdnp->n_flag |= NMODIFIED;
mtx_unlock(&tdnp->n_mtx);
if (fattrflag)
(void) nfscl_loadattrcache(&fdvp, &fnfsva, NULL, NULL, 0, 1);
else
fdnp->n_attrstamp = 0;
if (tattrflag)
(void) nfscl_loadattrcache(&tdvp, &tnfsva, NULL, NULL, 0, 1);
else
tdnp->n_attrstamp = 0;
if (error && NFS_ISV4(fdvp))
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
return (error);
}
/*
* nfs hard link create call
*/
static int
nfs_link(struct vop_link_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *tdvp = ap->a_tdvp;
struct componentname *cnp = ap->a_cnp;
struct nfsnode *tdnp;
struct nfsvattr nfsva, dnfsva;
int error = 0, attrflag, dattrflag;
if (vp->v_mount != tdvp->v_mount) {
return (EXDEV);
}
/*
* Push all writes to the server, so that the attribute cache
* doesn't get "out of sync" with the server.
* XXX There should be a better way!
*/
VOP_FSYNC(vp, MNT_WAIT, cnp->cn_thread);
error = nfsrpc_link(tdvp, vp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva, &attrflag,
&dattrflag, NULL);
tdnp = VTONFS(tdvp);
mtx_lock(&tdnp->n_mtx);
tdnp->n_flag |= NMODIFIED;
mtx_unlock(&tdnp->n_mtx);
if (attrflag)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
else
VTONFS(vp)->n_attrstamp = 0;
if (dattrflag)
(void) nfscl_loadattrcache(&tdvp, &dnfsva, NULL, NULL, 0, 1);
else
tdnp->n_attrstamp = 0;
/*
* If negative lookup caching is enabled, I might as well
* add an entry for this node. Not necessary for correctness,
* but if negative caching is enabled, then the system
* must care about lookup caching hit rate, so...
*/
if (VFSTONFS(vp->v_mount)->nm_negnametimeo != 0 &&
(cnp->cn_flags & MAKEENTRY))
cache_enter(tdvp, vp, cnp);
if (error && NFS_ISV4(vp))
error = nfscl_maperr(cnp->cn_thread, error, (uid_t)0,
(gid_t)0);
return (error);
}
/*
* nfs symbolic link create call
*/
static int
nfs_symlink(struct vop_symlink_args *ap)
{
struct vnode *dvp = ap->a_dvp;
struct vattr *vap = ap->a_vap;
struct componentname *cnp = ap->a_cnp;
struct nfsvattr nfsva, dnfsva;
struct nfsfh *nfhp;
struct nfsnode *np = NULL, *dnp;
struct vnode *newvp = NULL;
int error = 0, attrflag, dattrflag, ret;
vap->va_type = VLNK;
error = nfsrpc_symlink(dvp, cnp->cn_nameptr, cnp->cn_namelen,
ap->a_target, vap, cnp->cn_cred, cnp->cn_thread, &dnfsva,
&nfsva, &nfhp, &attrflag, &dattrflag, NULL);
if (nfhp) {
ret = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, cnp->cn_thread,
&np, NULL);
if (!ret)
newvp = NFSTOV(np);
else if (!error)
error = ret;
}
if (newvp != NULL) {
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
} else if (!error) {
/*
* If we do not have an error and we could not extract the
* newvp from the response due to the request being NFSv2, we
* have to do a lookup in order to obtain a newvp to return.
*/
error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_cred, cnp->cn_thread, &np);
if (!error)
newvp = NFSTOV(np);
}
if (error) {
if (newvp)
vput(newvp);
if (NFS_ISV4(dvp))
error = nfscl_maperr(cnp->cn_thread, error,
vap->va_uid, vap->va_gid);
} else {
/*
* If negative lookup caching is enabled, I might as well
* add an entry for this node. Not necessary for correctness,
* but if negative caching is enabled, then the system
* must care about lookup caching hit rate, so...
*/
if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 &&
(cnp->cn_flags & MAKEENTRY))
cache_enter(dvp, newvp, cnp);
*ap->a_vpp = newvp;
}
dnp = VTONFS(dvp);
mtx_lock(&dnp->n_mtx);
dnp->n_flag |= NMODIFIED;
mtx_unlock(&dnp->n_mtx);
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
else
dnp->n_attrstamp = 0;
return (error);
}
/*
* nfs make dir call
*/
static int
nfs_mkdir(struct vop_mkdir_args *ap)
{
struct vnode *dvp = ap->a_dvp;
struct vattr *vap = ap->a_vap;
struct componentname *cnp = ap->a_cnp;
struct nfsnode *np = NULL, *dnp;
struct vnode *newvp = NULL;
struct vattr vattr;
struct nfsfh *nfhp;
struct nfsvattr nfsva, dnfsva;
int error = 0, attrflag, dattrflag, ret;
if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
return (error);
vap->va_type = VDIR;
error = nfsrpc_mkdir(dvp, cnp->cn_nameptr, cnp->cn_namelen,
vap, cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva, &nfhp,
&attrflag, &dattrflag, NULL);
dnp = VTONFS(dvp);
mtx_lock(&dnp->n_mtx);
dnp->n_flag |= NMODIFIED;
mtx_unlock(&dnp->n_mtx);
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
else
dnp->n_attrstamp = 0;
if (nfhp) {
ret = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, cnp->cn_thread,
&np, NULL);
if (!ret) {
newvp = NFSTOV(np);
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL,
NULL, 0, 1);
} else if (!error)
error = ret;
}
if (!error && newvp == NULL) {
error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_cred, cnp->cn_thread, &np);
if (!error) {
newvp = NFSTOV(np);
if (newvp->v_type != VDIR)
error = EEXIST;
}
}
if (error) {
if (newvp)
vput(newvp);
if (NFS_ISV4(dvp))
error = nfscl_maperr(cnp->cn_thread, error,
vap->va_uid, vap->va_gid);
} else {
/*
* If negative lookup caching is enabled, I might as well
* add an entry for this node. Not necessary for correctness,
* but if negative caching is enabled, then the system
* must care about lookup caching hit rate, so...
*/
if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 &&
(cnp->cn_flags & MAKEENTRY))
cache_enter(dvp, newvp, cnp);
*ap->a_vpp = newvp;
}
return (error);
}
/*
* nfs remove directory call
*/
static int
nfs_rmdir(struct vop_rmdir_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
struct nfsnode *dnp;
struct nfsvattr dnfsva;
int error, dattrflag;
if (dvp == vp)
return (EINVAL);
error = nfsrpc_rmdir(dvp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_cred, cnp->cn_thread, &dnfsva, &dattrflag, NULL);
dnp = VTONFS(dvp);
mtx_lock(&dnp->n_mtx);
dnp->n_flag |= NMODIFIED;
mtx_unlock(&dnp->n_mtx);
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
else
dnp->n_attrstamp = 0;
cache_purge(dvp);
cache_purge(vp);
if (error && NFS_ISV4(dvp))
error = nfscl_maperr(cnp->cn_thread, error, (uid_t)0,
(gid_t)0);
/*
* Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
*/
if (error == ENOENT)
error = 0;
return (error);
}
/*
* nfs readdir call
*/
static int
nfs_readdir(struct vop_readdir_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct uio *uio = ap->a_uio;
int tresid, error = 0;
struct vattr vattr;
if (vp->v_type != VDIR)
return(EPERM);
/*
* First, check for hit on the EOF offset cache
*/
if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
(np->n_flag & NMODIFIED) == 0) {
if (VOP_GETATTR(vp, &vattr, ap->a_cred) == 0) {
mtx_lock(&np->n_mtx);
if ((NFS_ISV4(vp) && np->n_change == vattr.va_filerev) ||
!NFS_TIMESPEC_COMPARE(&np->n_mtime, &vattr.va_mtime)) {
mtx_unlock(&np->n_mtx);
NFSINCRGLOBAL(newnfsstats.direofcache_hits);
return (0);
} else
mtx_unlock(&np->n_mtx);
}
}
/*
* Call ncl_bioread() to do the real work.
*/
tresid = uio->uio_resid;
error = ncl_bioread(vp, uio, 0, ap->a_cred);
if (!error && uio->uio_resid == tresid)
NFSINCRGLOBAL(newnfsstats.direofcache_misses);
return (error);
}
/*
* Readdir rpc call.
* Called from below the buffer cache by ncl_doio().
*/
int
ncl_readdirrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
struct thread *td)
{
struct nfsvattr nfsva;
nfsuint64 *cookiep, cookie;
struct nfsnode *dnp = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0, eof, attrflag;
KASSERT(uiop->uio_iovcnt == 1 &&
(uiop->uio_offset & (DIRBLKSIZ - 1)) == 0 &&
(uiop->uio_resid & (DIRBLKSIZ - 1)) == 0,
("nfs readdirrpc bad uio"));
/*
* If there is no cookie, assume directory was stale.
*/
ncl_dircookie_lock(dnp);
cookiep = ncl_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep) {
cookie = *cookiep;
ncl_dircookie_unlock(dnp);
} else {
ncl_dircookie_unlock(dnp);
return (NFSERR_BAD_COOKIE);
}
if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp))
(void)ncl_fsinfo(nmp, vp, cred, td);
error = nfsrpc_readdir(vp, uiop, &cookie, cred, td, &nfsva,
&attrflag, &eof, NULL);
if (attrflag)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
if (!error) {
/*
* We are now either at the end of the directory or have filled
* the block.
*/
if (eof)
dnp->n_direofoffset = uiop->uio_offset;
else {
if (uiop->uio_resid > 0)
ncl_printf("EEK! readdirrpc resid > 0\n");
ncl_dircookie_lock(dnp);
cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1);
*cookiep = cookie;
ncl_dircookie_unlock(dnp);
}
} else if (NFS_ISV4(vp)) {
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
}
return (error);
}
/*
* NFS V3 readdir plus RPC. Used in place of ncl_readdirrpc().
*/
int
ncl_readdirplusrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
struct thread *td)
{
struct nfsvattr nfsva;
nfsuint64 *cookiep, cookie;
struct nfsnode *dnp = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0, attrflag, eof;
KASSERT(uiop->uio_iovcnt == 1 &&
(uiop->uio_offset & (DIRBLKSIZ - 1)) == 0 &&
(uiop->uio_resid & (DIRBLKSIZ - 1)) == 0,
("nfs readdirplusrpc bad uio"));
/*
* If there is no cookie, assume directory was stale.
*/
ncl_dircookie_lock(dnp);
cookiep = ncl_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep) {
cookie = *cookiep;
ncl_dircookie_unlock(dnp);
} else {
ncl_dircookie_unlock(dnp);
return (NFSERR_BAD_COOKIE);
}
if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp))
(void)ncl_fsinfo(nmp, vp, cred, td);
error = nfsrpc_readdirplus(vp, uiop, &cookie, cred, td, &nfsva,
&attrflag, &eof, NULL);
if (attrflag)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
if (!error) {
/*
* We are now either at end of the directory or have filled the
* the block.
*/
if (eof)
dnp->n_direofoffset = uiop->uio_offset;
else {
if (uiop->uio_resid > 0)
ncl_printf("EEK! readdirplusrpc resid > 0\n");
ncl_dircookie_lock(dnp);
cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1);
*cookiep = cookie;
ncl_dircookie_unlock(dnp);
}
} else if (NFS_ISV4(vp)) {
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
}
return (error);
}
/*
* Silly rename. To make the NFS filesystem that is stateless look a little
* more like the "ufs" a remove of an active vnode is translated to a rename
* to a funny looking filename that is removed by nfs_inactive on the
* nfsnode. There is the potential for another process on a different client
* to create the same funny name between the nfs_lookitup() fails and the
* nfs_rename() completes, but...
*/
static int
nfs_sillyrename(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
{
struct sillyrename *sp;
struct nfsnode *np;
int error;
short pid;
unsigned int lticks;
cache_purge(dvp);
np = VTONFS(vp);
KASSERT(vp->v_type != VDIR, ("nfs: sillyrename dir"));
MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename),
M_NEWNFSREQ, M_WAITOK);
sp->s_cred = crhold(cnp->cn_cred);
sp->s_dvp = dvp;
VREF(dvp);
/*
* Fudge together a funny name.
* Changing the format of the funny name to accomodate more
* sillynames per directory.
* The name is now changed to .nfs.<ticks>.<pid>.4, where ticks is
* CPU ticks since boot.
*/
pid = cnp->cn_thread->td_proc->p_pid;
lticks = (unsigned int)ticks;
for ( ; ; ) {
sp->s_namlen = sprintf(sp->s_name,
".nfs.%08x.%04x4.4", lticks,
pid);
if (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
cnp->cn_thread, NULL))
break;
lticks++;
}
error = nfs_renameit(dvp, vp, cnp, sp);
if (error)
goto bad;
error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
cnp->cn_thread, &np);
np->n_sillyrename = sp;
return (0);
bad:
vrele(sp->s_dvp);
crfree(sp->s_cred);
free((caddr_t)sp, M_NEWNFSREQ);
return (error);
}
/*
* Look up a file name and optionally either update the file handle or
* allocate an nfsnode, depending on the value of npp.
* npp == NULL --> just do the lookup
* *npp == NULL --> allocate a new nfsnode and make sure attributes are
* handled too
* *npp != NULL --> update the file handle in the vnode
*/
static int
nfs_lookitup(struct vnode *dvp, char *name, int len, struct ucred *cred,
struct thread *td, struct nfsnode **npp)
{
struct vnode *newvp = NULL, *vp;
struct nfsnode *np, *dnp = VTONFS(dvp);
struct nfsfh *nfhp, *onfhp;
struct nfsvattr nfsva, dnfsva;
struct componentname cn;
int error = 0, attrflag, dattrflag;
u_int hash;
error = nfsrpc_lookup(dvp, name, len, cred, td, &dnfsva, &nfsva,
&nfhp, &attrflag, &dattrflag, NULL);
if (dattrflag)
(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
if (npp && !error) {
if (*npp != NULL) {
np = *npp;
vp = NFSTOV(np);
/*
* For NFSv4, check to see if it is the same name and
* replace the name, if it is different.
*/
if (np->n_v4 != NULL && nfsva.na_type == VREG &&
(np->n_v4->n4_namelen != len ||
NFSBCMP(name, NFS4NODENAME(np->n_v4), len) ||
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))) {
#ifdef notdef
{ char nnn[100]; int nnnl;
nnnl = (len < 100) ? len : 99;
bcopy(name, nnn, nnnl);
nnn[nnnl] = '\0';
printf("replace=%s\n",nnn);
}
#endif
FREE((caddr_t)np->n_v4, M_NFSV4NODE);
MALLOC(np->n_v4, struct nfsv4node *,
sizeof (struct nfsv4node) +
dnp->n_fhp->nfh_len + len - 1,
M_NFSV4NODE, M_WAITOK);
np->n_v4->n4_fhlen = dnp->n_fhp->nfh_len;
np->n_v4->n4_namelen = len;
NFSBCOPY(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
dnp->n_fhp->nfh_len);
NFSBCOPY(name, NFS4NODENAME(np->n_v4), len);
}
hash = fnv_32_buf(nfhp->nfh_fh, nfhp->nfh_len,
FNV1_32_INIT);
onfhp = np->n_fhp;
/*
* Rehash node for new file handle.
*/
vfs_hash_rehash(vp, hash);
np->n_fhp = nfhp;
if (onfhp != NULL)
FREE((caddr_t)onfhp, M_NFSFH);
newvp = NFSTOV(np);
} else if (NFS_CMPFH(dnp, nfhp->nfh_fh, nfhp->nfh_len)) {
FREE((caddr_t)nfhp, M_NFSFH);
VREF(dvp);
newvp = dvp;
} else {
cn.cn_nameptr = name;
cn.cn_namelen = len;
error = nfscl_nget(dvp->v_mount, dvp, nfhp, &cn, td,
&np, NULL);
if (error)
return (error);
newvp = NFSTOV(np);
}
if (!attrflag && *npp == NULL) {
vrele(newvp);
return (ENOENT);
}
if (attrflag)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
}
if (npp && *npp == NULL) {
if (error) {
if (newvp) {
if (newvp == dvp)
vrele(newvp);
else
vput(newvp);
}
} else
*npp = np;
}
if (error && NFS_ISV4(dvp))
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
return (error);
}
/*
* Nfs Version 3 and 4 commit rpc
*/
int
ncl_commit(struct vnode *vp, u_quad_t offset, int cnt, struct ucred *cred,
struct thread *td)
{
struct nfsvattr nfsva;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error, attrflag;
u_char verf[NFSX_VERF];
mtx_lock(&nmp->nm_mtx);
if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0) {
mtx_unlock(&nmp->nm_mtx);
return (0);
}
mtx_unlock(&nmp->nm_mtx);
error = nfsrpc_commit(vp, offset, cnt, cred, td, verf, &nfsva,
&attrflag, NULL);
if (!error) {
if (NFSBCMP((caddr_t)nmp->nm_verf, verf, NFSX_VERF)) {
NFSBCOPY(verf, (caddr_t)nmp->nm_verf, NFSX_VERF);
error = NFSERR_STALEWRITEVERF;
}
if (!error && attrflag)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL,
0, 1);
} else if (NFS_ISV4(vp)) {
error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
}
return (error);
}
/*
* Strategy routine.
* For async requests when nfsiod(s) are running, queue the request by
* calling ncl_asyncio(), otherwise just all ncl_doio() to do the
* request.
*/
static int
nfs_strategy(struct vop_strategy_args *ap)
{
struct buf *bp = ap->a_bp;
struct ucred *cr;
KASSERT(!(bp->b_flags & B_DONE),
("nfs_strategy: buffer %p unexpectedly marked B_DONE", bp));
BUF_ASSERT_HELD(bp);
if (bp->b_iocmd == BIO_READ)
cr = bp->b_rcred;
else
cr = bp->b_wcred;
/*
* If the op is asynchronous and an i/o daemon is waiting
* queue the request, wake it up and wait for completion
* otherwise just do it ourselves.
*/
if ((bp->b_flags & B_ASYNC) == 0 ||
ncl_asyncio(VFSTONFS(ap->a_vp->v_mount), bp, NOCRED, curthread))
(void) ncl_doio(ap->a_vp, bp, cr, curthread, 1);
return (0);
}
/*
* fsync vnode op. Just call ncl_flush() with commit == 1.
*/
/* ARGSUSED */
static int
nfs_fsync(struct vop_fsync_args *ap)
{
return (ncl_flush(ap->a_vp, ap->a_waitfor, NULL, ap->a_td, 1, 0));
}
/*
* Flush all the blocks associated with a vnode.
* Walk through the buffer pool and push any dirty pages
* associated with the vnode.
* If the called_from_renewthread argument is TRUE, it has been called
* from the NFSv4 renew thread and, as such, cannot block indefinitely
* waiting for a buffer write to complete.
*/
int
ncl_flush(struct vnode *vp, int waitfor, struct ucred *cred, struct thread *td,
int commit, int called_from_renewthread)
{
struct nfsnode *np = VTONFS(vp);
struct buf *bp;
int i;
struct buf *nbp;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos;
int passone = 1, trycnt = 0;
u_quad_t off, endoff, toff;
struct ucred* wcred = NULL;
struct buf **bvec = NULL;
struct bufobj *bo;
#ifndef NFS_COMMITBVECSIZ
#define NFS_COMMITBVECSIZ 20
#endif
struct buf *bvec_on_stack[NFS_COMMITBVECSIZ];
int bvecsize = 0, bveccount;
if (called_from_renewthread != 0)
slptimeo = hz;
if (nmp->nm_flag & NFSMNT_INT)
slpflag = NFS_PCATCH;
if (!commit)
passone = 0;
bo = &vp->v_bufobj;
/*
* A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the
* server, but has not been committed to stable storage on the server
* yet. On the first pass, the byte range is worked out and the commit
* rpc is done. On the second pass, ncl_writebp() is called to do the
* job.
*/
again:
off = (u_quad_t)-1;
endoff = 0;
bvecpos = 0;
if (NFS_ISV34(vp) && commit) {
if (bvec != NULL && bvec != bvec_on_stack)
free(bvec, M_TEMP);
/*
* Count up how many buffers waiting for a commit.
*/
bveccount = 0;
BO_LOCK(bo);
TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
if (!BUF_ISLOCKED(bp) &&
(bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
== (B_DELWRI | B_NEEDCOMMIT))
bveccount++;
}
/*
* Allocate space to remember the list of bufs to commit. It is
* important to use M_NOWAIT here to avoid a race with nfs_write.
* If we can't get memory (for whatever reason), we will end up
* committing the buffers one-by-one in the loop below.
*/
if (bveccount > NFS_COMMITBVECSIZ) {
/*
* Release the vnode interlock to avoid a lock
* order reversal.
*/
BO_UNLOCK(bo);
bvec = (struct buf **)
malloc(bveccount * sizeof(struct buf *),
M_TEMP, M_NOWAIT);
BO_LOCK(bo);
if (bvec == NULL) {
bvec = bvec_on_stack;
bvecsize = NFS_COMMITBVECSIZ;
} else
bvecsize = bveccount;
} else {
bvec = bvec_on_stack;
bvecsize = NFS_COMMITBVECSIZ;
}
TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
if (bvecpos >= bvecsize)
break;
if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) {
nbp = TAILQ_NEXT(bp, b_bobufs);
continue;
}
if ((bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) !=
(B_DELWRI | B_NEEDCOMMIT)) {
BUF_UNLOCK(bp);
nbp = TAILQ_NEXT(bp, b_bobufs);
continue;
}
BO_UNLOCK(bo);
bremfree(bp);
/*
* Work out if all buffers are using the same cred
* so we can deal with them all with one commit.
*
* NOTE: we are not clearing B_DONE here, so we have
* to do it later on in this routine if we intend to
* initiate I/O on the bp.
*
* Note: to avoid loopback deadlocks, we do not
* assign b_runningbufspace.
*/
if (wcred == NULL)
wcred = bp->b_wcred;
else if (wcred != bp->b_wcred)
wcred = NOCRED;
vfs_busy_pages(bp, 1);
BO_LOCK(bo);
/*
* bp is protected by being locked, but nbp is not
* and vfs_busy_pages() may sleep. We have to
* recalculate nbp.
*/
nbp = TAILQ_NEXT(bp, b_bobufs);
/*
* A list of these buffers is kept so that the
* second loop knows which buffers have actually
* been committed. This is necessary, since there
* may be a race between the commit rpc and new
* uncommitted writes on the file.
*/
bvec[bvecpos++] = bp;
toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
bp->b_dirtyoff;
if (toff < off)
off = toff;
toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff);
if (toff > endoff)
endoff = toff;
}
BO_UNLOCK(bo);
}
if (bvecpos > 0) {
/*
* Commit data on the server, as required.
* If all bufs are using the same wcred, then use that with
* one call for all of them, otherwise commit each one
* separately.
*/
if (wcred != NOCRED)
retv = ncl_commit(vp, off, (int)(endoff - off),
wcred, td);
else {
retv = 0;
for (i = 0; i < bvecpos; i++) {
off_t off, size;
bp = bvec[i];
off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
bp->b_dirtyoff;
size = (u_quad_t)(bp->b_dirtyend
- bp->b_dirtyoff);
retv = ncl_commit(vp, off, (int)size,
bp->b_wcred, td);
if (retv) break;
}
}
if (retv == NFSERR_STALEWRITEVERF)
ncl_clearcommit(vp->v_mount);
/*
* Now, either mark the blocks I/O done or mark the
* blocks dirty, depending on whether the commit
* succeeded.
*/
for (i = 0; i < bvecpos; i++) {
bp = bvec[i];
bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
if (retv) {
/*
* Error, leave B_DELWRI intact
*/
vfs_unbusy_pages(bp);
brelse(bp);
} else {
/*
* Success, remove B_DELWRI ( bundirty() ).
*
* b_dirtyoff/b_dirtyend seem to be NFS
* specific. We should probably move that
* into bundirty(). XXX
*/
bufobj_wref(bo);
bp->b_flags |= B_ASYNC;
bundirty(bp);
bp->b_flags &= ~B_DONE;
bp->b_ioflags &= ~BIO_ERROR;
bp->b_dirtyoff = bp->b_dirtyend = 0;
bufdone(bp);
}
}
}
/*
* Start/do any write(s) that are required.
*/
loop:
BO_LOCK(bo);
TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) {
if (waitfor != MNT_WAIT || passone)
continue;
error = BUF_TIMELOCK(bp,
LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK,
BO_MTX(bo), "nfsfsync", slpflag, slptimeo);
if (error == 0) {
BUF_UNLOCK(bp);
goto loop;
}
if (error == ENOLCK) {
error = 0;
goto loop;
}
if (called_from_renewthread != 0) {
/*
* Return EIO so the flush will be retried
* later.
*/
error = EIO;
goto done;
}
if (newnfs_sigintr(nmp, td)) {
error = EINTR;
goto done;
}
if (slpflag & PCATCH) {
slpflag = 0;
slptimeo = 2 * hz;
}
goto loop;
}
if ((bp->b_flags & B_DELWRI) == 0)
panic("nfs_fsync: not dirty");
if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT)) {
BUF_UNLOCK(bp);
continue;
}
BO_UNLOCK(bo);
bremfree(bp);
if (passone || !commit)
bp->b_flags |= B_ASYNC;
else
bp->b_flags |= B_ASYNC;
bwrite(bp);
if (newnfs_sigintr(nmp, td)) {
error = EINTR;
goto done;
}
goto loop;
}
if (passone) {
passone = 0;
BO_UNLOCK(bo);
goto again;
}
if (waitfor == MNT_WAIT) {
while (bo->bo_numoutput) {
error = bufobj_wwait(bo, slpflag, slptimeo);
if (error) {
BO_UNLOCK(bo);
if (called_from_renewthread != 0) {
/*
* Return EIO so that the flush will be
* retried later.
*/
error = EIO;
goto done;
}
error = newnfs_sigintr(nmp, td);
if (error)
goto done;
if (slpflag & PCATCH) {
slpflag = 0;
slptimeo = 2 * hz;
}
BO_LOCK(bo);
}
}
if (bo->bo_dirty.bv_cnt != 0 && commit) {
BO_UNLOCK(bo);
goto loop;
}
/*
* Wait for all the async IO requests to drain
*/
BO_UNLOCK(bo);
mtx_lock(&np->n_mtx);
while (np->n_directio_asyncwr > 0) {
np->n_flag |= NFSYNCWAIT;
error = newnfs_msleep(td, &np->n_directio_asyncwr,
&np->n_mtx, slpflag | (PRIBIO + 1),
"nfsfsync", 0);
if (error) {
if (newnfs_sigintr(nmp, td)) {
mtx_unlock(&np->n_mtx);
error = EINTR;
goto done;
}
}
}
mtx_unlock(&np->n_mtx);
} else
BO_UNLOCK(bo);
mtx_lock(&np->n_mtx);
if (np->n_flag & NWRITEERR) {
error = np->n_error;
np->n_flag &= ~NWRITEERR;
}
if (commit && bo->bo_dirty.bv_cnt == 0 &&
bo->bo_numoutput == 0 && np->n_directio_asyncwr == 0)
np->n_flag &= ~NMODIFIED;
mtx_unlock(&np->n_mtx);
done:
if (bvec != NULL && bvec != bvec_on_stack)
free(bvec, M_TEMP);
if (error == 0 && commit != 0 && waitfor == MNT_WAIT &&
(bo->bo_dirty.bv_cnt != 0 || bo->bo_numoutput != 0 ||
np->n_directio_asyncwr != 0) && trycnt++ < 5) {
/* try, try again... */
passone = 1;
wcred = NULL;
bvec = NULL;
bvecsize = 0;
printf("try%d\n", trycnt);
goto again;
}
return (error);
}
/*
* NFS advisory byte-level locks.
*/
static int
nfs_advlock(struct vop_advlock_args *ap)
{
struct vnode *vp = ap->a_vp;
struct ucred *cred;
struct nfsnode *np = VTONFS(ap->a_vp);
struct proc *p = (struct proc *)ap->a_id;
struct thread *td = curthread; /* XXX */
struct vattr va;
int ret, error = EOPNOTSUPP;
u_quad_t size;
if (NFS_ISV4(vp) && (ap->a_flags & F_POSIX)) {
cred = p->p_ucred;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_iflag & VI_DOOMED) {
VOP_UNLOCK(vp, 0);
return (EBADF);
}
/*
* If this is unlocking a write locked region, flush and
* commit them before unlocking. This is required by
* RFC3530 Sec. 9.3.2.
*/
if (ap->a_op == F_UNLCK &&
nfscl_checkwritelocked(vp, ap->a_fl, cred, td))
(void) ncl_flush(vp, MNT_WAIT, cred, td, 1, 0);
/*
* Loop around doing the lock op, while a blocking lock
* must wait for the lock op to succeed.
*/
do {
ret = nfsrpc_advlock(vp, np->n_size, ap->a_op,
ap->a_fl, 0, cred, td);
if (ret == NFSERR_DENIED && (ap->a_flags & F_WAIT) &&
ap->a_op == F_SETLK) {
VOP_UNLOCK(vp, 0);
error = nfs_catnap(PZERO | PCATCH, ret,
"ncladvl");
if (error)
return (EINTR);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_iflag & VI_DOOMED) {
VOP_UNLOCK(vp, 0);
return (EBADF);
}
}
} while (ret == NFSERR_DENIED && (ap->a_flags & F_WAIT) &&
ap->a_op == F_SETLK);
if (ret == NFSERR_DENIED) {
VOP_UNLOCK(vp, 0);
return (EAGAIN);
} else if (ret == EINVAL || ret == EBADF || ret == EINTR) {
VOP_UNLOCK(vp, 0);
return (ret);
} else if (ret != 0) {
VOP_UNLOCK(vp, 0);
return (EACCES);
}
/*
* Now, if we just got a lock, invalidate data in the buffer
* cache, as required, so that the coherency conforms with
* RFC3530 Sec. 9.3.2.
*/
if (ap->a_op == F_SETLK) {
if ((np->n_flag & NMODIFIED) == 0) {
np->n_attrstamp = 0;
ret = VOP_GETATTR(vp, &va, cred);
}
if ((np->n_flag & NMODIFIED) || ret ||
np->n_change != va.va_filerev) {
(void) ncl_vinvalbuf(vp, V_SAVE, td, 1);
np->n_attrstamp = 0;
ret = VOP_GETATTR(vp, &va, cred);
if (!ret) {
np->n_mtime = va.va_mtime;
np->n_change = va.va_filerev;
}
}
}
VOP_UNLOCK(vp, 0);
return (0);
} else if (!NFS_ISV4(vp)) {
error = vn_lock(vp, LK_SHARED);
if (error)
return (error);
if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) {
size = VTONFS(vp)->n_size;
VOP_UNLOCK(vp, 0);
error = lf_advlock(ap, &(vp->v_lockf), size);
} else {
if (nfs_advlock_p != NULL)
error = nfs_advlock_p(ap);
else {
VOP_UNLOCK(vp, 0);
error = ENOLCK;
}
}
}
return (error);
}
/*
* NFS advisory byte-level locks.
*/
static int
nfs_advlockasync(struct vop_advlockasync_args *ap)
{
struct vnode *vp = ap->a_vp;
u_quad_t size;
int error;
if (NFS_ISV4(vp))
return (EOPNOTSUPP);
error = vn_lock(vp, LK_SHARED);
if (error)
return (error);
if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) {
size = VTONFS(vp)->n_size;
VOP_UNLOCK(vp, 0);
error = lf_advlockasync(ap, &(vp->v_lockf), size);
} else {
VOP_UNLOCK(vp, 0);
error = EOPNOTSUPP;
}
return (error);
}
/*
* Print out the contents of an nfsnode.
*/
static int
nfs_print(struct vop_print_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
ncl_printf("\tfileid %ld fsid 0x%x",
np->n_vattr.na_fileid, np->n_vattr.na_fsid);
if (vp->v_type == VFIFO)
fifo_printinfo(vp);
printf("\n");
return (0);
}
/*
* This is the "real" nfs::bwrite(struct buf*).
* We set B_CACHE if this is a VMIO buffer.
*/
int
ncl_writebp(struct buf *bp, int force __unused, struct thread *td)
{
int s;
int oldflags = bp->b_flags;
#if 0
int retv = 1;
off_t off;
#endif
BUF_ASSERT_HELD(bp);
if (bp->b_flags & B_INVAL) {
brelse(bp);
return(0);
}
bp->b_flags |= B_CACHE;
/*
* Undirty the bp. We will redirty it later if the I/O fails.
*/
s = splbio();
bundirty(bp);
bp->b_flags &= ~B_DONE;
bp->b_ioflags &= ~BIO_ERROR;
bp->b_iocmd = BIO_WRITE;
bufobj_wref(bp->b_bufobj);
curthread->td_ru.ru_oublock++;
splx(s);
/*
* Note: to avoid loopback deadlocks, we do not
* assign b_runningbufspace.
*/
vfs_busy_pages(bp, 1);
BUF_KERNPROC(bp);
bp->b_iooffset = dbtob(bp->b_blkno);
bstrategy(bp);
if( (oldflags & B_ASYNC) == 0) {
int rtval = bufwait(bp);
if (oldflags & B_DELWRI) {
s = splbio();
reassignbuf(bp);
splx(s);
}
brelse(bp);
return (rtval);
}
return (0);
}
/*
* nfs special file access vnode op.
* Essentially just get vattr and then imitate iaccess() since the device is
* local to the client.
*/
static int
nfsspec_access(struct vop_access_args *ap)
{
struct vattr *vap;
struct ucred *cred = ap->a_cred;
struct vnode *vp = ap->a_vp;
accmode_t accmode = ap->a_accmode;
struct vattr vattr;
int error;
/*
* Disallow write attempts on filesystems mounted read-only;
* unless the file is a socket, fifo, or a block or character
* device resident on the filesystem.
*/
if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG:
case VDIR:
case VLNK:
return (EROFS);
default:
break;
}
}
vap = &vattr;
error = VOP_GETATTR(vp, vap, cred);
if (error)
goto out;
error = vaccess(vp->v_type, vap->va_mode, vap->va_uid, vap->va_gid,
accmode, cred, NULL);
out:
return error;
}
/*
* Read wrapper for fifos.
*/
static int
nfsfifo_read(struct vop_read_args *ap)
{
struct nfsnode *np = VTONFS(ap->a_vp);
int error;
/*
* Set access flag.
*/
mtx_lock(&np->n_mtx);
np->n_flag |= NACC;
getnanotime(&np->n_atim);
mtx_unlock(&np->n_mtx);
error = fifo_specops.vop_read(ap);
return error;
}
/*
* Write wrapper for fifos.
*/
static int
nfsfifo_write(struct vop_write_args *ap)
{
struct nfsnode *np = VTONFS(ap->a_vp);
/*
* Set update flag.
*/
mtx_lock(&np->n_mtx);
np->n_flag |= NUPD;
getnanotime(&np->n_mtim);
mtx_unlock(&np->n_mtx);
return(fifo_specops.vop_write(ap));
}
/*
* Close wrapper for fifos.
*
* Update the times on the nfsnode then do fifo close.
*/
static int
nfsfifo_close(struct vop_close_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct vattr vattr;
struct timespec ts;
mtx_lock(&np->n_mtx);
if (np->n_flag & (NACC | NUPD)) {
getnanotime(&ts);
if (np->n_flag & NACC)
np->n_atim = ts;
if (np->n_flag & NUPD)
np->n_mtim = ts;
np->n_flag |= NCHG;
if (vrefcnt(vp) == 1 &&
(vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
VATTR_NULL(&vattr);
if (np->n_flag & NACC)
vattr.va_atime = np->n_atim;
if (np->n_flag & NUPD)
vattr.va_mtime = np->n_mtim;
mtx_unlock(&np->n_mtx);
(void)VOP_SETATTR(vp, &vattr, ap->a_cred);
goto out;
}
}
mtx_unlock(&np->n_mtx);
out:
return (fifo_specops.vop_close(ap));
}
/*
* Just call ncl_writebp() with the force argument set to 1.
*
* NOTE: B_DONE may or may not be set in a_bp on call.
*/
static int
nfs_bwrite(struct buf *bp)
{
return (ncl_writebp(bp, 1, curthread));
}
struct buf_ops buf_ops_newnfs = {
.bop_name = "buf_ops_nfs",
.bop_write = nfs_bwrite,
.bop_strategy = bufstrategy,
.bop_sync = bufsync,
.bop_bdflush = bufbdflush,
};
/*
* Cloned from vop_stdlock(), and then the ugly hack added.
*/
static int
nfs_lock1(struct vop_lock1_args *ap)
{
struct vnode *vp = ap->a_vp;
int error = 0;
/*
* Since vfs_hash_get() calls vget() and it will no longer work
* for FreeBSD8 with flags == 0, I can only think of this horrible
* hack to work around it. I call vfs_hash_get() with LK_EXCLOTHER
* and then handle it here. All I want for this case is a v_usecount
* on the vnode to use for recovery, while another thread might
* hold a lock on the vnode. I have the other threads blocked, so
* there isn't any race problem.
*/
if ((ap->a_flags & LK_TYPE_MASK) == LK_EXCLOTHER) {
if ((ap->a_flags & LK_INTERLOCK) == 0)
panic("ncllock1");
if ((vp->v_iflag & VI_DOOMED))
error = ENOENT;
VI_UNLOCK(vp);
return (error);
}
return (_lockmgr_args(vp->v_vnlock, ap->a_flags, VI_MTX(vp),
LK_WMESG_DEFAULT, LK_PRIO_DEFAULT, LK_TIMO_DEFAULT, ap->a_file,
ap->a_line));
}
-#ifdef NFS4_ACL_EXTATTR_NAME
static int
nfs_getacl(struct vop_getacl_args *ap)
{
int error;
if (ap->a_type != ACL_TYPE_NFS4)
return (EOPNOTSUPP);
error = nfsrpc_getacl(ap->a_vp, ap->a_cred, ap->a_td, ap->a_aclp,
NULL);
if (error > NFSERR_STALE) {
(void) nfscl_maperr(ap->a_td, error, (uid_t)0, (gid_t)0);
error = EPERM;
}
return (error);
}
static int
nfs_setacl(struct vop_setacl_args *ap)
{
int error;
if (ap->a_type != ACL_TYPE_NFS4)
return (EOPNOTSUPP);
error = nfsrpc_setacl(ap->a_vp, ap->a_cred, ap->a_td, ap->a_aclp,
NULL);
if (error > NFSERR_STALE) {
(void) nfscl_maperr(ap->a_td, error, (uid_t)0, (gid_t)0);
error = EPERM;
}
return (error);
}
-
-#endif /* NFS4_ACL_EXTATTR_NAME */
/*
* Return POSIX pathconf information applicable to nfs filesystems.
*/
static int
nfs_pathconf(struct vop_pathconf_args *ap)
{
struct nfsv3_pathconf pc;
struct nfsvattr nfsva;
struct vnode *vp = ap->a_vp;
struct thread *td = curthread;
int attrflag, error;
if (NFS_ISV34(vp)) {
error = nfsrpc_pathconf(vp, &pc, td->td_ucred, td, &nfsva,
&attrflag, NULL);
if (attrflag != 0)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
1);
if (error != 0)
return (error);
} else {
/* For NFSv2, just fake them. */
pc.pc_linkmax = LINK_MAX;
pc.pc_namemax = NFS_MAXNAMLEN;
pc.pc_notrunc = 1;
pc.pc_chownrestricted = 1;
pc.pc_caseinsensitive = 0;
pc.pc_casepreserving = 1;
error = 0;
}
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = pc.pc_linkmax;
break;
case _PC_NAME_MAX:
*ap->a_retval = pc.pc_namemax;
break;
case _PC_PATH_MAX:
*ap->a_retval = PATH_MAX;
break;
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
break;
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = pc.pc_chownrestricted;
break;
case _PC_NO_TRUNC:
*ap->a_retval = pc.pc_notrunc;
break;
case _PC_ACL_EXTENDED:
*ap->a_retval = 0;
break;
case _PC_ACL_NFS4:
if (NFS_ISV4(vp) && nfsrv_useacl != 0 && attrflag != 0 &&
NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL))
*ap->a_retval = 1;
else
*ap->a_retval = 0;
break;
case _PC_ACL_PATH_MAX:
if (NFS_ISV4(vp))
*ap->a_retval = ACL_MAX_ENTRIES;
else
*ap->a_retval = 3;
break;
case _PC_MAC_PRESENT:
*ap->a_retval = 0;
break;
case _PC_ASYNC_IO:
/* _PC_ASYNC_IO should have been handled by upper layers. */
KASSERT(0, ("_PC_ASYNC_IO should not get here"));
error = EINVAL;
break;
case _PC_PRIO_IO:
*ap->a_retval = 0;
break;
case _PC_SYNC_IO:
*ap->a_retval = 0;
break;
case _PC_ALLOC_SIZE_MIN:
*ap->a_retval = vp->v_mount->mnt_stat.f_bsize;
break;
case _PC_FILESIZEBITS:
if (NFS_ISV34(vp))
*ap->a_retval = 64;
else
*ap->a_retval = 32;
break;
case _PC_REC_INCR_XFER_SIZE:
*ap->a_retval = vp->v_mount->mnt_stat.f_iosize;
break;
case _PC_REC_MAX_XFER_SIZE:
*ap->a_retval = -1; /* means ``unlimited'' */
break;
case _PC_REC_MIN_XFER_SIZE:
*ap->a_retval = vp->v_mount->mnt_stat.f_iosize;
break;
case _PC_REC_XFER_ALIGN:
*ap->a_retval = PAGE_SIZE;
break;
case _PC_SYMLINK_MAX:
*ap->a_retval = NFS_MAXPATHLEN;
break;
default:
error = EINVAL;
break;
}
return (error);
}
Index: stable/8/sys
===================================================================
--- stable/8/sys (revision 221211)
+++ stable/8/sys (revision 221212)
Property changes on: stable/8/sys
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head/sys:r200069
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Aug 14, 5:49 AM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36611800
Default Alt Text
(399 KB)
Attached To
Mode
rS FreeBSD src repository - subversion
Attached
Detach File
Event Timeline
Log In to Comment