Page MenuHomeFreeBSD

No OneTemporary

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

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)

Event Timeline