nfs4_srv.c revision f3b585ce799a83688c5532c430f6133f098431c2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
* All Rights Reserved
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/vfs_opreg.h>
#include <sys/sysmacros.h>
#include <sys/systeminfo.h>
#include <sys/pathname.h>
#include <rpc/rpcsec_gss.h>
static int rfs4_maxlock_tries = RFS4_MAXLOCK_TRIES;
/* End of Tunables */
/*
* Used to bump the stateid4.seqid value and show changes in the stateid
*/
/*
* RFS4_MINLEN_ENTRY4: XDR-encoded size of smallest possible dirent.
* This is used to return NFS4ERR_TOOSMALL when clients specify
* maxcount that isn't large enough to hold the smallest possible
* XDR encoded dirent.
*
* sizeof cookie (8 bytes) +
* sizeof name_len (4 bytes) +
* sizeof smallest (padded) name (4 bytes) +
* sizeof bitmap4_len (12 bytes) + NOTE: we always encode len=2 bm4
* sizeof attrlist4_len (4 bytes) +
* sizeof next boolean (4 bytes)
*
* RFS4_MINLEN_RDDIR4: XDR-encoded size of READDIR op reply containing
* the smallest possible entry4 (assumes no attrs requested).
* sizeof nfsstat4 (4 bytes) +
* sizeof verifier4 (8 bytes) +
* sizeof entry4list bool (4 bytes) +
* sizeof entry4 (36 bytes) +
* sizeof eof bool (4 bytes)
*
* RFS4_MINLEN_RDDIR_BUF: minimum length of buffer server will provide to
* VOP_READDIR. Its value is the size of the maximum possible dirent
* for solaris. The DIRENT64_RECLEN macro returns the size of dirent
* required for a given name length. MAXNAMELEN is the maximum
* filename length allowed in Solaris. The first two DIRENT64_RECLEN()
* macros are to allow for . and .. entries -- just a minor tweak to try
* and guarantee that buffer we give to VOP_READDIR will be large enough
* to hold ., .., and the largest possible solaris dirent64.
*/
#define RFS4_MINLEN_ENTRY4 36
#define RFS4_MINLEN_RDDIR_BUF \
/*
* It would be better to pad to 4 bytes since that's what XDR would do,
* but the dirents UFS gives us are already padded to 8, so just take
* what we're given. Dircount is only a hint anyway. Currently the
* solaris kernel is ASCII only, so there's no point in calling the
* UTF8 functions.
*
* dirent64: named padded to provide 8 byte struct alignment
* d_ino(8) + d_off(8) + d_reclen(2) + d_name(namelen + null(1) + pad)
*
* cookie: uint64_t + utf8namelen: uint_t + utf8name padded to 8 bytes
*
*/
#define DIRENT64_TO_DIRCOUNT(dp) \
uint_t nfs4_srv_vkey = 0;
void rfs4_init_compound_state(struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct svc_req *, struct compound_state *);
struct svc_req *, struct compound_state *);
struct compound_state *);
static void rfs4_op_getattr_free(nfs_resop4 *);
struct compound_state *);
static void rfs4_op_getfh_free(nfs_resop4 *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
static void lock_denied_free(nfs_resop4 *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct svc_req *, struct compound_state *);
struct svc_req *, struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
static void rfs4_op_read_free(nfs_resop4 *);
struct compound_state *);
static void rfs4_op_readlink_free(nfs_resop4 *);
struct svc_req *, struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct compound_state *);
struct svc_req *, struct compound_state *);
struct compound_state *);
static void rfs4_op_secinfo_free(nfs_resop4 *);
struct compound_state *, struct svc_req *);
static int rfs4_shrlock(rfs4_state_t *, int);
static int rfs4_share(rfs4_state_t *);
/*
* translation table for attrs
*/
struct nfs4_ntov_table {
union nfs4_attr_u *na;
int attrcnt;
};
struct nfs4_svgetit_arg *sargp);
int rfs4_seen_first_compound; /* set first time we see one */
/*
* NFS4 op dispatch table
*/
struct rfsv4disp {
void (*dis_proc)(); /* proc to call */
void (*dis_resfree)(); /* frees space allocated by proc */
int dis_flags; /* RPC_IDEMPOTENT, etc... */
};
static struct rfsv4disp rfsv4disptab[] = {
/*
* NFS VERSION 4
*/
/* RFS_NULL = 0 */
{rfs4_op_illegal, nullfree, 0},
/* UNUSED = 1 */
{rfs4_op_illegal, nullfree, 0},
/* UNUSED = 2 */
{rfs4_op_illegal, nullfree, 0},
/* OP_ACCESS = 3 */
/* OP_CLOSE = 4 */
{rfs4_op_close, nullfree, 0},
/* OP_COMMIT = 5 */
/* OP_CREATE = 6 */
{rfs4_op_create, nullfree, 0},
/* OP_DELEGPURGE = 7 */
{rfs4_op_delegpurge, nullfree, 0},
/* OP_DELEGRETURN = 8 */
{rfs4_op_delegreturn, nullfree, 0},
/* OP_GETATTR = 9 */
/* OP_GETFH = 10 */
/* OP_LINK = 11 */
{rfs4_op_link, nullfree, 0},
/* OP_LOCK = 12 */
{rfs4_op_lock, lock_denied_free, 0},
/* OP_LOCKT = 13 */
{rfs4_op_lockt, lock_denied_free, 0},
/* OP_LOCKU = 14 */
{rfs4_op_locku, nullfree, 0},
/* OP_LOOKUP = 15 */
/* OP_LOOKUPP = 16 */
/* OP_NVERIFY = 17 */
/* OP_OPEN = 18 */
{rfs4_op_open, rfs4_free_reply, 0},
/* OP_OPENATTR = 19 */
{rfs4_op_openattr, nullfree, 0},
/* OP_OPEN_CONFIRM = 20 */
{rfs4_op_open_confirm, nullfree, 0},
/* OP_OPEN_DOWNGRADE = 21 */
{rfs4_op_open_downgrade, nullfree, 0},
/* OP_OPEN_PUTFH = 22 */
/* OP_PUTPUBFH = 23 */
/* OP_PUTROOTFH = 24 */
/* OP_READ = 25 */
/* OP_READDIR = 26 */
/* OP_READLINK = 27 */
/* OP_REMOVE = 28 */
{rfs4_op_remove, nullfree, 0},
/* OP_RENAME = 29 */
{rfs4_op_rename, nullfree, 0},
/* OP_RENEW = 30 */
{rfs4_op_renew, nullfree, 0},
/* OP_RESTOREFH = 31 */
/* OP_SAVEFH = 32 */
/* OP_SECINFO = 33 */
/* OP_SETATTR = 34 */
{rfs4_op_setattr, nullfree, 0},
/* OP_SETCLIENTID = 35 */
{rfs4_op_setclientid, nullfree, 0},
/* OP_SETCLIENTID_CONFIRM = 36 */
/* OP_VERIFY = 37 */
/* OP_WRITE = 38 */
{rfs4_op_write, nullfree, 0},
/* OP_RELEASE_LOCKOWNER = 39 */
};
#define OP_ILLEGAL_IDX (rfsv4disp_cnt)
#ifdef DEBUG
int rfs4_fillone_debug = 0;
int rfs4_shrlock_debug = 0;
int rfs4_no_stub_access = 1;
int rfs4_rddir_debug = 0;
static char *rfs4_op_string[] = {
"rfs4_op_null",
"rfs4_op_1 unused",
"rfs4_op_2 unused",
"rfs4_op_access",
"rfs4_op_close",
"rfs4_op_commit",
"rfs4_op_create",
"rfs4_op_delegpurge",
"rfs4_op_delegreturn",
"rfs4_op_getattr",
"rfs4_op_getfh",
"rfs4_op_link",
"rfs4_op_lock",
"rfs4_op_lockt",
"rfs4_op_locku",
"rfs4_op_lookup",
"rfs4_op_lookupp",
"rfs4_op_nverify",
"rfs4_op_open",
"rfs4_op_openattr",
"rfs4_op_open_confirm",
"rfs4_op_open_downgrade",
"rfs4_op_putfh",
"rfs4_op_putpubfh",
"rfs4_op_putrootfh",
"rfs4_op_read",
"rfs4_op_readdir",
"rfs4_op_readlink",
"rfs4_op_remove",
"rfs4_op_rename",
"rfs4_op_renew",
"rfs4_op_restorefh",
"rfs4_op_savefh",
"rfs4_op_secinfo",
"rfs4_op_setattr",
"rfs4_op_setclientid",
"rfs4_op_setclient_confirm",
"rfs4_op_verify",
"rfs4_op_write",
"rfs4_op_release_lockowner",
"rfs4_op_illegal"
};
#endif
void rfs4_ss_chkclid(rfs4_client_t *);
#ifdef nextdp
#endif
static const fs_operation_def_t nfs4_rd_deleg_tmpl[] = {
};
static const fs_operation_def_t nfs4_wr_deleg_tmpl[] = {
};
int
rfs4_srvrinit(void)
{
int error;
extern void rfs4_attr_init();
extern krwlock_t rfs4_deleg_policy_lock;
/*
* The following algorithm attempts to find a unique verifier
* to be used as the write verifier returned from the server
* to the client. It is important that this verifier change
* whenever the server reboots. Of secondary importance, it
* is important for the verifier to be unique between two
* different servers.
*
* Thus, an attempt is made to use the system hostid and the
* current time in seconds when the nfssrv kernel module is
* loaded. It is assumed that an NFS server will not be able
* to boot and then to reboot in less than a second. If the
* hostid has not been set, then the current high resolution
* time is used. This will ensure different verifiers each
* time the server reboots and minimize the chances that two
* different servers will have the same verifier.
* XXX - this is broken on LP64 kernels.
*/
} else {
gethrestime(&tverf);
}
/* Used to manage access to server instance linked list */
/* Used to manage access to rfs4_deleg_policy */
if (error != 0) {
} else {
&deleg_wrops);
if (error != 0) {
}
}
return (0);
}
void
rfs4_srvrfini(void)
{
extern krwlock_t rfs4_deleg_policy_lock;
if (lockt_sysid != LM_NOSYSID) {
}
}
void
{
}
void
{
}
/*
* returns true if the instance's grace period has never been started
*/
int
{
return (start_time == 0);
}
/*
* Indicates if server instance is within the
* grace period.
*/
int
{
return (gethrestime_sec() < grace_expiry);
}
int
{
}
/*
* reset all currently active grace periods
*/
void
rfs4_grace_reset_all(void)
{
if (rfs4_servinst_in_grace(sip))
}
/*
* start any new instances' grace periods
*/
void
rfs4_grace_start_new(void)
{
if (rfs4_servinst_grace_new(sip))
}
static rfs4_dss_path_t *
{
/*
* Take a copy of the string, since the original may be overwritten.
* Sadly, no strdup() in the kernel.
*/
/* allow for NUL */
/* associate with servinst */
/*
* Add to list of served paths.
* No locking required, as we're only ever called at startup.
*/
if (rfs4_dss_pathlist == NULL) {
/* this is the first dss_path_t */
} else {
}
return (dss_path);
}
/*
* Create a new server instance, and make it the currently active instance.
* Note that starting the grace period too early will reduce the clients'
* recovery window.
*/
void
{
unsigned i;
/*
* It must be skipped over whenever the list is traversed.
*/
sizeof (rfs4_dss_path_t *), KM_SLEEP);
for (i = 0; i < dss_npaths; i++) {
}
if (rfs4_cur_servinst != NULL) {
/* add to linked list */
}
if (start_grace)
/* make the new instance "current" */
}
/*
* In future, we might add a rfs4_servinst_destroy(sip) but, for now, destroy
* all instances directly.
*/
void
{
#ifdef DEBUG
int n = 0;
#endif
#ifdef DEBUG
n++;
#endif
}
}
/*
* Assign the current server instance to a client_t.
* Should be called with cp->dbe held.
*/
void
{
/*
* The lock ensures that if the current instance is in the process
* of changing, we will see the new one.
*/
}
{
return (cp->server_instance);
}
/* ARGSUSED */
static void
{
}
/*
* This is a fall-through for invalid or not implemented (yet) ops
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
}
/*
* Check if the security flavor, nfsnum, is in the flavor_list.
*/
{
int i;
for (i = 0; i < count; i++) {
if (nfsnum == flavor_list[i])
return (TRUE);
}
return (FALSE);
}
/*
* Used by rfs4_op_secinfo to get the security information from the
* export structure associated with the component.
*/
/* ARGSUSED */
static nfsstat4
{
int error, different_export = 0;
seconfig_t *si;
/*
* If dotdotting, then need to check whether it's above the
* root of a filesystem, or above an export point.
*/
if (dotdot) {
/*
* If dotdotting at the root of a filesystem, then
* need to traverse back to the mounted-on filesystem
* and do the dotdot lookup there.
*/
/*
* If at the system root, then can
* go up no further.
*/
/*
* Traverse back to the mounted-on filesystem
*/
/*
* Set the different_export flag so we remember
* to pick up a new exportinfo entry for
* this new filesystem.
*/
different_export = 1;
} else {
/*
* If dotdotting above an export point then set
* the different_export to get new export info.
*/
}
}
/*
* Get the vnode for the component "nm".
*/
if (error)
/*
* If the vnode is in a pseudo filesystem, or if the security flavor
* used in the request is valid but not an explicitly shared flavor,
* or the access bit indicates that this is a limited access,
* check whether this vnode is visible.
*/
if (!different_export &&
}
}
/*
* If it's a mountpoint, then traverse it.
*/
if (vn_ismntpt(vp)) {
}
/* remember that we had to traverse mountpoint */
did_traverse = TRUE;
different_export = 1;
/*
* If vp isn't a mountpoint and the vfs ptrs aren't the same,
* then vp is probably an LOFS object. We don't need the
* realvp, we just need to know that we might have crossed
* a server fs boundary and need to call checkexport4.
* (LOFS lookup hides server fs mountpoints, and actually calls
* traverse)
*/
different_export = 1;
}
/*
* Get the export information for it.
*/
if (different_export) {
if (error) {
}
if (dotdot)
else
if (did_traverse == TRUE) {
/*
* If this vnode is a mounted-on vnode,
* but the mounted-on file system is not
* exported, send back the secinfo for
* the exported node that the mounted-on
* vnode lives in.
*/
} else {
}
}
} else {
}
/*
* Create the secinfo result based on the security information
* from the exportinfo structure (exi).
*
* Return all flavors for a pseudo node.
* For a real export node, return the flavor that the client
* has access with.
*/
for (i = 0; i < count; i++) {
/* get oid opaque data */
}
}
} else {
int ret_cnt = 0, k = 0;
int *flavor_list;
/* find out which flavors to return */
for (i = 0; i < count; i ++) {
if (! (access & NFSAUTH_DENIED) &&
! (access & NFSAUTH_WRONGSEC)) {
ret_cnt++;
}
}
/* Create the returning SECINFO value */
for (i = 0; i < count; i++) {
/*
* If the flavor is in the flavor list,
* fill in resok_val.
*/
flavor_list, ret_cnt)) {
si->sc_service;
/* get oid opaque data */
KM_SLEEP);
}
k++;
}
if (k >= ret_cnt)
break;
}
}
return (NFS4_OK);
}
/*
* SECINFO (Operation 33): Obtain required security information on
* the component name in the format of (security-mechanism-oid, qop, service)
* triplets.
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
char *nm;
SECINFO4args *, args);
/*
* Current file handle (cfh) should have been set before getting
* into this function. If not, return error.
*/
goto out;
}
goto out;
}
/*
* Verify the component name. If failed, error out, but
* do not error out if the component name is a "..".
* SECINFO will return its parents secinfo data for SECINFO "..".
*/
if (!utf8_dir_verify(utfnm)) {
goto out;
}
}
goto out;
}
if (len > MAXNAMELEN) {
goto out;
}
out:
SECINFO4res *, resp);
}
/*
* Free SECINFO result.
*/
/* ARGSUSED */
static void
{
int count, i;
/* If this is not an Ok result, nothing to free. */
return;
}
for (i = 0; i < count; i++) {
}
}
resp->SECINFO4resok_len = 0;
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
int checkwriteperm;
ACCESS4args *, args);
#if 0 /* XXX allow access even if !cs->access. Eventually only pseudo fs */
goto out;
}
#endif
goto out;
}
/*
* If the file system is exported read only, it is not appropriate
* to check write permissions for regular files and directories.
* Special files are interpreted by the client, so the underlying
* permissions are sent back to the client for interpretation.
*/
checkwriteperm = 0;
else
checkwriteperm = 1;
/*
* XXX
* We need the mode so that we can correctly determine access
* permissions relative to a mandatory lock file. Access to
* mandatory lock files is denied on the server, so it might
* as well be reflected to the server during the open.
*/
if (error) {
goto out;
}
if (is_system_labeled()) {
"got client label from request(1)",
goto out;
}
char *, "got server label(1) for vp(2)",
} else
}
(!is_system_labeled() || admin_low_client ||
}
}
if (checkwriteperm &&
(!is_system_labeled() || admin_low_client ||
}
if (checkwriteperm &&
}
(!is_system_labeled() || admin_low_client ||
}
if (is_system_labeled() && !admin_low_client)
out:
ACCESS4res *, resp);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
COMMIT4args *, args);
goto out;
}
goto out;
}
goto out;
}
/*
* If we can't get the attributes, then we can't do the
* right access checking. So, we'll fail the request.
*/
if (error) {
goto out;
}
goto out;
}
else
goto out;
}
goto out;
}
if (!error)
if (error) {
goto out;
}
out:
COMMIT4res *, resp);
}
/*
* rfs4_op_mknod is called from rfs4_op_create after all initial verification
* was completed. It does the nfsv4 create for special files.
*/
/* ARGSUSED */
static vnode_t *
{
int error;
int mode;
case NF4CHR:
case NF4BLK:
if (secpolicy_sys_devices(cr) != 0) {
return (NULL);
}
else
break;
case NF4SOCK:
break;
case NF4FIFO:
break;
default:
return (NULL);
}
/*
* Must specify the mode.
*/
return (NULL);
}
mode = 0;
if (error) {
return (NULL);
}
return (vp);
}
/*
* nfsv4 create is used to create non-regular files. For regular files,
* use nfsv4 open.
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
int syncval = 0;
struct nfs4_svgetit_arg sarg;
struct nfs4_ntov_table ntov;
CREATE4args *, args);
goto out;
}
/*
* If there is an unshared filesystem mounted on this vnode,
* do not allow to create an object in this directory.
*/
if (vn_ismntpt(dvp)) {
goto out;
}
/* Verify that type is correct */
case NF4LNK:
case NF4BLK:
case NF4CHR:
case NF4SOCK:
case NF4FIFO:
case NF4DIR:
break;
default:
goto out;
};
goto out;
}
goto out;
}
goto out;
}
goto out;
}
/*
* Name of newly created object
*/
goto out;
}
if (len > MAXNAMELEN) {
goto out;
}
goto out;
}
/* Get "before" change value */
if (error) {
goto out;
}
/*
* Set default initial values for attributes when not specified
* in createattrs.
*/
}
}
case NF4DIR:
}
if (error)
break;
/*
* Get the initial "after" sequence number, if it fails,
* set to zero
*/
break;
case NF4LNK:
}
/*
* symlink names must be treated as data
*/
goto out;
}
if (llen > MAXPATHLEN) {
goto out;
}
if (error)
break;
/*
* Get the initial "after" sequence number, if it fails,
* set to zero
*/
if (error)
break;
/*
* va_seq is not safe over VOP calls, check it again
* if it has changed zero out iva to force atomic = FALSE.
*/
break;
default:
/*
* probably a special file.
*/
}
/*
* We know this will only generate one VOP call
*/
goto out;
}
/*
* Get the initial "after" sequence number, if it fails,
* set to zero
*/
break;
}
if (error) {
}
/*
* Force modified data and metadata out to stable storage.
*/
goto out;
}
/*
* Finish setup of cinfo response, "before" value already set.
* Get "after" change value, if it fails, simply return the
* before value.
*/
}
/*
* True verification that object was created with correct
* attrs is impossible. The attrs could have been changed
* immediately after object creation. If attributes did
* not verify, the only recourse for the server is to
* destroy the object. Maybe if some attrs (like gid)
* are set incorrectly, the object should be destroyed;
* however, seems bad as a default policy. Do we really
* want to destroy an object over one of the times not
* verifying correctly? For these reasons, the server
* currently sets bits in attrset for createattrs
* that were set; however, no verification is done.
*
* vmask_to_nmask accounts for vattr bits set on create
* [do_rfs4_set_attrs() only sets resp bits for
* Mask off any bits set by default so as not to return
* more attrset bits than were requested in createattrs
*/
if (error) {
}
/*
* The cinfo.atomic = TRUE only if we got no errors, we have
* non-zero va_seq's, and it has incremented by exactly one
* during the creation and it didn't change during the VOP_LOOKUP
* or VOP_FSYNC.
*/
else
goto out;
}
out:
CREATE4res *, resp);
}
/*ARGSUSED*/
static void
struct compound_state *cs)
{
}
/*ARGSUSED*/
static void
struct compound_state *cs)
{
DELEGRETURN4args *, args);
goto out;
/* Ensure specified filehandle matches */
} else
out:
DELEGRETURN4res *, resp);
}
/*
* Check to see if a given "flavor" is an explicitly shared flavor.
* The assumption of this routine is the "flavor" is already a valid
* flavor in the secinfo list of "exi".
*
* e.g.
* # share -o sec=flavor1 /export
*
* flavor2 is not an explicitly shared flavor for /export,
* however it is in the secinfo list for /export thru the
* server namespace setup.
*/
int
{
int i;
return (SEC_REF_EXPORTED(&sp[i]));
}
}
/* Should not reach this point based on the assumption */
return (0);
}
/*
* Check if the security flavor used in the request matches what is
* required at the export point or at the root pseudo node (exi_root).
*
* returns 1 if there's a match or if exported with AUTH_NONE; 0 otherwise.
*
*/
static int
{
int i;
/*
* Check cs->nfsflavor (from the request) against
* the current export data in cs->exi.
*/
return (1);
}
return (0);
}
/*
* Check the access authority for the client and return the correct error.
*/
{
int authres;
/*
* First, check if the security flavor used in the request
* are among the flavors set in the server namespace.
*/
if (!secinfo_match_or_authnone(cs)) {
}
if (authres > 0) {
} else if (authres == 0) {
} else if (authres == -2) {
} else {
}
}
/*
* bitmap4_to_attrmask is called by getattr and readdir.
* It sets up the vattr mask and determines whether vfsstat call is needed
* based on the input bitmap.
* Returns nfsv4 status.
*/
static nfsstat4
{
int i;
else
/*
* Set rdattr_error_req to true if return error per
* failed entry rather than fail the readdir.
*/
if (breq & FATTR4_RDATTR_ERROR_MASK)
else
sargp->rdattr_error_req = 0;
/*
* generate the va_mask
* Handle the easy cases first
*/
switch (breq) {
case NFS4_NTOV_ATTR_MASK:
return (NFS4_OK);
case NFS4_FS_ATTR_MASK:
return (NFS4_OK);
return (NFS4_OK);
case FATTR4_LEASE_TIME_MASK:
return (NFS4_OK);
default:
va_mask = 0;
for (i = 0; i < nfs4_ntov_map_size; i++) {
nfs4_ntov_map[i].vbit)
}
/*
* Check is vfsstat is needed
*/
if (breq & NFS4_FS_ATTR_MASK)
return (NFS4_OK);
}
/* NOTREACHED */
}
/*
* bitmap4_get_sysattrs is called by getattr and readdir.
* It calls both VOP_GETATTR and VFS_STATVFS calls to get the attrs.
* Returns nfsv4 status.
*/
static nfsstat4
{
int error;
}
}
}
static void
{
KM_SLEEP);
}
static void
struct nfs4_svgetit_arg *sargp)
{
int i;
union nfs4_attr_u *na;
/*
* XXX Should do the same checks for whether the bit is set
*/
}
/*
* xdr_free for getattr will be done later
*/
}
}
}
/*
* do_rfs4_op_getattr gets the system attrs and converts into fattr4.
*/
static nfsstat4
struct nfs4_svgetit_arg *sargp)
{
int error = 0;
int i, k;
struct nfs4_ntov_table ntov;
char *xdr_attrs;
union nfs4_attr_u *na;
/* if no bits requested, then return empty fattr4 */
if (breq == 0) {
fattrp->attrlist4_len = 0;
return (NFS4_OK);
}
/*
* return NFS4ERR_INVAL when client requests write-only attrs
*/
return (NFS4ERR_INVAL);
/*
* Now loop to get or verify the attrs
*/
for (i = 0; i < nfs4_ntov_map_size; i++) {
if ((*nfs4_ntov_map[i].sv_getit)(
/*
* Possible error values:
* >0 if sv_getit failed to
* get the attr; 0 if succeeded;
* <0 if rdattr_error and the
* attribute cannot be returned.
*/
goto done;
/*
* If error then just for entry
*/
if (error == 0) {
nfs4_ntov_map[i].fbit;
*amap++ =
na++;
} else if ((error > 0) &&
}
error = 0;
}
}
}
/*
* If rdattr_error was set after the return value for it was assigned,
* update it.
*/
k = *amap;
if (k < FATTR4_RDATTR_ERROR) {
continue;
}
if ((k == FATTR4_RDATTR_ERROR) &&
((*nfs4_ntov_map[k].sv_getit)(
(void) (*nfs4_ntov_map[k].sv_getit)(
}
break;
}
}
xdr_size = 0;
}
if (xdr_size) {
/* freed by rfs4_op_getattr_free() */
"encode of attribute %d failed\n", *amap);
break;
}
}
/* xdrmem_destroy(&xdrs); */ /* NO-OP */
} else {
}
done:
if (error != 0)
return (status);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
struct nfs4_svgetit_arg sarg;
GETATTR4args *, args);
goto out;
}
goto out;
}
}
out:
GETATTR4res *, resp);
}
static void
{
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
goto out;
}
goto out;
}
out:
}
static void
{
}
}
/*
* illegal: args: void
* res : status (NFS4ERR_OP_ILLEGAL)
*/
/* ARGSUSED */
static void
{
}
/*
* link: args: SAVED_FH: file, CURRENT_FH: target directory
* res: status. If success - CURRENT_FH unchanged, return change_info
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
char *nm;
/* SAVED_FH: source object */
goto out;
}
/* CURRENT_FH: target directory */
goto out;
}
/*
* If there is a non-shared filesystem mounted on this vnode,
* do not allow to link any file in this directory.
*/
if (vn_ismntpt(dvp)) {
goto out;
}
goto out;
}
/* Check source object's type validity */
goto out;
}
/* Check target directory's type */
goto out;
}
goto out;
}
goto out;
}
goto out;
}
if (len > MAXNAMELEN) {
goto out;
}
goto out;
}
/* Get "before" change value */
if (error) {
goto out;
}
/*
* Get the initial "after" sequence number, if it fails, set to zero
*/
/*
* Force modified data and metadata out to stable storage.
*/
if (error) {
goto out;
}
/*
* Get "after" change value, if it fails, simply return the
* before value.
*/
}
/*
* The cinfo.atomic = TRUE only if we have
* non-zero va_seq's, and it has incremented by exactly one
* during the VOP_LINK and it didn't change during the VOP_FSYNC.
*/
else
out:
}
/*
* Used by rfs4_op_lookup and rfs4_op_lookupp to do the actual work.
*/
/* ARGSUSED */
static nfsstat4
struct compound_state *cs)
{
int error;
int different_export = 0;
attrdir = 1;
} else {
attrdir = 0;
}
/*
* If dotdotting, then need to check whether it's
* above the root of a filesystem, or above an
* export point.
*/
if (dotdot) {
/*
* If dotdotting at the root of a filesystem, then
* need to traverse back to the mounted-on filesystem
* and do the dotdot lookup there.
*/
/*
* If at the system root, then can
* go up no further.
*/
/*
* Traverse back to the mounted-on filesystem
*/
/*
* Set the different_export flag so we remember
* to pick up a new exportinfo entry for
* this new filesystem.
*/
different_export = 1;
} else {
/*
* If dotdotting above an export point then set
* the different_export to get new export info.
*/
}
}
if (error)
/*
* If the vnode is in a pseudo filesystem, check whether it is visible.
*
* XXX if the vnode is a symlink and it is not visible in
* a pseudo filesystem, return ENOENT (not following symlink).
* V4 client can not mount such symlink. This is a regression
*
* In the same exported filesystem, if the security flavor used
* is not an explicitly shared flavor, limit the view to the visible
* list entries only. This is not a WRONGSEC case because it's already
*/
if (!different_export &&
}
}
/*
* If it's a mountpoint, then traverse it.
*/
if (vn_ismntpt(vp)) {
/*
* hold pre_tvp to counteract rele by traverse. We will
* need pre_tvp below if checkexport4 fails
*/
}
different_export = 1;
/*
* The vfsp comparison is to handle the case where
* a LOFS mount is shared. lo_lookup traverses mount points,
* and NFS is unaware of local fs transistions because
* v_vfsmountedhere isn't set. For this special LOFS case,
* the dir and the obj returned by lookup will have different
* vfs ptrs.
*/
different_export = 1;
}
if (different_export) {
if (error) {
if (pre_tvp)
}
if (dotdot)
else
if (pre_tvp) {
/*
* If this vnode is a mounted-on vnode,
* but the mounted-on file system is not
* exported, send back the filehandle for
* the mounted-on vnode, not the root of
* the mounted-on file system.
*/
} else {
}
} else if (pre_tvp) {
/* we're done with pre_tvp now. release extra hold */
}
/*
* Now we do a checkauth4. The reason is that
* exported file system, and if he does,
*
* We start with a new cr, because the checkauth4 done
* in the PUT*FH operation over wrote the cred's uid,
* gid, etc, and we want the real thing before calling
* checkauth4()
*/
return (stat);
}
}
/*
* After various NFS checks, do a label check on the path
* component. The label on this path should either be the
* global zone's label or a zone's label. We are only
* interested in the zone's label because exported files
* in global zone is accessible (though read-only) to
* clients. The exportability/visibility check is already
* done before reaching this code.
*/
if (is_system_labeled()) {
goto err_out;
}
} else {
/*
* We grant access to admin_low label clients
* only if the client is trusted, i.e. also
* running Solaris Trusted Extension.
*/
int addr_type;
void *ipaddr;
tsol_tpc_t *tp;
ipaddr = &((struct sockaddr_in6 *)
}
SUN_CIPSO) {
goto err_out;
}
}
}
if (error) {
if (is_newvp) {
} else
}
if (!is_newvp) {
} else if (oldvp)
/*
* if did lookup on attrdir and didn't lookup .., set named
* attr fh flag
*/
/* Assume false for now, open proc will set this */
return (NFS4_OK);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
char *nm;
LOOKUP4args *, args);
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
if (len > MAXNAMELEN) {
goto out;
}
out:
LOOKUP4res *, resp);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
goto out;
}
goto out;
}
/*
* From NFSV4 Specification, LOOKUPP should not check for
* NFS4ERR_WRONGSEC. Retrun NFS4_OK instead.
*/
}
out:
LOOKUPP4res *, resp);
}
/*ARGSUSED2*/
static void
struct compound_state *cs)
{
int exp_ro = 0;
OPENATTR4args *, args);
goto out;
}
/*
* Make a couple of checks made by copen()
*
* Check to make sure underlying fs supports xattrs. This
* is required because solaris filesystem implementations
* in VOP_LOOKUP(LOOKUP_XATTR). If fs doesn't support this
* pathconf cmd or if fs supports cmd but doesn't claim
* support for xattr, return NOTSUPP. It would be better
* to use VOP_PATHCONF( _PC_XATTR_ENABLED) for this; however,
* that cmd is not available to VOP_PATHCONF interface
* (it's only implemented inside pathconf syscall)...
*
* Verify permission to put attributes on files (access
* checks from copen).
*/
goto out;
}
goto out;
}
/*
* The CREATE_XATTR_DIR VOP flag cannot be specified if
* the file system is exported read-only -- regardless of
* createdir flag. Otherwise the attrdir would be created
* (assuming server fs isn't mounted readonly locally). If
* VOP_LOOKUP returns ENOENT in this case, the error will
* be translated into EROFS. ENOSYS is mapped to ENOTSUP
* because specfs has no VOP_LOOKUP op, so the macro would
* return ENOSYS. EINVAL is returned by all (current)
* Solaris file system implementations when any of their
* restrictions are violated (xattr(dir) can't have xattrdir).
* Returning NOTSUPP is more appropriate in this case
* because the object will never be able to have an attrdir.
*/
if (error) {
else
goto out;
}
if (error) {
goto out;
}
/*
* There is no requirement for an attrdir fh flag
* because the attrdir has a vnode flag to distinguish
* it from regular (non-xattr) directories. The
* FH4_ATTRDIR flag is set for future sanity checks.
*/
out:
OPENATTR4res *, resp);
}
static int
{
int error;
int i;
/*
* Don't block on mandatory locks. If this routine returns
* EAGAIN, the caller should return NFS4ERR_LOCKED.
*/
for (i = 0; i < rfs4_maxlock_tries; i++) {
} else {
}
break;
if (i < rfs4_maxlock_tries - 1) {
delaytime *= 2;
}
}
return (error);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
int verror;
int in_crit = 0;
int alloc_err = 0;
goto out;
}
goto out;
}
goto out;
}
/*
* Enter the critical region before calling VOP_RWLOCK
* to avoid a deadlock with write requests.
*/
if (nbl_need_check(vp)) {
in_crit = 1;
&ct)) {
goto out;
}
}
goto out;
}
/*
* If we can't get the attributes, then we can't do the
* right access checking. So, we'll fail the request.
*/
if (verror) {
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
/*
* Do not allocate memory more than maximum allowed
* transfer size
*/
/*
* mp will contain the data to be sent out in the read reply.
* It will be freed after the reply has been sent.
* Let's roundup the data to a BYTES_PER_XDR_UNIT multiple,
* so that the call to xdrmblk_putmblk() never fails.
* If the first alloc of the requested size fails, then
* decrease the size to something more reasonable and wait
* for the allocation to occur.
*/
}
if (error) {
goto out;
}
else
out:
if (in_crit)
}
static void
{
}
}
static void
{
}
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
}
goto out;
}
if (is_system_labeled()) {
"got client label from request(1)",
return;
}
}
}
if (error != 0) {
goto out;
}
if (exi_public == exi_root) {
/*
* No filesystem is actually shared public, so we default
* to exi_root. In this case, we must check whether root
* is exported.
*/
/*
* if root filesystem is exported, the exportinfo struct that we
* should use is what checkexport4 returns, because root_exi is
* actually a mostly empty struct.
*/
} else {
/*
* it's a properly shared filesystem
*/
}
goto out;
}
out:
PUTPUBFH4res *, resp);
}
/*
* or joe have restrictive search permissions, then we shouldn't let
* the client get a file handle. This is easy to enforce. However, we
* don't know what security flavor should be used until we resolve the
* path name. Another complication is uid mapping. If root is
* the user, then it will be mapped to the anonymous user by default,
* but we won't know that till we've resolved the path name. And we won't
* know what the anonymous user is.
* Luckily, SECINFO is specified to take a full filename.
* So what we will have to in rfs4_op_lookup is check that flavor of
* the target object matches that of the request, and if root was the
* caller, check for the root= and anon= options, and if necessary,
* repeat the lookup using the right cred_t. But that's not done yet.
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
PUTFH4args *, args);
}
}
goto out;
}
NULL);
goto out;
}
goto out;
}
goto out;
}
out:
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
}
/*
* Using rootdir, the system root vnode,
* get its fid.
*/
if (error != 0) {
goto out;
}
/*
* Then use the root fsid & fid it to find out if it's exported
*
* If the server root isn't exported directly, then
* it should at least be a pseudo export based on
* one or more exports further down in the server's
* file tree.
*/
(CE_WARN, "rfs4_op_putrootfh: export check failure"));
goto out;
}
/*
* Now make a filehandle based on the root
* export and root vnode.
*/
if (error != 0) {
goto out;
}
goto out;
}
out:
PUTROOTFH4res *, resp);
}
/*
* A directory entry is a valid nfsv4 entry if
* - it has a non-zero ino
* - it is not a dot or dotdot name
* - it is visible in a pseudo export or in a real export that can
* only have a limited view.
*/
static bool_t
int *expseudo, int check_visible)
{
*expseudo = 0;
return (FALSE);
}
if (! check_visible) {
*expseudo = 0;
return (TRUE);
}
}
/*
* set_rdattr_params sets up the variables used to manage what information
* to get for each directory entry.
*/
static nfsstat4
{
/*
* could not even figure attr mask
*/
return (status);
}
/*
* dirent's d_ino is always correct value for mounted_on_fileid.
* mntdfid_set is set once here, but mounted_on_fileid is
* set in main dirent processing loop for each dirent.
* The mntdfid_set is a simple optimization that lets the
* server attr code avoid work when caller is readdir.
*/
/*
* Lookup entry only if client asked for any of the following:
* a) vattr attrs
* b) vfs attrs
* c) attrs w/per-object scope requested (change, filehandle, etc)
* other than mounted_on_fileid (which we can take from dirent)
*/
*need_to_lookup = TRUE;
else
*need_to_lookup = FALSE;
return (NFS4_OK);
/*
* If filesystem attrs are requested, get them now from the
* directory vp, as most entries will have same filesystem. The only
* exception are mounted over entries but we handle
* those as we go (XXX mounted over detection not yet implemented).
*/
/*
* Failed to get filesystem attributes.
* Return a rdattr_error for each entry, but don't fail.
* However, don't get any obj-dependent attrs.
*/
*need_to_lookup = FALSE;
/*
* At least get fileid for regular readdir output
*/
}
return (status);
}
/*
* readlink: args: CURRENT_FH.
* res: status. If success - CURRENT_FH unchanged, return linktext.
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
char *data;
/* CURRENT_FH: directory */
goto out;
}
goto out;
}
goto out;
}
goto out;
}
if (error) {
goto out;
}
goto out;
}
uio.uio_loffset = 0;
if (error) {
goto out;
}
/*
* treat link name as data
*/
out:
READLINK4res *, resp);
}
static void
{
if (symlink->utf8string_val) {
}
}
/*
* release_lockowner:
* Release any state associated with the supplied
* lockowner. Note if any lo_state is holding locks we will not
* rele that lo_state and thus the lockowner will not be destroyed.
* A client using lock after the lock owner stateid has been released
* will suffer the consequence of NFS4ERR_BAD_STATEID and would have
* to reissue the lock with new_lock_owner set to TRUE.
* args: lock_owner
* res: status
*/
/* ARGSUSED */
static void
{
/* Make sure there is a clientid around for this request */
goto out;
}
goto out;
}
/*
* Check for EXPIRED client. If so will reap state with in a lease
* period or on next set_clientid_confirm step
*/
goto out;
}
/*
* If no sysid has been assigned, then no locks exist; just return.
*/
goto out;
}
/*
* Mark the lockowner invalid.
*/
/*
* sysid-pid pair should now not be used since the lockowner is
* invalid. If the client were to instantiate the lockowner again
* it would be assigned a new pid. Thus we can get the list of
* current locks.
*/
/* If we are still holding locks fail */
/*
* We need to unhide the lockowner so the client can
* try it again. The bad thing here is if the client
* has a logic error that took it here in the first place
* he probably has lost accounting of the locks that it
* is holding. So we may have dangling state until the
* open owner state is reaped via close. One scenario
* that could possibly occur is that the client has
* sent the unlock request(s) in separate threads
* and has not waited for the replies before sending the
* RELEASE_LOCKOWNER request. Presumably, it would expect
* and deal appropriately with NFS4ERR_LOCKS_HELD, by
* reissuing the request.
*/
goto out;
}
/*
* For the corresponding client we need to check each open
* owner for any opens that have lockowner state associated
* with this lockowner.
*/
}
}
}
}
out:
}
/*
* short utility function to lookup a file and recall the delegation
*/
static rfs4_file_t *
{
int error;
if (vpp)
NULL)) == 0) {
if (vpp)
else
}
if (lkup_error)
*lkup_error = error;
return (fp);
}
/*
* remove: args: CURRENT_FH: directory; name.
* res: status. If success - CURRENT_FH unchanged, return change_info
* for directory.
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
char *nm;
int in_crit = 0;
REMOVE4args *, args);
/* CURRENT_FH: directory */
goto out;
}
goto out;
}
/*
* If there is an unshared filesystem mounted on this vnode,
* Do not allow to remove anything in this directory.
*/
if (vn_ismntpt(dvp)) {
goto out;
}
goto out;
}
goto out;
}
/*
* Lookup the file so that we can check if it's a directory
*/
goto out;
}
if (len > MAXNAMELEN) {
goto out;
}
goto out;
}
/*
* Lookup the file to determine type and while we are see if
* there is a file struct around and check for delegation.
* We don't need to acquire va_seq before this lookup, if
* it causes an update, cinfo.before will not match, which will
* trigger a cache flush even if atomic is TRUE.
*/
NULL)) {
goto out;
}
}
/* Didn't find anything to remove */
goto out;
}
if (nbl_need_check(vp)) {
in_crit = 1;
if (fp) {
}
goto out;
}
}
/* check label before allowing removal */
if (is_system_labeled()) {
"got client label from request(1)",
if (in_crit)
if (fp) {
}
goto out;
}
}
}
/* Get dir "before" change value */
if (error) {
goto out;
}
/* Actually do the REMOVE operation */
/*
* Can't remove a directory that has a mounted-on filesystem.
*/
if (vn_ismntpt(vp)) {
} else {
/*
* System V defines rmdir to return EEXIST,
* not * ENOTEMPTY, if the directory is not
* empty. A System V NFS server needs to map
* NFS4ERR_EXIST to NFS4ERR_NOTEMPTY to
* transmit over the wire.
*/
}
} else {
if (tvp)
if (tvp) {
/*
* This is va_seq safe because we are not
* manipulating dvp.
*/
/* Remove state on file remove */
if (in_crit) {
in_crit = 0;
}
}
}
}
}
if (in_crit)
if (fp) {
}
if (error) {
goto out;
}
/*
* Get the initial "after" sequence number, if it fails, set to zero
*/
/*
* Force modified data and metadata out to stable storage.
*/
/*
* Get "after" change value, if it fails, simply return the
* before value.
*/
}
/*
* The cinfo.atomic = TRUE only if we have
* non-zero va_seq's, and it has incremented by exactly one
* during the VOP_REMOVE/RMDIR and it didn't change during
* the VOP_FSYNC.
*/
else
out:
REMOVE4res *, resp);
}
/*
* rename: args: SAVED_FH: from directory, CURRENT_FH: target directory,
* oldname and newname.
* res: status. If success - CURRENT_FH unchanged, return change_info
* for both from and target directories.
*/
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
int in_crit_src, in_crit_targ;
RENAME4args *, args);
in_crit_src = in_crit_targ = 0;
/* CURRENT_FH: target directory */
goto out;
}
/* SAVED_FH: from directory */
goto out;
}
goto out;
}
/*
* If there is an unshared filesystem mounted on this vnode,
* do not allow to rename objects in this directory.
*/
if (vn_ismntpt(odvp)) {
goto out;
}
/*
* If there is an unshared filesystem mounted on this vnode,
* do not allow to rename to this directory.
*/
if (vn_ismntpt(ndvp)) {
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
/* check label of the target dir */
if (is_system_labeled()) {
"got client label from request(1)",
EQUALITY_CHECK)) {
goto out;
}
}
}
/*
* Is the source a file and have a delegation?
* We don't need to acquire va_seq before these lookups, if
* it causes an update, cinfo.before will not match, which will
* trigger a cache flush even if atomic is TRUE.
*/
NULL)) {
goto err_out;
}
}
goto out;
}
sfp_rele_grant_hold = 1;
/* Does the destination exist and a file and have a delegation? */
NULL)) {
goto err_out;
}
}
fp_rele_grant_hold = 1;
/* Check for NBMAND lock on both source and target */
if (nbl_need_check(srcvp)) {
in_crit_src = 1;
goto err_out;
}
}
in_crit_targ = 1;
goto err_out;
}
}
/* Get source "before" change value */
if (!error) {
}
if (error) {
goto err_out;
}
if (tvp)
if (tvp) {
/* The file is gone and so should the state */
if (in_crit_targ) {
in_crit_targ = 0;
}
}
}
}
if (error == 0) {
char *tmp;
/* fix the path name for the renamed file */
}
if (in_crit_src)
if (srcvp)
if (in_crit_targ)
if (targvp)
if (sfp) {
}
if (fp) {
}
/*
* Get the initial "after" sequence number, if it fails, set to zero
*/
/*
* Force modified data and metadata out to stable storage.
*/
if (error) {
goto out;
}
/*
* Get "after" change values, if it fails, simply return the
* before value.
*/
}
}
/*
* The cinfo.atomic = TRUE only if we have
* non-zero va_seq's, and it has incremented by exactly one
* during the VOP_RENAME and it didn't change during the VOP_FSYNC.
*/
else
else
#ifdef VOLATILE_FH_TEST
{
/*
* Add the renamed file handle to the volatile rename list
*/
/* file handles may expire on rename */
/*
* Already know that nnm will be a valid string
*/
if (!error) {
}
}
}
#endif /* VOLATILE_FH_TEST */
out:
RENAME4res *, resp);
return;
if (sfp) {
}
if (fp) {
}
RENAME4res *, resp);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
RENEW4args *, args);
goto out;
}
if (rfs4_lease_expired(cp)) {
goto out;
}
} else {
}
out:
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
/* No need to check cs->access - we are not accessing any object */
goto out;
}
}
out:
RESTOREFH4res *, resp);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
/* No need to check cs->access - we are not accessing any object */
goto out;
}
}
/*
* since SAVEFH is fairly rare, don't alloc space for its fh
* unless necessary.
*/
}
out:
SAVEFH4res *, resp);
}
/*
* rfs4_verify_attr is called when nfsv4 Setattr failed, but we wish to
* return the bitmap of attrs that were set successfully. It is also
* always be called only after rfs4_do_set_attrs().
*
* Verify that the attributes are same as the expected ones. sargp->vap
* and sargp->sbp contain the input attributes as translated from fattr4.
*
* This function verifies only the attrs that correspond to a vattr or
* vfsstat struct. That is because of the extra step needed to get the
* corresponding system structs. Other attributes have already been set or
* verified by do_rfs4_set_attrs.
*
* Return 0 if all attrs match, -1 if some don't, error if error processing.
*/
static int
{
int i, k;
union nfs4_attr_u *na;
if (sva_mask != 0) {
/*
* Okay to overwrite sargp->vap because we verify based
* on the incoming values.
*/
if (ret_error) {
return (ret_error);
/*
* Must return bitmap of successful attrs
*/
sva_mask = 0; /* to prevent checking vap later */
} else {
/*
* Some file systems clobber va_mask. it is probably
* wrong of them to do so, nonethless we practice
* defensive coding.
* See bug id 4276830.
*/
}
}
if (getsb) {
/*
* Now get the superblock and loop on the bitmap, as there is
* no simple way of translating from superblock to bitmap4.
*/
if (ret_error) {
goto errout;
}
}
/*
* Now loop and verify each attribute which getattr returned
* whether it's the same as the input.
*/
goto errout;
k = 0;
k = *amap;
/*
* If vattr attribute but VOP_GETATTR failed, or it's
* superblock attribute but VFS_STATVFS failed, skip
*/
if (vbit) {
continue;
continue;
}
if (error)
else /* update response bitmap */
continue;
}
if (error) {
break;
}
}
return (ret_error);
}
/*
* (VOP_GETATTR, VFS_VFSSTAT), and the request is to verify, then don't
* call the sv_getit function for it, because the sys op hasn't yet been done.
* Return 0 for success, error code if failed.
*
* Note: the decoded arg is not freed here but in nfs4_ntov_table_free.
*/
static int
{
int error = 0;
/*
* don't verify yet if a vattr or sb dependent attr,
* because we don't have their sys values yet.
* Will be done later.
*/
/*
* ACLs are a special case, since setting the MODE
* conflicts with setting the ACL. We delay setting
* the ACL until all other attributes have been set.
* The ACL gets set in do_rfs4_op_setattr().
*/
if (error) {
}
}
}
} else {
#ifdef DEBUG
"decoding attribute %d\n", k);
#endif
}
}
return (error);
}
/*
* Set vattr based on incoming fattr4 attrs - used by setattr.
* Set response mask. Ignore any values that are not writable vattr attrs.
*/
static nfsstat4
{
int error = 0;
int i;
union nfs4_attr_u *na;
#ifndef lint
/*
* Make sure that maximum attribute number can be expressed as an
* 8 bit quantity.
*/
#endif
if (resp)
*resp = 0;
return (NFS4ERR_NOFILEHANDLE);
}
if (resp)
*resp = 0;
return (NFS4ERR_ACCESS);
}
/* sargp->sbp is set by the caller */
/*
* The following loop iterates on the nfs4_ntov_map checking
* if the fbit is set in the requested bitmap.
* If set then we process the arguments using the
* rfs4_fattr4 conversion functions to populate the setattr
* vattr and va_mask. Any settable attrs that are not using vattr
* will be set in this loop.
*/
for (i = 0; i < nfs4_ntov_map_size; i++) {
continue;
}
/*
* If setattr, must be a writable attr.
*/
/*
* unsupported attribute, tries to set
* a read only attr or verify a write
* only one - error!
*/
break;
}
/*
*/
if (error)
break;
na++;
if (nfs4_ntov_map[i].vfsstat)
}
if (error != 0)
/* xdrmem_destroy(&xdrs); */ /* NO-OP */
return (status);
}
static nfsstat4
{
int error = 0;
struct nfs4_svgetit_arg sarg;
struct nfs4_ntov_table ntov;
int in_crit = 0;
uint_t saved_mask = 0;
*resp = 0;
/*
* failed set attrs
*/
goto done;
}
/*
* no further work to be done
*/
goto done;
}
/*
* If we got a request to set the ACL and the MODE, only
* allow changing VSUID, VSGID, and VSVTX. Attempting
* to change any other bits, along with setting an ACL,
* gives NFS4ERR_INVAL.
*/
if (error) {
goto done;
}
goto done;
}
}
/* Check stateid only if size has been set */
goto done;
} else {
}
/* XXX start of possible race with delegations */
/*
* We need to specially handle size changes because it is
* possible for the client to create a file with read-only
* modes, but with the file opened for writing. If the client
* then tries to set the file size, e.g. ftruncate(3C),
* fcntl(F_FREESP), the normal access checking done in
* VOP_SETATTR would prevent the client from doing it even though
* it should be allowed to do so. To get around this, we do the
* access checking for ourselves and use VOP_SPACE which doesn't
* do the access checking.
* Also the client should not be allowed to change the file
* size if there is a conflicting non-blocking mandatory lock in
* the region of the change.
*/
/*
* ufs_setattr clears AT_SIZE from vap->va_mask, but
* before returning, sarg.vap->va_mask is used to
* generate the setattr reply bitmap. We also clear
* AT_SIZE below before calling VOP_SPACE. For both
* of these cases, the va_mask needs to be saved here
* and restored after calling VOP_SETATTR.
*/
/*
* Check any possible conflict due to NBMAND locks.
* Get into critical region before VOP_GETATTR, so the
* size attribute is valid when checking conflicts.
*/
if (nbl_need_check(vp)) {
in_crit = 1;
}
goto done;
}
if (in_crit) {
} else {
}
&ct)) {
goto done;
}
}
}
}
/* restore va_mask -- ufs_setattr clears AT_SIZE */
if (saved_mask & AT_SIZE)
/*
* If an ACL was being set, it has been delayed until now,
* in order to set the mode (via the VOP_SETATTR() above) first.
*/
int i;
for (i = 0; i < NFS4_MAXNUM_ATTRS; i++)
break;
if (i < NFS4_MAXNUM_ATTRS) {
if (error == 0) {
*resp |= FATTR4_ACL_MASK;
goto done;
}
} else {
(CE_NOTE, "do_rfs4_op_setattr: "
"unable to find ACL in fattr4"));
}
}
if (error) {
/*
* Set the response bitmap when setattr failed.
* If VOP_SETATTR partially succeeded, test by doing a
* VOP_GETATTR on the object and comparing the data
* to the setattr arguments.
*/
} else {
/*
* Force modified metadata out to stable storage.
*/
/*
* Set response bitmap
*/
}
/* Return early and already have a NFSv4 error */
done:
/*
* Except for nfs4_vmask_to_nmask_set(), vattr --> fattr
* conversion sets both readable and writeable NFS4 attrs
* for AT_MTIME and AT_ATIME. The line below masks out
* unrequested attrs from the setattr result bitmap. This
* is placed after the done: label to catch the ATTRNOTSUP
* case.
*/
if (in_crit)
return (status);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
SETATTR4args *, args);
goto out;
}
/*
* If there is an unshared filesystem mounted on this vnode,
* do not allow to setattr on this vnode.
*/
goto out;
}
goto out;
}
/* check label before setting attributes */
if (is_system_labeled()) {
"got client label from request(1)",
EQUALITY_CHECK)) {
goto out;
}
}
}
out:
SETATTR4res *, resp);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
/*
* verify and nverify are exactly the same, except that nverify
* succeeds when some argument changed, and verify succeeds when
* when none changed.
*/
int error;
struct nfs4_svgetit_arg sarg;
struct nfs4_ntov_table ntov;
VERIFY4args *, args);
goto out;
}
/*
* do_rfs4_set_attrs will try to verify systemwide attrs,
* so could return -1 for "no match".
*/
goto done;
}
switch (error) {
case 0:
break;
case -1:
break;
default:
break;
}
done:
out:
VERIFY4res *, resp);
}
/* ARGSUSED */
static void
struct compound_state *cs)
{
/*
* verify and nverify are exactly the same, except that nverify
* succeeds when some argument changed, and verify succeeds when
* when none changed.
*/
int error;
struct nfs4_svgetit_arg sarg;
struct nfs4_ntov_table ntov;
NVERIFY4args *, args);
NVERIFY4res *, resp);
return;
}
/*
* do_rfs4_set_attrs will try to verify systemwide attrs,
* so could return -1 for "no match".
*/
goto done;
}
switch (error) {
case 0:
break;
case -1:
break;
default:
break;
}
done:
NVERIFY4res *, resp);
}
/*
* XXX - This should live in an NFS header file.
*/
#define MAX_IOVECS 12
/* ARGSUSED */
static void
struct compound_state *cs)
{
int error;
int iovcnt;
int ioflag;
int in_crit = 0;
WRITE4args *, args);
goto out;
}
goto out;
}
goto out;
}
/*
* We have to enter the critical region before calling VOP_RWLOCK
* to avoid a deadlock with ufs.
*/
if (nbl_need_check(vp)) {
in_crit = 1;
goto out;
}
}
/*
* If we can't get the attributes, then we can't do the
* right access checking. So, we'll fail the request.
*/
if (error) {
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
goto out;
}
mblk_t *m;
iovcnt = 0;
bytes = 0;
m = m->b_cont) {
iovcnt++;
}
#ifdef DEBUG
/* should have ended on an mblk boundary */
printf("bytes=0x%x, round_len=0x%x, req len=0x%x\n",
}
#endif
if (iovcnt <= MAX_IOVECS) {
} else {
}
} else {
iovcnt = 1;
}
ioflag = 0;
else {
goto out;
}
/*
* We're changing creds because VM may fault and we need
* the cred of the current thread to be used if quota
* checking is enabled.
*/
if (error) {
goto out;
}
if (ioflag == 0)
else
out:
if (in_crit)
}
/* XXX put in a header file */
void
{
uint_t i;
struct compound_state cs;
/*
* Form a reply tag by copying over the reqeuest tag.
*/
/*
* XXX for now, minorversion should be zero
*/
return;
}
KM_SLEEP);
return;
}
COMPOUND4args *, args);
/*
* For now, NFS4 compound processing must be protected by
* exported_lock because it can access more than one exportinfo
* exinfo structs. The NFS2/3 code only refs 1 exportinfo
* per proc (excluding public exinfo), and exi_count design
* is sufficient to protect concurrent execution of NFS2/3
* ops along with unexport. This lock will be removed as
* part of the NFSv4 phase 2 namespace redesign work.
*/
/*
* If this is the first compound we've seen, we need to start all
* new instances' grace periods.
*/
if (rfs4_seen_first_compound == 0) {
/*
* This must be set after rfs4_grace_start_new(), otherwise
* another thread could proceed past here before the former
* is finished.
*/
}
if (op < rfsv4disp_cnt) {
/*
* Count the individual ops here; NULL and COMPOUND
* are counted in common_dispatch()
*/
} else {
/*
* This is effectively dead code since XDR code
* will have already returned BADXDR if op doesn't
* decode to legal value. This only done for a
* day when XDR code doesn't verify v4 opcodes.
*/
op = OP_ILLEGAL;
}
/*
* If not at last op, and if we are to stop, then
* compact the results array.
*/
}
}
COMPOUND4res *, resp);
/*
* done with this compound request, free the label
*/
}
}
/*
* XXX because of what appears to be duplicate calls to rfs4_compound_free
* XXX zero out the tag and array values. Need to investigate why the
* XXX calls occur, but at least prevent the panic for now.
*/
void
{
uint_t i;
}
if (op < rfsv4disp_cnt) {
}
}
}
}
/*
* Process the value of the compound request rpc flags, as a bit-AND
* of the individual per-op flags (idempotent, allowork, publicfh_ok)
*/
void
{
int i;
if (op < rfsv4disp_cnt)
else
flag = 0;
}
}
{
nfsstat4 e;
e = NFS4_OK;
e = NFS4_OK;
"rfs4_client_sysid: allocated 0x%x\n", *sp));
} else
e = NFS4ERR_DELAY;
return (e);
}
{
switch (operation) {
break;
break;
break;
default: op = "F_UNKNOWN";
break;
}
break;
break;
break;
default: type = "F_UNKNOWN";
break;
}
}
#define LOCK_PRINT(d, s, t, f) if (d) lock_print(s, t, f)
#else
#define LOCK_PRINT(d, s, t, f)
#endif
/*ARGSUSED*/
static bool_t
{
return (TRUE);
}
/*
* Look up the pathname using the vp in cs as the directory vnode.
* cs->vp will be the vnode for the file on success
*/
static nfsstat4
struct compound_state *cs)
{
char *nm;
return (NFS4ERR_NOFILEHANDLE);
}
return (NFS4ERR_NOTDIR);
}
if (!utf8_dir_verify(component))
return (NFS4ERR_INVAL);
return (NFS4ERR_INVAL);
}
if (len > MAXNAMELEN) {
return (NFS4ERR_NAMETOOLONG);
}
return (status);
}
static nfsstat4
{
int error;
/* Get "before" change value */
if (error)
/* rfs4_lookup may VN_RELE directory */
return (status);
}
/*
* Get "after" change value, if it fails, simply return the
* before value.
*/
}
/*
* Validate the file is a file
*/
if (error)
return (NFS4ERR_ISDIR);
return (NFS4ERR_SYMLINK);
return (NFS4ERR_INVAL);
}
/*
* It is undefined if VOP_LOOKUP will change va_seq, so
* cinfo.atomic = TRUE only if we have
* non-zero va_seq's, and they have not changed.
*/
else
/* Check for mandatory locking */
}
static nfsstat4
{
int error;
/*
* The file open mode used is VWRITE. If the client needs
* some other semantic, then it should do the access checking
* itself. It would have been nice to have the file open mode
* passed as part of the arguments.
*/
if (error) {
/*
* If we got something other than file already exists
* then just return this error. Otherwise, we got
* EEXIST. If we were doing a GUARDED create, then
* just return this error. Otherwise, we need to
* make sure that this wasn't a duplicate of an
* exclusive create request.
*
* The assumption is made that a non-exclusive create
* request will never return EEXIST.
*/
return (status);
}
if (error) {
/*
* We couldn't find the file that we thought that
* we just created. So, we'll just try creating
* it again.
*/
goto tryagain;
return (status);
}
if (mode == UNCHECKED4) {
/* existing object must be regular file */
else
return (status);
}
return (NFS4_OK);
}
/* Check for duplicate request */
if (!error) {
/* We found the file */
/* but its not our creation */
return (NFS4ERR_EXIST);
}
return (NFS4_OK);
}
return (NFS4ERR_EXIST);
}
return (NFS4_OK);
}
static nfsstat4
{
int error;
return (NFS4ERR_ACCESS);
}
/*
* If the file system is exported read only and we are trying
* to open for write, then return NFS4ERR_ROFS
*/
return (NFS4ERR_ROFS);
if (access & OPEN4_SHARE_ACCESS_READ) {
return (NFS4ERR_ACCESS);
}
}
if (access & OPEN4_SHARE_ACCESS_WRITE) {
if (error)
return (NFS4ERR_ACCESS);
}
return (NFS4_OK);
}
static nfsstat4
{
struct nfs4_svgetit_arg sarg;
struct nfs4_ntov_table ntov;
int error;
/* Check if the file system is read only */
return (NFS4ERR_ROFS);
/* check the label of including directory */
if (is_system_labeled()) {
"got client label from request(1)",
return (NFS4ERR_ACCESS);
}
}
}
/*
* Get the last component of path name in nm. cs will reference
* the including directory on success.
*/
if (!utf8_dir_verify(component))
return (NFS4ERR_INVAL);
return (NFS4ERR_RESOURCE);
if (buflen > MAXNAMELEN) {
return (NFS4ERR_NAMETOOLONG);
}
if (error) {
}
return (NFS4ERR_NOTDIR);
}
case GUARDED4:
/*FALLTHROUGH*/
case UNCHECKED4:
*attrset = 0;
else
}
*attrset = 0;
return (status);
}
}
/* Disallow create with a non-zero size */
*attrset = 0;
return (NFS4ERR_INVAL);
}
}
break;
case EXCLUSIVE4:
/* prohibit EXCL create of named attributes */
*attrset = 0;
return (NFS4ERR_INVAL);
}
/*
* Ensure no time overflows. Assumes underlying
* filesystem supports at least 32 bits.
* Truncate nsec to usec resolution to allow valid
* compares even if the underlying filesystem truncates.
*/
break;
}
if (ntov_table_init)
*attrset = 0;
return (status);
}
/*
* True verification that object was created with correct
* attrs is impossible. The attrs could have been changed
* immediately after object creation. If attributes did
* not verify, the only recourse for the server is to
* destroy the object. Maybe if some attrs (like gid)
* are set incorrectly, the object should be destroyed;
* however, seems bad as a default policy. Do we really
* want to destroy an object over one of the times not
* verifying correctly? For these reasons, the server
* currently sets bits in attrset for createattrs
* that were set; however, no verification is done.
*
* vmask_to_nmask accounts for vattr bits set on create
* [do_rfs4_set_attrs() only sets resp bits for
* Mask off any bits we set by default so as not to return
* more attrset bits than were requested in createattrs
*/
if (created) {
*attrset &= createmask;
} else {
/*
* We did not create the vnode (we tried but it
* already existed). In this case, the only createattr
* that the spec allows the server to set is size,
* and even then, it can only be set if it is 0.
*/
*attrset = 0;
if (trunc)
}
}
if (ntov_table_init)
/*
* Get the initial "after" sequence number, if it fails,
* set to zero, time to before.
*/
}
/*
* create_vnode attempts to create the file exclusive,
* if it already exists the VOP_CREATE will fail and
* may not increase va_seq. It is atomic if
* we haven't changed the directory, but if it has changed
* we don't know what changed it.
*/
if (!created) {
else
} else {
/*
* The entry was created, we need to sync the
* directory metadata.
*/
/*
* Get "after" change value, if it fails, simply return the
* before value.
*/
}
/*
* The cinfo->atomic = TRUE only if we have
* non-zero va_seq's, and it has incremented by exactly one
* during the create_vnode and it didn't
* change during the VOP_FSYNC.
*/
else
}
/* Check for mandatory locking and that the size gets set. */
if (setsize)
/* Assume the worst */
/*
* Truncate the file if necessary; this would be
* the case for create over an existing file.
*/
if (trunc) {
int in_crit = 0;
/*
* We are writing over an existing file.
* Check to see if we need to recall a delegation.
*/
*attrset = 0;
return (NFS4ERR_DELAY);
}
}
if (nbl_need_check(vp)) {
in_crit = 1;
in_crit = 0;
*attrset = 0;
return (NFS4ERR_ACCESS);
}
}
if (in_crit)
}
}
/*
* Force modified data and metadata out to stable storage.
*/
if (error) {
*attrset = 0;
}
/* if parent dir is attrdir, set namedattr fh flag */
/*
* if we did not create the file, we will need to check
* the access bits on the file
*/
if (!created) {
if (setsize)
*attrset = 0;
}
return (status);
}
/*ARGSUSED*/
static void
{
/* XXX Currently not using req */
struct shr_locowner shr_loco;
int fflags = 0;
int recall = 0;
int err;
int cmd;
/* get the file struct and hold a lock on it during initial open */
(CE_NOTE, "rfs4_do_open: can't find file"));
return;
}
(CE_NOTE, "rfs4_do_open: can't find state"));
/* No need to keep any reference */
return;
}
/* try to get the sysid before continuing */
/* Not a fully formed open; "close" it */
return;
}
/* Calculate the fflags for this OPEN. */
if (access & OPEN4_SHARE_ACCESS_READ)
if (access & OPEN4_SHARE_ACCESS_WRITE)
/*
* Calculate the new deny and access mode that this open is adding to
* the file for this open owner;
*/
/*
* Check to see the client has already sent an open for this
* If so, we don't need to check for a conflict and we don't
* need to add another shrlock. If not, then we need to
* check for conflicts in deny and access before checking for
* conflicts in delegation. We don't want to recall a
* delegation based on an open that will eventually fail based
* on shares modes.
*/
/* Not a fully formed open; "close" it */
return;
}
}
/*
* Check to see if this file is delegated and if so, if a
* recall needs to be done.
*/
/* Let's see if the delegation was returned */
/* Not a fully formed open; "close" it */
return;
}
}
/*
* the share check passed and any delegation conflict has been
* taken care of, now call vop_open.
* if this is the first open then call vop_open with fflags.
* if not, call vn_open_upgrade with just the upgrade flags.
*
* if the file has been opened already, it will have the current
* access mode in the state struct. if it has no share access, then
* this is a new open.
*
* However, if this is open with CLAIM_DLEGATE_CUR, then don't
* call VOP_OPEN(), just do the open upgrade.
*/
!deleg_cur) {
if (err) {
/* Not a fully formed open; "close" it */
return;
}
} else { /* open upgrade */
/*
* calculate the fflags for the new mode that is being added
* by this upgrade.
*/
fflags = 0;
if (amodes & OPEN4_SHARE_ACCESS_READ)
if (amodes & OPEN4_SHARE_ACCESS_WRITE)
}
if (dmodes & OPEN4_SHARE_DENY_READ)
if (dmodes & OPEN4_SHARE_DENY_WRITE)
file->deny_write++;
if (amodes & OPEN4_SHARE_ACCESS_READ)
file->access_read++;
if (amodes & OPEN4_SHARE_ACCESS_WRITE)
file->access_write++;
/*
* Check for delegation here. if the deleg argument is not
* DELEG_ANY, then this is a reclaim from a client and
* we must honor the delegation requested. If necessary we can
* set the recall flag.
*/
if (dsp) {
}
}
/*ARGSUSED*/
static void
{
else {
/* inhibit delegation grants during exclusive create */
}
/* cs->vp cs->fh now reference the desired file */
/*
* If rfs4_createfile set attrset, we must
* clear this attrset before the response is copied.
*/
}
}
else
}
/*ARGSUSED*/
static void
{
int error = 0;
/* Verify that we have a regular file */
else
return;
}
if (error) {
return;
}
/*
* Check if we have access to the file, Note the the file
* could have originally been open UNCHECKED or GUARDED
* with mode bits that will now fail, but there is nothing
* we can really do about that except in the case that the
* owner of the file is the one requesting the open.
*/
return;
}
}
/*
* cinfo on a CLAIM_PREVIOUS is undefined, initialize to zero
*/
}
static void
{
int error;
/*
* Find the state info from the stateid and confirm that the
* file is delegated. If the state openowner is the same as
* the supplied openowner we're done. If not, get the file
* info from the found state info. Use that file info to
* create the state for this lock owner. Note solaris doen't
* really need the pathname to find the file. We may want to
* lookup the pathname and make sure that the vp exist and
* matches the vp in the file structure. However it is
* possible that the pathname nolonger exists (local process
* unlinks the file), so this may not be that useful.
*/
return;
}
/*
* New lock owner, create state. Since this was probably called
* in response to a CB_RECALL we set deleg to DELEG_NONE
*/
return;
}
/* Mark progress for delegation returns */
}
/*ARGSUSED*/
static void
{
/*
* Lookup the pathname, it must already exist since this file
* was delegated.
*
* Find the file and state info for this vp and open owner pair.
* check that they are in fact delegated.
* check that the state access and deny modes are the same.
*
* Return the delgation possibly seting the recall flag.
*/
/* Note we ignore oflags */
return;
}
/* get the file struct and hold a lock on it during initial open */
(CE_NOTE, "rfs4_do_opendelprev: can't find file"));
return;
}
(CE_NOTE, "rfs4_do_opendelprev: can't find state"));
return;
}
(CE_NOTE, "rfs4_do_opendelprev: state mixup"));
return;
}
return;
}
} else {
}
/* XXX For now */
ace->access_mask = 0;
}
typedef enum {
NFS4_CHKSEQ_OKAY = 0,
NFS4_CHKSEQ_REPLAY = 1,
NFS4_CHKSEQ_BAD = 2
/*
* Generic function for sequence number checks.
*/
static rfs4_chkseq_t
{
/* Same sequence ids and matching operations? */
}
"Replayed SEQID %d\n", seqid));
return (NFS4_CHKSEQ_REPLAY);
}
/* If the incoming sequence is not the next expected then it is bad */
(CE_NOTE, "BAD SEQID: Replayed sequence id "
"but last op was %d current op is %d\n",
return (NFS4_CHKSEQ_BAD);
}
(CE_NOTE, "BAD SEQID: got %u expecting %u\n",
return (NFS4_CHKSEQ_BAD);
}
/* Everything okay -- next expected */
return (NFS4_CHKSEQ_OKAY);
}
static rfs4_chkseq_t
{
if (rc == NFS4_CHKSEQ_OKAY)
return (rc);
}
static rfs4_chkseq_t
{
return (rc);
}
static rfs4_chkseq_t
{
if (!lp->skip_seqid_check)
return (rc);
}
static void
{
int can_reclaim;
goto end;
}
/*
* Need to check clientid and lease expiration first based on
* error ordering and incrementing sequence id.
*/
goto end;
}
if (rfs4_lease_expired(cp)) {
goto end;
}
/*
* Find the open_owner for use from this point forward. Take
* care in updating the sequence id based on the type of error
* being returned.
*/
goto end;
}
/* Hold off access to the sequence space while the open is done */
/*
* If the open_owner existed before at the server, then check
* the sequence id.
*/
case NFS4_CHKSEQ_BAD:
oo->need_confirm) {
goto retry;
}
goto out;
case NFS4_CHKSEQ_REPLAY: /* replay of previous request */
goto out;
default:
break;
}
/*
* Sequence was ok and open owner exists
* check to see if we have yet to see an
* open_confirm.
*/
if (oo->need_confirm) {
goto retry;
}
}
/* Grace only applies to regular-type OPENs */
if (rfs4_clnt_in_grace(cp) &&
goto out;
}
/*
* If previous state at the server existed then can_reclaim
* will be set. If not reply NFS4ERR_NO_GRACE to the
* client.
*/
goto out;
}
/*
* Reject the open if the client has missed the grace period
*/
goto out;
}
/* Couple of up-front bookkeeping items */
if (oo->need_confirm) {
/*
* If this is a reclaim OPEN then we should not ask
* for a confirmation of the open_owner per the
* protocol specification.
*/
if (claim == CLAIM_PREVIOUS)
else
}
/*
* If there is an unshared filesystem mounted on this vnode,
*/
goto out;
}
/*
* access must READ, WRITE, or BOTH. No access is invalid.
* deny can be READ, WRITE, BOTH, or NONE.
*/
goto out;
}
/*
* make sure attrset is zero before response is built.
*/
switch (claim) {
case CLAIM_NULL:
break;
case CLAIM_PREVIOUS:
break;
case CLAIM_DELEGATE_CUR:
break;
case CLAIM_DELEGATE_PREV:
break;
default:
break;
}
out:
/* Catch sequence id handling here to make it a little easier */
case NFS4ERR_BADXDR:
case NFS4ERR_BAD_SEQID:
case NFS4ERR_BAD_STATEID:
case NFS4ERR_NOFILEHANDLE:
case NFS4ERR_RESOURCE:
case NFS4ERR_STALE_CLIENTID:
case NFS4ERR_STALE_STATEID:
/*
* The protocol states that if any of these errors are
* being returned, the sequence id should not be
* incremented. Any other return requires an
* increment.
*/
break;
default:
/* Always update the lease in this case */
/* Regular response - copy the result */
if (!replay)
/*
* REPLAY case: Only if the previous response was OK
* do we copy the filehandle. If not OK, no
* filehandle to copy.
*/
/*
* If this is a replay, we must restore the
* current filehandle/vp to that of what was
* returned originally. Try our best to do
* it.
*/
goto finish;
}
goto finish;
}
/*
* If this was a replay, no need to update the
* sequence id. If the open_owner was not created on
* this pass, then update. The first use of an
* open_owner will not bump the sequence id.
*/
/*
* If the client is receiving an error and the
* open_owner needs to be confirmed, there is no way
* to notify the client of this fact ignoring the fact
* that the server has no method of returning a
* stateid to confirm. Therefore, the server needs to
* mark this open_owner in a way as to avoid the
* sequence id checking the next time the client uses
* this open_owner.
*/
/*
* If OK response then clear the postpone flag and
* reset the sequence id to keep in sync with the
* client.
*/
}
break;
}
end:
}
/*ARGSUSED*/
void
{
OPEN_CONFIRM4args *, args);
goto out;
}
goto out;
}
/* Ensure specified filehandle matches */
goto out;
}
/* hold off other access to open_owner while we tinker */
case NFS4_CHECK_STATEID_OKAY:
resop) != 0) {
break;
}
/*
* If it is the appropriate stateid and determined to
* be "OKAY" then this means that the stateid does not
* need to be confirmed and the client is in error for
* sending an OPEN_CONFIRM.
*/
break;
case NFS4_CHECK_STATEID_OLD:
break;
case NFS4_CHECK_STATEID_BAD:
break;
break;
break;
case NFS4_CHKSEQ_OKAY:
/*
* This is replayed stateid; if seqid matches
* next expected, then client is using wrong seqid.
*/
/* fall through */
case NFS4_CHKSEQ_BAD:
break;
case NFS4_CHKSEQ_REPLAY:
/*
* Note this case is the duplicate case so
* resp->status is already set.
*/
break;
}
break;
resop) != NFS4_CHKSEQ_OKAY) {
break;
}
break;
default:
break;
}
out:
OPEN_CONFIRM4res *, resp);
}
/*ARGSUSED*/
void
{
int fflags = 0;
OPEN_DOWNGRADE4args *, args);
goto out;
}
goto out;
}
/* Ensure specified filehandle matches */
goto out;
}
/* hold off other access to open_owner while we tinker */
case NFS4_CHECK_STATEID_OKAY:
resop) != NFS4_CHKSEQ_OKAY) {
goto end;
}
break;
case NFS4_CHECK_STATEID_OLD:
goto end;
case NFS4_CHECK_STATEID_BAD:
goto end;
goto end;
goto end;
goto end;
/* Check the sequence id for the open owner */
case NFS4_CHKSEQ_OKAY:
/*
* This is replayed stateid; if seqid matches
* next expected, then client is using wrong seqid.
*/
/* fall through */
case NFS4_CHKSEQ_BAD:
goto end;
case NFS4_CHKSEQ_REPLAY:
/*
* Note this case is the duplicate case so
* resp->status is already set.
*/
goto end;
}
break;
default:
break;
}
/*
* Check that the new access modes and deny modes are valid.
* Check that no invalid bits are set.
*/
goto end;
}
/*
* The new modes must be a subset of the current modes and
* the access must specify at least one mode. To test that
* the new mode is a subset of the current modes we bitwise
* AND them together and check that the result equals the new
* mode. For example:
* New mode, access == R and current mode, sp->share_access == RW
* access & sp->share_access == R == access, so the new access mode
* is valid. Consider access == RW, sp->share_access = R
* access & sp->share_access == R != access, so the new access mode
* is invalid.
*/
(access &
(OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WRITE)) == 0) {
goto end;
}
/*
* Release any share locks associated with this stateID.
* Strictly speaking, this violates the spec because the
* spec effectively requires that open downgrade be atomic.
* At present, fs_shrlock does not have this capability.
*/
/*
* If the current mode has deny read and the new mode
* does not, decrement the number of deny read mode bits
* and if it goes to zero turn off the deny read bit
* on the file.
*/
(deny & OPEN4_SHARE_DENY_READ) == 0) {
}
/*
* If the current mode has deny write and the new mode
* does not, decrement the number of deny write mode bits
* and if it goes to zero turn off the deny write bit
* on the file.
*/
(deny & OPEN4_SHARE_DENY_WRITE) == 0) {
fp->deny_write--;
if (fp->deny_write == 0)
}
/*
* If the current mode has access read and the new mode
* does not, decrement the number of access read mode bits
* and if it goes to zero turn off the access read bit
* on the file. set fflags to FREAD for the call to
* vn_open_downgrade().
*/
(access & OPEN4_SHARE_ACCESS_READ) == 0) {
fp->access_read--;
if (fp->access_read == 0)
}
/*
* If the current mode has access write and the new mode
* does not, decrement the number of access write mode bits
* and if it goes to zero turn off the access write bit
* on the file. set fflags to FWRITE for the call to
* vn_open_downgrade().
*/
(access & OPEN4_SHARE_ACCESS_WRITE) == 0) {
fp->access_write--;
if (fp->access_write == 0)
}
/* Set the new access and deny modes */
/* Check that the file is still accessible */
goto end;
}
/*
* we successfully downgraded the share lock, now we need to downgrade
* the open. it is possible that the downgrade was only for a deny
* mode and we have nothing else to do.
*/
/* Update the stateid */
/* Update the lease */
/* And the sequence */
end:
out:
OPEN_DOWNGRADE4res *, resp);
}
/*
* The logic behind this function is detailed in the NFSv4 RFC in the
* SETCLIENTID operation description under IMPLEMENTATION. Refer to
* that section for explicit guidance to server behavior for
* SETCLIENTID.
*/
void
{
int len;
SETCLIENTID4args *, args);
/*
* In search of an EXISTING client matching the incoming
* request to establish a new client identifier at the server
*/
/* Should never happen */
goto out;
}
/*
* Easiest case. Client identifier is newly created and is
* unconfirmed. Also note that for this case, no other
* entries exist for the client identifier. Nothing else to
* check. Just setup the response and respond.
*/
if (create) {
/* Setup callback information; CB_NULL confirmation later */
goto out;
}
/*
* An existing, confirmed client may exist but it may not have
* been active for at least one lease period. If so, then
* "close" the client and create a new client identifier
*/
if (rfs4_lease_expired(cp)) {
goto retry;
}
cp_unconfirmed = cp;
else
cp_confirmed = cp;
/*
* We have a confirmed client, now check for an
* unconfimred entry
*/
if (cp_confirmed) {
/* If creds don't match then client identifier is inuse */
/*
* Some one else has established this client
* id. Try and say * who they are. We will use
* the call back address supplied by * the
* first client.
*/
}
}
/*
* Confirmed, creds match, and verifier matches; must
* be an update of the callback info
*/
/* Setup callback information */
/* everything okay -- move ahead */
/* update the confirm_verifier and return it */
goto out;
}
/*
* Creds match but the verifier doesn't. Must search
* for an unconfirmed client that would be replaced by
* this request.
*/
}
/*
* At this point, we have taken care of the brand new client
* struct, INUSE case, update of an existing, and confirmed
* client struct.
*/
/*
* check to see if things have changed while we originally
* picked up the client struct. If they have, then return and
* retry the processing of this SETCLIENTID request.
*/
if (cp_unconfirmed) {
if (!cp_unconfirmed->need_confirm) {
if (cp_confirmed)
goto retry;
}
/* do away with the old unconfirmed one */
}
/*
* This search will temporarily hide the confirmed client
* struct while a new client struct is created as the
* unconfirmed one.
*/
goto out;
}
/*
* If one was not created, then a similar request must be in
* process so release and start over with this one
*/
if (cp_confirmed)
goto retry;
}
/* Setup callback information; CB_NULL confirmation later */
out:
SETCLIENTID4res *, res);
}
/*ARGSUSED*/
void
{
struct compound_state *, cs,
goto out;
}
goto out;
}
/* If the verifier doesn't match, the record doesn't match */
goto out;
}
if (cp->cp_confirmed) {
}
/*
* Update the client's associated server instance, if it's changed
* since the client was created.
*/
/*
* Record clientid in stable storage.
* Must be done after server instance has been assigned.
*/
if (cptoclose)
/* don't need to rele, client_close does it */
/* If needed, initiate CB_NULL call for callback path */
/*
* Check to see if client can perform reclaims
*/
out:
struct compound_state *, cs,
SETCLIENTID_CONFIRM4 *, res);
}
/*ARGSUSED*/
void
{
CLOSE4args *, args);
goto out;
}
goto out;
}
/* Ensure specified filehandle matches */
goto out;
}
/* hold off other access to open_owner while we tinker */
case NFS4_CHECK_STATEID_OKAY:
resop) != NFS4_CHKSEQ_OKAY) {
goto end;
}
break;
case NFS4_CHECK_STATEID_OLD:
goto end;
case NFS4_CHECK_STATEID_BAD:
goto end;
goto end;
goto end;
goto end;
/* Check the sequence id for the open owner */
case NFS4_CHKSEQ_OKAY:
/*
* This is replayed stateid; if seqid matches
* next expected, then client is using wrong seqid.
*/
/* FALL THROUGH */
case NFS4_CHKSEQ_BAD:
goto end;
case NFS4_CHKSEQ_REPLAY:
/*
* Note this case is the duplicate case so
* resp->status is already set.
*/
goto end;
}
break;
default:
break;
}
/* Update the stateid. */
end:
out:
}
/*
* Manage the counts on the file struct and close all file locks
*/
/*ARGSUSED*/
void
{
struct shr_locowner shr_loco;
/*
* Decrement the count for each access and deny bit that this
* state has contributed to the file. If the file counts go to zero
* clear the appropriate bit in the appropriate mask.
*/
fp->access_read--;
if (fp->access_read == 0)
}
fp->access_write--;
if (fp->access_write == 0)
}
}
fp->deny_write--;
if (fp->deny_write == 0)
}
/*
* If this call is part of the larger closing down of client
* state then it is just easier to release all locks
* associated with this client instead of going through each
* individual file and cleaning locks there.
*/
if (close_of_client) {
/* Is the PxFS kernel module loaded? */
if (lm_remove_file_locks != NULL) {
int new_sysid;
/* Encode the cluster nodeid in new sysid */
/*
* This PxFS routine removes file locks for a
* client over all nodes of a cluster.
*/
"lm_remove_file_locks(sysid=0x%x)\n",
new_sysid));
} else {
/* Release all locks for this client */
}
}
}
/*
* Release all locks on this file by this lock owner or at
* least mark the locks as having been released
*/
/* Was this already taken care of above? */
if (!close_of_client &&
}
/*
* Release any shrlocks associated with this open state ID.
* This must be done before the rfs4_state gets marked closed.
*/
}
}
/*
* lock_denied: Fill in a LOCK4deneid structure given an flock64 structure.
*/
static nfsstat4
{
if (rfs4_lease_expired(cp)) {
return (NFS4ERR_EXPIRED);
}
goto finish;
}
/*
* Its not a NFS4 lock. We take advantage that the upper 32 bits
* of the client id contain the boot time for a NFS4 lock. So we
* fabricate and identity by setting clientid to the sysid, and
* the lock owner to the pid.
*/
else
return (NFS4ERR_INVAL); /* no mapping from POSIX ltype to v4 */
return (NFS4_OK);
}
static int
{
int error;
int i;
int cmd;
for (i = 0; i < rfs4_maxlock_tries; i++) {
break;
if (i < rfs4_maxlock_tries - 1) {
delaytime *= 2;
}
}
/* Get the owner of the lock */
/* No longer locked, retry */
goto retry;
}
}
}
return (error);
}
/*ARGSUSED*/
static nfsstat4
{
int flag;
int error;
return (NFS4ERR_EXPIRED);
}
return (status);
/* Check for zero length. To lock to end of file use all ones for V4 */
if (length == 0)
return (NFS4ERR_INVAL);
length = 0; /* Posix to end of file */
switch (locktype) {
case READ_LT:
case READW_LT:
if ((sp->share_access
& OPEN4_SHARE_ACCESS_READ) == 0) {
return (NFS4ERR_OPENMODE);
}
break;
case WRITE_LT:
case WRITEW_LT:
if ((sp->share_access
& OPEN4_SHARE_ACCESS_WRITE) == 0) {
return (NFS4ERR_OPENMODE);
}
break;
}
} else
/* Note that length4 is uint64_t but l_len and l_start are off64_t */
return (NFS4ERR_INVAL);
}
/*
* N.B. FREAD has the same value as OPEN4_SHARE_ACCESS_READ and
* FWRITE has the same value as OPEN4_SHARE_ACCESS_WRITE.
*/
if (error == 0) {
}
/*
* N.B. We map error values to nfsv4 errors. This is differrent
* than puterrno4 routine.
*/
switch (error) {
case 0:
break;
case EAGAIN:
case EACCES: /* Old value */
/* Can only get here if op is OP_LOCK */
== NFS4ERR_EXPIRED)
goto retry;
break;
case ENOLCK:
break;
case EOVERFLOW:
break;
case EINVAL:
break;
default:
break;
}
return (status);
}
/*ARGSUSED*/
void
{
int rc;
return;
}
/* Create a new lockowner for this instance */
return;
}
/* Ensure specified filehandle matches */
return;
}
/* hold off other access to open_owner while we tinker */
case NFS4_CHECK_STATEID_OLD:
goto end;
case NFS4_CHECK_STATEID_BAD:
goto end;
goto end;
goto end;
goto end;
case NFS4_CHECK_STATEID_OKAY:
case NFS4_CHKSEQ_OKAY:
if (rc == NFS4_CHECK_STATEID_OKAY)
break;
/*
* This is replayed stateid; if seqid
* matches next expected, then client
* is using wrong seqid.
*/
/* FALLTHROUGH */
case NFS4_CHKSEQ_BAD:
goto end;
case NFS4_CHKSEQ_REPLAY:
/* This is a duplicate LOCK request */
/*
* For a duplicate we do not want to
* create a new lockowner as it should
* already exist.
* Turn off the lockowner create flag.
*/
}
break;
}
(CE_NOTE, "rfs4_op_lock: no lock owner"));
goto end;
}
/*
* Only update theh open_seqid if this is not
* a duplicate request
*/
}
(CE_NOTE, "rfs4_op_lock: no state"));
goto end;
}
/*
* This is the new_lock_owner branch and the client is
* supposed to be associating a new lock_owner with
* the open file at this point. If we find that a
* lock_owner/state association already exists and a
* successful LOCK request was returned to the client,
* an error is returned to the client since this is
* not appropriate. The client should be using the
* existing lock_owner branch.
*/
goto end;
}
}
/*
* Only update theh open_seqid if this is not
* a duplicate request
*/
}
/*
* If this is a duplicate lock request, just copy the
* previously saved reply and return.
*/
/* verify that lock_seqid's match */
(CE_NOTE, "rfs4_op_lock: Dup-Lock seqid bad"
"lsp->seqid=%d old->seqid=%d",
} else {
/*
* Make sure to copy the just
* retrieved reply status into the
* overall compound status
*/
}
goto end;
}
/* Make sure to update the lock sequence id */
/*
* This is used to signify the newly created lockowner
* stateid and its sequence number. The checks for
* sequence number and increment don't occur on the
* very first lock request for a lockowner.
*/
/* hold off other access to lsp while we tinker */
ls_sw_held = TRUE;
} else {
/* get lsp and hold the lock on the underlying file struct */
!= NFS4_OK) {
return;
}
/* Ensure specified filehandle matches */
return;
}
/* hold off other access to lsp while we tinker */
ls_sw_held = TRUE;
/*
* The stateid looks like it was okay (expected to be
* the next one)
*/
case NFS4_CHECK_STATEID_OKAY:
/*
* The sequence id is now checked. Determine
* if this is a replay or if it is in the
* expected (next) sequence. In the case of a
* replay, there are two replay conditions
* that may occur. The first is the normal
* condition where a LOCK is done with a
* NFS4_OK response and the stateid is
* updated. That case is handled below when
* the stateid is identified as a REPLAY. The
* second is the case where an error is
* returned, like NFS4ERR_DENIED, and the
* sequence number is updated but the stateid
* is not updated. This second case is dealt
* with here. So it may seem odd that the
* stateid is okay but the sequence id is a
* replay but it is okay.
*/
switch (rfs4_check_lock_seqid(
case NFS4_CHKSEQ_REPLAY:
/*
* Here is our replay and need
* to verify that the last
* response was an error.
*/
goto end;
}
/*
* This is done since the sequence id
* looked like a replay but it didn't
* pass our check so a BAD_SEQID is
* returned as a result.
*/
/*FALLTHROUGH*/
case NFS4_CHKSEQ_BAD:
goto end;
case NFS4_CHKSEQ_OKAY:
/* Everything looks okay move ahead */
break;
}
break;
case NFS4_CHECK_STATEID_OLD:
goto end;
case NFS4_CHECK_STATEID_BAD:
goto end;
goto end;
goto end;
switch (rfs4_check_lock_seqid(
case NFS4_CHKSEQ_OKAY:
/*
* This is a replayed stateid; if
* seqid matches the next expected,
* then client is using wrong seqid.
*/
case NFS4_CHKSEQ_BAD:
goto end;
case NFS4_CHKSEQ_REPLAY:
goto end;
}
break;
default:
break;
}
}
/*
* NFS4 only allows locking on regular files, so
* verify type of object.
*/
else
goto out;
}
goto out;
}
goto out;
}
goto out;
}
out:
}
/*
* Only update the "OPEN" response here if this was a new
* lock_owner
*/
if (sp)
end:
if (lsp) {
if (ls_sw_held)
/*
* If an sp obtained, then the lsp does not represent
* a lock on the file struct.
*/
else
}
if (sp) {
}
}
static void
{
case OP_LOCK:
break;
case OP_LOCKT:
break;
default:
break;
}
if (dp)
}
/*ARGSUSED*/
void
{
LOCKU4args *, args);
return;
}
return;
}
/* Ensure specified filehandle matches */
return;
}
/* hold off other access to lsp while we tinker */
case NFS4_CHECK_STATEID_OKAY:
!= NFS4_CHKSEQ_OKAY) {
goto end;
}
break;
case NFS4_CHECK_STATEID_OLD:
goto end;
case NFS4_CHECK_STATEID_BAD:
goto end;
goto end;
goto end;
case NFS4_CHKSEQ_OKAY:
/*
* This is a replayed stateid; if
* seqid matches the next expected,
* then client is using wrong seqid.
*/
case NFS4_CHKSEQ_BAD:
goto end;
case NFS4_CHKSEQ_REPLAY:
goto end;
}
break;
default:
break;
}
/*
* NFS4 only allows locking on regular files, so
* verify type of object.
*/
else
goto out;
}
goto out;
}
out:
end:
}
/*
* LOCKT is a best effort routine, the client can not be guaranteed that
* the status return is still in effect by the time the reply is received.
* They are numerous race conditions in this routine, but we are not required
* and can not be accurate.
*/
/*ARGSUSED*/
void
{
int error;
int ltype;
LOCKT4args *, args);
goto out;
}
/*
* NFS4 only allows locking on regular files, so
* verify type of object.
*/
else
goto out;
}
/*
* Check out the clientid to ensure the server knows about it
* so that we correctly inform the client of a server reboot.
*/
== NULL) {
goto out;
}
if (rfs4_lease_expired(cp)) {
/*
* Protocol doesn't allow returning NFS4ERR_STALE as
* other operations do on this check so STALE_CLIENTID
* is returned instead
*/
goto out;
}
if (rfs4_clnt_in_grace(cp)) {
goto out;
}
case READ_LT:
case READW_LT:
break;
case WRITE_LT:
case WRITEW_LT:
break;
}
/* Check for zero length. To lock to end of file use all ones for V4 */
if (posix_length == 0) {
goto out;
} else if (posix_length == (length4)(~0)) {
posix_length = 0; /* Posix to end of file */
}
/* Find or create a lockowner */
if (lo) {
goto err;
goto out;
} else {
pid = 0;
sysid = lockt_sysid;
}
flag |= F_REMOTELOCK;
/* Note that length4 is uint64_t but l_len and l_start are off64_t */
goto err;
}
/*
* N.B. We map error values to nfsv4 errors. This is differrent
* than puterrno4 routine.
*/
switch (error) {
case 0:
else {
goto retry;
}
break;
case EOVERFLOW:
break;
case EINVAL:
break;
default:
error);
break;
}
err:
if (lo)
out:
}
static int
{
int err;
return (0);
(CE_NOTE, "rfs4_shrlock %s vp=%p acc=%d dny=%d sysid=%d "
err));
return (err);
}
static int
{
struct shr_locowner shr_loco;
int fflags;
}
}
}
static int
{
int cmd;
}
void
{
}