1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
|
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <syslog.h>
#include <sys/param.h>
#include <rpc/rpc.h>
#include <sys/stat.h>
#include <netconfig.h>
#include <netdir.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/resource.h>
#include <rpcsvc/mount.h>
#include <sys/pathconf.h>
#include <sys/systeminfo.h>
#include <sys/utsname.h>
#include <signal.h>
#include <locale.h>
#include <unistd.h>
#include <thread.h>
#include <sharefs/share.h>
#include "../lib/sharetab.h"
#include "mountd.h"
struct cache_entry {
char *cache_host;
time_t cache_time;
int cache_belong;
char **cache_grl;
int cache_grc;
struct cache_entry *cache_next;
};
static struct cache_entry *cache_head;
#define VALID_TIME 60 /* seconds */
static rwlock_t cache_lock; /* protect the cache chain */
static void cache_free(struct cache_entry *entry);
static int cache_check(char *host, char **grl, int grc, int *belong);
static void cache_enter(char *host, char **grl, int grc, int belong);
void
netgroup_init()
{
(void) rwlock_init(&cache_lock, USYNC_THREAD, NULL);
}
/*
* Check whether any of the hostnames in clnames are
* members (or non-members) of the netgroups in glist.
* Since the innetgr lookup is rather expensive, the
* result is cached. The cached entry is valid only
* for VALID_TIME seconds. This works well because
* typically these lookups occur in clusters when
* a client is mounting.
*
* Note that this routine establishes a host membership
* in a list of netgroups - we've no idea just which
* netgroup in the list it is a member of.
*
* glist is a character array containing grc strings
* representing netgroup names (optionally prefixed
* with '-'). Each string is ended with '\0' and
* followed immediately by the next string.
*/
int
netgroup_check(struct nd_hostservlist *clnames, char *glist, int grc)
{
char **grl;
char *gr;
int nhosts = clnames->h_cnt;
char *host0, *host;
int i, j, n;
int response;
int belong = 0;
static char *domain;
if (domain == NULL) {
int ssize;
domain = exmalloc(SYS_NMLN);
ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN);
if (ssize > SYS_NMLN) {
free(domain);
domain = exmalloc(ssize);
ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize);
}
/* Check for error in syscall or NULL domain name */
if (ssize <= 1) {
syslog(LOG_ERR, "No default domain set");
return (0);
}
}
grl = calloc(grc, sizeof (char *));
if (grl == NULL)
return (0);
for (i = 0, gr = glist; i < grc && !belong; ) {
/*
* If the netgroup name has a '-' prepended
* then a match of this name implies a failure
* instead of success.
*/
response = (*gr != '-') ? 1 : 0;
/*
* Subsequent names with or without a '-' (but no mix)
* can be grouped together for a single check.
*/
for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) {
if ((response && *gr == '-') ||
(!response && *gr != '-'))
break;
grl[n] = response ? gr : gr + 1;
}
host0 = clnames->h_hostservs[0].h_host;
/*
* If not in cache check the netgroup for each
* of the hosts names (usually just one).
* Enter the result into the cache.
*/
if (!cache_check(host0, grl, n, &belong)) {
for (j = 0; j < nhosts && !belong; j++) {
host = clnames->h_hostservs[j].h_host;
if (__multi_innetgr(n, grl,
1, &host,
0, NULL,
1, &domain))
belong = 1;
}
cache_enter(host0, grl, n, belong);
}
}
free(grl);
return (belong ? response : 0);
}
/*
* Free a cache entry and all entries
* further down the chain since they
* will also be expired.
*/
static void
cache_free(struct cache_entry *entry)
{
struct cache_entry *ce, *next;
int i;
for (ce = entry; ce; ce = next) {
if (ce->cache_host)
free(ce->cache_host);
for (i = 0; i < ce->cache_grc; i++)
if (ce->cache_grl[i])
free(ce->cache_grl[i]);
if (ce->cache_grl)
free(ce->cache_grl);
next = ce->cache_next;
free(ce);
}
}
/*
* Search the entries in the cache chain looking
* for an entry with a matching hostname and group
* list. If a match is found then return the "belong"
* value which may be 1 or 0 depending on whether the
* client is a member of the list or not. This is
* both a positive and negative cache.
*
* Cache entries have a validity of VALID_TIME seconds.
* If we find an expired entry then blow away the entry
* and the rest of the chain since entries further down
* the chain will be expired too because we always add
* new entries to the head of the chain.
*/
static int
cache_check(char *host, char **grl, int grc, int *belong)
{
struct cache_entry *ce, *prev;
time_t timenow = time(NULL);
int i;
(void) rw_rdlock(&cache_lock);
for (ce = cache_head; ce; ce = ce->cache_next) {
/*
* If we find a stale entry, there can't
* be any valid entries from here on.
* Acquire a write lock, search the chain again
* and delete the stale entry and all following
* entries.
*/
if (timenow > ce->cache_time) {
(void) rw_unlock(&cache_lock);
(void) rw_wrlock(&cache_lock);
for (prev = NULL, ce = cache_head; ce;
prev = ce, ce = ce->cache_next)
if (timenow > ce->cache_time)
break;
if (ce != NULL) {
if (prev)
prev->cache_next = NULL;
else
cache_head = NULL;
cache_free(ce);
}
(void) rw_unlock(&cache_lock);
return (0);
}
if (ce->cache_grc != grc)
continue; /* no match */
if (strcasecmp(host, ce->cache_host) != 0)
continue; /* no match */
for (i = 0; i < grc; i++)
if (strcasecmp(ce->cache_grl[i], grl[i]) != 0)
break; /* no match */
if (i < grc)
continue;
*belong = ce->cache_belong;
(void) rw_unlock(&cache_lock);
return (1);
}
(void) rw_unlock(&cache_lock);
return (0);
}
/*
* Put a new entry in the cache chain by
* prepending it to the front.
* If there isn't enough memory then just give up.
*/
static void
cache_enter(char *host, char **grl, int grc, int belong)
{
struct cache_entry *entry;
int i;
entry = malloc(sizeof (*entry));
if (entry == NULL)
return;
(void) memset((caddr_t)entry, 0, sizeof (*entry));
entry->cache_host = strdup(host);
if (entry->cache_host == NULL) {
cache_free(entry);
return;
}
entry->cache_time = time(NULL) + VALID_TIME;
entry->cache_belong = belong;
entry->cache_grl = malloc(grc * sizeof (char *));
if (entry->cache_grl == NULL) {
cache_free(entry);
return;
}
for (i = 0; i < grc; i++) {
entry->cache_grl[i] = strdup(grl[i]);
if (entry->cache_grl[i] == NULL) {
entry->cache_grc = i;
cache_free(entry);
return;
}
}
entry->cache_grc = grc;
(void) rw_wrlock(&cache_lock);
entry->cache_next = cache_head;
cache_head = entry;
(void) rw_unlock(&cache_lock);
}
/*
* Full cache flush
*/
void
netgrp_cache_flush(void)
{
(void) rw_wrlock(&cache_lock);
cache_free(cache_head);
cache_head = NULL;
(void) rw_unlock(&cache_lock);
}
|