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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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