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 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <string.h>
  30 #include <libshare.h>
  31 #include "libshare_impl.h"
  32 #include <dlfcn.h>
  33 #include <link.h>
  34 #include <sys/types.h>
  35 #include <sys/param.h>
  36 #include <sys/stat.h>
  37 #include <dirent.h>
  38 #include <libintl.h>
  39 #include <sys/systeminfo.h>
  40 #include <thread.h>
  41 #include <synch.h>
  42 
  43 #define MAXISALEN       257     /* based on sysinfo(2) man page */
  44 
  45 /*
  46  * protocol plugin interface
  47  *
  48  * finds plugins and makes them accessible. This is only "used" by
  49  * libshare.so.
  50  */
  51 
  52 struct sa_proto_plugin *sap_proto_list;
  53 
  54 static struct sa_proto_handle sa_proto_handle;
  55 
  56 extern mutex_t sa_global_lock;
  57 
  58 void proto_plugin_fini();
  59 
  60 static void proto_plugin_fini_impl(boolean_t);
  61 
  62 /*
  63  * Returns true if name is "." or "..", otherwise returns false.
  64  */
  65 static boolean_t
  66 proto_is_dot_or_dotdot(const char *name)
  67 {
  68         if (*name != '.')
  69                 return (B_FALSE);
  70 
  71         if ((name[1] == '\0') || (name[1] == '.' && name[2] == '\0'))
  72                 return (B_TRUE);
  73 
  74         return (B_FALSE);
  75 }
  76 
  77 /*
  78  * proto_plugin_init()
  79  *
  80  * Initialize the protocol specific plugin modules.
  81  *
  82  * Walk /usr/lib/fs/\* for libshare_*.so modules, for example,
  83  * /usr/lib/fs/nfs/libshare_nfs.so. A protocol specific directory
  84  * would have modules with names of the form libshare_<proto>.so.
  85  * For each protocol found, initialize it and add it to the internal
  86  * list of protocols. These are used for protocol specific operations.
  87  */
  88 
  89 int
  90 proto_plugin_init()
  91 {
  92         struct sa_proto_plugin *proto;
  93         int num_protos = 0;
  94         struct sa_plugin_ops *plugin_ops;
  95         void *dlhandle;
  96         DIR *dir;
  97         struct dirent *dent;
  98         int ret = SA_OK;
  99         struct stat st;
 100         char isa[MAXISALEN];
 101 
 102         assert(MUTEX_HELD(&sa_global_lock));
 103 
 104 #if defined(_LP64)
 105         if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
 106                 isa[0] = '\0';
 107 #else
 108         isa[0] = '\0';
 109 #endif
 110         if (sap_proto_list != NULL && sa_proto_handle.sa_proto != NULL) {
 111                 sa_proto_handle.sa_ref_count++;
 112                 return (SA_OK);
 113         }
 114 
 115         if ((dir = opendir(SA_LIB_DIR)) == NULL)
 116                 return (SA_OK);
 117 
 118         while ((dent = readdir(dir)) != NULL) {
 119                 char path[MAXPATHLEN];
 120 
 121                 if (proto_is_dot_or_dotdot(dent->d_name))
 122                         continue;
 123 
 124                 (void) snprintf(path, MAXPATHLEN,
 125                     "%s/%s/%s/libshare_%s.so.1", SA_LIB_DIR,
 126                     dent->d_name, isa, dent->d_name);
 127 
 128                 /*
 129                  * If file doesn't exist, don't try to map it
 130                  */
 131                 if (stat(path, &st) < 0)
 132                         continue;
 133 
 134                 if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL) {
 135                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 136                             "Error in plugin for protocol %s: %s\n"),
 137                             dent->d_name, dlerror());
 138                         continue;
 139                 }
 140 
 141                 plugin_ops = (struct sa_plugin_ops *)
 142                     dlsym(dlhandle, "sa_plugin_ops");
 143                 if (plugin_ops == NULL) {
 144                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 145                             "Error in plugin ops for protocol %s: %s\n"),
 146                             dent->d_name, dlerror());
 147                         (void) dlclose(dlhandle);
 148                         continue;
 149                 }
 150 
 151                 proto = (struct sa_proto_plugin *)
 152                     calloc(1, sizeof (struct sa_proto_plugin));
 153                 if (proto == NULL) {
 154                         (void) dlclose(dlhandle);
 155                         ret = SA_NO_MEMORY;
 156                         continue;
 157                 }
 158 
 159                 proto->plugin_ops = plugin_ops;
 160                 proto->plugin_handle = dlhandle;
 161                 num_protos++;
 162                 proto->plugin_next = sap_proto_list;
 163                 sap_proto_list = proto;
 164         }
 165 
 166         (void) closedir(dir);
 167 
 168         if (num_protos != 0 && sa_proto_handle.sa_proto == NULL) {
 169                 sa_proto_handle.sa_proto =
 170                     (char **)calloc(num_protos, sizeof (char *));
 171                 sa_proto_handle.sa_ops =
 172                     (struct sa_plugin_ops **)calloc(num_protos,
 173                     sizeof (struct sa_plugin_ops *));
 174                 if (sa_proto_handle.sa_proto != NULL &&
 175                     sa_proto_handle.sa_ops != NULL) {
 176                         int i;
 177                         struct sa_proto_plugin *tmp;
 178 
 179                         for (i = 0, tmp = sap_proto_list;
 180                             i < num_protos && tmp != NULL;
 181                             tmp = tmp->plugin_next) {
 182                                 int err;
 183                                 err = SA_OK;
 184                                 if (tmp->plugin_ops->sa_init != NULL)
 185                                         err = tmp->plugin_ops->sa_init();
 186                                 if (err == SA_OK) {
 187                                         /*
 188                                          * Only include if the init
 189                                          * succeeded or was NULL
 190                                          */
 191                                         sa_proto_handle.sa_num_proto++;
 192                                         sa_proto_handle.sa_ops[i] =
 193                                             tmp->plugin_ops;
 194                                         sa_proto_handle.sa_proto[i] =
 195                                             tmp->plugin_ops->sa_protocol;
 196                                         i++;
 197                                 }
 198                         }
 199                 } else {
 200                         ret = SA_NO_MEMORY;
 201                 }
 202         }
 203 
 204         /*
 205          * There was an error, so cleanup prior to return of failure.
 206          */
 207         if (ret != SA_OK) {
 208                 proto_plugin_fini_impl(B_TRUE);
 209         } else {
 210                 assert(sa_proto_handle.sa_ref_count >= 0);
 211                 sa_proto_handle.sa_ref_count++;
 212         }
 213 
 214         return (ret);
 215 }
 216 
 217 /*
 218  * proto_plugin_fini()
 219  *
 220  * Uninitialize all the plugin modules.
 221  */
 222 
 223 void
 224 proto_plugin_fini()
 225 {
 226         assert(MUTEX_HELD(&sa_global_lock));
 227 
 228         proto_plugin_fini_impl(B_FALSE);
 229 }
 230 
 231 static void
 232 proto_plugin_fini_impl(boolean_t forcefini)
 233 {
 234         struct sa_proto_plugin *p;
 235 
 236         assert(MUTEX_HELD(&sa_global_lock));
 237 
 238         sa_proto_handle.sa_ref_count--;
 239         /*
 240          * If another thread has a reference to the proto list,
 241          * we don't want to clear it out while they're using it
 242          * or find_protocol() could fail.
 243          */
 244         if (!forcefini && sa_proto_handle.sa_ref_count > 0) {
 245                 return;
 246         } else {
 247                 sa_proto_handle.sa_ref_count = 0;
 248         }
 249 
 250         /*
 251          * Protocols may call this framework during _fini
 252          * (the smbfs plugin is known to do this) so do
 253          * two passes: 1st call _fini; 2nd free, dlclose.
 254          */
 255         for (p = sap_proto_list; p != NULL; p = p->plugin_next)
 256                 p->plugin_ops->sa_fini();
 257 
 258         while ((p = sap_proto_list) != NULL) {
 259                 sap_proto_list = p->plugin_next;
 260 
 261                 if (p->plugin_handle != NULL)
 262                         (void) dlclose(p->plugin_handle);
 263                 free(p);
 264         }
 265         if (sap_proto_list != NULL)
 266                 sap_proto_list = NULL;
 267         if (sa_proto_handle.sa_ops != NULL) {
 268                 free(sa_proto_handle.sa_ops);
 269                 sa_proto_handle.sa_ops = NULL;
 270         }
 271         if (sa_proto_handle.sa_proto != NULL) {
 272                 free(sa_proto_handle.sa_proto);
 273                 sa_proto_handle.sa_proto = NULL;
 274         }
 275         sa_proto_handle.sa_num_proto = 0;
 276 }
 277 
 278 /*
 279  * find_protocol(proto)
 280  *
 281  * Search the plugin list for the specified protocol and return the
 282  * ops vector.  NULL if protocol is not defined.
 283  */
 284 
 285 static struct sa_plugin_ops *
 286 find_protocol(char *proto)
 287 {
 288         int i;
 289         struct sa_plugin_ops *ops = NULL;
 290 
 291         (void) mutex_lock(&sa_global_lock);
 292         if (proto != NULL) {
 293                 for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
 294                         if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0) {
 295                                 ops = sa_proto_handle.sa_ops[i];
 296                                 break;
 297                         }
 298                 }
 299         }
 300         (void) mutex_unlock(&sa_global_lock);
 301         return (ops);
 302 }
 303 
 304 /*
 305  * sa_proto_share(proto, share)
 306  *
 307  * Activate a share for the specified protocol.
 308  */
 309 
 310 int
 311 sa_proto_share(char *proto, sa_share_t share)
 312 {
 313         struct sa_plugin_ops *ops = find_protocol(proto);
 314         int ret = SA_INVALID_PROTOCOL;
 315 
 316         if (ops != NULL && ops->sa_share != NULL)
 317                 ret = ops->sa_share(share);
 318         return (ret);
 319 }
 320 
 321 /*
 322  * sa_proto_unshare(proto, share)
 323  *
 324  * Deactivate (unshare) the share for this protocol.
 325  */
 326 
 327 int
 328 sa_proto_unshare(sa_share_t share, char *proto, char *path)
 329 {
 330         struct sa_plugin_ops *ops = find_protocol(proto);
 331         int ret = SA_INVALID_PROTOCOL;
 332 
 333         if (ops != NULL && ops->sa_unshare != NULL)
 334                 ret = ops->sa_unshare(share, path);
 335         return (ret);
 336 }
 337 
 338 /*
 339  * sa_proto_share_resource(char *proto, sa_resource_t resource)
 340  *
 341  * For protocols that actually enable at the resource level, do the
 342  * protocol specific resource enable. If it doesn't, return an error.
 343  * Note that the resource functions are optional so can return
 344  * SA_NOT_SUPPORTED.
 345  */
 346 
 347 int
 348 sa_proto_share_resource(char *proto, sa_resource_t resource)
 349 {
 350         struct sa_plugin_ops *ops = find_protocol(proto);
 351         int ret = SA_INVALID_PROTOCOL;
 352 
 353         if (ops != NULL) {
 354                 if (ops->sa_enable_resource != NULL)
 355                         ret = ops->sa_enable_resource(resource);
 356                 else
 357                         ret = SA_NOT_SUPPORTED;
 358         }
 359         return (ret);
 360 }
 361 
 362 /*
 363  * sa_proto_unshare_resource(char *proto, sa_resource_t resource)
 364  *
 365  * For protocols that actually disable at the resource level, do the
 366  * protocol specific resource disable. If it doesn't, return an error.
 367  */
 368 
 369 int
 370 sa_proto_unshare_resource(char *proto, sa_resource_t resource)
 371 {
 372         struct sa_plugin_ops *ops = find_protocol(proto);
 373         int ret = SA_INVALID_PROTOCOL;
 374 
 375         if (ops != NULL) {
 376                 if (ops->sa_disable_resource != NULL)
 377                         ret = ops->sa_disable_resource(resource);
 378                 else
 379                         ret = SA_NOT_SUPPORTED;
 380         }
 381         return (ret);
 382 }
 383 
 384 /*
 385  * sa_proto_valid_prop(handle, proto, prop, opt)
 386  *
 387  * Check to see if the specified prop is valid for this protocol.
 388  */
 389 
 390 int
 391 sa_proto_valid_prop(sa_handle_t handle, char *proto, sa_property_t prop,
 392     sa_optionset_t opt)
 393 {
 394         struct sa_plugin_ops *ops = find_protocol(proto);
 395         int ret = 0;
 396 
 397         if (ops != NULL && ops->sa_valid_prop != NULL)
 398                 ret = ops->sa_valid_prop(handle, prop, opt);
 399         return (ret);
 400 }
 401 
 402 /*
 403  * sa_proto_valid_space(proto, space)
 404  *
 405  * Check if space is valid optionspace for proto.
 406  * Protocols that don't implement this don't support spaces.
 407  */
 408 int
 409 sa_proto_valid_space(char *proto, char *token)
 410 {
 411         struct sa_plugin_ops *ops = find_protocol(proto);
 412         int ret = 0;
 413 
 414         if (ops != NULL && ops->sa_valid_space != NULL)
 415                 ret = ops->sa_valid_space(token);
 416         return (ret);
 417 }
 418 
 419 /*
 420  * sa_proto_space_alias(proto, space)
 421  *
 422  * If the name for space is an alias, return its proper name.  This is
 423  * used to translate "default" values into proper form.
 424  */
 425 char *
 426 sa_proto_space_alias(char *proto, char *space)
 427 {
 428         struct sa_plugin_ops *ops = find_protocol(proto);
 429         char *ret = space;
 430 
 431         if (ops != NULL && ops->sa_space_alias != NULL)
 432                 ret = ops->sa_space_alias(space);
 433         return (ret);
 434 }
 435 
 436 /*
 437  * sa_proto_security_prop(proto, token)
 438  *
 439  * Check to see if the property name in token is a valid named
 440  * optionset property.
 441  */
 442 
 443 int
 444 sa_proto_security_prop(char *proto, char *token)
 445 {
 446         struct sa_plugin_ops *ops = find_protocol(proto);
 447         int ret = 0;
 448 
 449         if (ops != NULL && ops->sa_security_prop != NULL)
 450                 ret = ops->sa_security_prop(token);
 451         return (ret);
 452 }
 453 
 454 /*
 455  * sa_proto_legacy_opts(proto, grouup, options)
 456  *
 457  * Have the protocol specific parser parse the options string and add
 458  * an appropriate optionset to group.
 459  */
 460 
 461 int
 462 sa_proto_legacy_opts(char *proto, sa_group_t group, char *options)
 463 {
 464         struct sa_plugin_ops *ops = find_protocol(proto);
 465         int ret = SA_INVALID_PROTOCOL;
 466 
 467         if (ops != NULL && ops->sa_legacy_opts != NULL)
 468                 ret = ops->sa_legacy_opts(group, options);
 469         return (ret);
 470 }
 471 
 472 /*
 473  * sa_proto_legacy_format(proto, group, hier)
 474  *
 475  * Return a legacy format string representing either the group's
 476  * properties or the groups hierarchical properties.
 477  */
 478 
 479 char *
 480 sa_proto_legacy_format(char *proto, sa_group_t group, int hier)
 481 {
 482         struct sa_plugin_ops *ops = find_protocol(proto);
 483         char *ret = NULL;
 484 
 485         if (ops != NULL && ops->sa_legacy_format != NULL)
 486                 ret = ops->sa_legacy_format(group, hier);
 487         return (ret);
 488 }
 489 
 490 void
 491 sa_format_free(char *str)
 492 {
 493         free(str);
 494 }
 495 
 496 /*
 497  * sharectl related API functions
 498  */
 499 
 500 /*
 501  * sa_proto_get_properties(proto)
 502  *
 503  * Return the set of properties that are specific to the
 504  * protocol. These are usually in /etc/dfs/<proto> and related files,
 505  * but only the protocol module knows which ones for sure.
 506  */
 507 
 508 sa_protocol_properties_t
 509 sa_proto_get_properties(char *proto)
 510 {
 511         struct sa_plugin_ops *ops = find_protocol(proto);
 512         sa_protocol_properties_t props = NULL;
 513 
 514         if (ops != NULL && ops->sa_get_proto_set != NULL)
 515                 props = ops->sa_get_proto_set();
 516         return (props);
 517 }
 518 
 519 /*
 520  * sa_proto_set_property(proto, prop)
 521  *
 522  * Update the protocol specific property.
 523  */
 524 
 525 int
 526 sa_proto_set_property(char *proto, sa_property_t prop)
 527 {
 528         struct sa_plugin_ops *ops = find_protocol(proto);
 529         int ret = SA_OK;
 530 
 531         if (ops != NULL && ops->sa_set_proto_prop != NULL)
 532                 ret = ops->sa_set_proto_prop(prop);
 533         return (ret);
 534 }
 535 
 536 /*
 537  * sa_valid_protocol(proto)
 538  *
 539  * Check to see if the protocol specified is defined by a
 540  * plugin. Returns true (1) or false (0)
 541  */
 542 
 543 int
 544 sa_valid_protocol(char *proto)
 545 {
 546         struct sa_plugin_ops *ops = find_protocol(proto);
 547         return (ops != NULL);
 548 }
 549 
 550 /*
 551  * Return the current operational status of the protocol
 552  */
 553 
 554 char *
 555 sa_get_protocol_status(char *proto)
 556 {
 557         struct sa_plugin_ops *ops = find_protocol(proto);
 558         char *ret = NULL;
 559         if (ops != NULL && ops->sa_get_proto_status != NULL)
 560                 ret = ops->sa_get_proto_status(proto);
 561         return (ret);
 562 }
 563 
 564 /*
 565  * sa_proto_update_legacy(proto, share)
 566  *
 567  * Update the protocol specific legacy files if necessary for the
 568  * specified share.
 569  */
 570 
 571 int
 572 sa_proto_update_legacy(char *proto, sa_share_t share)
 573 {
 574         struct sa_plugin_ops *ops = find_protocol(proto);
 575         int ret = SA_NOT_IMPLEMENTED;
 576 
 577         if (ops != NULL) {
 578                 if (ops->sa_update_legacy != NULL)
 579                         ret = ops->sa_update_legacy(share);
 580         }
 581         return (ret);
 582 }
 583 
 584 /*
 585  * sa_delete_legacy(proto, share)
 586  *
 587  * Remove the specified share from the protocol specific legacy files.
 588  */
 589 
 590 int
 591 sa_proto_delete_legacy(char *proto, sa_share_t share)
 592 {
 593         struct sa_plugin_ops *ops = find_protocol(proto);
 594         int ret = SA_NOT_IMPLEMENTED;
 595 
 596         if (ops != NULL) {
 597                 if (ops->sa_delete_legacy != NULL)
 598                         ret = ops->sa_delete_legacy(share);
 599         } else {
 600                 if (proto != NULL)
 601                         ret = SA_NOT_IMPLEMENTED;
 602                 else
 603                         ret = SA_INVALID_PROTOCOL;
 604         }
 605         return (ret);
 606 }
 607 
 608 /*
 609  * sa_proto_delete_section(proto, section)
 610  *
 611  * Remove the specified section from the protocol specific legacy files,
 612  * if supported.
 613  */
 614 
 615 int
 616 sa_proto_delete_section(char *proto, char *section)
 617 {
 618         struct sa_plugin_ops *ops = find_protocol(proto);
 619         int ret = SA_OK;
 620 
 621         if (ops != NULL) {
 622                 if (ops->sa_delete_proto_section != NULL)
 623                         ret = ops->sa_delete_proto_section(section);
 624         } else {
 625                 if (proto != NULL)
 626                         ret = SA_NOT_IMPLEMENTED;
 627                 else
 628                         ret = SA_INVALID_PROTOCOL;
 629         }
 630         return (ret);
 631 }
 632 
 633 /*
 634  * sa_proto_change_notify(share, char *protocol)
 635  *
 636  * Notify the protocol that a change has been made to the share
 637  */
 638 
 639 int
 640 sa_proto_change_notify(sa_share_t share, char *proto)
 641 {
 642         struct sa_plugin_ops *ops = find_protocol(proto);
 643         int ret = SA_NOT_IMPLEMENTED;
 644 
 645         if (ops != NULL) {
 646                 if (ops->sa_change_notify != NULL)
 647                         ret = ops->sa_change_notify(share);
 648         } else  if (proto == NULL) {
 649 
 650                         ret = SA_INVALID_PROTOCOL;
 651         }
 652         return (ret);
 653 }
 654 
 655 /*
 656  * sa_proto_notify_resource(resource, char *protocol)
 657  *
 658  * Notify the protocol that a change has been made to the share
 659  */
 660 
 661 int
 662 sa_proto_notify_resource(sa_resource_t resource, char *proto)
 663 {
 664         struct sa_plugin_ops *ops = find_protocol(proto);
 665         int ret = SA_NOT_IMPLEMENTED;
 666 
 667         if (ops != NULL) {
 668                 if (ops->sa_notify_resource != NULL)
 669                         ret = ops->sa_notify_resource(resource);
 670         } else if (proto == NULL) {
 671                         ret = SA_INVALID_PROTOCOL;
 672         }
 673         return (ret);
 674 }
 675 
 676 /*
 677  * sa_proto_get_featureset(protocol)
 678  *
 679  * Get bitmask of defined features of the protocol. These are
 680  * primarily things like SA_FEATURE_RESOURCE (shares are by resource
 681  * name rather than path) and other operational features that affect
 682  * behavior.
 683  */
 684 
 685 uint64_t
 686 sa_proto_get_featureset(char *proto)
 687 {
 688         struct sa_plugin_ops *ops = find_protocol(proto);
 689         uint64_t ret = 0;
 690 
 691         if (ops != NULL) {
 692                 if (ops->sa_features != NULL)
 693                         ret = ops->sa_features();
 694         }
 695         /* if not implemented, zero is valid */
 696         return (ret);
 697 }
 698 
 699 /*
 700  * sa_proto_get_transients(sa_handle_t)
 701  *
 702  * Called to get any protocol specific transient shares.  NFS doesn't
 703  * use this since the info is in sharetab which is processed as a
 704  * common transient store.
 705  *
 706  * The protocol plugin should verify that the share isn't in the
 707  * repository and then add it as a transient.
 708  *
 709  * Not having an entry is not a problem. It returns 0 in that case.
 710  */
 711 
 712 int
 713 sa_proto_get_transients(sa_handle_t handle, char *proto)
 714 {
 715         struct sa_plugin_ops *ops = find_protocol(proto);
 716         int ret = 0;
 717 
 718         if (ops != NULL) {
 719                 if (ops->sa_get_transient_shares != NULL)
 720                         ret = ops->sa_get_transient_shares(handle);
 721         }
 722         return (ret);
 723 }
 724 
 725 /*
 726  * sa_proto_rename_resource(sa_handle_t, proto, sa_resource_t, newname)
 727  *
 728  * Protocols may need to know when a resource has changed names in
 729  * order to notify clients. This must be done "before" the name in the
 730  * resource has been changed. Not being implemented is not a problem.
 731  */
 732 
 733 int
 734 sa_proto_rename_resource(sa_handle_t handle, char *proto,
 735     sa_resource_t resource, char *newname)
 736 {
 737         struct sa_plugin_ops *ops = find_protocol(proto);
 738         int ret = SA_OK;
 739 
 740         if (ops != NULL) {
 741                 if (ops->sa_rename_resource != NULL)
 742                         ret = ops->sa_rename_resource(handle, resource,
 743                             newname);
 744         }
 745         return (ret);
 746 }