Print this page
NEX-9755 Race in libshare can cause zfs set sharenfs to fail
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
@@ -51,12 +51,16 @@
struct sa_proto_plugin *sap_proto_list;
static struct sa_proto_handle sa_proto_handle;
+extern mutex_t sa_global_lock;
+
void proto_plugin_fini();
+static void proto_plugin_fini_impl(boolean_t);
+
/*
* Returns true if name is "." or "..", otherwise returns false.
*/
static boolean_t
proto_is_dot_or_dotdot(const char *name)
@@ -93,16 +97,22 @@
struct dirent *dent;
int ret = SA_OK;
struct stat st;
char isa[MAXISALEN];
+ assert(MUTEX_HELD(&sa_global_lock));
+
#if defined(_LP64)
if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
isa[0] = '\0';
#else
isa[0] = '\0';
#endif
+ if (sap_proto_list != NULL && sa_proto_handle.sa_proto != NULL) {
+ sa_proto_handle.sa_ref_count++;
+ return (SA_OK);
+ }
if ((dir = opendir(SA_LIB_DIR)) == NULL)
return (SA_OK);
while ((dent = readdir(dir)) != NULL) {
@@ -153,11 +163,11 @@
sap_proto_list = proto;
}
(void) closedir(dir);
- if (num_protos != 0) {
+ if (num_protos != 0 && sa_proto_handle.sa_proto == NULL) {
sa_proto_handle.sa_proto =
(char **)calloc(num_protos, sizeof (char *));
sa_proto_handle.sa_ops =
(struct sa_plugin_ops **)calloc(num_protos,
sizeof (struct sa_plugin_ops *));
@@ -192,12 +202,16 @@
}
/*
* There was an error, so cleanup prior to return of failure.
*/
- if (ret != SA_OK)
- proto_plugin_fini();
+ if (ret != SA_OK) {
+ proto_plugin_fini_impl(B_TRUE);
+ } else {
+ assert(sa_proto_handle.sa_ref_count >= 0);
+ sa_proto_handle.sa_ref_count++;
+ }
return (ret);
}
/*
@@ -207,13 +221,35 @@
*/
void
proto_plugin_fini()
{
+ assert(MUTEX_HELD(&sa_global_lock));
+
+ proto_plugin_fini_impl(B_FALSE);
+}
+
+static void
+proto_plugin_fini_impl(boolean_t forcefini)
+{
struct sa_proto_plugin *p;
+ assert(MUTEX_HELD(&sa_global_lock));
+
+ sa_proto_handle.sa_ref_count--;
/*
+ * If another thread has a reference to the proto list,
+ * we don't want to clear it out while they're using it
+ * or find_protocol() could fail.
+ */
+ if (!forcefini && sa_proto_handle.sa_ref_count > 0) {
+ return;
+ } else {
+ sa_proto_handle.sa_ref_count = 0;
+ }
+
+ /*
* Protocols may call this framework during _fini
* (the smbfs plugin is known to do this) so do
* two passes: 1st call _fini; 2nd free, dlclose.
*/
for (p = sap_proto_list; p != NULL; p = p->plugin_next)
@@ -224,10 +260,12 @@
if (p->plugin_handle != NULL)
(void) dlclose(p->plugin_handle);
free(p);
}
+ if (sap_proto_list != NULL)
+ sap_proto_list = NULL;
if (sa_proto_handle.sa_ops != NULL) {
free(sa_proto_handle.sa_ops);
sa_proto_handle.sa_ops = NULL;
}
if (sa_proto_handle.sa_proto != NULL) {
@@ -247,11 +285,10 @@
static struct sa_plugin_ops *
find_protocol(char *proto)
{
int i;
struct sa_plugin_ops *ops = NULL;
- extern mutex_t sa_global_lock;
(void) mutex_lock(&sa_global_lock);
if (proto != NULL) {
for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0) {