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 2006 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 <libzfs.h> 30 #include <string.h> 31 #include <libshare.h> 32 #include "libshare_impl.h" 33 #include <libintl.h> 34 35 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *); 36 extern sa_group_t _sa_create_zfs_group(sa_group_t, char *); 37 extern char *sa_fstype(char *); 38 extern void set_node_attr(void *, char *, char *); 39 extern int sa_is_share(void *); 40 41 /* 42 * File system specific code for ZFS. The original code was stolen 43 * from the "zfs" command and modified to better suit this library's 44 * usage. 45 */ 46 47 typedef struct get_all_cbdata { 48 zfs_handle_t **cb_handles; 49 size_t cb_alloc; 50 size_t cb_used; 51 } get_all_cbdata_t; 52 53 static libzfs_handle_t *zfs_libhandle = NULL; 54 static zfs_handle_t **zfs_list = NULL; 55 static size_t zfs_list_count = 0; 56 57 /* 58 * sa_zfs_init() 59 * 60 * initialize an access handle into libzfs 61 */ 62 63 void 64 sa_zfs_init() 65 { 66 zfs_libhandle = libzfs_init(); 67 libzfs_print_on_error(zfs_libhandle, B_TRUE); 68 } 69 70 /* 71 * sa_zfs_fini() 72 * 73 * cleanup data structures and the libzfs handle used for accessing 74 * zfs file share info. 75 */ 76 77 void 78 sa_zfs_fini() 79 { 80 if (zfs_libhandle != NULL) { 81 libzfs_fini(zfs_libhandle); 82 zfs_libhandle = NULL; 83 if (zfs_list != NULL) { 84 /* 85 * contents of zfs_list were already freed by the call to 86 * libzfs_fini(). 87 */ 88 free(zfs_list); 89 zfs_list = NULL; 90 } 91 } 92 } 93 94 /* 95 * get_one_filesystem(zfs_handle_t, data) 96 * 97 * an interator function called while iterating through the ZFS 98 * root. It accumulates into an array of file system handles that can 99 * be used to derive info about those file systems. 100 */ 101 102 static int 103 get_one_filesystem(zfs_handle_t *zhp, void *data) 104 { 105 get_all_cbdata_t *cbp = data; 106 107 /* 108 * Skip any zvols 109 */ 110 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 111 zfs_close(zhp); 112 return (0); 113 } 114 115 if (cbp->cb_alloc == cbp->cb_used) { 116 zfs_handle_t **handles; 117 118 if (cbp->cb_alloc == 0) 119 cbp->cb_alloc = 64; 120 else 121 cbp->cb_alloc *= 2; 122 123 handles = calloc(1, cbp->cb_alloc * sizeof (void *)); 124 if (handles == NULL) { 125 return (0); 126 } 127 128 if (cbp->cb_handles) { 129 (void) memcpy(handles, cbp->cb_handles, 130 cbp->cb_used * sizeof (void *)); 131 free(cbp->cb_handles); 132 } 133 134 cbp->cb_handles = handles; 135 } 136 137 cbp->cb_handles[cbp->cb_used++] = zhp; 138 139 return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 140 } 141 142 /* 143 * get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 144 * 145 * iterate through all ZFS file systems starting at the root. Returns 146 * a count and an array of handle pointers. Allocating is only done 147 * once. The caller does not need to free since it will be done at 148 * sa_zfs_fini() time. 149 */ 150 151 static void 152 get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 153 { 154 get_all_cbdata_t cb = { 0 }; 155 156 if (zfs_list != NULL) { 157 *fslist = zfs_list; 158 *count = zfs_list_count; 159 return; 160 } 161 162 (void) zfs_iter_root(zfs_libhandle, get_one_filesystem, &cb); 163 164 zfs_list = *fslist = cb.cb_handles; 165 zfs_list_count = *count = cb.cb_used; 166 } 167 168 /* 169 * mountpoint_compare(a, b) 170 * 171 * compares the mountpoint on two zfs file systems handles. 172 * returns values following strcmp() model. 173 */ 174 175 static int 176 mountpoint_compare(const void *a, const void *b) 177 { 178 zfs_handle_t **za = (zfs_handle_t **)a; 179 zfs_handle_t **zb = (zfs_handle_t **)b; 180 char mounta[MAXPATHLEN]; 181 char mountb[MAXPATHLEN]; 182 183 verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 184 sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 185 verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 186 sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 187 188 return (strcmp(mounta, mountb)); 189 } 190 191 /* 192 * get_zfs_dataset(path) 193 * 194 * get the name of the ZFS dataset the path is equivalent to. The 195 * dataset name is used for get/set of ZFS properties since libzfs 196 * requires a dataset to do a zfs_open(). 197 */ 198 199 static char * 200 get_zfs_dataset(char *path) 201 { 202 size_t i, count = 0; 203 char *dataset = NULL; 204 zfs_handle_t **zlist; 205 char mountpoint[ZFS_MAXPROPLEN]; 206 207 get_all_filesystems(&zlist, &count); 208 qsort(zlist, count, sizeof (void *), mountpoint_compare); 209 for (i = 0; i < count; i++) { 210 /* must have a mountpoint */ 211 if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint, 212 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 213 /* no mountpoint */ 214 continue; 215 } 216 217 /* mountpoint must be a path */ 218 if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 219 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 220 continue; 221 222 /* canmount must be set */ 223 if (!zfs_prop_get_int(zlist[i], ZFS_PROP_CANMOUNT)) 224 continue; 225 226 /* 227 * have a mountable handle but want to skip those marked none 228 * and legacy 229 */ 230 if (strcmp(mountpoint, path) == 0) { 231 dataset = (char *)zfs_get_name(zlist[i]); 232 break; 233 } 234 235 } 236 237 if (dataset != NULL) { 238 dataset = strdup(dataset); 239 } 240 return (dataset); 241 } 242 243 /* 244 * get_zfs_property(dataset, property) 245 * 246 * Get the file system property specified from the ZFS dataset. 247 */ 248 249 static char * 250 get_zfs_property(char *dataset, zfs_prop_t property) 251 { 252 zfs_handle_t *handle = NULL; 253 char shareopts[ZFS_MAXPROPLEN]; 254 libzfs_handle_t *libhandle; 255 256 libhandle = libzfs_init(); 257 if (libhandle != NULL) { 258 handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM); 259 if (handle != NULL) { 260 if (zfs_prop_get(handle, property, shareopts, 261 sizeof (shareopts), NULL, NULL, 0, 262 B_FALSE) == 0) { 263 zfs_close(handle); 264 libzfs_fini(libhandle); 265 return (strdup(shareopts)); 266 } 267 zfs_close(handle); 268 } 269 libzfs_fini(libhandle); 270 } 271 return (NULL); 272 } 273 274 /* 275 * sa_zfs_is_shared(path) 276 * 277 * Check to see if the ZFS path provided has the sharenfs option set 278 * or not. 279 */ 280 281 int 282 sa_zfs_is_shared(char *path) 283 { 284 int ret = 0; 285 char *dataset; 286 zfs_handle_t *handle = NULL; 287 char shareopts[ZFS_MAXPROPLEN]; 288 libzfs_handle_t *libhandle; 289 290 dataset = get_zfs_dataset(path); 291 if (dataset != NULL) { 292 libhandle = libzfs_init(); 293 if (libhandle != NULL) { 294 handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM); 295 if (handle != NULL) { 296 if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts, 297 sizeof (shareopts), NULL, NULL, 0, 298 B_FALSE) == 0 && 299 strcmp(shareopts, "off") != 0) 300 ret = 1; /* it is shared */ 301 zfs_close(handle); 302 } 303 libzfs_fini(libhandle); 304 } 305 free(dataset); 306 } 307 return (ret); 308 } 309 310 /* 311 * find_or_create_group(groupname, proto, *err) 312 * 313 * While walking the ZFS tree, we need to add shares to a defined 314 * group. If the group doesn't exist, create it first, making sure it 315 * is marked as a ZFS group. 316 * 317 * Note that all ZFS shares are in a subgroup of the top level group 318 * called "zfs". 319 */ 320 321 static sa_group_t 322 find_or_create_group(char *groupname, char *proto, int *err) 323 { 324 sa_group_t group; 325 sa_optionset_t optionset; 326 int ret = SA_OK; 327 328 /* 329 * we check to see if the "zfs" group exists. Since this 330 * should be the top level group, we don't want the 331 * parent. This is to make sure the zfs group has been created 332 * and to created if it hasn't been. 333 */ 334 group = sa_get_group(groupname); 335 if (group == NULL) { 336 group = sa_create_group(groupname, &ret); 337 338 /* make sure this is flagged as a ZFS group */ 339 if (group != NULL) 340 ret = sa_set_group_attr(group, "zfs", "true"); 341 } 342 if (group != NULL) { 343 if (proto != NULL) { 344 optionset = sa_get_optionset(group, proto); 345 if (optionset == NULL) { 346 optionset = sa_create_optionset(group, proto); 347 } else { 348 char **protolist; 349 int numprotos, i; 350 numprotos = sa_get_protocols(&protolist); 351 for (i = 0; i < numprotos; i++) { 352 optionset = sa_create_optionset(group, protolist[i]); 353 } 354 if (protolist != NULL) 355 free(protolist); 356 } 357 } 358 } 359 if (err != NULL) 360 *err = ret; 361 return (group); 362 } 363 364 /* 365 * find_or_create_zfs_subgroup(groupname, optstring, *err) 366 * 367 * ZFS shares will be in a subgroup of the "zfs" master group. This 368 * function looks to see if the groupname exists and returns it if it 369 * does or else creates a new one with the specified name and returns 370 * that. The "zfs" group will exist before we get here, but we make 371 * sure just in case. 372 * 373 * err must be a valid pointer. 374 */ 375 376 static sa_group_t 377 find_or_create_zfs_subgroup(char *groupname, char *optstring, int *err) 378 { 379 sa_group_t group = NULL; 380 sa_group_t zfs; 381 char *name; 382 char *options; 383 384 /* start with the top-level "zfs" group */ 385 zfs = sa_get_group("zfs"); 386 *err = SA_OK; 387 if (zfs != NULL) { 388 for (group = sa_get_sub_group(zfs); group != NULL; 389 group = sa_get_next_group(group)) { 390 name = sa_get_group_attr(group, "name"); 391 if (name != NULL && strcmp(name, groupname) == 0) { 392 /* have the group so break out of here */ 393 sa_free_attr_string(name); 394 break; 395 } 396 if (name != NULL) 397 sa_free_attr_string(name); 398 } 399 400 if (group == NULL) { 401 /* need to create the sub-group since it doesn't exist */ 402 group = _sa_create_zfs_group(zfs, groupname); 403 if (group != NULL) { 404 set_node_attr(group, "zfs", "true"); 405 } 406 if (strcmp(optstring, "on") == 0) 407 optstring = "rw"; 408 if (group != NULL) { 409 options = strdup(optstring); 410 if (options != NULL) { 411 *err = sa_parse_legacy_options(group, options, "nfs"); 412 free(options); 413 } else { 414 *err = SA_NO_MEMORY; 415 } 416 } 417 } 418 } 419 return (group); 420 } 421 422 /* 423 * sa_get_zfs_shares(groupname) 424 * 425 * Walk the mnttab for all zfs mounts and determine which are 426 * shared. Find or create the appropriate group/sub-group to contain 427 * the shares. 428 * 429 * All shares are in a sub-group that will hold the properties. This 430 * allows representing the inherited property model. 431 */ 432 433 int 434 sa_get_zfs_shares(char *groupname) 435 { 436 sa_group_t group; 437 sa_group_t zfsgroup; 438 int legacy = 0; 439 int err; 440 zfs_handle_t **zlist; 441 char shareopts[ZFS_MAXPROPLEN]; 442 sa_share_t share; 443 zfs_source_t source; 444 char sourcestr[ZFS_MAXPROPLEN]; 445 char mountpoint[ZFS_MAXPROPLEN]; 446 char *options; 447 size_t count = 0, i; 448 449 /* 450 * if we can't access libzfs, don't bother doing anything. 451 */ 452 if (zfs_libhandle == NULL) 453 return (SA_SYSTEM_ERR); 454 455 zfsgroup = find_or_create_group(groupname, "nfs", &err); 456 if (zfsgroup != NULL) { 457 /* 458 * need to walk the mounted ZFS pools and datasets to 459 * find shares that are possible. 460 */ 461 get_all_filesystems(&zlist, &count); 462 qsort(zlist, count, sizeof (void *), mountpoint_compare); 463 464 group = zfsgroup; 465 for (i = 0; i < count; i++) { 466 char *dataset; 467 468 source = ZFS_SRC_ALL; 469 if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint, 470 sizeof (mountpoint), NULL, NULL, 0, 471 B_FALSE) != 0) { 472 /* no mountpoint */ 473 continue; 474 } 475 476 /* 477 * zfs_get_name value must not be freed. It is just a 478 * pointer to a value in the handle. 479 */ 480 if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL) 481 continue; 482 483 /* 484 * only deal with "mounted" file systems since 485 * unmounted file systems can't actually be shared. 486 */ 487 488 if (!zfs_is_mounted(zlist[i], NULL)) 489 continue; 490 491 if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts, 492 sizeof (shareopts), &source, sourcestr, 493 ZFS_MAXPROPLEN, 494 B_FALSE) == 0 && 495 strcmp(shareopts, "off") != 0) { 496 /* it is shared so add to list */ 497 share = sa_find_share(mountpoint); 498 err = SA_OK; 499 if (share != NULL) { 500 /* 501 * A zfs file system had been shared 502 * through traditional methods 503 * (share/dfstab or added to a non-zfs 504 * group. Now it has been added to a 505 * ZFS group via the zfs 506 * command. Remove from previous 507 * config and setup with current 508 * options. 509 */ 510 err = sa_remove_share(share); 511 share = NULL; 512 } 513 if (err == SA_OK) { 514 if (source & ZFS_SRC_INHERITED) { 515 int doshopt = 0; 516 /* 517 * Need to find the "real" parent 518 * sub-group. It may not be mounted, but it 519 * was identified in the "sourcestr" 520 * variable. The real parent not mounted can 521 * occur if "canmount=off and sharenfs=on". 522 */ 523 group = find_or_create_zfs_subgroup(sourcestr, 524 shareopts, &doshopt); 525 if (group != NULL) { 526 share = _sa_add_share(group, mountpoint, 527 SA_SHARE_TRANSIENT, 528 &err); 529 /* 530 * some options may only be on 531 * shares. If the opt string 532 * contains one of those, we 533 * put it just on the share. 534 */ 535 if (share != NULL && 536 doshopt == SA_PROP_SHARE_ONLY) { 537 options = strdup(shareopts); 538 if (options != NULL) { 539 err = sa_parse_legacy_options(share, 540 options, "nfs"); 541 free(options); 542 } 543 } 544 } else { 545 err = SA_NO_MEMORY; 546 } 547 } else { 548 group = _sa_create_zfs_group(zfsgroup, dataset); 549 if (group == NULL) { 550 static int err = 0; 551 /* 552 * there is a problem, but we can't do 553 * anything about it at this point so 554 * we issue a warning an move on. 555 */ 556 if (err == 0) { 557 /* only print error once */ 558 (void) fprintf(stderr, 559 gettext("Cannot create ZFS subgroup " 560 "during initialization:" 561 " %s\n"), 562 sa_errorstr(SA_SYSTEM_ERR)); 563 err = 1; 564 } 565 continue; 566 } 567 set_node_attr(group, "zfs", "true"); 568 share = _sa_add_share(group, mountpoint, 569 SA_SHARE_TRANSIENT, &err); 570 if (err == SA_OK) { 571 if (strcmp(shareopts, "on") != 0) { 572 options = strdup(shareopts); 573 if (options != NULL) { 574 err = sa_parse_legacy_options(group, 575 options, 576 "nfs"); 577 free(options); 578 } 579 if (err == SA_PROP_SHARE_ONLY) { 580 /* 581 * Same as above, some 582 * properties may only be on 583 * shares, but due to the ZFS 584 * sub-groups being 585 * artificial, we sometimes 586 * get this and have to deal 587 * with it. We do it by 588 * attempting to put it on the 589 * share. 590 */ 591 options = strdup(shareopts); 592 if (options != NULL) 593 err = sa_parse_legacy_options( 594 share, 595 options, 596 "nfs"); 597 free(options); 598 } 599 /* unmark the share's changed state */ 600 set_node_attr(share, "changed", NULL); 601 } 602 } 603 } 604 } 605 } 606 } 607 } 608 /* 609 * Don't need to free the "zlist" variable since it is only a 610 * pointer to a cached value that will be freed when 611 * sa_fini() is called. 612 */ 613 return (legacy); 614 } 615 616 #define COMMAND "/usr/sbin/zfs" 617 618 /* 619 * sa_zfs_set_sharenfs(group, path, on) 620 * 621 * Update the "sharenfs" property on the path. If on is true, then set 622 * to the properties on the group or "on" if no properties are 623 * defined. Set to "off" if on is false. 624 */ 625 626 int 627 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on) 628 { 629 int ret = SA_NOT_IMPLEMENTED; 630 char *command; 631 632 command = malloc(ZFS_MAXPROPLEN * 2); 633 if (command != NULL) { 634 char *opts = NULL; 635 char *dataset; 636 FILE *pfile; 637 /* for now, NFS is always available for "zfs" */ 638 if (on) { 639 opts = sa_proto_legacy_format("nfs", group, 1); 640 if (opts != NULL && strlen(opts) == 0) { 641 free(opts); 642 opts = strdup("on"); 643 } 644 } 645 dataset = get_zfs_dataset(path); 646 if (dataset != NULL) { 647 (void) snprintf(command, ZFS_MAXPROPLEN * 2, 648 "%s set sharenfs=\"%s\" %s", COMMAND, 649 opts != NULL ? opts : "off", 650 dataset); 651 pfile = popen(command, "r"); 652 if (pfile != NULL) { 653 ret = pclose(pfile); 654 if (ret != 0) 655 ret = SA_SYSTEM_ERR; 656 } 657 } 658 if (opts != NULL) 659 free(opts); 660 if (dataset != NULL) 661 free(dataset); 662 free(command); 663 } 664 return (ret); 665 } 666 667 /* 668 * sa_zfs_update(group) 669 * 670 * call back to ZFS to update the share if necessary. 671 * Don't do it if it isn't a real change. 672 */ 673 int 674 sa_zfs_update(sa_group_t group) 675 { 676 sa_optionset_t protopt; 677 sa_group_t parent; 678 char *command; 679 char *optstring; 680 int ret = SA_OK; 681 int doupdate = 0; 682 FILE *pfile; 683 684 if (sa_is_share(group)) 685 parent = sa_get_parent_group(group); 686 else 687 parent = group; 688 689 if (parent != NULL) { 690 command = malloc(ZFS_MAXPROPLEN * 2); 691 if (command == NULL) 692 return (SA_NO_MEMORY); 693 694 *command = '\0'; 695 for (protopt = sa_get_optionset(parent, NULL); protopt != NULL; 696 protopt = sa_get_next_optionset(protopt)) { 697 698 char *proto = sa_get_optionset_attr(protopt, "type"); 699 char *path; 700 char *dataset = NULL; 701 char *zfsopts = NULL; 702 703 if (sa_is_share(group)) { 704 path = sa_get_share_attr((sa_share_t)group, "path"); 705 if (path != NULL) { 706 dataset = get_zfs_dataset(path); 707 sa_free_attr_string(path); 708 } 709 } else { 710 dataset = sa_get_group_attr(group, "name"); 711 } 712 /* update only when there is an optstring found */ 713 doupdate = 0; 714 if (proto != NULL && dataset != NULL) { 715 optstring = sa_proto_legacy_format(proto, group, 1); 716 zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS); 717 718 if (optstring != NULL && zfsopts != NULL) { 719 if (strcmp(optstring, zfsopts) != 0) 720 doupdate++; 721 } 722 723 if (doupdate) { 724 if (optstring != NULL && strlen(optstring) > 0) { 725 (void) snprintf(command, ZFS_MAXPROPLEN * 2, 726 "%s set sharenfs=%s %s", COMMAND, 727 optstring, dataset); 728 } else { 729 (void) snprintf(command, ZFS_MAXPROPLEN * 2, 730 "%s set sharenfs=on %s", COMMAND, 731 dataset); 732 } 733 pfile = popen(command, "r"); 734 if (pfile != NULL) 735 ret = pclose(pfile); 736 switch (ret) { 737 default: 738 case 1: 739 ret = SA_SYSTEM_ERR; 740 break; 741 case 2: 742 ret = SA_SYNTAX_ERR; 743 break; 744 case 0: 745 break; 746 } 747 } 748 if (optstring != NULL) { 749 free(optstring); 750 } 751 if (zfsopts != NULL) 752 free(zfsopts); 753 } 754 if (proto != NULL) 755 sa_free_attr_string(proto); 756 if (dataset != NULL) 757 free(dataset); 758 } 759 free(command); 760 } 761 return (ret); 762 } 763 764 /* 765 * sa_group_is_zfs(group) 766 * 767 * Given the group, determine if the zfs attribute is set. 768 */ 769 770 int 771 sa_group_is_zfs(sa_group_t group) 772 { 773 char *zfs; 774 int ret = 0; 775 776 zfs = sa_get_group_attr(group, "zfs"); 777 if (zfs != NULL) { 778 ret = 1; 779 sa_free_attr_string(zfs); 780 } 781 return (ret); 782 } 783 784 /* 785 * sa_path_is_zfs(path) 786 * 787 * Check to see if the file system path represents is of type "zfs". 788 */ 789 790 int 791 sa_path_is_zfs(char *path) 792 { 793 char *fstype; 794 int ret = 0; 795 796 fstype = sa_fstype(path); 797 if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 798 ret = 1; 799 } 800 if (fstype != NULL) 801 sa_free_fstype(fstype); 802 return (ret); 803 } 804