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 <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 dgettext(TEXT_DOMAIN, 560 "Cannot create ZFS subgroup " 561 "during initialization:" 562 " %s\n"), 563 sa_errorstr(SA_SYSTEM_ERR)); 564 err = 1; 565 } 566 continue; 567 } 568 set_node_attr(group, "zfs", "true"); 569 share = _sa_add_share(group, mountpoint, 570 SA_SHARE_TRANSIENT, &err); 571 if (err == SA_OK) { 572 if (strcmp(shareopts, "on") != 0) { 573 options = strdup(shareopts); 574 if (options != NULL) { 575 err = sa_parse_legacy_options(group, 576 options, 577 "nfs"); 578 free(options); 579 } 580 if (err == SA_PROP_SHARE_ONLY) { 581 /* 582 * Same as above, some 583 * properties may only be on 584 * shares, but due to the ZFS 585 * sub-groups being 586 * artificial, we sometimes 587 * get this and have to deal 588 * with it. We do it by 589 * attempting to put it on the 590 * share. 591 */ 592 options = strdup(shareopts); 593 if (options != NULL) 594 err = sa_parse_legacy_options( 595 share, 596 options, 597 "nfs"); 598 free(options); 599 } 600 /* unmark the share's changed state */ 601 set_node_attr(share, "changed", NULL); 602 } 603 } 604 } 605 } 606 } 607 } 608 } 609 /* 610 * Don't need to free the "zlist" variable since it is only a 611 * pointer to a cached value that will be freed when 612 * sa_fini() is called. 613 */ 614 return (legacy); 615 } 616 617 #define COMMAND "/usr/sbin/zfs" 618 619 /* 620 * sa_zfs_set_sharenfs(group, path, on) 621 * 622 * Update the "sharenfs" property on the path. If on is true, then set 623 * to the properties on the group or "on" if no properties are 624 * defined. Set to "off" if on is false. 625 */ 626 627 int 628 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on) 629 { 630 int ret = SA_NOT_IMPLEMENTED; 631 char *command; 632 633 command = malloc(ZFS_MAXPROPLEN * 2); 634 if (command != NULL) { 635 char *opts = NULL; 636 char *dataset; 637 FILE *pfile; 638 /* for now, NFS is always available for "zfs" */ 639 if (on) { 640 opts = sa_proto_legacy_format("nfs", group, 1); 641 if (opts != NULL && strlen(opts) == 0) { 642 free(opts); 643 opts = strdup("on"); 644 } 645 } 646 dataset = get_zfs_dataset(path); 647 if (dataset != NULL) { 648 (void) snprintf(command, ZFS_MAXPROPLEN * 2, 649 "%s set sharenfs=\"%s\" %s", COMMAND, 650 opts != NULL ? opts : "off", 651 dataset); 652 pfile = popen(command, "r"); 653 if (pfile != NULL) { 654 ret = pclose(pfile); 655 if (ret != 0) 656 ret = SA_SYSTEM_ERR; 657 } 658 } 659 if (opts != NULL) 660 free(opts); 661 if (dataset != NULL) 662 free(dataset); 663 free(command); 664 } 665 return (ret); 666 } 667 668 /* 669 * sa_zfs_update(group) 670 * 671 * call back to ZFS to update the share if necessary. 672 * Don't do it if it isn't a real change. 673 */ 674 int 675 sa_zfs_update(sa_group_t group) 676 { 677 sa_optionset_t protopt; 678 sa_group_t parent; 679 char *command; 680 char *optstring; 681 int ret = SA_OK; 682 int doupdate = 0; 683 FILE *pfile; 684 685 if (sa_is_share(group)) 686 parent = sa_get_parent_group(group); 687 else 688 parent = group; 689 690 if (parent != NULL) { 691 command = malloc(ZFS_MAXPROPLEN * 2); 692 if (command == NULL) 693 return (SA_NO_MEMORY); 694 695 *command = '\0'; 696 for (protopt = sa_get_optionset(parent, NULL); protopt != NULL; 697 protopt = sa_get_next_optionset(protopt)) { 698 699 char *proto = sa_get_optionset_attr(protopt, "type"); 700 char *path; 701 char *dataset = NULL; 702 char *zfsopts = NULL; 703 704 if (sa_is_share(group)) { 705 path = sa_get_share_attr((sa_share_t)group, "path"); 706 if (path != NULL) { 707 dataset = get_zfs_dataset(path); 708 sa_free_attr_string(path); 709 } 710 } else { 711 dataset = sa_get_group_attr(group, "name"); 712 } 713 /* update only when there is an optstring found */ 714 doupdate = 0; 715 if (proto != NULL && dataset != NULL) { 716 optstring = sa_proto_legacy_format(proto, group, 1); 717 zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS); 718 719 if (optstring != NULL && zfsopts != NULL) { 720 if (strcmp(optstring, zfsopts) != 0) 721 doupdate++; 722 } 723 724 if (doupdate) { 725 if (optstring != NULL && strlen(optstring) > 0) { 726 (void) snprintf(command, ZFS_MAXPROPLEN * 2, 727 "%s set sharenfs=%s %s", COMMAND, 728 optstring, dataset); 729 } else { 730 (void) snprintf(command, ZFS_MAXPROPLEN * 2, 731 "%s set sharenfs=on %s", COMMAND, 732 dataset); 733 } 734 pfile = popen(command, "r"); 735 if (pfile != NULL) 736 ret = pclose(pfile); 737 switch (ret) { 738 default: 739 case 1: 740 ret = SA_SYSTEM_ERR; 741 break; 742 case 2: 743 ret = SA_SYNTAX_ERR; 744 break; 745 case 0: 746 break; 747 } 748 } 749 if (optstring != NULL) { 750 free(optstring); 751 } 752 if (zfsopts != NULL) 753 free(zfsopts); 754 } 755 if (proto != NULL) 756 sa_free_attr_string(proto); 757 if (dataset != NULL) 758 free(dataset); 759 } 760 free(command); 761 } 762 return (ret); 763 } 764 765 /* 766 * sa_group_is_zfs(group) 767 * 768 * Given the group, determine if the zfs attribute is set. 769 */ 770 771 int 772 sa_group_is_zfs(sa_group_t group) 773 { 774 char *zfs; 775 int ret = 0; 776 777 zfs = sa_get_group_attr(group, "zfs"); 778 if (zfs != NULL) { 779 ret = 1; 780 sa_free_attr_string(zfs); 781 } 782 return (ret); 783 } 784 785 /* 786 * sa_path_is_zfs(path) 787 * 788 * Check to see if the file system path represents is of type "zfs". 789 */ 790 791 int 792 sa_path_is_zfs(char *path) 793 { 794 char *fstype; 795 int ret = 0; 796 797 fstype = sa_fstype(path); 798 if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 799 ret = 1; 800 } 801 if (fstype != NULL) 802 sa_free_fstype(fstype); 803 return (ret); 804 } 805