/*
* 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
*/
/*
*/
/*
* NFS specific functions
*/
#include <strings.h>
#include <ctype.h>
#include <limits.h>
#include <pwd.h>
#include <libnvpair.h>
#include <unistd.h>
#include <locale.h>
#include <errno.h>
#include <zone.h>
#include <signal.h>
#include <libscf.h>
#include <note.h>
#include <sys/sysmacros.h>
#include <rpcsvc/daemon_utils.h>
#include <libxprop.h>
#include "nfslog_config.h"
#include "nfslogtab.h"
#include <sharefs/sharetab.h>
#include <libshare.h>
#include <libshare_impl.h>
#include "smfcfg.h"
#include "libshare_nfs.h"
static int debug = 0;
/* should really be in some global place */
/* used to sort out autofs and lofs */
static int sa_nfs_add_property(nvlist_t *, char *, char *,
char *, size_t);
static int sa_nfs_parse_val(char **, char *, int, char *, size_t);
char **, char *, char **, char *, size_t);
char *, char *, size_t);
char *, char *, size_t);
static void sanfs_free_proto_proplist(void);
static int find_gbl_prop(const char *);
static int find_sec_prop(const char *);
static int find_gbl_prop_type(const char *);
static int find_sec_prop_type(const char *);
static void nfs_check_services(boolean_t);
static int nfslogtab_add(char *, char *, char *);
static int nfslogtab_remove(const char *);
static char *nfs_space_alias(char *);
static scf_type_t getscftype(int);
static int showmount_info_check(const char *);
/*
* plugin ops routines
*/
static int sa_nfs_init(void);
static void sa_nfs_fini(void);
static int sa_nfs_share_set_def_proto(nvlist_t *);
static int sa_nfs_share_validate_name(const char *, sa_validate_flags_t, char *,
size_t);
sa_validate_flags_t, char *, size_t);
static int sa_nfs_share_publish(nvlist_t *, int);
static int sa_nfs_share_unpublish(nvlist_t *, int);
static int sa_nfs_share_unpublish_byname(const char *, const char *, int);
static int sa_nfs_share_publish_admin(const char *);
static int sa_nfs_fs_publish(nvlist_t *, int);
static int sa_nfs_fs_unpublish(nvlist_t *, int);
static int sa_nfs_format_props(nvlist_t *, char **);
size_t);
/* protocol property routines */
static int sa_nfs_proto_get_features(uint64_t *);
static int sa_nfs_proto_get_proplist(nvlist_t **);
static int sa_nfs_proto_get_status(char **);
static int sa_nfs_proto_get_property(const char *, const char *, char **);
static int sa_nfs_proto_set_property(const char *, const char *, const char *);
.sap_hdr = {
.pi_type = SA_PROT_NFS,
.pi_name = "nfs",
.pi_flags = 0,
.pi_init = sa_nfs_init,
},
};
/*
* option definitions. Make sure to keep the #define for the option
* index just before the entry it is the index for. Changing the order
* can cause breakage. E.g OPT_RW is index 1 and must precede the
* line that includes the SHOPT_RW and OPT_RW entries.
*/
#define OPT_ANON 0
#ifdef VOLATILE_FH_TEST /* added for testing volatile fh's only */
#endif /* VOLATILE_FH_TEST */
};
#define OPT_RO 0
};
/*
* List of security flavors.
*/
static char *sec_flavors[] = {
#define NFS_SEC_SYS 0
"sys",
"default", /* special case */
"dh",
"krb5",
"krb5i",
"krb5p",
"none",
};
/*
* Codesets that may need to be converted to UTF-8 for file paths.
* Add new names here to add new property support. If we ever get a
* way to query the kernel for character sets, this should become
* dynamically loaded. Make sure changes here are reflected in
*/
static char *legal_charset[] = {
"euc-cn",
"euc-jp",
"euc-jpms",
"euc-kr",
"euc-tw",
"iso8859-1",
"iso8859-2",
"iso8859-5",
"iso8859-6",
"iso8859-7",
"iso8859-8",
"iso8859-9",
"iso8859-13",
"iso8859-15",
"koi8-r",
};
/*
* list of support services needed
* defines should come from head/rpcsvc/daemon_utils.h
*/
static char *nfs_service_list_default[] =
static char *nfs_service_list_logging[] =
NULL };
/*
* the proplist holds the defined options so we don't have to read
* them multiple times
*/
static int
sa_nfs_init(void)
{
return (SA_OK);
}
static void
sa_nfs_fini(void)
{
}
static int
{
char *namep;
char *valp;
char *nextp;
int ret;
return (SA_INTERNAL_ERR);
}
/*
* allocate a new nvlist for nfs properties
*/
ret = SA_NO_MEMORY;
goto err_out;
}
/*
* return prot_nvl with no properties.
*/
if (prot_props == NULL) {
return (SA_OK);
}
ret = SA_NO_MEMORY;
goto err_out;
}
*nextp = '\0';
else
nextp++;
}
goto err_out;
} else {
/*
* If this is not a valid security or global
* property, then return with invalid property
* error.
*/
/*
* this is a security property,
* if we have seen a sec= property then
* add it to the sec_nvl, otherwise
* add it to the global list. It will be
* moved to the default security list
* in sa_nfs_cleanup_sec_props later.
*/
} else {
}
} else {
goto err_out;
}
}
goto err_out;
}
/*
* done parsing protocol properties,
* attach any outstanding security nvlists to prot_nvl
*/
goto err_out;
}
goto err_out;
return (SA_OK);
return (ret);
}
static int
{
char *valp;
/* Verify the name starts with "share.nfs." */
return (SA_INTERNAL_ERR);
return (SA_INTERNAL_ERR);
/* Global property share.nfs.prop */
return (SA_OK);
}
/* Charset property follows "charset" */
return (SA_OK);
}
/* Get the security flavor */
return (SA_SYNTAX_ERR);
/* Security property follows flavor */
return (SA_OK);
}
return (SA_NO_SUCH_PROP);
}
static int
{
int nseen_sec = 0;
char *namep;
char *valp;
int ret;
int sec;
int idx;
char *strval;
return (SA_INTERNAL_ERR);
}
/* If any RAW property is specified, it takes precedence */
buflen));
}
/*
* allocate a new nvlist for nfs properties
*/
ret = SA_NO_MEMORY;
goto err_out_str;
}
goto err_out_str;
}
assert(0); /* should never happen */
continue;
}
goto err_out_str;
}
/* Global property or charset property or "sec" */
/* Check for "share.nfs.sec" */
goto err_out;
}
/*
* Special handling for share.nfs.sec
* that gives the order of the security modes.
* e.g. share.nfs.sec=sys:krb5:dh:default
*/
char *valstart;
do {
int sec;
*valp++ = '\0';
goto err_out_str;
}
NV_UNIQUE_NAME, 0) != 0) {
ret = SA_NO_MEMORY;
goto err_out_str;
}
} while (!done);
continue;
}
goto err_out_str;
}
/*
* Normal global property, or charset property.
* Add this property to the global list
*/
goto err_out;
}
} else {
/* Security property */
if (sec < 0) {
goto err_out;
}
char *defval;
/*
* Special case of "default" where we just want
* to know that there is a defined default.
* We don't want to convert the default name
* to the current default.
*/
ret = SA_NO_MEMORY;
goto err_out_str;
}
"%s: value not defined for"
" sec=default"),
sa_strerror(ret));
goto err_out;
}
}
/* Create the security nvlist for this security type */
NV_UNIQUE_NAME, 0) != 0) {
ret = SA_NO_MEMORY;
goto err_out_str;
}
/* Null values should have been weeded out by caller */
goto err_out_str;
}
/* Add the property to the right security list */
if (ret != 0)
goto err_out;
}
}
/*
* Now add the security lists to the share-style nvlist.
* There are no security properties in the global list.
* If there was a "share.nfs.sec" list of modes, start with those.
*/
ret = SA_NO_MEMORY;
goto err_out_str;
}
}
}
/*
* Then add in any other security modes that were not in the sec list.
*/
ret = SA_NO_MEMORY;
goto err_out_str;
}
}
}
return (SA_OK);
sa_strerror(ret));
}
}
return (ret);
}
static int
{
char *propname;
char *propval;
continue;
continue;
return (SA_NO_MEMORY);
}
return (SA_OK);
}
static int
{
char *propname;
char *propval;
int nseen_sec = 0;
/*
* first include all global protocol properties
*/
continue;
continue;
if (idx < 0)
return (SA_INTERNAL_ERR);
propname);
return (SA_NO_MEMORY);
}
/*
* now print out security properties
*/
continue;
continue;
if (sec < 0) {
return (SA_INVALID_SECURITY);
}
return (ret);
}
/* set share.nfs.sec to list of security modes in orig order */
if (nseen_sec > 0) {
cnt = 0;
}
return (SA_NO_MEMORY);
}
return (SA_OK);
}
static int
{
int ptype;
char *propval;
int ret;
if (ptype == -1)
if (ptype == OPT_TYPE_ACCLIST) {
/*
* append access list value for this property
*/
}
/*
* all other property types will
* use the last value defined.
*/
}
/*
* save property to list
*/
sa_strerror(ret));
}
return (ret);
}
/*
* sa_nfs_parse_val
*
*/
static int
{
char *valp;
int ptype;
/* no equal sign found */
if (unset) {
/* not needed for unset */
*valpp = "";
} else {
/* property with no value, set to default */
if (ptype == -1)
switch (ptype) {
case OPT_TYPE_BOOLEAN:
*valpp = "true";
break;
case OPT_TYPE_ACCLIST:
*valpp = "*";
break;
case OPT_TYPE_LOGTAG:
*valpp = "global";
break;
default:
ret = SA_SYNTAX_ERR;
"syntax error: no value for "
"'%s' property"), namep);
break;
}
}
/* equal sign found but no value specified */
if (unset) {
*valp = '\0';
*valpp = "";
} else {
ret = SA_SYNTAX_ERR;
"syntax error: no value for '%s' property"),
namep);
}
} else {
*valp++ = '\0';
}
return (ret);
}
static int
{
char *modep;
char *sec_name;
int ret;
return (ret);
}
/*
* is this a multi-mode security property?
* ie sec=sys:dh,ro=*,rw=grp1
*/
/*
* yes, null terminate first mode
* and setup for next mode.
*/
*modep++ = '\0';
} else {
}
ret = SA_NO_MEMORY;
return (ret);
}
/*
* is this a valid security mode
*/
return (ret);
}
char *defval;
/*
* Special case of "default" where we just want to
* know that there is a defined default. We don't want
* to convert the default name to the current default.
*/
ret = SA_NO_MEMORY;
return (ret);
}
"%s: value not defined for sec=default"),
sa_strerror(ret));
return (ret);
}
}
/*
* allocate a new security nvlist
*/
ret = SA_NO_MEMORY;
return (ret);
}
return (SA_OK);
}
/*
* sa_nfs_finalize_sec_list
*
* sec_nvl is added to prot_nvl
* For multi-mode, sa_nfs_dup_sec_list is called to add
* security property lists for all modes specified in modep
*
* cur_nvl is deallocated.
*/
static int
{
/*
* Are we working on a multi-mode security?
*/
/*
* save cur sec_nvl and add security
* lists for all modes specified in
* security property
*/
} else {
/*
* add the current sec_nvl
*/
sec_nvl) != 0) {
ret = SA_NO_MEMORY;
}
}
return (ret);
}
/*
* sa_nfs_dup_sec_list
*
* This routine handles the case when multiple security modes
* are specified with a single sec= property (ie sec=krb5:krb5i,ro=*,rw=grp1)
*
* INPUTS:
* prot_nvl: protocol property list
* sec_nvl: security property list for first mode
* sec_name: security mode of sec_nvl
* modep: points to next mode in the string
* buflen: size of errbuf
*
* OUTPUTS:
* errbuf: on error, set to error string
*
* NOTES:
* The security properties have already been parsed and added to sec_nvl.
* modep points to the second security mode in the list
* sec_nvl will added to prot_nvl, and a copy will be made and added for
* each security mode specified in modep.
* sec_nvl will not be deallocated by this routine.
*/
static int
{
char *valp;
int rc;
/*
* add the current sec_nvl
*/
rc = SA_NO_MEMORY;
return (rc);
}
do {
/*
* search for the next security mode
* and null terminate the mode name
*/
*modep = '\0';
modep++;
}
if (*valp != '\0') {
/*
* is this a valid security mode?
*/
return (rc);
}
/*
* Special case of "default" converts
* to defined default value. We will
* be keeping the default name but do
* need to validate that it would map
* to something.
*/
rc = SA_NO_MEMORY;
valp);
return (rc);
}
"%s: value not defined for "
return (rc);
}
}
rc = SA_NO_MEMORY;
return (rc);
}
/*
* dup the original security property list
*/
rc = SA_NO_MEMORY;
return (rc);
}
/*
* and add it to the protocol property list
* using the next mode name in the list
*/
new_sec_nvl) != 0) {
rc = SA_NO_MEMORY;
return (rc);
}
}
return (SA_OK);
}
/*
* This routine will check for any security properties outside of a
* security nvlist. If found, move the properties to a new security list.
* When done, merge sec=sys property list (if exists) into new security list
* and then save it to the protocol nvlist as sec=sys.
* Also add rw=* to all empty security lists.
*/
static int
int unset)
{
char *propname;
char *propval;
/* skip security nvlists */
continue;
}
/* skip non security properties */
continue;
}
/*
* found a security property
* if sys security list already exists, then
* add to the list, otherwise create one first
*/
ret = SA_NO_MEMORY;
sa_strerror(ret));
return (ret);
}
}
/* Get value from nvpair */
return (ret);
}
/* add to security property list */
sa_strerror(ret));
return (ret);
}
/* remove from protocol property list */
nvpair_type(curr_nvp)) != 0) {
ret = SA_SYNTAX_ERR;
"%s: must follow sec property: %s"),
return (ret);
}
}
if (sec_sys_exists) {
/*
* move security properties from existing sec=sys
* security nvlist to the new_nvl. This will preserve
* the order of the properties.
*/
ret = SA_NO_MEMORY;
sa_strerror(ret));
return (ret);
}
}
/*
* save the new security nvlist to the prot_nvl
* This will replace any existing sec=sys nvlist
*/
ret = SA_NO_MEMORY;
sa_strerror(ret));
return (ret);
}
}
/*
* If this is a unset request then leave the property
* lists the way they are. For example, an empty security
* list is a request to remove all properties for the specified
* security mode. So we only want to add default properties
* for update requests.
*/
if (unset)
return (SA_OK);
/* make sure each security list has at least one property */
}
/*
* Go through all security lists and make sure each
* has at least one property. If not then add
* default property (rw=*)
*/
static int
{
int ret;
/* skip non-security properties */
continue;
sa_strerror(ret));
return (ret);
}
/*
* make sure we have at least one security property. If
* not, then add an "rw".
*/
sa_strerror(ret));
return (ret);
}
}
}
return (SA_OK);
}
/*
* nfs_merge_security
*
* INPUTS:
* dst_prot_nvl: the destination protocol property list.
* src_sec_nvl: the new security property list to be merged
* into dst_prot_nvl
* sec_mode_name: security mode name of src_sec_nvl
* unset : if set, remove sec mode from dst_prot_nvl
*
* OUTPUTS:
* dst_pro_nvl: the destination protocol property list is
* updated with new security mode properties,
* or if 'unset', the security mode properties
* are removed from the property list.
* errbuf: if there are any errors, the error message
* is copied to this buffer.
*/
static int
{
int rc;
if (unset) {
/*
* If there are no specific security properties
* specified, then remove the security nvlist
* otherwise remove just the properties given.
*/
if (sa_prop_empty_list(src_sec_nvl)) {
DATA_TYPE_NVLIST) != 0) {
return (rc);
}
} else {
&dst_sec_nvl) != 0) {
return (rc);
}
if (nvlist_remove(dst_sec_nvl,
return (rc);
}
}
}
} else {
/*
* If there is a matching security nvlist in the
* destination protocol nvlist, then merge the two
* security lists, otherwise add the source security
* nvlist to the destination protocol nvlist.
*/
&dst_sec_nvl) == 0) {
rc = SA_NO_MEMORY;
sa_strerror(rc));
return (rc);
}
} else {
src_sec_nvl) != 0) {
rc = SA_NO_MEMORY;
sa_strerror(rc));
return (rc);
}
}
}
return (SA_OK);
}
/*
* sa_nfs_share_merge
*
* Merge a new nfs protocol property list with an existing property list.
* Existing properties will be overwritten, new properties will be added.
* If the 'unset' flag is set, then existing properties will be removed.
*
* INPUTS:
* dst_prot_nvl: existing protocol property list. Values from src_prot_nvl
* will be merged into this list.
* src_prot_nvl: New protocol property list. This is a list of properties
* that will be updated in dst_prot_nvl.
* unset: If set, the properties identified in src_prot_nvl will be
* removed from dst_prot_nvl.
*
* OUTPUTS:
* dst_prot_nvl: Updated property list
* errbuf: if error, error message is copied here.
*/
static int
{
char *propname;
int rc;
rc = SA_NO_MEMORY;
sa_strerror(rc));
return (rc);
}
return (rc);
} else {
if (unset) {
DATA_TYPE_STRING) != 0) {
return (rc);
}
} else {
/*
* Just add the new nvpair to the destination
* protocol nvlist. Because the nvlist was
* created with the NV_UNIQUE_NAME flag,
* existing properties will be overwritten
* with the new property.
*/
rc = SA_NO_MEMORY;
sa_strerror(rc));
return (rc);
}
}
}
}
return (SA_OK);
}
/*
* sa_nfs_share_set_def_proto
*
* add a default proto property list to the share
*/
static int
{
int rc;
return (SA_NO_MEMORY);
}
return (rc);
}
/*
* is_a_number(number)
*
* is the string a number in one of the forms we want to use?
*/
static int
{
int hex = 0;
number += 2;
hex = 1;
} else if (*number == '-') {
number++; /* skip the minus */
}
if (hex) {
} else {
}
}
return (ret);
}
static int
{
int pwd_rc;
int rc;
if (!is_a_number(val)) {
/*
* in this case it would have to be a
* user name
*/
rc = SA_NO_MEMORY;
return (rc);
}
endpwent();
return (rc);
}
} else {
rc = SA_INVALID_UID;
return (rc);
}
}
return (SA_OK);
}
/*
* sa_nfs_check_acclist
*
* check syntax of access list value
* This will only check multi-entry values.
* Cannot have '*' with other values
*
* This could be enhanced to check for duplicates
*/
static int
{
char *token;
char *lasts;
int tok_cnt = 0;
int found_wildcard = 0;
return (SA_NO_MEMORY);
found_wildcard = 1;
tok_cnt++;
}
"%s: cannot mix \"*\" with other values: %s=%s"),
}
return (rc);
}
static boolean_t
{
return (B_TRUE);
else
return (B_FALSE);
}
/*
* Validate access list values. Should eventually validate that all
* the values make sense. Also, ro and rw may have cross value conflicts.
* These are the current applicable rules:
*
* ro != rw
* ro != none
* rw != none
*/
static int
{
int rc;
goto invalid;
}
prop2 = SHOPT_NONE;
goto invalid;
}
prop2 = SHOPT_NONE;
goto invalid;
}
return (SA_OK);
return (rc);
}
static const char *
{
const char *values;
switch (type) {
case OPT_TYPE_ANY:
break;
case OPT_TYPE_STRING:
break;
case OPT_TYPE_BOOLEAN:
break;
case OPT_TYPE_NUMBER:
break;
case OPT_TYPE_RANGE:
break;
case OPT_TYPE_USER:
break;
case OPT_TYPE_ACCLIST:
break;
case OPT_TYPE_DEPRECATED:
break;
case OPT_TYPE_SECURITY:
break;
case OPT_TYPE_PATH:
break;
case OPT_TYPE_FILE:
break;
case OPT_TYPE_LOGTAG:
break;
case OPT_TYPE_STRINGSET:
break;
case OPT_TYPE_DOMAIN:
break;
case OPT_TYPE_ONOFF:
break;
case OPT_TYPE_PROTOCOL:
break;
default:
}
return (values);
}
static int
struct option_defs *defs)
{
int err;
return (0);
if (is_boolean) {
return (err);
} else {
NULL)) != 0)
return (err);
}
return (0);
}
static int
{
int i, j;
return (SA_NO_MEMORY);
return (SA_NO_MEMORY);
}
/* Add in all the global properties at top level */
&gbl_prop_defs[i]) != 0)
goto error;
}
/* Add in all the charset properties */
for (i = 0; legal_charset[i] != NULL; i++) {
goto error;
}
/* Add in all the security properties */
for (i = 0; sec_flavors[i] != NULL; i++) {
/* Add in the proper modes for each sec prop */
/*
* The "window" property only applies to the "dh"
* security mode.
*/
continue;
/*
* The "root" property does not apply to the "none"
* security mode.
*/
continue;
&sec_prop_defs[j]) != 0)
goto error;
}
}
/* Special-case properties that do not flow from the tables */
goto error;
/*
* The following sublists can be omitted, permitting shorter aliases for
* the dotted property names. For example, "sys" can be omitted because
* it is the default security mode.
*/
goto error;
return (0);
return (SA_NO_MEMORY);
}
static int
{
int i;
return (-1);
return (i);
}
return (OPT_CHARSET_MAP);
return (-1);
}
static int
{
int i;
return (-1);
return (i);
}
return (-1);
}
static int
{
int i;
return (-1);
return (gbl_prop_defs[i].type);
}
return (-1);
}
static int
{
int i;
return (-1);
return (sec_prop_defs[i].type);
}
return (-1);
}
/*
* nfs_space_alias(alias)
*
* Lookup the space (security) name. If it is default, convert to the
* real name. In a multithreaded environment or if nfs_getseconfig is
* called later, can't trust that name will survive so strdup() it.
*/
static char *
{
/*
* Only the space named "default" is special. If it is used,
* the default needs to be looked up and the real name used.
* This is normally "sys" but could be changed. We always
* change default to the real name.
*/
nfs_getseconfig_default(&secconf) == 0) {
}
}
static int
{
int prop;
int proptype;
char *propname;
char *propval;
int rc;
if (prop == -1) {
return (rc);
} else {
}
return (rc);
}
switch (proptype) {
case OPT_TYPE_NUMBER:
/* check that the value is all digits */
if (!is_a_number(propval)) {
return (rc);
}
break;
case OPT_TYPE_ACCLIST:
return (rc);
if (!acclist_validated) {
return (rc);
}
break;
case OPT_TYPE_USER:
return (rc);
}
break;
}
}
return (SA_OK);
}
/*
* validate that this path has not been shared (for NFS) or
* no other NFS share exists in path (ancestor/decendents)
*/
static int
{
int rc;
void *hdl;
"error obtaining mountpoint for %s", sh_path);
return (rc);
}
/*
* iterate through all shares on dataset
*/
char *p;
char *name;
char *path;
/*
* ignore if this is not an NFS share or has the
* same share name.
*/
continue;
}
== 0) {
if (sh_pathlen == pathlen)
else if (sh_pathlen < pathlen) {
p = path + sh_pathlen;
if (*p == '/' ||
} else { /* pathlen < sh_pathlen */
if (*p == '/' ||
}
"NFS: %s: %s in %s"),
break;
}
}
}
}
/*
* Even if it passes the above tests, it could be a LOFS file
* system inside the autofs. In this case, if the st_rdev's
* are the same we reject it. The code also catches the auto
* mount not being there.
*/
}
}
return (rc);
}
/*
* nfs_public_exists
*
* search permanent storage for an existing share with
* the public option.
*/
static boolean_t
{
void *hdl;
char *sh_path;
char *sh_name;
return (B_FALSE);
"%s: only one instance of \"public\" allowed: "
}
}
return (found);
}
/*
* nfs_public_active
*
* search active shares for a share with public option
*/
static boolean_t
{
void *hdl;
char *sh_path;
return (B_FALSE);
}
}
}
return (found);
}
static int
{
sa_strerror(rc));
return (rc);
}
return (rc);
}
static int
{
int prop;
char *sh_name;
char *sh_path;
char *propname;
char *propval;
errbuf[0] = '\0';
sa_strerror(rc));
return (rc);
}
sa_strerror(rc));
return (rc);
}
/*
* Errors that a caller wants to ignore when creating a share but not
* when publishing a share must not prevent the caller from getting a
* validation error, so defer publish-time errors to the very end.
* Only check for path conflicts for shares that will be enabled.
*/
}
/*
* MUST have a nfs protocol property list
* It is ok if list is empty
*/
sa_strerror(rc));
return (rc);
}
/*
* if nvpair type == nvlist, then security list
*/
return (rc);
}
rc = SA_NO_MEMORY;
sa_strerror(rc));
return (rc);
}
return (rc);
continue;
}
if (prop == -1) {
rc = SA_SYNTAX_ERR;
"%s: must follow sec property: %s"),
return (rc);
} else {
return (rc);
}
}
if (prop == OPT_PUBLIC &&
/*
* Public is special in that only one instance can
* be in the repository at the same time.
*/
if (publish_rc == SA_OK)
}
return (rc);
}
case OPT_TYPE_NUMBER:
/* check that the value is all digits */
if (!is_a_number(propval)) {
return (rc);
}
break;
case OPT_TYPE_BOOLEAN:
return (rc);
}
break;
case OPT_TYPE_USER:
return (rc);
}
break;
case OPT_TYPE_FILE:
return (rc);
}
break;
case OPT_TYPE_ACCLIST:
return (rc);
break;
case OPT_TYPE_LOGTAG:
if (nfsl_getconfig_list(&configlist) == 0) {
int error;
propval = "global";
if (publish_rc == SA_OK) {
"%s: %s=%s"),
}
}
/* Must always free when done */
} else {
if (publish_rc == SA_OK)
}
break;
case OPT_TYPE_STRING:
if (*propval == '\0') {
return (rc);
}
break;
default:
break;
}
}
rc = publish_rc;
if (errbuf[0] == '\0') {
}
}
return (rc);
}
/*
* Look for the specified tag in the configuration file. If it is found,
* enable logging and set the logging configuration information for exp.
*/
static int
{
int error = 0;
/*
* Sends config errors to stderr
*/
/*
* get the list of configuration settings
*/
if (error) {
salog_error(0, "NFS: Cannot get log configuration: %s",
goto err;
}
/* bad configuration */
goto err;
}
goto out;
}
goto out;
}
out:
if (configlist != NULL)
err:
if (error != 0) {
salog_error(0, "NFS: Cannot set log configuration: %s",
}
return (error);
}
static int
{
int count = 0;
count++;
}
}
return (count);
}
/*
* Given a seconfig entry and a colon-separated
* list of names, allocate an array big enough
* to hold the root list, then convert each name to
* a principal name according to the security
* info and assign it to an array element.
* Return the array and its size.
*/
static caddr_t *
{
caddr_t *a;
int c, i;
char *host, *p;
char *lasts;
/*
* Count the number of strings in the list.
* This is the number of colon separators + 1.
*/
c = 1;
for (p = list; *p; p++)
if (*p == ':')
c++;
*count = c;
if (a == NULL) {
} else {
for (i = 0; i < c; i++) {
free(a);
a = NULL;
break;
}
}
}
return (a);
}
static int
{
char *name;
char *value;
int type;
int pwd_rc;
int val;
/* named security type needs secinfo to be filled in */
} else {
/* default case */
}
if (err != SC_NOERROR) {
switch (err) {
case SC_NOMEM:
err = SA_NO_MEMORY;
break;
case SC_OPENFAIL:
break;
case SC_NOTFOUND:
default:
break;
}
"NFS: %s: error obtaining security config for %s",
return (err);
}
continue;
salog_error(0,
"NFS: %s: invalid security property: %s:%s",
name);
return (SA_INVALID_PROP_VAL);
}
switch (type) {
case OPT_RO:
break;
case OPT_RW:
break;
case OPT_ROOT:
/*
* if we are using AUTH_UNIX, handle like other things
*/
continue;
/* not AUTH_UNIX */
err = SA_NO_MEMORY;
"NFS: %s: error processing "
"security properties", sh_path);
return (err);
}
"NFS: %s: invalid root list: %s=%s",
return (err);
}
}
break;
case OPT_WINDOW:
/* just in case */
}
break;
case OPT_NONE:
break;
case OPT_ROOT_MAPPING:
} else {
if (pwd_buflen == 0) {
"NFS: %s: error processing "
"security properties",
sh_path);
return (SA_NO_MEMORY);
}
}
&pwdp);
val = UID_NOBODY;
else
endpwent();
}
break;
}
}
return (SA_OK);
}
/*
* cleanup_export(export)
*
* Cleanup the allocated areas so we don't leak memory
* This frees added structure pointers.
*/
static void
{
int i, j;
}
}
}
}
}
#ifdef VOLATILE_FH_TEST
/*
* Set the ex_flags to indicate which fh expire type.
* Return 0 for success, error otherwise.
*/
static int
{
int err = 0;
*next = '\0';
else {
}
*next = ':';
}
return (err);
}
#endif /* VOLATILE_FH_TEST */
static int
struct exportdata *export)
{
char *name;
int type;
char *value;
int pwd_rc;
int err;
int numsec;
int whichsec = 0;
/*
* must allocate for default security options
*/
if (numsec == 0)
numsec = 1;
return (SA_NO_MEMORY);
}
/*
* since we must have one security option defined, we
* init to the default and then override as we find
* defined security options. This handles the case
* where we have no defined options but we need to set
* up one.
*/
/* setup a default in case no properties defined */
salog_error(0,
"NFS: %s: failed to get default security mode: %d",
rc = SA_NO_MEMORY;
else if (err == SC_OPENFAIL)
else
return (rc);
}
/*
* Security options are in a sub-nvlist and
* are filled separately.
*/
rc = SA_NO_MEMORY;
return (rc);
}
if (rc != SA_NO_SUCH_SECURITY) {
/* error is logged in fill_security */
return (rc);
} else {
/*
* bad security mode, remove
* from property list
*/
DATA_TYPE_NVLIST) != 0) {
"NFS: %s: error processing"
" '%s' security properties",
return (rc);
} else {
}
}
} else {
whichsec++;
}
continue;
}
salog_error(0, "sa_nfs_fill_export: "
"invalid value for %s", name);
return (SA_INTERNAL_ERR);
}
switch (type) {
case OPT_ANON:
} else {
"sa_nfs_fill_export");
return (SA_NO_MEMORY);
}
&pwdp);
val = UID_NOBODY;
else
endpwent();
}
break;
case OPT_NOSUID:
else
break;
case OPT_ACLOK:
else
break;
case OPT_NOSUB:
else
break;
case OPT_PUBLIC:
if (nfs_public_active(sh_path)) {
salog_error(0, "NFS:: "
"public share exists");
return (SA_INVALID_NFS_PROP);
}
else
break;
case OPT_INDEX:
/* this is an error */
salog_error(0, "NFS: index=\"%s\" not valid: "
"must be a filename.", value);
return (SA_INVALID_PROP_VAL);
}
/* valid index file string */
/* left over from "default" */
}
/* remember to free */
"NFS: error setting index "
"property");
return (SA_NO_MEMORY);
}
}
break;
case OPT_LOG:
return (rc);
}
}
break;
case OPT_CKSUM:
break;
case OPT_CHARSET_MAP:
/*
* Set EX_CHARMAP when there is at least one
* charmap conversion property. This will get
* checked by the nfs server when it needs to.
*/
break;
case OPT_NOACLFAB:
else
break;
#ifdef VOLATILE_FH_TEST
case OPT_VOLFH:
/* volatile filehandles - expire on share */
"missing volatile fh types\n"));
"invalid volatile fh types\n"));
break;
#endif /* VOLATILE_FH_TEST */
default:
/* have a syntactic error */
"sa_nfs_fill_export: %s=%s",
break;
}
}
/*
* Make sure at least one of the security
* modes defined is valid.
*/
/* error is logged in fill_security */
return (sec_err);
}
return (SA_OK);
}
static int
{
char *buf_endp;
int ptype;
return (SA_INTERNAL_ERR);
/*
* A future RFE would be to replace this with more
* generic code and to possibly handle more types.
*/
if (ptype == -1)
switch (ptype) {
case OPT_TYPE_BOOLEAN:
/*
* For NFS, boolean value of FALSE means it
* doesn't show up in the option list at all.
*/
return (SA_OK);
}
break;
case OPT_TYPE_ACCLIST:
} else {
}
break;
case OPT_TYPE_LOGTAG:
} else {
}
break;
default:
break;
}
} else {
*buf_len = 0;
return (SA_NO_MEMORY);
}
}
propname);
else
*sep = ",";
return (SA_OK);
}
static int
{
char *bufp;
char *propname;
char *propval;
int propid;
return (SA_NO_MEMORY);
/*
* First, add all global NFS properties
*/
/*
* security properties are contained in an embedded
* nvlist, ignore them for now.
*/
continue;
switch (propid) {
case -1:
continue;
default:
return (SA_INVALID_PROP);
}
return (SA_NO_MEMORY);
}
break;
}
}
/*
* now add all security mode properties
*/
/*
* looking for sec property nvlists
*/
continue;
/*
* add sec=mode. If we are working on the sharetab
* entry, then convert sec=default into the correct
* form based on the defined default value.
*/
return (SA_NO_MEMORY);
}
return (SA_INVALID_SECURITY);
}
}
return (SA_NO_MEMORY);
}
/*
* retrieve the security property list
*/
return (SA_INVALID_PROP);
}
/*
* now add all properties for this security mode
*/
continue;
continue;
return (SA_INVALID_PROP);
}
return (SA_NO_MEMORY);
}
prop_found = B_TRUE;
}
if (!prop_found) {
/* add 'rw' to security options */
return (SA_NO_MEMORY);
}
}
}
if (!sec_prop_found) {
/* add defined default security option */
return (SA_NO_MEMORY);
}
}
return (SA_OK);
}
static int
{
}
static void
{
}
}
static int
{
char *sh_name;
int ret;
goto out;
}
ret = SA_NO_MEMORY;
goto out;
}
if (publish) {
goto out;
}
if ((ret = sa_nfs_format_props_impl(
goto out;
NV_ENCODE_XDR, 0) != 0) {
ret = SA_NO_MEMORY;
goto out;
}
}
return (SA_OK);
out:
return (ret);
}
/*
* This is for testing only
* It displays the export structure that
* goes into the kernel.
*/
static void
{
int i, j;
if (debug == 0)
return;
(void) printf("NOSUID ");
(void) printf("ACLOK ");
(void) printf("PUBLIC ");
(void) printf("NOSUB ");
(void) printf("LOG ");
(void) printf("CHARMAP ");
(void) printf("LOG_ALLOPS ");
(void) printf("NOACLFAB ");
(void) printf("(none)");
(void) printf("\n");
(void) printf("\tex_log_buffer = %s\n",
(void) printf("\tex_tag = %s\n",
}
(void) printf("\n");
(void) printf("\n");
(void) printf("\n\n");
}
}
static int
{
char *sh_path;
int ret;
"sa_nfs_share_publish");
return (SA_NO_SHARE_PATH);
}
"sa_nfs_share_publish");
return (SA_NO_SHARE_PROTO);
}
/*
* walk through the options and fill in the structure
* appropriately.
*/
/*
* do non-security options first since there is only one after
* the derived group is constructed.
*/
return (ret);
}
/*
* nfs_sharefs_one
*
* Send a single share to sharefs system call.
*
* Returns 0 on success, libshare_error on failure
*
* sharefs returns -1 on failure with return value set in errno.
* If sharefs returns success (0), then return value will be
* obtained from share_status, which will either be 0 (success) or
* an errno value.
*/
static int
{
int share_status;
&ea, EXPORTFS_DEPENDENT);
if (rc == 0)
err = share_status;
else
switch (err) {
case 0:
return (SA_OK);
case EBUSY:
return (SA_SHARE_OTHERZONE);
case EEXIST:
return (SA_DUPLICATE_NAME);
case ENOENT:
return (SA_PATH_NOT_FOUND);
case EPERM:
return (SA_NO_PERMISSION);
case EINVAL:
return (SA_OK);
case ENOMEM:
return (SA_NO_MEMORY);
default:
return (SA_SYSTEM_ERR);
}
}
/*
* sa_nfs_share_publish(share, wait)
*
* Convert the share nvlist into an exportdata structure then issue
* the exportfs call.
*/
static int
{
struct exportdata export;
char *sh_name;
char *sh_path;
int rc;
return (rc);
}
return (rc);
}
sa_tracef("nfs_share_publish_start: %s:%s:%d",
sa_tracef("nfs_share_publish_stop: %s: %s",
return (rc);
}
/*
* Fill in share structure and send to the kernel.
*/
goto out;
}
sa_emptyshare(&sh);
"error sharing filesystem",
} else {
/*
* check to see if logging and other services need to
* be triggered, but only if there wasn't an
* error.
*/
/* enable logging */
"Could not enable logging for %s\n"),
sh_path);
}
} else {
/*
* don't have logging so remove it from file. It might
* not be there, but that doesn't matter.
*/
(void) nfslogtab_remove(sh_path);
}
}
out:
sa_tracef("nfs_share_publish_stop: %s: %s",
return (rc);
}
static int
{
int rc;
sa_tracef("nfs_share_unpublish_start: %s:%s:%d",
"error unsharing filesystem",
} else {
/* just in case it was logged */
(void) nfslogtab_remove(sh_path);
}
sa_tracef("nfs_share_unpublish_stop: %s: %s",
return (rc);
}
static int
{
char *sh_path;
char *sh_name;
"sa_nfs_share_unpublish");
return (SA_NO_SHARE_NAME);
}
"sa_nfs_share_unpublish: %s", sh_name);
return (SA_NO_SHARE_PATH);
}
}
static int
int wait)
{
}
static void
{
}
}
}
static void
{
}
}
static int
{
char *sh_name;
char *sh_path;
continue;
/*
* retrieve the share nvlist from nvpair
*/
ret = SA_NO_MEMORY;
goto err;
}
ret = SA_NO_MEMORY;
goto err;
}
ret = SA_NO_MEMORY;
goto err;
}
ret = SA_NO_MEMORY;
goto err;
}
if (publish) {
ret = SA_NO_MEMORY;
goto err;
}
if (ret == SA_NO_MEMORY) {
goto err;
} else {
"NFS: share not published: %s:%s",
continue;
}
}
}
if (ret == SA_NO_MEMORY) {
goto err;
} else {
"NFS: share not published: %s:%s",
continue;
}
}
/*
* add to list
*/
}
return (ret);
err:
return (ret);
}
/*
* nfs_check_shares()
*
* if any one share succeeded, return true
*/
static boolean_t
{
if (*list->ea_statusp == 0)
return (B_TRUE);
}
return (B_FALSE);
}
/*
* nfs_check_logging
*
* Looks for EX_LOG being set and enables logging. Since this could be
* called as a result of changing the properties which are then
* inherited, we have to assume that logging was on for any share that
* doesn't have EX_LOG set and disable it.
*/
static void
{
else
}
}
static int
{
return (SA_OK);
}
static int
{
if (ret != 0)
goto err;
if (ret != 0) {
switch (errno) {
case EBUSY:
break;
case EPERM:
break;
default:
break;
}
}
if (nfs_check_shares(list))
err:
return (ret);
}
static int
{
int ret;
if (ret != 0)
goto done;
if (ret != 0) {
switch (errno) {
case EEXIST:
break;
case ENOENT:
break;
case EPERM:
break;
default:
break;
}
} else {
}
done:
return (ret);
}
static int
{
return (SA_OK);
return (SA_INVALID_PROP_VAL);
}
/*
* Protocol management functions
*
* Properties defined in the default files are defined in
* proto_option_defs for parsing and validation. If "other" and
* "compare" are set, then the value for this property should be
* compared against the property specified in "other" using the
* "compare" check (either <= or >=) in order to ensure that the
* values are in the correct range. E.g. setting server_versmin
* higher than server_versmax should not be allowed.
*/
struct proto_option_defs {
char *tag;
int index;
int type;
union {
int intval;
char *string;
} defvalue;
char *other;
int compare;
#define OPT_CMP_GE 0
int (*check)(const char *);
} proto_options[] = {
#define PROTO_OPT_NFSD_SERVERS 0
{"nfsd_servers",
1, INT32_MAX },
{"lockd_listen_backlog",
"lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
{"lockd_servers",
{"lockd_retransmit_timeout",
"lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
{"grace_period",
{"nfs_server_versmin",
{"nfs_server_versmax",
{"nfs_client_versmin",
"client_versmax", OPT_CMP_LE},
{"nfs_client_versmax",
"client_versmin", OPT_CMP_GE},
{"nfs_server_delegation",
"server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
{"nfsmapid_domain",
NULL, SVC_NFSMAPID, 0, 0 },
{"nfsd_max_connections",
"max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
{"nfsd_protocol",
SVC_NFSD, 0, 0 },
{"nfsd_listen_backlog",
"listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
OPT_TYPE_NUMBER, 0,
{"nfsd_device",
"device", PROTO_OPT_NFSD_DEVICE,
{"mountd_showmount_info",
"showmount_info", PROTO_OPT_MOUNTD_SHOWMOUNT_INFO,
{NULL}
};
typedef struct def_file {
char *line;
} def_file_t;
/*
* service_in_state(service, chkstate)
*
* Want to know if the specified service is in the desired state
* (chkstate) or not. Return true (1) if it is and false (0) if it
* isn't.
*/
static boolean_t
{
char *state;
/* got the state so get the equality for the return value */
}
return (ret);
}
/*
* restart_service(svcs)
*
* Walk through the bit mask of services that need to be restarted in
* order to use the new property values. Some properties affect
* multiple daemons. Should only restart a service if it is currently
* enabled (online).
*/
static void
{
int ret;
char *service;
case SVC_LOCKD:
break;
case SVC_STATD:
break;
case SVC_NFS4CBD:
break;
case SVC_NFSMAPID:
break;
case SVC_RQUOTAD:
break;
case SVC_NFSLOGD:
break;
case SVC_REPARSED:
break;
case SVC_CLIENT:
break;
case SVC_MOUNTD:
case SVC_NFSD:
break;
default:
continue;
}
/*
* Only attempt to restart the service if it is
* currently running. In the future, it may be
* desirable to use smf_refresh_instance if the NFS
* services ever implement the refresh method.
*/
/*
* There are only a few SMF errors at this point, but
* it is also possible that a bad value may have put
* the service into maintenance if there wasn't an
* SMF level error.
*/
if (ret != 0) {
"%s failed to restart: %s\n"),
} else {
/*
* Check whether it has gone to "maintenance"
* mode or not. Maintenance implies something
* went wrong.
*/
if (service_in_state(service,
"%s failed to restart\n"),
service);
}
}
}
}
}
/*
* nfs_minmax_check(name, value)
*
* Verify that the value for the property specified by index is valid
* Currently, server_minvers/server_maxvers and
* client_minvers/client_maxvers are the only ones to check.
*/
static boolean_t
{
char *propval;
int val;
/* have a property to compare against */
}
}
}
return (ret);
}
static int
{
int i;
if (whichname == 1) {
return (i);
} else {
return (i);
}
}
return (-1);
}
/*
* fixcaselower(str)
*
* convert a string to lower case (inplace).
*/
static void
{
while (*str) {
str++;
}
}
/*
* skipwhitespace(str)
*
* Skip leading white space. It is assumed that it is called with a
* valid pointer.
*/
static char *
{
str++;
return (str);
}
/*
* nfs_validate_proto_prop(index, name, value)
*
* Verify that the property specified by name can take the new
* value. This is a sanity check to prevent bad values getting into
* the default files. All values need to be checked against what is
* allowed by their defined type. If a type isn't explicitly defined
* here, it is treated as a string. In addition to data type check
* custom check function is invoked if it is defined for the property.
*
* Note that OPT_TYPE_NUMBER will additionally check that the value is
* within the range specified and potentially against another property
* value as well as specified in the proto_options members other and
* compare.
*/
static int
{
char *cp;
case OPT_TYPE_NUMBER:
if (!is_a_number(value)) {
} else {
int val;
/*
* For server_versmin/server_versmax and
* client_versmin/client_versmax, the
* value of the min(max) should be checked
* to be correct relative to the current
* max(min).
*/
}
break;
case OPT_TYPE_DOMAIN:
/*
* needs to be a qualified domain so will have at
* least one period and other characters on either
* side of it. A zero length string is also allowed
* and is the way to turn off the override.
*/
break;
break;
case OPT_TYPE_BOOLEAN:
} else {
}
break;
case OPT_TYPE_ONOFF:
}
break;
case OPT_TYPE_PROTOCOL:
break;
default:
/* treat as a string */
break;
}
/* Invoke custom check function if defined */
return (ret);
}
/*
* extractprop()
*
* Extract the property and value out of the line and add
*/
static int
{
int index;
/*
* Remove any leading white spaces.
*/
if (index >= 0) {
ret = SA_NO_MEMORY;
}
return (ret);
}
static scf_type_t
{
switch (type) {
case OPT_TYPE_NUMBER:
break;
case OPT_TYPE_ONOFF:
case OPT_TYPE_BOOLEAN:
break;
default:
}
return (ret);
}
static char *
{
char *service;
switch (svcs) {
case SVC_LOCKD:
break;
case SVC_STATD:
break;
case SVC_CLIENT:
break;
case SVC_NFS4CBD:
break;
case SVC_NFSMAPID:
break;
case SVC_RQUOTAD:
break;
case SVC_NFSLOGD:
break;
case SVC_REPARSED:
break;
case SVC_MOUNTD:
case SVC_NFSD:
default:
}
return (service);
}
/*
* add_default_proto_prop
*
* Add the default values for any property not defined in the parsing
* of the default files. Values are set according to their defined
* types.
*/
static int
{
int ret;
return (SA_INVALID_PROP);
case OPT_TYPE_NUMBER:
break;
case OPT_TYPE_ONOFF:
case OPT_TYPE_BOOLEAN:
"on" : "off");
break;
default:
/* treat as strings of zero length */
break;
}
}
static void
{
if (nfs_proto_proplist != NULL) {
}
}
/*
* sanfs_init_proto_proplist
*
* Read NFS SMF properties and add the defined values to the
* proplist. Note that default values are known from the built in
* table in case SMF doesn't have a definition. Not having
* SMF properties is OK since we have builtin default
* values.
*/
static int
{
char *svc_name;
int bufsz = 0, i;
return (SA_NO_MEMORY);
/* Replace NULL with the correct instance */
(char *)DEFAULT_INSTANCE, sctype,
if (ret == 0) {
/*
* Special case for server_delegation as the
*/
else
}
/* add property to list */
} else {
/* add default value to list */
ret = add_default_proto_prop(i);
}
break;
}
}
return (ret);
}
/*
* sa_nfs_get_proto_features
*
* Return a mask of the features required.
*/
static int
{
return (SA_OK);
}
static int
{
if (nfs_proto_proplist == NULL) {
if (sanfs_init_proto_proplist() != SA_OK) {
return (SA_NO_MEMORY);
}
}
return (SA_OK);
else
return (SA_NO_MEMORY);
}
/*
* sa_nfs_proto_get_status()
*
* What is the current status of the nfsd? We use the SMF state here.
* Caller must free the returned value.
*/
static int
{
if (*status_str != NULL)
return (SA_OK);
else
return (SA_NO_MEMORY);
}
static int
char **propval)
{
char *val;
if (nfs_proto_proplist == NULL) {
if (sanfs_init_proto_proplist() != SA_OK) {
return (SA_NO_MEMORY);
}
}
return (SA_OK);
else
return (SA_NO_MEMORY);
} else {
return (SA_NO_SUCH_PROP);
}
}
static int
const char *propval)
{
int index;
int ret;
char *svc_name;
return (SA_INVALID_PROP);
return (SA_INVALID_PROP_VAL);
return (SA_INVALID_PROP);
if (sctype == SCF_TYPE_BOOLEAN)
if (ret == 0) {
(void) sanfs_init_proto_proplist();
} else {
ret = SA_SCF_ERROR;
}
}
return (ret);
}
/*
* nfs_check_services
*
* check to see if the NFS services need to be started. If we've
* already done this in nfs_svc_delta seconds, don't do it again until
* either the service list changes or the delta has expired.
*/
static void
{
service_list = logging ?
if ((char **)service_list != last_list ||
}
}
/*
* Append an entry to the nfslogtab file
*/
static int
{
FILE *f;
/*
* Open the file for update and create it if necessary.
* This may leave the I/O offset at the end of the file,
* so rewind back to the beginning of the file.
*/
if (f == NULL) {
else
goto out;
}
rewind(f);
"nfslog: failed to lock %s "
goto out;
}
if (logtab_deactivate_after_boot(f) == -1) {
"nfslog: could not deactivate "
"entries in %s\n", NFSLOGTAB);
error = -1;
goto out;
}
/*
* Remove entries matching buffer and sharepoint since we're
* going to replace it with perhaps an entry with a new tag.
*/
"nfslog: could not remove matching "
"entries in %s\n", NFSLOGTAB);
goto out;
}
/*
* Deactivate all active entries matching this sharepoint
*/
"nfslog: could not deactivate matching "
"entries in %s\n", NFSLOGTAB);
goto out;
}
/*
* Add new sharepoint / buffer location to nfslogtab
*/
if (logtab_putent(f, &lep) < 0) {
"nfslog: could not add %s to %s\n",
}
out:
if (f != NULL)
(void) fclose(f);
return (error);
}
/*
* Deactivate an entry from the nfslogtab file
*/
static int
{
FILE *f;
if (f == NULL) {
goto out;
}
"nfslog: could not lock %s for "
goto out;
}
}
out:
if (f != NULL)
(void) fclose(f);
return (error);
}