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