1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <sys/types.h>
31 #include <string.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/file.h>
35 #include <sys/time.h>
36 #include <errno.h>
37 #include <rpcsvc/mount.h>
38 #include <sys/pathconf.h>
39 #include <sys/systeminfo.h>
40 #include <sys/utsname.h>
41 #include <signal.h>
42 #include <locale.h>
43 #include <unistd.h>
44 #include <thread.h>
45 #include <syslog.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <sharefs/share.h>
50 #include "../lib/sharetab.h"
51 #include "hashset.h"
52 #include "mountd.h"
53
54 static char RMTAB[] = "/etc/rmtab";
55 static FILE *rmtabf = NULL;
56
57 /*
58 * There is nothing magic about the value selected here. Too low,
59 * and mountd might spend too much time rewriting the rmtab file.
60 * Too high, it won't do it frequently enough.
61 */
62 static int rmtab_del_thresh = 250;
63
64 #define RMTAB_TOOMANY_DELETED() \
65 ((rmtab_deleted > rmtab_del_thresh) && (rmtab_deleted > rmtab_inuse))
66
67 /*
68 * mountd's version of a "struct mountlist". It is the same except
69 * for the added ml_pos field.
70 */
71 struct mntentry {
72 char *m_host;
73 char *m_path;
74 long m_pos;
75 };
76
77 static HASHSET mntlist;
78
79 static int mntentry_equal(const void *, const void *);
80 static uint32_t mntentry_hash(const void *);
81 static int mntlist_contains(char *, char *);
82 static void rmtab_delete(long);
83 static long rmtab_insert(char *, char *);
84 static void rmtab_rewrite(void);
85 static void rmtab_parse(char *buf);
86 static bool_t xdr_mntlistencode(XDR * xdrs, HASHSET * mntlist);
87
88 #define exstrdup(s) \
89 strcpy(exmalloc(strlen(s)+1), s)
90
91
92 static int rmtab_inuse;
93 static int rmtab_deleted;
94
95 static rwlock_t rmtab_lock; /* lock to protect rmtab list */
96
97
98 /*
99 * Check whether the given client/path combination
100 * already appears in the mount list.
101 */
102
103 static int
mntlist_contains(char * host,char * path)104 mntlist_contains(char *host, char *path)
105 {
106 struct mntentry m;
107
108 m.m_host = host;
109 m.m_path = path;
110
111 return (h_get(mntlist, &m) != NULL);
112 }
113
114
115 /*
116 * Add an entry to the mount list.
117 * First check whether it's there already - the client
118 * may have crashed and be rebooting.
119 */
120
121 static void
mntlist_insert(char * host,char * path)122 mntlist_insert(char *host, char *path)
123 {
124 if (!mntlist_contains(host, path)) {
125 struct mntentry *m;
126
127 m = exmalloc(sizeof (struct mntentry));
128
129 m->m_host = exstrdup(host);
130 m->m_path = exstrdup(path);
131 m->m_pos = rmtab_insert(host, path);
132 (void) h_put(mntlist, m);
133 }
134 }
135
136 void
mntlist_new(char * host,char * path)137 mntlist_new(char *host, char *path)
138 {
139 (void) rw_wrlock(&rmtab_lock);
140 mntlist_insert(host, path);
141 (void) rw_unlock(&rmtab_lock);
142 }
143
144 /*
145 * Delete an entry from the mount list.
146 */
147
148 void
mntlist_delete(char * host,char * path)149 mntlist_delete(char *host, char *path)
150 {
151 struct mntentry *m, mm;
152
153 mm.m_host = host;
154 mm.m_path = path;
155
156 (void) rw_wrlock(&rmtab_lock);
157
158 if (m = (struct mntentry *)h_get(mntlist, &mm)) {
159 rmtab_delete(m->m_pos);
160
161 (void) h_delete(mntlist, m);
162
163 free(m->m_path);
164 free(m->m_host);
165 free(m);
166
167 if (RMTAB_TOOMANY_DELETED())
168 rmtab_rewrite();
169 }
170 (void) rw_unlock(&rmtab_lock);
171 }
172
173 /*
174 * Delete all entries for a host from the mount list
175 */
176
177 void
mntlist_delete_all(char * host)178 mntlist_delete_all(char *host)
179 {
180 HASHSET_ITERATOR iterator;
181 struct mntentry *m;
182
183 (void) rw_wrlock(&rmtab_lock);
184
185 iterator = h_iterator(mntlist);
186
187 while (m = (struct mntentry *)h_next(iterator)) {
188 if (strcasecmp(m->m_host, host))
189 continue;
190
191 rmtab_delete(m->m_pos);
192
193 (void) h_delete(mntlist, m);
194
195 free(m->m_path);
196 free(m->m_host);
197 free(m);
198 }
199
200 if (RMTAB_TOOMANY_DELETED())
201 rmtab_rewrite();
202
203 (void) rw_unlock(&rmtab_lock);
204
205 if (iterator != NULL)
206 free(iterator);
207 }
208
209 /*
210 * Equivalent to xdr_mountlist from librpcsvc but for HASHSET
211 * rather that for a linked list. It is used only to encode data
212 * from HASHSET before sending it over the wire.
213 */
214
215 static bool_t
xdr_mntlistencode(XDR * xdrs,HASHSET * mntlist)216 xdr_mntlistencode(XDR *xdrs, HASHSET *mntlist)
217 {
218 HASHSET_ITERATOR iterator = h_iterator(*mntlist);
219
220 for (;;) {
221 struct mntentry *m = (struct mntentry *)h_next(iterator);
222 bool_t more_data = (m != NULL);
223
224 if (!xdr_bool(xdrs, &more_data)) {
225 if (iterator != NULL)
226 free(iterator);
227 return (FALSE);
228 }
229
230 if (!more_data)
231 break;
232
233 if ((!xdr_name(xdrs, &m->m_host)) ||
234 (!xdr_dirpath(xdrs, &m->m_path))) {
235 if (iterator != NULL)
236 free(iterator);
237 return (FALSE);
238 }
239
240 }
241
242 if (iterator != NULL)
243 free(iterator);
244
245 return (TRUE);
246 }
247
248 void
mntlist_send(SVCXPRT * transp)249 mntlist_send(SVCXPRT *transp)
250 {
251 (void) rw_rdlock(&rmtab_lock);
252
253 errno = 0;
254 if (!svc_sendreply(transp, xdr_mntlistencode, (char *)&mntlist))
255 log_cant_reply(transp);
256
257 (void) rw_unlock(&rmtab_lock);
258 }
259
260 /*
261 * Compute a 32 bit hash value for an mntlist entry.
262 */
263
264 /*
265 * The string hashing algorithm is from the "Dragon Book" --
266 * "Compilers: Principles, Tools & Techniques", by Aho, Sethi, Ullman
267 *
268 * And is modified for this application from usr/src/uts/common/os/modhash.c
269 */
270
271 static uint_t
mntentry_str_hash(char * s,uint_t hash)272 mntentry_str_hash(char *s, uint_t hash)
273 {
274 uint_t g;
275
276 for (; *s != '\0'; s++) {
277 hash = (hash << 4) + *s;
278 if ((g = (hash & 0xf0000000)) != 0) {
279 hash ^= (g >> 24);
280 hash ^= g;
281 }
282 }
283
284 return (hash);
285 }
286
287 static uint32_t
mntentry_hash(const void * p)288 mntentry_hash(const void *p)
289 {
290 struct mntentry *m = (struct mntentry *)p;
291 uint_t hash;
292
293 hash = mntentry_str_hash(m->m_host, 0);
294 hash = mntentry_str_hash(m->m_path, hash);
295
296 return (hash);
297 }
298
299 /*
300 * Compare mntlist entries.
301 * The comparison ignores a value of m_pos.
302 */
303
304 static int
mntentry_equal(const void * p1,const void * p2)305 mntentry_equal(const void *p1, const void *p2)
306 {
307 struct mntentry *m1 = (struct mntentry *)p1;
308 struct mntentry *m2 = (struct mntentry *)p2;
309
310 return ((strcasecmp(m1->m_host, m2->m_host) ||
311 strcmp(m1->m_path, m2->m_path)) ? 0 : 1);
312 }
313
314 /*
315 * Rewrite /etc/rmtab with a current content of mntlist.
316 */
317 static void
rmtab_rewrite()318 rmtab_rewrite()
319 {
320 if (rmtabf)
321 (void) fclose(rmtabf);
322
323 /* Rewrite the file. */
324 if (rmtabf = fopen(RMTAB, "w+")) {
325 HASHSET_ITERATOR iterator;
326 struct mntentry *m;
327
328 (void) fchmod(fileno(rmtabf),
329 (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH));
330 rmtab_inuse = rmtab_deleted = 0;
331
332 iterator = h_iterator(mntlist);
333
334 while (m = (struct mntentry *)h_next(iterator))
335 m->m_pos = rmtab_insert(m->m_host, m->m_path);
336 if (iterator != NULL)
337 free(iterator);
338 }
339 }
340
341 /*
342 * Parse the content of /etc/rmtab and insert the entries into mntlist.
343 * The buffer s should be ended with a NUL char.
344 */
345
346 static void
rmtab_parse(char * s)347 rmtab_parse(char *s)
348 {
349 char *host;
350 char *path;
351 char *tmp;
352 struct in6_addr ipv6addr;
353
354 host_part:
355 if (*s == '#')
356 goto skip_rest;
357
358 host = s;
359 for (;;) {
360 switch (*s++) {
361 case '\0':
362 return;
363 case '\n':
364 goto host_part;
365 case ':':
366 s[-1] = '\0';
367 goto path_part;
368 case '[':
369 tmp = strchr(s, ']');
370 if (tmp) {
371 *tmp = '\0';
372 if (inet_pton(AF_INET6, s, &ipv6addr) > 0) {
373 host = s;
374 s = ++tmp;
375 } else
376 *tmp = ']';
377 }
378 default:
379 continue;
380 }
381 }
382
383 path_part:
384 path = s;
385 for (;;) {
386 switch (*s++) {
387 case '\n':
388 s[-1] = '\0';
389 if (*host && *path)
390 mntlist_insert(host, path);
391 goto host_part;
392 case '\0':
393 if (*host && *path)
394 mntlist_insert(host, path);
395 return;
396 default:
397 continue;
398 }
399 }
400
401 skip_rest:
402 for (;;) {
403 switch (*++s) {
404 case '\n':
405 goto host_part;
406 case '\0':
407 return;
408 default:
409 continue;
410 }
411 }
412 }
413
414 /*
415 * Read in contents of rmtab.
416 * Call rmtab_parse to parse the file and store entries in mntlist.
417 * Rewrites the file to get rid of unused entries.
418 */
419
420 #define RMTAB_LOADLEN (16*2024) /* Max bytes to read at a time */
421
422 void
rmtab_load()423 rmtab_load()
424 {
425 FILE *fp;
426
427 (void) rwlock_init(&rmtab_lock, USYNC_THREAD, NULL);
428
429 /*
430 * Don't need to lock the list at this point
431 * because there's only a single thread running.
432 */
433 mntlist = h_create(mntentry_hash, mntentry_equal, 101, 0.75);
434
435 if (fp = fopen(RMTAB, "r")) {
436 char buf[RMTAB_LOADLEN+1];
437 size_t len;
438
439 /*
440 * Read at most RMTAB_LOADLEN bytes from /etc/rmtab.
441 * - if fread returns RMTAB_LOADLEN we can be in the middle
442 * of a line so change the last newline character into NUL
443 * and seek back to the next character after newline.
444 * - otherwise set NUL behind the last character read.
445 */
446 while ((len = fread(buf, 1, RMTAB_LOADLEN, fp)) > 0) {
447 if (len == RMTAB_LOADLEN) {
448 int i;
449
450 for (i = 1; i < len; i++) {
451 if (buf[len-i] == '\n') {
452 buf[len-i] = '\0';
453 (void) fseek(fp, -i + 1,
454 SEEK_CUR);
455 goto parse;
456 }
457 }
458 }
459
460 /* Put a NUL character at the end of buffer */
461 buf[len] = '\0';
462 parse:
463 rmtab_parse(buf);
464 }
465 (void) fclose(fp);
466 }
467 rmtab_rewrite();
468 }
469
470 /*
471 * Write an entry at the current location in rmtab
472 * for the given client and path.
473 *
474 * Returns the starting position of the entry
475 * or -1 if there was an error.
476 */
477
478 long
rmtab_insert(char * host,char * path)479 rmtab_insert(char *host, char *path)
480 {
481 long pos;
482 struct in6_addr ipv6addr;
483
484 if (rmtabf == NULL || fseek(rmtabf, 0L, 2) == -1) {
485 return (-1);
486 }
487 pos = ftell(rmtabf);
488
489 /*
490 * Check if host is an IPv6 literal
491 */
492
493 if (inet_pton(AF_INET6, host, &ipv6addr) > 0) {
494 if (fprintf(rmtabf, "[%s]:%s\n", host, path) == EOF) {
495 return (-1);
496 }
497 } else {
498 if (fprintf(rmtabf, "%s:%s\n", host, path) == EOF) {
499 return (-1);
500 }
501 }
502 if (fflush(rmtabf) == EOF) {
503 return (-1);
504 }
505 rmtab_inuse++;
506 return (pos);
507 }
508
509 /*
510 * Mark as unused the rmtab entry at the given offset in the file.
511 */
512
513 void
rmtab_delete(long pos)514 rmtab_delete(long pos)
515 {
516 if (rmtabf != NULL && pos != -1 && fseek(rmtabf, pos, 0) == 0) {
517 (void) fprintf(rmtabf, "#");
518 (void) fflush(rmtabf);
519
520 rmtab_inuse--;
521 rmtab_deleted++;
522 }
523 }
524