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.
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.
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]
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
34 #include <sys/types.h>
36 #include <sys/param.h>
41 #include <rpcsvc/mount.h>
42 #include <sys/pathconf.h>
43 #include <sys/systeminfo.h>
44 #include <sys/utsname.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>
53 #include <sharefs/share.h>
54 #include "../lib/sharetab.h"
58 static char RMTAB
[] = "/etc/rmtab";
59 static FILE *rmtabf
= NULL
;
62 * There is nothing magic about the value selected here. Too low,
63 * and mountd might spend too much time rewriting the rmtab file.
64 * Too high, it won't do it frequently enough.
66 static int rmtab_del_thresh
= 250;
68 #define RMTAB_TOOMANY_DELETED() \
69 ((rmtab_deleted > rmtab_del_thresh) && (rmtab_deleted > rmtab_inuse))
72 * mountd's version of a "struct mountlist". It is the same except
73 * for the added ml_pos field.
81 static HASHSET mntlist
;
83 static int mntentry_equal(const void *, const void *);
84 static uint32_t mntentry_hash(const void *);
85 static int mntlist_contains(char *, char *);
86 static void rmtab_delete(long);
87 static long rmtab_insert(char *, char *);
88 static void rmtab_rewrite(void);
89 static void rmtab_parse(char *buf
);
90 static bool_t
xdr_mntlistencode(XDR
* xdrs
, HASHSET
* mntlist
);
93 strcpy(exmalloc(strlen(s)+1), s)
96 static int rmtab_inuse
;
97 static int rmtab_deleted
;
99 static rwlock_t rmtab_lock
; /* lock to protect rmtab list */
103 * Check whether the given client/path combination
104 * already appears in the mount list.
108 mntlist_contains(char *host
, char *path
)
115 return (h_get(mntlist
, &m
) != NULL
);
120 * Add an entry to the mount list.
121 * First check whether it's there already - the client
122 * may have crashed and be rebooting.
126 mntlist_insert(char *host
, char *path
)
128 if (!mntlist_contains(host
, path
)) {
131 m
= exmalloc(sizeof (struct mntentry
));
133 m
->m_host
= exstrdup(host
);
134 m
->m_path
= exstrdup(path
);
135 m
->m_pos
= rmtab_insert(host
, path
);
136 (void) h_put(mntlist
, m
);
141 mntlist_new(char *host
, char *path
)
143 (void) rw_wrlock(&rmtab_lock
);
144 mntlist_insert(host
, path
);
145 (void) rw_unlock(&rmtab_lock
);
149 * Delete an entry from the mount list.
153 mntlist_delete(char *host
, char *path
)
155 struct mntentry
*m
, mm
;
160 (void) rw_wrlock(&rmtab_lock
);
162 if ((m
= (struct mntentry
*)h_get(mntlist
, &mm
)) != NULL
) {
163 rmtab_delete(m
->m_pos
);
165 (void) h_delete(mntlist
, m
);
171 if (RMTAB_TOOMANY_DELETED())
174 (void) rw_unlock(&rmtab_lock
);
178 * Delete all entries for a host from the mount list
182 mntlist_delete_all(char *host
)
184 HASHSET_ITERATOR iterator
;
187 (void) rw_wrlock(&rmtab_lock
);
189 iterator
= h_iterator(mntlist
);
191 while ((m
= (struct mntentry
*)h_next(iterator
)) != NULL
) {
192 if (strcasecmp(m
->m_host
, host
))
195 rmtab_delete(m
->m_pos
);
197 (void) h_delete(mntlist
, m
);
204 if (RMTAB_TOOMANY_DELETED())
207 (void) rw_unlock(&rmtab_lock
);
213 * Equivalent to xdr_mountlist from librpcsvc but for HASHSET
214 * rather that for a linked list. It is used only to encode data
215 * from HASHSET before sending it over the wire.
219 xdr_mntlistencode(XDR
*xdrs
, HASHSET
*mntlist
)
221 HASHSET_ITERATOR iterator
= h_iterator(*mntlist
);
224 struct mntentry
*m
= (struct mntentry
*)h_next(iterator
);
225 bool_t more_data
= (m
!= NULL
);
227 if (!xdr_bool(xdrs
, &more_data
)) {
235 if ((!xdr_name(xdrs
, &m
->m_host
)) ||
236 (!xdr_dirpath(xdrs
, &m
->m_path
))) {
249 mntlist_send(SVCXPRT
*transp
)
251 (void) rw_rdlock(&rmtab_lock
);
254 if (!svc_sendreply(transp
, xdr_mntlistencode
, (char *)&mntlist
))
255 log_cant_reply(transp
);
257 (void) rw_unlock(&rmtab_lock
);
261 * Compute a 32 bit hash value for an mntlist entry.
265 * The string hashing algorithm is from the "Dragon Book" --
266 * "Compilers: Principles, Tools & Techniques", by Aho, Sethi, Ullman
268 * And is modified for this application from kernel/os/modhash.c
272 mntentry_str_hash(char *s
, uint_t hash
)
276 for (; *s
!= '\0'; s
++) {
277 hash
= (hash
<< 4) + *s
;
278 if ((g
= (hash
& 0xf0000000)) != 0) {
288 mntentry_hash(const void *p
)
290 struct mntentry
*m
= (struct mntentry
*)p
;
293 hash
= mntentry_str_hash(m
->m_host
, 0);
294 hash
= mntentry_str_hash(m
->m_path
, hash
);
300 * Compare mntlist entries.
301 * The comparison ignores a value of m_pos.
305 mntentry_equal(const void *p1
, const void *p2
)
307 struct mntentry
*m1
= (struct mntentry
*)p1
;
308 struct mntentry
*m2
= (struct mntentry
*)p2
;
310 return ((strcasecmp(m1
->m_host
, m2
->m_host
) ||
311 strcmp(m1
->m_path
, m2
->m_path
)) ? 0 : 1);
315 * Rewrite /etc/rmtab with a current content of mntlist.
321 (void) fclose(rmtabf
);
323 /* Rewrite the file. */
324 if ((rmtabf
= fopen(RMTAB
, "w+")) != NULL
) {
325 HASHSET_ITERATOR iterator
;
328 (void) fchmod(fileno(rmtabf
),
329 (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
));
330 rmtab_inuse
= rmtab_deleted
= 0;
332 iterator
= h_iterator(mntlist
);
334 while ((m
= (struct mntentry
*)h_next(iterator
)) != NULL
)
335 m
->m_pos
= rmtab_insert(m
->m_host
, m
->m_path
);
341 * Parse the content of /etc/rmtab and insert the entries into mntlist.
342 * The buffer s should be ended with a NUL char.
351 struct in6_addr ipv6addr
;
368 tmp
= strchr(s
, ']');
371 if (inet_pton(AF_INET6
, s
, &ipv6addr
) > 0) {
390 mntlist_insert(host
, path
);
394 mntlist_insert(host
, path
);
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.
420 #define RMTAB_LOADLEN (16*2024) /* Max bytes to read at a time */
427 (void) rwlock_init(&rmtab_lock
, USYNC_THREAD
, NULL
);
430 * Don't need to lock the list at this point
431 * because there's only a single thread running.
433 mntlist
= h_create(mntentry_hash
, mntentry_equal
, 101, 0.75);
435 if ((fp
= fopen(RMTAB
, "r")) != NULL
) {
436 char buf
[RMTAB_LOADLEN
+1];
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.
446 while ((len
= fread(buf
, 1, RMTAB_LOADLEN
, fp
)) > 0) {
447 if (len
== RMTAB_LOADLEN
) {
450 for (i
= 1; i
< len
; i
++) {
451 if (buf
[len
-i
] == '\n') {
453 (void) fseek(fp
, -i
+ 1,
460 /* Put a NUL character at the end of buffer */
471 * Write an entry at the current location in rmtab
472 * for the given client and path.
474 * Returns the starting position of the entry
475 * or -1 if there was an error.
479 rmtab_insert(char *host
, char *path
)
482 struct in6_addr ipv6addr
;
484 if (rmtabf
== NULL
|| fseek(rmtabf
, 0L, 2) == -1) {
490 * Check if host is an IPv6 literal
493 if (inet_pton(AF_INET6
, host
, &ipv6addr
) > 0) {
494 if (fprintf(rmtabf
, "[%s]:%s\n", host
, path
) == EOF
) {
498 if (fprintf(rmtabf
, "%s:%s\n", host
, path
) == EOF
) {
502 if (fflush(rmtabf
) == EOF
) {
510 * Mark as unused the rmtab entry at the given offset in the file.
514 rmtab_delete(long pos
)
516 if (rmtabf
!= NULL
&& pos
!= -1 && fseek(rmtabf
, pos
, 0) == 0) {
517 (void) fprintf(rmtabf
, "#");
518 (void) fflush(rmtabf
);