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