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