1eda14cbcSMatt Macy /* 2eda14cbcSMatt Macy * CDDL HEADER START 3eda14cbcSMatt Macy * 4eda14cbcSMatt Macy * The contents of this file are subject to the terms of the 5eda14cbcSMatt Macy * Common Development and Distribution License (the "License"). 6eda14cbcSMatt Macy * You may not use this file except in compliance with the License. 7eda14cbcSMatt Macy * 8eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9eda14cbcSMatt Macy * or http://www.opensolaris.org/os/licensing. 10eda14cbcSMatt Macy * See the License for the specific language governing permissions 11eda14cbcSMatt Macy * and limitations under the License. 12eda14cbcSMatt Macy * 13eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each 14eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the 16eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying 17eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner] 18eda14cbcSMatt Macy * 19eda14cbcSMatt Macy * CDDL HEADER END 20eda14cbcSMatt Macy */ 21eda14cbcSMatt Macy 22eda14cbcSMatt Macy /* 23eda14cbcSMatt Macy * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. 24eda14cbcSMatt Macy * Copyright (c) 2011,2012 Turbo Fredriksson <turbo@bayour.com>, based on nfs.c 25eda14cbcSMatt Macy * by Gunnar Beutner 26eda14cbcSMatt Macy * Copyright (c) 2019, 2020 by Delphix. All rights reserved. 27eda14cbcSMatt Macy * 28eda14cbcSMatt Macy * This is an addition to the zfs device driver to add, modify and remove SMB 29eda14cbcSMatt Macy * shares using the 'net share' command that comes with Samba. 30eda14cbcSMatt Macy * 31eda14cbcSMatt Macy * TESTING 32eda14cbcSMatt Macy * Make sure that samba listens to 'localhost' (127.0.0.1) and that the options 33eda14cbcSMatt Macy * 'usershare max shares' and 'usershare owner only' have been reviewed/set 34eda14cbcSMatt Macy * accordingly (see zfs(8) for information). 35eda14cbcSMatt Macy * 36eda14cbcSMatt Macy * Once configuration in samba have been done, test that this 37eda14cbcSMatt Macy * works with the following three commands (in this case, my ZFS 38eda14cbcSMatt Macy * filesystem is called 'share/Test1'): 39eda14cbcSMatt Macy * 40eda14cbcSMatt Macy * (root)# net -U root -S 127.0.0.1 usershare add Test1 /share/Test1 \ 41eda14cbcSMatt Macy * "Comment: /share/Test1" "Everyone:F" 42eda14cbcSMatt Macy * (root)# net usershare list | grep -i test 43eda14cbcSMatt Macy * (root)# net -U root -S 127.0.0.1 usershare delete Test1 44eda14cbcSMatt Macy * 45eda14cbcSMatt Macy * The first command will create a user share that gives everyone full access. 46eda14cbcSMatt Macy * To limit the access below that, use normal UNIX commands (chmod, chown etc). 47eda14cbcSMatt Macy */ 48eda14cbcSMatt Macy 49eda14cbcSMatt Macy #include <time.h> 50eda14cbcSMatt Macy #include <stdlib.h> 51eda14cbcSMatt Macy #include <stdio.h> 52eda14cbcSMatt Macy #include <string.h> 53eda14cbcSMatt Macy #include <fcntl.h> 54eda14cbcSMatt Macy #include <sys/wait.h> 55eda14cbcSMatt Macy #include <unistd.h> 56eda14cbcSMatt Macy #include <dirent.h> 57eda14cbcSMatt Macy #include <sys/types.h> 58eda14cbcSMatt Macy #include <sys/stat.h> 59eda14cbcSMatt Macy #include <libzfs.h> 60eda14cbcSMatt Macy #include <libshare.h> 61eda14cbcSMatt Macy #include "libshare_impl.h" 62eda14cbcSMatt Macy #include "smb.h" 63eda14cbcSMatt Macy 64eda14cbcSMatt Macy static boolean_t smb_available(void); 65eda14cbcSMatt Macy 66*716fd348SMartin Matuska static smb_share_t *smb_shares; 67eda14cbcSMatt Macy static int smb_disable_share(sa_share_impl_t impl_share); 68eda14cbcSMatt Macy static boolean_t smb_is_share_active(sa_share_impl_t impl_share); 69eda14cbcSMatt Macy 70eda14cbcSMatt Macy /* 71eda14cbcSMatt Macy * Retrieve the list of SMB shares. 72eda14cbcSMatt Macy */ 73eda14cbcSMatt Macy static int 74eda14cbcSMatt Macy smb_retrieve_shares(void) 75eda14cbcSMatt Macy { 76eda14cbcSMatt Macy int rc = SA_OK; 77eda14cbcSMatt Macy char file_path[PATH_MAX], line[512], *token, *key, *value; 78eda14cbcSMatt Macy char *dup_value = NULL, *path = NULL, *comment = NULL, *name = NULL; 79eda14cbcSMatt Macy char *guest_ok = NULL; 80eda14cbcSMatt Macy DIR *shares_dir; 81eda14cbcSMatt Macy FILE *share_file_fp = NULL; 82eda14cbcSMatt Macy struct dirent *directory; 83eda14cbcSMatt Macy struct stat eStat; 84eda14cbcSMatt Macy smb_share_t *shares, *new_shares = NULL; 85eda14cbcSMatt Macy 86eda14cbcSMatt Macy /* opendir(), stat() */ 87eda14cbcSMatt Macy shares_dir = opendir(SHARE_DIR); 88eda14cbcSMatt Macy if (shares_dir == NULL) 89eda14cbcSMatt Macy return (SA_SYSTEM_ERR); 90eda14cbcSMatt Macy 91eda14cbcSMatt Macy /* Go through the directory, looking for shares */ 92eda14cbcSMatt Macy while ((directory = readdir(shares_dir))) { 93eda14cbcSMatt Macy if (directory->d_name[0] == '.') 94eda14cbcSMatt Macy continue; 95eda14cbcSMatt Macy 96eda14cbcSMatt Macy snprintf(file_path, sizeof (file_path), 97eda14cbcSMatt Macy "%s/%s", SHARE_DIR, directory->d_name); 98eda14cbcSMatt Macy 99eda14cbcSMatt Macy if (stat(file_path, &eStat) == -1) { 100eda14cbcSMatt Macy rc = SA_SYSTEM_ERR; 101eda14cbcSMatt Macy goto out; 102eda14cbcSMatt Macy } 103eda14cbcSMatt Macy 104eda14cbcSMatt Macy if (!S_ISREG(eStat.st_mode)) 105eda14cbcSMatt Macy continue; 106eda14cbcSMatt Macy 10716038816SMartin Matuska if ((share_file_fp = fopen(file_path, "re")) == NULL) { 108eda14cbcSMatt Macy rc = SA_SYSTEM_ERR; 109eda14cbcSMatt Macy goto out; 110eda14cbcSMatt Macy } 111eda14cbcSMatt Macy 112eda14cbcSMatt Macy name = strdup(directory->d_name); 113eda14cbcSMatt Macy if (name == NULL) { 114eda14cbcSMatt Macy rc = SA_NO_MEMORY; 115eda14cbcSMatt Macy goto out; 116eda14cbcSMatt Macy } 117eda14cbcSMatt Macy 118eda14cbcSMatt Macy while (fgets(line, sizeof (line), share_file_fp)) { 119eda14cbcSMatt Macy if (line[0] == '#') 120eda14cbcSMatt Macy continue; 121eda14cbcSMatt Macy 122eda14cbcSMatt Macy /* Trim trailing new-line character(s). */ 123eda14cbcSMatt Macy while (line[strlen(line) - 1] == '\r' || 124eda14cbcSMatt Macy line[strlen(line) - 1] == '\n') 125eda14cbcSMatt Macy line[strlen(line) - 1] = '\0'; 126eda14cbcSMatt Macy 127eda14cbcSMatt Macy /* Split the line in two, separated by '=' */ 128eda14cbcSMatt Macy token = strchr(line, '='); 129eda14cbcSMatt Macy if (token == NULL) 130eda14cbcSMatt Macy continue; 131eda14cbcSMatt Macy 132eda14cbcSMatt Macy key = line; 133eda14cbcSMatt Macy value = token + 1; 134eda14cbcSMatt Macy *token = '\0'; 135eda14cbcSMatt Macy 136eda14cbcSMatt Macy dup_value = strdup(value); 137eda14cbcSMatt Macy if (dup_value == NULL) { 138eda14cbcSMatt Macy rc = SA_NO_MEMORY; 139eda14cbcSMatt Macy goto out; 140eda14cbcSMatt Macy } 141eda14cbcSMatt Macy 142eda14cbcSMatt Macy if (strcmp(key, "path") == 0) { 143eda14cbcSMatt Macy free(path); 144eda14cbcSMatt Macy path = dup_value; 145eda14cbcSMatt Macy } else if (strcmp(key, "comment") == 0) { 146eda14cbcSMatt Macy free(comment); 147eda14cbcSMatt Macy comment = dup_value; 148eda14cbcSMatt Macy } else if (strcmp(key, "guest_ok") == 0) { 149eda14cbcSMatt Macy free(guest_ok); 150eda14cbcSMatt Macy guest_ok = dup_value; 151eda14cbcSMatt Macy } else 152eda14cbcSMatt Macy free(dup_value); 153eda14cbcSMatt Macy 154eda14cbcSMatt Macy dup_value = NULL; 155eda14cbcSMatt Macy 156eda14cbcSMatt Macy if (path == NULL || comment == NULL || guest_ok == NULL) 157eda14cbcSMatt Macy continue; /* Incomplete share definition */ 158eda14cbcSMatt Macy else { 159eda14cbcSMatt Macy shares = (smb_share_t *) 160eda14cbcSMatt Macy malloc(sizeof (smb_share_t)); 161eda14cbcSMatt Macy if (shares == NULL) { 162eda14cbcSMatt Macy rc = SA_NO_MEMORY; 163eda14cbcSMatt Macy goto out; 164eda14cbcSMatt Macy } 165eda14cbcSMatt Macy 166eda14cbcSMatt Macy (void) strlcpy(shares->name, name, 167eda14cbcSMatt Macy sizeof (shares->name)); 168eda14cbcSMatt Macy 169eda14cbcSMatt Macy (void) strlcpy(shares->path, path, 170eda14cbcSMatt Macy sizeof (shares->path)); 171eda14cbcSMatt Macy 172eda14cbcSMatt Macy (void) strlcpy(shares->comment, comment, 173eda14cbcSMatt Macy sizeof (shares->comment)); 174eda14cbcSMatt Macy 175eda14cbcSMatt Macy shares->guest_ok = atoi(guest_ok); 176eda14cbcSMatt Macy 177eda14cbcSMatt Macy shares->next = new_shares; 178eda14cbcSMatt Macy new_shares = shares; 179eda14cbcSMatt Macy 180eda14cbcSMatt Macy free(path); 181eda14cbcSMatt Macy free(comment); 182eda14cbcSMatt Macy free(guest_ok); 183eda14cbcSMatt Macy 184eda14cbcSMatt Macy path = NULL; 185eda14cbcSMatt Macy comment = NULL; 186eda14cbcSMatt Macy guest_ok = NULL; 187eda14cbcSMatt Macy } 188eda14cbcSMatt Macy } 189eda14cbcSMatt Macy 190eda14cbcSMatt Macy out: 191eda14cbcSMatt Macy if (share_file_fp != NULL) { 192eda14cbcSMatt Macy fclose(share_file_fp); 193eda14cbcSMatt Macy share_file_fp = NULL; 194eda14cbcSMatt Macy } 195eda14cbcSMatt Macy 196eda14cbcSMatt Macy free(name); 197eda14cbcSMatt Macy free(path); 198eda14cbcSMatt Macy free(comment); 199eda14cbcSMatt Macy free(guest_ok); 200eda14cbcSMatt Macy 201eda14cbcSMatt Macy name = NULL; 202eda14cbcSMatt Macy path = NULL; 203eda14cbcSMatt Macy comment = NULL; 204eda14cbcSMatt Macy guest_ok = NULL; 205eda14cbcSMatt Macy } 206eda14cbcSMatt Macy closedir(shares_dir); 207eda14cbcSMatt Macy 208eda14cbcSMatt Macy smb_shares = new_shares; 209eda14cbcSMatt Macy 210eda14cbcSMatt Macy return (rc); 211eda14cbcSMatt Macy } 212eda14cbcSMatt Macy 213eda14cbcSMatt Macy /* 214eda14cbcSMatt Macy * Used internally by smb_enable_share to enable sharing for a single host. 215eda14cbcSMatt Macy */ 216eda14cbcSMatt Macy static int 217eda14cbcSMatt Macy smb_enable_share_one(const char *sharename, const char *sharepath) 218eda14cbcSMatt Macy { 219eda14cbcSMatt Macy char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX]; 220eda14cbcSMatt Macy 221eda14cbcSMatt Macy /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */ 222eda14cbcSMatt Macy strlcpy(name, sharename, sizeof (name)); 223*716fd348SMartin Matuska for (char *itr = name; *itr != '\0'; ++itr) 224*716fd348SMartin Matuska switch (*itr) { 225eda14cbcSMatt Macy case '/': 226eda14cbcSMatt Macy case '-': 227eda14cbcSMatt Macy case ':': 228eda14cbcSMatt Macy case ' ': 229*716fd348SMartin Matuska *itr = '_'; 230eda14cbcSMatt Macy } 231eda14cbcSMatt Macy 232eda14cbcSMatt Macy /* 233eda14cbcSMatt Macy * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \ 234eda14cbcSMatt Macy * "Comment" "Everyone:F" 235eda14cbcSMatt Macy */ 236eda14cbcSMatt Macy snprintf(comment, sizeof (comment), "Comment: %s", sharepath); 237eda14cbcSMatt Macy 238*716fd348SMartin Matuska char *argv[] = { 239*716fd348SMartin Matuska (char *)NET_CMD_PATH, 240*716fd348SMartin Matuska (char *)"-S", 241*716fd348SMartin Matuska (char *)NET_CMD_ARG_HOST, 242*716fd348SMartin Matuska (char *)"usershare", 243*716fd348SMartin Matuska (char *)"add", 244*716fd348SMartin Matuska name, 245*716fd348SMartin Matuska (char *)sharepath, 246*716fd348SMartin Matuska comment, 247*716fd348SMartin Matuska (char *)"Everyone:F", 248*716fd348SMartin Matuska NULL, 249*716fd348SMartin Matuska }; 250eda14cbcSMatt Macy 251*716fd348SMartin Matuska if (libzfs_run_process(argv[0], argv, 0) < 0) 252eda14cbcSMatt Macy return (SA_SYSTEM_ERR); 253eda14cbcSMatt Macy 254eda14cbcSMatt Macy /* Reload the share file */ 255eda14cbcSMatt Macy (void) smb_retrieve_shares(); 256eda14cbcSMatt Macy 257eda14cbcSMatt Macy return (SA_OK); 258eda14cbcSMatt Macy } 259eda14cbcSMatt Macy 260eda14cbcSMatt Macy /* 261eda14cbcSMatt Macy * Enables SMB sharing for the specified share. 262eda14cbcSMatt Macy */ 263eda14cbcSMatt Macy static int 264eda14cbcSMatt Macy smb_enable_share(sa_share_impl_t impl_share) 265eda14cbcSMatt Macy { 266eda14cbcSMatt Macy if (!smb_available()) 267eda14cbcSMatt Macy return (SA_SYSTEM_ERR); 268eda14cbcSMatt Macy 269eda14cbcSMatt Macy if (smb_is_share_active(impl_share)) 270eda14cbcSMatt Macy smb_disable_share(impl_share); 271eda14cbcSMatt Macy 272*716fd348SMartin Matuska if (impl_share->sa_shareopts == NULL) /* on/off */ 273eda14cbcSMatt Macy return (SA_SYSTEM_ERR); 274eda14cbcSMatt Macy 275*716fd348SMartin Matuska if (strcmp(impl_share->sa_shareopts, "off") == 0) 276eda14cbcSMatt Macy return (SA_OK); 277eda14cbcSMatt Macy 278eda14cbcSMatt Macy /* Magic: Enable (i.e., 'create new') share */ 279eda14cbcSMatt Macy return (smb_enable_share_one(impl_share->sa_zfsname, 280eda14cbcSMatt Macy impl_share->sa_mountpoint)); 281eda14cbcSMatt Macy } 282eda14cbcSMatt Macy 283eda14cbcSMatt Macy /* 284eda14cbcSMatt Macy * Used internally by smb_disable_share to disable sharing for a single host. 285eda14cbcSMatt Macy */ 286eda14cbcSMatt Macy static int 287eda14cbcSMatt Macy smb_disable_share_one(const char *sharename) 288eda14cbcSMatt Macy { 289eda14cbcSMatt Macy /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */ 290*716fd348SMartin Matuska char *argv[] = { 291*716fd348SMartin Matuska (char *)NET_CMD_PATH, 292*716fd348SMartin Matuska (char *)"-S", 293*716fd348SMartin Matuska (char *)NET_CMD_ARG_HOST, 294*716fd348SMartin Matuska (char *)"usershare", 295*716fd348SMartin Matuska (char *)"delete", 296*716fd348SMartin Matuska (char *)sharename, 297*716fd348SMartin Matuska NULL, 298*716fd348SMartin Matuska }; 299eda14cbcSMatt Macy 300*716fd348SMartin Matuska if (libzfs_run_process(argv[0], argv, 0) < 0) 301eda14cbcSMatt Macy return (SA_SYSTEM_ERR); 302eda14cbcSMatt Macy else 303eda14cbcSMatt Macy return (SA_OK); 304eda14cbcSMatt Macy } 305eda14cbcSMatt Macy 306eda14cbcSMatt Macy /* 307eda14cbcSMatt Macy * Disables SMB sharing for the specified share. 308eda14cbcSMatt Macy */ 309eda14cbcSMatt Macy static int 310eda14cbcSMatt Macy smb_disable_share(sa_share_impl_t impl_share) 311eda14cbcSMatt Macy { 312eda14cbcSMatt Macy if (!smb_available()) { 313eda14cbcSMatt Macy /* 314eda14cbcSMatt Macy * The share can't possibly be active, so nothing 315eda14cbcSMatt Macy * needs to be done to disable it. 316eda14cbcSMatt Macy */ 317eda14cbcSMatt Macy return (SA_OK); 318eda14cbcSMatt Macy } 319eda14cbcSMatt Macy 320*716fd348SMartin Matuska for (const smb_share_t *i = smb_shares; i != NULL; i = i->next) 321*716fd348SMartin Matuska if (strcmp(impl_share->sa_mountpoint, i->path) == 0) 322*716fd348SMartin Matuska return (smb_disable_share_one(i->name)); 323eda14cbcSMatt Macy 324eda14cbcSMatt Macy return (SA_OK); 325eda14cbcSMatt Macy } 326eda14cbcSMatt Macy 327eda14cbcSMatt Macy /* 328eda14cbcSMatt Macy * Checks whether the specified SMB share options are syntactically correct. 329eda14cbcSMatt Macy */ 330eda14cbcSMatt Macy static int 331eda14cbcSMatt Macy smb_validate_shareopts(const char *shareopts) 332eda14cbcSMatt Macy { 333eda14cbcSMatt Macy /* TODO: Accept 'name' and sec/acl (?) */ 334eda14cbcSMatt Macy if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0)) 335eda14cbcSMatt Macy return (SA_OK); 336eda14cbcSMatt Macy 337eda14cbcSMatt Macy return (SA_SYNTAX_ERR); 338eda14cbcSMatt Macy } 339eda14cbcSMatt Macy 340eda14cbcSMatt Macy /* 341eda14cbcSMatt Macy * Checks whether a share is currently active. 342eda14cbcSMatt Macy */ 343eda14cbcSMatt Macy static boolean_t 344eda14cbcSMatt Macy smb_is_share_active(sa_share_impl_t impl_share) 345eda14cbcSMatt Macy { 346eda14cbcSMatt Macy if (!smb_available()) 347eda14cbcSMatt Macy return (B_FALSE); 348eda14cbcSMatt Macy 349eda14cbcSMatt Macy /* Retrieve the list of (possible) active shares */ 350eda14cbcSMatt Macy smb_retrieve_shares(); 351eda14cbcSMatt Macy 352*716fd348SMartin Matuska for (const smb_share_t *i = smb_shares; i != NULL; i = i->next) 353*716fd348SMartin Matuska if (strcmp(impl_share->sa_mountpoint, i->path) == 0) 354eda14cbcSMatt Macy return (B_TRUE); 355eda14cbcSMatt Macy 356eda14cbcSMatt Macy return (B_FALSE); 357eda14cbcSMatt Macy } 358eda14cbcSMatt Macy 359eda14cbcSMatt Macy static int 360eda14cbcSMatt Macy smb_update_shares(void) 361eda14cbcSMatt Macy { 362eda14cbcSMatt Macy /* Not implemented */ 363eda14cbcSMatt Macy return (0); 364eda14cbcSMatt Macy } 365eda14cbcSMatt Macy 366*716fd348SMartin Matuska const sa_fstype_t libshare_smb_type = { 367eda14cbcSMatt Macy .enable_share = smb_enable_share, 368eda14cbcSMatt Macy .disable_share = smb_disable_share, 369eda14cbcSMatt Macy .is_shared = smb_is_share_active, 370eda14cbcSMatt Macy 371eda14cbcSMatt Macy .validate_shareopts = smb_validate_shareopts, 372eda14cbcSMatt Macy .commit_shares = smb_update_shares, 373eda14cbcSMatt Macy }; 374eda14cbcSMatt Macy 375eda14cbcSMatt Macy /* 376eda14cbcSMatt Macy * Provides a convenient wrapper for determining SMB availability 377eda14cbcSMatt Macy */ 378eda14cbcSMatt Macy static boolean_t 379eda14cbcSMatt Macy smb_available(void) 380eda14cbcSMatt Macy { 381*716fd348SMartin Matuska static int avail; 382*716fd348SMartin Matuska 383*716fd348SMartin Matuska if (!avail) { 384eda14cbcSMatt Macy struct stat statbuf; 385eda14cbcSMatt Macy 386*716fd348SMartin Matuska if (access(NET_CMD_PATH, F_OK) != 0 || 387*716fd348SMartin Matuska lstat(SHARE_DIR, &statbuf) != 0 || 388eda14cbcSMatt Macy !S_ISDIR(statbuf.st_mode)) 389*716fd348SMartin Matuska avail = -1; 390*716fd348SMartin Matuska else 391*716fd348SMartin Matuska avail = 1; 392eda14cbcSMatt Macy } 393eda14cbcSMatt Macy 394*716fd348SMartin Matuska return (avail == 1); 395eda14cbcSMatt Macy } 396