1*3034Sdougm /* 2*3034Sdougm * CDDL HEADER START 3*3034Sdougm * 4*3034Sdougm * The contents of this file are subject to the terms of the 5*3034Sdougm * Common Development and Distribution License (the "License"). 6*3034Sdougm * You may not use this file except in compliance with the License. 7*3034Sdougm * 8*3034Sdougm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*3034Sdougm * or http://www.opensolaris.org/os/licensing. 10*3034Sdougm * See the License for the specific language governing permissions 11*3034Sdougm * and limitations under the License. 12*3034Sdougm * 13*3034Sdougm * When distributing Covered Code, include this CDDL HEADER in each 14*3034Sdougm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*3034Sdougm * If applicable, add the following below this CDDL HEADER, with the 16*3034Sdougm * fields enclosed by brackets "[]" replaced with your own identifying 17*3034Sdougm * information: Portions Copyright [yyyy] [name of copyright owner] 18*3034Sdougm * 19*3034Sdougm * CDDL HEADER END 20*3034Sdougm */ 21*3034Sdougm 22*3034Sdougm /* 23*3034Sdougm * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*3034Sdougm * Use is subject to license terms. 25*3034Sdougm */ 26*3034Sdougm 27*3034Sdougm #pragma ident "%Z%%M% %I% %E% SMI" 28*3034Sdougm 29*3034Sdougm #include "libfsmgt.h" 30*3034Sdougm #include <libzfs.h> 31*3034Sdougm #include <string.h> 32*3034Sdougm #include <libshare.h> 33*3034Sdougm #include "libshare_impl.h" 34*3034Sdougm 35*3034Sdougm extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *); 36*3034Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *); 37*3034Sdougm extern char *sa_fstype(char *); 38*3034Sdougm extern void set_node_attr(void *, char *, char *); 39*3034Sdougm extern int sa_is_share(void *); 40*3034Sdougm /* 41*3034Sdougm * File system specific code for ZFS 42*3034Sdougm */ 43*3034Sdougm 44*3034Sdougm /* 45*3034Sdougm * get_zfs_dataset(path) 46*3034Sdougm * 47*3034Sdougm * get the name of the ZFS dataset the path is equivalent to. The 48*3034Sdougm * dataset name is used for get/set of ZFS properties since libzfs 49*3034Sdougm * requires a dataset to do a zfs_open(). 50*3034Sdougm */ 51*3034Sdougm 52*3034Sdougm static char * 53*3034Sdougm get_zfs_dataset(char *path) 54*3034Sdougm { 55*3034Sdougm fs_mntlist_t *list; 56*3034Sdougm fs_mntlist_t *cur; 57*3034Sdougm int err; 58*3034Sdougm char *dataset = NULL; 59*3034Sdougm 60*3034Sdougm list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL, 61*3034Sdougm NULL, 0, &err); 62*3034Sdougm for (cur = list; cur != NULL; cur = cur->next) { 63*3034Sdougm if (strcmp(path, cur->mountp) == 0 || 64*3034Sdougm strncmp(path, cur->mountp, strlen(cur->mountp)) == 0) { 65*3034Sdougm /* 66*3034Sdougm * we want the longest resource so keep trying. This 67*3034Sdougm * check avoids dropping out on a partial match. ZFS 68*3034Sdougm * resources are ordered when mounted in order to 69*3034Sdougm * ensure inheritence of properties. 70*3034Sdougm */ 71*3034Sdougm dataset = cur->resource; 72*3034Sdougm } 73*3034Sdougm } 74*3034Sdougm if (dataset != NULL) { 75*3034Sdougm dataset = strdup(dataset); 76*3034Sdougm } 77*3034Sdougm fs_free_mount_list(list); 78*3034Sdougm return (dataset); 79*3034Sdougm } 80*3034Sdougm 81*3034Sdougm /* 82*3034Sdougm * get_zfs_property(dataset, property) 83*3034Sdougm * 84*3034Sdougm * Get the file system property specified from the ZFS dataset. 85*3034Sdougm */ 86*3034Sdougm 87*3034Sdougm static char * 88*3034Sdougm get_zfs_property(char *dataset, zfs_prop_t property) 89*3034Sdougm { 90*3034Sdougm zfs_handle_t *handle = NULL; 91*3034Sdougm char shareopts[ZFS_MAXPROPLEN]; 92*3034Sdougm libzfs_handle_t *libhandle; 93*3034Sdougm 94*3034Sdougm libhandle = libzfs_init(); 95*3034Sdougm if (libhandle != NULL) { 96*3034Sdougm handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM); 97*3034Sdougm if (handle != NULL) { 98*3034Sdougm if (zfs_prop_get(handle, property, shareopts, 99*3034Sdougm sizeof (shareopts), NULL, NULL, 0, 100*3034Sdougm FALSE) == 0) { 101*3034Sdougm zfs_close(handle); 102*3034Sdougm libzfs_fini(libhandle); 103*3034Sdougm return (strdup(shareopts)); 104*3034Sdougm } 105*3034Sdougm zfs_close(handle); 106*3034Sdougm } 107*3034Sdougm libzfs_fini(libhandle); 108*3034Sdougm } 109*3034Sdougm return (NULL); 110*3034Sdougm } 111*3034Sdougm 112*3034Sdougm /* 113*3034Sdougm * sa_zfs_is_shared(path) 114*3034Sdougm * 115*3034Sdougm * Check to see if the ZFS path provided has the sharenfs option set 116*3034Sdougm * or not. 117*3034Sdougm */ 118*3034Sdougm 119*3034Sdougm int 120*3034Sdougm sa_zfs_is_shared(char *path) 121*3034Sdougm { 122*3034Sdougm int ret = 0; 123*3034Sdougm char *dataset; 124*3034Sdougm zfs_handle_t *handle = NULL; 125*3034Sdougm char shareopts[ZFS_MAXPROPLEN]; 126*3034Sdougm libzfs_handle_t *libhandle; 127*3034Sdougm 128*3034Sdougm dataset = get_zfs_dataset(path); 129*3034Sdougm if (dataset != NULL) { 130*3034Sdougm libhandle = libzfs_init(); 131*3034Sdougm if (libhandle != NULL) { 132*3034Sdougm handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM); 133*3034Sdougm if (handle != NULL) { 134*3034Sdougm if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts, 135*3034Sdougm sizeof (shareopts), NULL, NULL, 0, 136*3034Sdougm FALSE) == 0 && 137*3034Sdougm strcmp(shareopts, "off") != 0) 138*3034Sdougm ret = 1; /* it is shared */ 139*3034Sdougm zfs_close(handle); 140*3034Sdougm } 141*3034Sdougm libzfs_fini(libhandle); 142*3034Sdougm } 143*3034Sdougm free(dataset); 144*3034Sdougm } 145*3034Sdougm return (ret); 146*3034Sdougm } 147*3034Sdougm 148*3034Sdougm /* 149*3034Sdougm * find_or_create_group(groupname, proto, *err) 150*3034Sdougm * 151*3034Sdougm * While walking the ZFS tree, we need to add shares to a defined 152*3034Sdougm * group. If the group doesn't exist, create it first, making sure it 153*3034Sdougm * is marked as a ZFS group. 154*3034Sdougm * 155*3034Sdougm * Not that all ZFS shares are in a subgroup of the top level group 156*3034Sdougm * "zfs". 157*3034Sdougm */ 158*3034Sdougm 159*3034Sdougm static sa_group_t 160*3034Sdougm find_or_create_group(char *groupname, char *proto, int *err) 161*3034Sdougm { 162*3034Sdougm sa_group_t group; 163*3034Sdougm sa_optionset_t optionset; 164*3034Sdougm int ret = SA_OK; 165*3034Sdougm 166*3034Sdougm /* 167*3034Sdougm * we check to see if the "zfs" group exists. Since this 168*3034Sdougm * should be the top level group, we don't want the 169*3034Sdougm * parent. This is to make sure the zfs group has been created 170*3034Sdougm * and to created if it hasn't been. 171*3034Sdougm */ 172*3034Sdougm group = sa_get_group(groupname); 173*3034Sdougm if (group == NULL) { 174*3034Sdougm group = sa_create_group(groupname, &ret); 175*3034Sdougm if (group != NULL) 176*3034Sdougm ret = sa_set_group_attr(group, "zfs", "true"); 177*3034Sdougm } 178*3034Sdougm if (group != NULL) { 179*3034Sdougm if (proto != NULL) { 180*3034Sdougm optionset = sa_get_optionset(group, proto); 181*3034Sdougm if (optionset == NULL) { 182*3034Sdougm optionset = sa_create_optionset(group, proto); 183*3034Sdougm } else { 184*3034Sdougm char **protolist; 185*3034Sdougm int numprotos, i; 186*3034Sdougm numprotos = sa_get_protocols(&protolist); 187*3034Sdougm for (i = 0; i < numprotos; i++) { 188*3034Sdougm optionset = sa_create_optionset(group, protolist[i]); 189*3034Sdougm } 190*3034Sdougm if (protolist != NULL) 191*3034Sdougm free(protolist); 192*3034Sdougm } 193*3034Sdougm } 194*3034Sdougm } 195*3034Sdougm if (err != NULL) 196*3034Sdougm *err = ret; 197*3034Sdougm return (group); 198*3034Sdougm } 199*3034Sdougm 200*3034Sdougm /* 201*3034Sdougm * sa_get_zfs_shares(groupname) 202*3034Sdougm * 203*3034Sdougm * Walk the mnttab for all zfs mounts and determine which are 204*3034Sdougm * shared. Find or create the appropriate group/sub-group to contain 205*3034Sdougm * the shares. 206*3034Sdougm * 207*3034Sdougm * All shares are in a sub-group that will hold the properties. This 208*3034Sdougm * allows representing the inherited property model. 209*3034Sdougm */ 210*3034Sdougm 211*3034Sdougm int 212*3034Sdougm sa_get_zfs_shares(char *groupname) 213*3034Sdougm { 214*3034Sdougm sa_group_t group; 215*3034Sdougm sa_group_t zfsgroup; 216*3034Sdougm int legacy = 0; 217*3034Sdougm int err; 218*3034Sdougm fs_mntlist_t *list; 219*3034Sdougm fs_mntlist_t *cur; 220*3034Sdougm zfs_handle_t *handle = NULL; 221*3034Sdougm char shareopts[ZFS_MAXPROPLEN]; 222*3034Sdougm sa_share_t share; 223*3034Sdougm zfs_source_t source; 224*3034Sdougm char sourcestr[ZFS_MAXPROPLEN]; 225*3034Sdougm libzfs_handle_t *libhandle; 226*3034Sdougm 227*3034Sdougm /* 228*3034Sdougm * if we can't access libzfs, don't bother doing anything. 229*3034Sdougm */ 230*3034Sdougm libhandle = libzfs_init(); 231*3034Sdougm if (libhandle == NULL) 232*3034Sdougm return (SA_SYSTEM_ERR); 233*3034Sdougm 234*3034Sdougm zfsgroup = find_or_create_group(groupname, "nfs", &err); 235*3034Sdougm if (zfsgroup != NULL) { 236*3034Sdougm /* 237*3034Sdougm * need to walk the mounted ZFS pools and datasets to 238*3034Sdougm * find shares that are possible. 239*3034Sdougm */ 240*3034Sdougm list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL, 241*3034Sdougm NULL, 0, &err); 242*3034Sdougm group = zfsgroup; 243*3034Sdougm for (cur = list; cur != NULL; cur = cur->next) { 244*3034Sdougm handle = zfs_open(libhandle, cur->resource, 245*3034Sdougm ZFS_TYPE_FILESYSTEM); 246*3034Sdougm if (handle != NULL) { 247*3034Sdougm source = ZFS_SRC_ALL; 248*3034Sdougm if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts, 249*3034Sdougm sizeof (shareopts), &source, sourcestr, 250*3034Sdougm ZFS_MAXPROPLEN, 251*3034Sdougm FALSE) == 0 && 252*3034Sdougm strcmp(shareopts, "off") != 0) { 253*3034Sdougm /* it is shared so add to list */ 254*3034Sdougm share = sa_find_share(cur->mountp); 255*3034Sdougm err = SA_OK; 256*3034Sdougm if (share != NULL) { 257*3034Sdougm /* 258*3034Sdougm * A zfs file system had been shared 259*3034Sdougm * through tradiditional methods 260*3034Sdougm * (share/dfstab or added to a non-zfs 261*3034Sdougm * group. Now it has been added to a 262*3034Sdougm * ZFS group via the zfs 263*3034Sdougm * command. Remove from previous 264*3034Sdougm * config and setup with current 265*3034Sdougm * options. 266*3034Sdougm */ 267*3034Sdougm err = sa_remove_share(share); 268*3034Sdougm share = NULL; 269*3034Sdougm } 270*3034Sdougm if (err == SA_OK) { 271*3034Sdougm if (source & ZFS_SRC_INHERITED) { 272*3034Sdougm share = _sa_add_share(group, cur->mountp, 273*3034Sdougm SA_SHARE_TRANSIENT, 274*3034Sdougm &err); 275*3034Sdougm } else { 276*3034Sdougm group = _sa_create_zfs_group(zfsgroup, 277*3034Sdougm cur->resource); 278*3034Sdougm set_node_attr(group, "zfs", "true"); 279*3034Sdougm share = _sa_add_share(group, cur->mountp, 280*3034Sdougm SA_SHARE_TRANSIENT, 281*3034Sdougm &err); 282*3034Sdougm if (err == SA_OK) { 283*3034Sdougm char *options; 284*3034Sdougm if (strcmp(shareopts, "on") != 0) { 285*3034Sdougm options = strdup(shareopts); 286*3034Sdougm if (options != NULL) { 287*3034Sdougm err = sa_parse_legacy_options(group, 288*3034Sdougm options, 289*3034Sdougm "nfs"); 290*3034Sdougm free(options); 291*3034Sdougm } 292*3034Sdougm /* unmark the share's changed state */ 293*3034Sdougm set_node_attr(share, "changed", NULL); 294*3034Sdougm } 295*3034Sdougm } 296*3034Sdougm } 297*3034Sdougm } 298*3034Sdougm } 299*3034Sdougm } 300*3034Sdougm } 301*3034Sdougm if (list != NULL) 302*3034Sdougm fs_free_mount_list(list); 303*3034Sdougm } 304*3034Sdougm if (libhandle != NULL) 305*3034Sdougm libzfs_fini(libhandle); 306*3034Sdougm return (legacy); 307*3034Sdougm } 308*3034Sdougm 309*3034Sdougm #define COMMAND "/usr/sbin/zfs" 310*3034Sdougm 311*3034Sdougm /* 312*3034Sdougm * sa_zfs_set_sharenfs(group, path, on) 313*3034Sdougm * 314*3034Sdougm * Update the "sharenfs" property on the path. If on is true, then set 315*3034Sdougm * to the properties on the group or "on" if no properties are 316*3034Sdougm * defined. Set to "off" if on is false. 317*3034Sdougm */ 318*3034Sdougm 319*3034Sdougm int 320*3034Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on) 321*3034Sdougm { 322*3034Sdougm int ret = SA_NOT_IMPLEMENTED; 323*3034Sdougm char *command; 324*3034Sdougm 325*3034Sdougm command = malloc(ZFS_MAXPROPLEN * 2); 326*3034Sdougm if (command != NULL) { 327*3034Sdougm char *opts = NULL; 328*3034Sdougm char *dataset; 329*3034Sdougm FILE *pfile; 330*3034Sdougm /* for now, NFS is always available for "zfs" */ 331*3034Sdougm if (on) { 332*3034Sdougm opts = sa_proto_legacy_format("nfs", group, 1); 333*3034Sdougm if (opts != NULL && strlen(opts) == 0) { 334*3034Sdougm free(opts); 335*3034Sdougm opts = strdup("on"); 336*3034Sdougm } 337*3034Sdougm } 338*3034Sdougm dataset = get_zfs_dataset(path); 339*3034Sdougm if (dataset != NULL) { 340*3034Sdougm (void) snprintf(command, ZFS_MAXPROPLEN * 2, 341*3034Sdougm "%s set sharenfs=\"%s\" %s", COMMAND, 342*3034Sdougm opts != NULL ? opts : "off", 343*3034Sdougm dataset); 344*3034Sdougm pfile = popen(command, "r"); 345*3034Sdougm if (pfile != NULL) { 346*3034Sdougm ret = pclose(pfile); 347*3034Sdougm if (ret != 0) 348*3034Sdougm ret = SA_SYSTEM_ERR; 349*3034Sdougm } 350*3034Sdougm } 351*3034Sdougm if (opts != NULL) 352*3034Sdougm free(opts); 353*3034Sdougm if (dataset != NULL) 354*3034Sdougm free(dataset); 355*3034Sdougm free(command); 356*3034Sdougm } 357*3034Sdougm return (ret); 358*3034Sdougm } 359*3034Sdougm 360*3034Sdougm /* 361*3034Sdougm * sa_zfs_update(group) 362*3034Sdougm * 363*3034Sdougm * call back to ZFS to update the share if necessary. 364*3034Sdougm * Don't do it if it isn't a real change. 365*3034Sdougm */ 366*3034Sdougm int 367*3034Sdougm sa_zfs_update(sa_group_t group) 368*3034Sdougm { 369*3034Sdougm sa_optionset_t protopt; 370*3034Sdougm sa_group_t parent; 371*3034Sdougm char *command; 372*3034Sdougm char *optstring; 373*3034Sdougm int ret = SA_OK; 374*3034Sdougm int doupdate = 0; 375*3034Sdougm FILE *pfile; 376*3034Sdougm 377*3034Sdougm if (sa_is_share(group)) 378*3034Sdougm parent = sa_get_parent_group(group); 379*3034Sdougm else 380*3034Sdougm parent = group; 381*3034Sdougm 382*3034Sdougm if (parent != NULL) { 383*3034Sdougm command = malloc(ZFS_MAXPROPLEN * 2); 384*3034Sdougm if (command == NULL) 385*3034Sdougm return (SA_NO_MEMORY); 386*3034Sdougm 387*3034Sdougm *command = '\0'; 388*3034Sdougm for (protopt = sa_get_optionset(parent, NULL); protopt != NULL; 389*3034Sdougm protopt = sa_get_next_optionset(protopt)) { 390*3034Sdougm 391*3034Sdougm char *proto = sa_get_optionset_attr(protopt, "type"); 392*3034Sdougm char *path; 393*3034Sdougm char *dataset = NULL; 394*3034Sdougm char *zfsopts = NULL; 395*3034Sdougm 396*3034Sdougm if (sa_is_share(group)) { 397*3034Sdougm path = sa_get_share_attr((sa_share_t)group, "path"); 398*3034Sdougm if (path != NULL) { 399*3034Sdougm dataset = get_zfs_dataset(path); 400*3034Sdougm sa_free_attr_string(path); 401*3034Sdougm } 402*3034Sdougm } else { 403*3034Sdougm dataset = sa_get_group_attr(group, "name"); 404*3034Sdougm } 405*3034Sdougm /* update only when there is an optstring found */ 406*3034Sdougm doupdate = 0; 407*3034Sdougm if (proto != NULL && dataset != NULL) { 408*3034Sdougm optstring = sa_proto_legacy_format(proto, group, 1); 409*3034Sdougm zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS); 410*3034Sdougm 411*3034Sdougm if (optstring != NULL && zfsopts != NULL) { 412*3034Sdougm if (strcmp(optstring, zfsopts) != 0) 413*3034Sdougm doupdate++; 414*3034Sdougm } 415*3034Sdougm 416*3034Sdougm if (doupdate) { 417*3034Sdougm if (optstring != NULL && strlen(optstring) > 0) { 418*3034Sdougm (void) snprintf(command, ZFS_MAXPROPLEN * 2, 419*3034Sdougm "%s set sharenfs=%s %s", COMMAND, 420*3034Sdougm optstring, dataset); 421*3034Sdougm } else { 422*3034Sdougm (void) snprintf(command, ZFS_MAXPROPLEN * 2, 423*3034Sdougm "%s set sharenfs=on %s", COMMAND, 424*3034Sdougm dataset); 425*3034Sdougm } 426*3034Sdougm pfile = popen(command, "r"); 427*3034Sdougm if (pfile != NULL) 428*3034Sdougm ret = pclose(pfile); 429*3034Sdougm switch (ret) { 430*3034Sdougm default: 431*3034Sdougm case 1: 432*3034Sdougm ret = SA_SYSTEM_ERR; 433*3034Sdougm break; 434*3034Sdougm case 2: 435*3034Sdougm ret = SA_SYNTAX_ERR; 436*3034Sdougm break; 437*3034Sdougm case 0: 438*3034Sdougm break; 439*3034Sdougm } 440*3034Sdougm } 441*3034Sdougm if (optstring != NULL) { 442*3034Sdougm free(optstring); 443*3034Sdougm } 444*3034Sdougm if (zfsopts != NULL) 445*3034Sdougm free(zfsopts); 446*3034Sdougm } 447*3034Sdougm if (proto != NULL) 448*3034Sdougm sa_free_attr_string(proto); 449*3034Sdougm if (dataset != NULL) 450*3034Sdougm free(dataset); 451*3034Sdougm } 452*3034Sdougm free(command); 453*3034Sdougm } 454*3034Sdougm return (ret); 455*3034Sdougm } 456*3034Sdougm 457*3034Sdougm /* 458*3034Sdougm * sa_group_is_zfs(group) 459*3034Sdougm * 460*3034Sdougm * Given the group, determine if the zfs attribute is set. 461*3034Sdougm */ 462*3034Sdougm 463*3034Sdougm int 464*3034Sdougm sa_group_is_zfs(sa_group_t group) 465*3034Sdougm { 466*3034Sdougm char *zfs; 467*3034Sdougm int ret = 0; 468*3034Sdougm 469*3034Sdougm zfs = sa_get_group_attr(group, "zfs"); 470*3034Sdougm if (zfs != NULL) { 471*3034Sdougm ret = 1; 472*3034Sdougm sa_free_attr_string(zfs); 473*3034Sdougm } 474*3034Sdougm return (ret); 475*3034Sdougm } 476*3034Sdougm 477*3034Sdougm /* 478*3034Sdougm * sa_path_is_zfs(path) 479*3034Sdougm * 480*3034Sdougm * Check to see if the file system path represents is of type "zfs". 481*3034Sdougm */ 482*3034Sdougm 483*3034Sdougm int 484*3034Sdougm sa_path_is_zfs(char *path) 485*3034Sdougm { 486*3034Sdougm char *fstype; 487*3034Sdougm int ret = 0; 488*3034Sdougm 489*3034Sdougm fstype = sa_fstype(path); 490*3034Sdougm if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 491*3034Sdougm ret = 1; 492*3034Sdougm } 493*3034Sdougm if (fstype != NULL) 494*3034Sdougm sa_free_fstype(fstype); 495*3034Sdougm return (ret); 496*3034Sdougm } 497