1 /* $NetBSD: fixmount.c,v 1.1.1.2 2009/03/20 20:26:55 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2009 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/fixmount/fixmount.c 43 * 44 */ 45 46 #ifdef HAVE_CONFIG_H 47 # include <config.h> 48 #endif /* HAVE_CONFIG_H */ 49 #include <am_defs.h> 50 51 #define CREATE_TIMEOUT 2 /* seconds */ 52 #define CALL_TIMEOUT 5 /* seconds */ 53 54 /* Constant defs */ 55 #define ALL 1 56 #define DIRS 2 57 58 #define DODUMP 0x1 59 #define DOEXPORTS 0x2 60 #define DOREMOVE 0x4 61 #define DOVERIFY 0x8 62 #define DOREMALL 0x10 63 64 extern int fixmount_check_mount(char *host, struct in_addr hostaddr, char *path); 65 66 static char dir_path[NFS_MAXPATHLEN]; 67 static char localhost[] = "localhost"; 68 static char thishost[MAXHOSTNAMELEN + 1] = ""; 69 static exports mntexports; 70 static int quiet = 0; 71 static int type = 0; 72 static jmp_buf before_rpc; 73 static mountlist mntdump; 74 static struct in_addr thisaddr; 75 static CLIENT *clnt_create_timeout(char *, struct timeval *); 76 77 RETSIGTYPE create_timeout(int); 78 int is_same_host(char *, char *, struct in_addr); 79 int main(int, char *[]); 80 int remove_all(CLIENT *, char *); 81 int remove_mount(CLIENT *, char *, mountlist, int); 82 void fix_rmtab(CLIENT *, char *, mountlist, int, int); 83 void print_dump(mountlist); 84 void usage(void); 85 86 87 void 88 usage(void) 89 { 90 fprintf(stderr, "usage: fixmount [-adervAqf] [-h hostname] host ...\n"); 91 exit(1); 92 } 93 94 95 /* 96 * Check hostname against other name and its IP address 97 */ 98 int 99 is_same_host(char *name1, char *name2, struct in_addr addr2) 100 { 101 if (strcasecmp(name1, name2) == 0) { 102 return 1; 103 } else if (addr2.s_addr == INADDR_NONE) { 104 return 0; 105 } else { 106 static char lasthost[MAXHOSTNAMELEN] = ""; 107 static struct in_addr addr1; 108 struct hostent *he; 109 110 /* 111 * To save nameserver lookups, and because this function 112 * is typically called repeatedly on the same names, 113 * cache the last lookup result and reuse it if possible. 114 */ 115 if (strcasecmp(name1, lasthost) == 0) { 116 return (addr1.s_addr == addr2.s_addr); 117 } else if (!(he = gethostbyname(name1))) { 118 return 0; 119 } else { 120 xstrlcpy(lasthost, name1, sizeof(lasthost)); 121 memcpy(&addr1, he->h_addr, sizeof(addr1)); 122 return (addr1.s_addr == addr2.s_addr); 123 } 124 } 125 } 126 127 128 /* 129 * Print the binary tree in inorder so that output is sorted. 130 */ 131 void 132 print_dump(mountlist mp) 133 { 134 if (mp == NULL) 135 return; 136 if (is_same_host(mp->ml_hostname, thishost, thisaddr)) { 137 switch (type) { 138 case ALL: 139 printf("%s:%s\n", mp->ml_hostname, mp->ml_directory); 140 break; 141 case DIRS: 142 printf("%s\n", mp->ml_directory); 143 break; 144 default: 145 printf("%s\n", mp->ml_hostname); 146 break; 147 }; 148 } 149 if (mp->ml_next) 150 print_dump(mp->ml_next); 151 } 152 153 154 /* 155 * remove entry from remote rmtab 156 */ 157 int 158 remove_mount(CLIENT *client, char *host, mountlist ml, int fixit) 159 { 160 enum clnt_stat estat; 161 struct timeval tv; 162 char *pathp = dir_path; 163 164 xstrlcpy(dir_path, ml->ml_directory, sizeof(dir_path)); 165 166 if (!fixit) { 167 printf("%s: bogus mount %s:%s\n", host, ml->ml_hostname, ml->ml_directory); 168 fflush(stdout); 169 } else { 170 printf("%s: removing %s:%s\n", host, ml->ml_hostname, ml->ml_directory); 171 fflush(stdout); 172 173 tv.tv_sec = CALL_TIMEOUT; 174 tv.tv_usec = 0; 175 176 if ((estat = clnt_call(client, 177 MOUNTPROC_UMNT, 178 (XDRPROC_T_TYPE) xdr_dirpath, 179 (char *) &pathp, 180 (XDRPROC_T_TYPE) xdr_void, 181 (char *) NULL, 182 tv)) != RPC_SUCCESS) { 183 fprintf(stderr, "%s:%s MOUNTPROC_UMNT: ", 184 host, ml->ml_directory); 185 clnt_perrno(estat); 186 fflush(stderr); 187 return -1; 188 } 189 } 190 return 0; 191 } 192 193 194 /* 195 * fix mount list on remote host 196 */ 197 void 198 fix_rmtab(CLIENT *client, char *host, mountlist mp, int fixit, int force) 199 { 200 mountlist p; 201 struct hostent *he; 202 struct in_addr hostaddr; 203 204 /* 205 * Obtain remote address for comparisons 206 */ 207 if ((he = gethostbyname(host))) { 208 memcpy(&hostaddr, he->h_addr, sizeof(hostaddr)); 209 } else { 210 hostaddr.s_addr = INADDR_NONE; 211 } 212 213 for (p = mp; p; p = p->ml_next) { 214 if (is_same_host(p->ml_hostname, thishost, thisaddr)) { 215 if (force || !fixmount_check_mount(host, hostaddr, p->ml_directory)) 216 remove_mount(client, host, p, fixit); 217 } 218 } 219 } 220 221 222 /* 223 * remove all entries from remote rmtab 224 */ 225 int 226 remove_all(CLIENT *client, char *host) 227 { 228 enum clnt_stat estat; 229 struct timeval tv; 230 231 printf("%s: removing ALL\n", host); 232 fflush(stdout); 233 234 tv.tv_sec = CALL_TIMEOUT; 235 tv.tv_usec = 0; 236 237 if ((estat = clnt_call(client, 238 MOUNTPROC_UMNTALL, 239 (XDRPROC_T_TYPE) xdr_void, 240 (char *) NULL, 241 (XDRPROC_T_TYPE) xdr_void, 242 (char *) NULL, 243 tv)) != RPC_SUCCESS) { 244 /* 245 * RPC_SYSTEMERROR is returned even if all went well 246 */ 247 if (estat != RPC_SYSTEMERROR) { 248 fprintf(stderr, "%s MOUNTPROC_UMNTALL: ", host); 249 clnt_perrno(estat); 250 fflush(stderr); 251 return -1; 252 } 253 } 254 255 return 0; 256 } 257 258 259 /* 260 * This command queries the NFS mount daemon for it's mount list and/or 261 * it's exports list and prints them out. 262 * See "NFS: Network File System Protocol Specification, RFC1094, Appendix A" 263 * for detailed information on the protocol. 264 */ 265 int 266 main(int argc, char *argv[]) 267 { 268 AUTH *auth; 269 CLIENT *client; 270 char *host; 271 enum clnt_stat estat; 272 exports exp; 273 extern char *optarg; 274 extern int optind; 275 groups grp; 276 int ch; 277 int force = 0; 278 int morethanone; 279 register int rpcs = 0; 280 struct timeval tv; 281 282 while ((ch = getopt(argc, argv, "adervAqfh:")) != -1) 283 switch ((char) ch) { 284 285 case 'a': 286 if (type == 0) { 287 type = ALL; 288 rpcs |= DODUMP; 289 } else 290 usage(); 291 break; 292 293 case 'd': 294 if (type == 0) { 295 type = DIRS; 296 rpcs |= DODUMP; 297 } else 298 usage(); 299 break; 300 301 case 'e': 302 rpcs |= DOEXPORTS; 303 break; 304 305 case 'r': 306 rpcs |= DOREMOVE; 307 break; 308 309 case 'A': 310 rpcs |= DOREMALL; 311 break; 312 313 case 'v': 314 rpcs |= DOVERIFY; 315 break; 316 317 case 'q': 318 quiet = 1; 319 break; 320 321 case 'f': 322 force = 1; 323 break; 324 325 case 'h': 326 xstrlcpy(thishost, optarg, sizeof(thishost)); 327 break; 328 329 case '?': 330 default: 331 usage(); 332 } 333 334 if (optind == argc) 335 usage(); 336 337 if (rpcs == 0) 338 rpcs = DODUMP; 339 340 if (!*thishost) { 341 struct hostent *he; 342 343 if (gethostname(thishost, sizeof(thishost)) < 0) { 344 perror("gethostname"); 345 exit(1); 346 } 347 thishost[sizeof(thishost) - 1] = '\0'; 348 349 /* 350 * We need the hostname as it appears to the other side's 351 * mountd, so get our own hostname by reverse address 352 * resolution. 353 */ 354 if (!(he = gethostbyname(thishost))) { 355 fprintf(stderr, "gethostbyname failed on %s\n", 356 thishost); 357 exit(1); 358 } 359 memcpy(&thisaddr, he->h_addr, sizeof(thisaddr)); 360 if (!(he = gethostbyaddr((char *) &thisaddr, sizeof(thisaddr), 361 he->h_addrtype))) { 362 fprintf(stderr, "gethostbyaddr failed on %s\n", 363 inet_ntoa(thisaddr)); 364 exit(1); 365 } 366 xstrlcpy(thishost, he->h_name, sizeof(thishost)); 367 } else { 368 thisaddr.s_addr = INADDR_NONE; 369 } 370 371 if (!(auth = authunix_create_default())) { 372 fprintf(stderr, "couldn't create authentication handle\n"); 373 exit(1); 374 } 375 morethanone = (optind + 1 < argc); 376 377 for (; optind < argc; optind++) { 378 379 host = argv[optind]; 380 tv.tv_sec = CREATE_TIMEOUT; 381 tv.tv_usec = 0; 382 383 if (!(client = clnt_create_timeout(host, &tv))) 384 continue; 385 386 client->cl_auth = auth; 387 tv.tv_sec = CALL_TIMEOUT; 388 tv.tv_usec = 0; 389 390 if (rpcs & (DODUMP | DOREMOVE | DOVERIFY)) 391 if ((estat = clnt_call(client, 392 MOUNTPROC_DUMP, 393 (XDRPROC_T_TYPE) xdr_void, 394 (char *) NULL, 395 (XDRPROC_T_TYPE) xdr_mountlist, 396 (char *) &mntdump, 397 tv)) != RPC_SUCCESS) { 398 fprintf(stderr, "%s: MOUNTPROC_DUMP: ", host); 399 clnt_perrno(estat); 400 fflush(stderr); 401 mntdump = NULL; 402 goto next; 403 } 404 if (rpcs & DOEXPORTS) 405 if ((estat = clnt_call(client, 406 MOUNTPROC_EXPORT, 407 (XDRPROC_T_TYPE) xdr_void, 408 (char *) NULL, 409 (XDRPROC_T_TYPE) xdr_exports, 410 (char *) &mntexports, 411 tv)) != RPC_SUCCESS) { 412 fprintf(stderr, "%s: MOUNTPROC_EXPORT: ", host); 413 clnt_perrno(estat); 414 fflush(stderr); 415 mntexports = NULL; 416 goto next; 417 } 418 419 /* Now just print out the results */ 420 if ((rpcs & (DODUMP | DOEXPORTS)) && 421 morethanone) { 422 printf(">>> %s <<<\n", host); 423 fflush(stdout); 424 } 425 426 if (rpcs & DODUMP) { 427 print_dump(mntdump); 428 } 429 430 if (rpcs & DOEXPORTS) { 431 exp = mntexports; 432 while (exp) { 433 printf("%-35s", exp->ex_dir); 434 grp = exp->ex_groups; 435 if (grp == NULL) { 436 printf("Everyone\n"); 437 } else { 438 while (grp) { 439 printf("%s ", grp->gr_name); 440 grp = grp->gr_next; 441 } 442 printf("\n"); 443 } 444 exp = exp->ex_next; 445 } 446 } 447 448 if (rpcs & DOVERIFY) 449 fix_rmtab(client, host, mntdump, 0, force); 450 451 if (rpcs & DOREMOVE) 452 fix_rmtab(client, host, mntdump, 1, force); 453 454 if (rpcs & DOREMALL) 455 remove_all(client, host); 456 457 next: 458 if (mntdump) 459 (void) clnt_freeres(client, 460 (XDRPROC_T_TYPE) xdr_mountlist, 461 (char *) &mntdump); 462 if (mntexports) 463 (void) clnt_freeres(client, 464 (XDRPROC_T_TYPE) xdr_exports, 465 (char *) &mntexports); 466 467 clnt_destroy(client); 468 } 469 exit(0); 470 return 0; /* should never reach here */ 471 } 472 473 474 RETSIGTYPE 475 create_timeout(int sig) 476 { 477 signal(SIGALRM, SIG_DFL); 478 longjmp(before_rpc, 1); 479 } 480 481 482 #ifndef HAVE_TRANSPORT_TYPE_TLI 483 /* 484 * inetresport creates a datagram socket and attempts to bind it to a 485 * secure port. 486 * returns: The bound socket, or -1 to indicate an error. 487 */ 488 static int 489 inetresport(int ty) 490 { 491 int alport; 492 struct sockaddr_in addr; 493 int fd; 494 495 memset(&addr, 0, sizeof(addr)); 496 /* as per POSIX, sin_len need not be set (used internally by kernel) */ 497 498 addr.sin_family = AF_INET; /* use internet address family */ 499 addr.sin_addr.s_addr = INADDR_ANY; 500 if ((fd = socket(AF_INET, ty, 0)) < 0) 501 return -1; 502 503 for (alport = IPPORT_RESERVED - 1; alport > IPPORT_RESERVED / 2 + 1; alport--) { 504 addr.sin_port = htons((u_short) alport); 505 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) >= 0) 506 return fd; 507 if (errno != EADDRINUSE) { 508 close(fd); 509 return -1; 510 } 511 } 512 close(fd); 513 errno = EAGAIN; 514 return -1; 515 } 516 517 518 /* 519 * Privsock() calls inetresport() to attempt to bind a socket to a secure 520 * port. If inetresport() fails, privsock returns a magic socket number which 521 * indicates to RPC that it should make its own socket. 522 * returns: A privileged socket # or RPC_ANYSOCK. 523 */ 524 static int 525 privsock(int ty) 526 { 527 int sock = inetresport(ty); 528 529 if (sock < 0) { 530 errno = 0; 531 /* Couldn't get a secure port, let RPC make an insecure one */ 532 sock = RPC_ANYSOCK; 533 } 534 return sock; 535 } 536 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 537 538 539 static CLIENT * 540 clnt_create_timeout(char *host, struct timeval *tvp) 541 { 542 CLIENT *clnt; 543 struct sockaddr_in host_addr; 544 struct hostent *hp; 545 #ifndef HAVE_TRANSPORT_TYPE_TLI 546 int s; 547 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 548 549 if (setjmp(before_rpc)) { 550 if (!quiet) { 551 fprintf(stderr, "%s: ", host); 552 clnt_perrno(RPC_TIMEDOUT); 553 fprintf(stderr, "\n"); 554 fflush(stderr); 555 } 556 return NULL; 557 } 558 signal(SIGALRM, create_timeout); 559 ualarm(tvp->tv_sec * 1000000 + tvp->tv_usec, 0); 560 561 /* 562 * Get address of host 563 */ 564 if ((hp = gethostbyname(host)) == 0 && !STREQ(host, localhost)) { 565 fprintf(stderr, "can't get address of %s\n", host); 566 return NULL; 567 } 568 memset(&host_addr, 0, sizeof(host_addr)); 569 /* as per POSIX, sin_len need not be set (used internally by kernel) */ 570 host_addr.sin_family = AF_INET; 571 if (hp) { 572 memmove((voidp) &host_addr.sin_addr, (voidp) hp->h_addr, 573 sizeof(host_addr.sin_addr)); 574 } else { 575 /* fake "localhost" */ 576 host_addr.sin_addr.s_addr = htonl(0x7f000001); 577 } 578 579 #ifdef HAVE_TRANSPORT_TYPE_TLI 580 /* try TCP first (in case return data is large), then UDP */ 581 clnt = clnt_create(host, MOUNTPROG, MOUNTVERS, "tcp"); 582 if (!clnt) 583 clnt = clnt_create(host, MOUNTPROG, MOUNTVERS, "udp"); 584 #else /* not HAVE_TRANSPORT_TYPE_TLI */ 585 s = RPC_ANYSOCK; 586 clnt = clnttcp_create(&host_addr, MOUNTPROG, MOUNTVERS, &s, 0, 0); 587 if (!clnt) { 588 /* XXX: do we need to close(s) ? */ 589 s = privsock(SOCK_DGRAM); 590 clnt = clntudp_create(&host_addr, MOUNTPROG, MOUNTVERS, *tvp, &s); 591 } 592 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 593 594 if (!clnt) { 595 ualarm(0, 0); 596 if (!quiet) { 597 clnt_pcreateerror(host); 598 fflush(stderr); 599 } 600 return NULL; 601 } 602 603 ualarm(0, 0); 604 return clnt; 605 } 606