diff --git a/lib/libshare/os/linux/smb.c b/lib/libshare/os/linux/smb.c index 157b19aa85f4..8eb1894de531 100644 --- a/lib/libshare/os/linux/smb.c +++ b/lib/libshare/os/linux/smb.c @@ -1,395 +1,406 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011,2012 Turbo Fredriksson , based on nfs.c * by Gunnar Beutner * Copyright (c) 2019, 2020 by Delphix. All rights reserved. * * This is an addition to the zfs device driver to add, modify and remove SMB * shares using the 'net share' command that comes with Samba. * * TESTING * Make sure that samba listens to 'localhost' (127.0.0.1) and that the options * 'usershare max shares' and 'usershare owner only' have been reviewed/set * accordingly (see zfs(8) for information). * * Once configuration in samba have been done, test that this * works with the following three commands (in this case, my ZFS * filesystem is called 'share/Test1'): * * (root)# net -U root -S 127.0.0.1 usershare add Test1 /share/Test1 \ * "Comment: /share/Test1" "Everyone:F" * (root)# net usershare list | grep -i test * (root)# net -U root -S 127.0.0.1 usershare delete Test1 * * The first command will create a user share that gives everyone full access. * To limit the access below that, use normal UNIX commands (chmod, chown etc). */ #include #include #include #include #include #include #include #include #include #include #include #include #include "libshare_impl.h" #include "smb.h" static boolean_t smb_available(void); static smb_share_t *smb_shares; static int smb_disable_share(sa_share_impl_t impl_share); static boolean_t smb_is_share_active(sa_share_impl_t impl_share); /* * Retrieve the list of SMB shares. */ static int smb_retrieve_shares(void) { int rc = SA_OK; char file_path[PATH_MAX], line[512], *token, *key, *value; char *dup_value = NULL, *path = NULL, *comment = NULL, *name = NULL; char *guest_ok = NULL; DIR *shares_dir; FILE *share_file_fp = NULL; struct dirent *directory; struct stat eStat; smb_share_t *shares, *new_shares = NULL; /* opendir(), stat() */ shares_dir = opendir(SHARE_DIR); if (shares_dir == NULL) return (SA_SYSTEM_ERR); /* Go through the directory, looking for shares */ while ((directory = readdir(shares_dir))) { + int fd; + if (directory->d_name[0] == '.') continue; snprintf(file_path, sizeof (file_path), "%s/%s", SHARE_DIR, directory->d_name); + if ((fd = open(file_path, O_RDONLY | O_CLOEXEC)) == -1) { + rc = SA_SYSTEM_ERR; + goto out; + } + if (stat(file_path, &eStat) == -1) { + close(fd); rc = SA_SYSTEM_ERR; goto out; } - if (!S_ISREG(eStat.st_mode)) + if (!S_ISREG(eStat.st_mode)) { + close(fd); continue; + } - if ((share_file_fp = fopen(file_path, "re")) == NULL) { + if ((share_file_fp = fdopen(fd, "r")) == NULL) { + close(fd); rc = SA_SYSTEM_ERR; goto out; } name = strdup(directory->d_name); if (name == NULL) { rc = SA_NO_MEMORY; goto out; } while (fgets(line, sizeof (line), share_file_fp)) { if (line[0] == '#') continue; /* Trim trailing new-line character(s). */ while (line[strlen(line) - 1] == '\r' || line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; /* Split the line in two, separated by '=' */ token = strchr(line, '='); if (token == NULL) continue; key = line; value = token + 1; *token = '\0'; dup_value = strdup(value); if (dup_value == NULL) { rc = SA_NO_MEMORY; goto out; } if (strcmp(key, "path") == 0) { free(path); path = dup_value; } else if (strcmp(key, "comment") == 0) { free(comment); comment = dup_value; } else if (strcmp(key, "guest_ok") == 0) { free(guest_ok); guest_ok = dup_value; } else free(dup_value); dup_value = NULL; if (path == NULL || comment == NULL || guest_ok == NULL) continue; /* Incomplete share definition */ else { shares = (smb_share_t *) malloc(sizeof (smb_share_t)); if (shares == NULL) { rc = SA_NO_MEMORY; goto out; } (void) strlcpy(shares->name, name, sizeof (shares->name)); (void) strlcpy(shares->path, path, sizeof (shares->path)); (void) strlcpy(shares->comment, comment, sizeof (shares->comment)); shares->guest_ok = atoi(guest_ok); shares->next = new_shares; new_shares = shares; free(path); free(comment); free(guest_ok); path = NULL; comment = NULL; guest_ok = NULL; } } out: if (share_file_fp != NULL) { fclose(share_file_fp); share_file_fp = NULL; } free(name); free(path); free(comment); free(guest_ok); name = NULL; path = NULL; comment = NULL; guest_ok = NULL; } closedir(shares_dir); smb_shares = new_shares; return (rc); } /* * Used internally by smb_enable_share to enable sharing for a single host. */ static int smb_enable_share_one(const char *sharename, const char *sharepath) { char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX]; /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */ strlcpy(name, sharename, sizeof (name)); for (char *itr = name; *itr != '\0'; ++itr) switch (*itr) { case '/': case '-': case ':': case ' ': *itr = '_'; } /* * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \ * "Comment" "Everyone:F" */ snprintf(comment, sizeof (comment), "Comment: %s", sharepath); char *argv[] = { (char *)NET_CMD_PATH, (char *)"-S", (char *)NET_CMD_ARG_HOST, (char *)"usershare", (char *)"add", name, (char *)sharepath, comment, (char *)"Everyone:F", NULL, }; if (libzfs_run_process(argv[0], argv, 0) != 0) return (SA_SYSTEM_ERR); /* Reload the share file */ (void) smb_retrieve_shares(); return (SA_OK); } /* * Enables SMB sharing for the specified share. */ static int smb_enable_share(sa_share_impl_t impl_share) { if (!smb_available()) return (SA_SYSTEM_ERR); if (smb_is_share_active(impl_share)) smb_disable_share(impl_share); if (impl_share->sa_shareopts == NULL) /* on/off */ return (SA_SYSTEM_ERR); if (strcmp(impl_share->sa_shareopts, "off") == 0) return (SA_OK); /* Magic: Enable (i.e., 'create new') share */ return (smb_enable_share_one(impl_share->sa_zfsname, impl_share->sa_mountpoint)); } /* * Used internally by smb_disable_share to disable sharing for a single host. */ static int smb_disable_share_one(const char *sharename) { /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */ char *argv[] = { (char *)NET_CMD_PATH, (char *)"-S", (char *)NET_CMD_ARG_HOST, (char *)"usershare", (char *)"delete", (char *)sharename, NULL, }; if (libzfs_run_process(argv[0], argv, 0) != 0) return (SA_SYSTEM_ERR); else return (SA_OK); } /* * Disables SMB sharing for the specified share. */ static int smb_disable_share(sa_share_impl_t impl_share) { if (!smb_available()) { /* * The share can't possibly be active, so nothing * needs to be done to disable it. */ return (SA_OK); } for (const smb_share_t *i = smb_shares; i != NULL; i = i->next) if (strcmp(impl_share->sa_mountpoint, i->path) == 0) return (smb_disable_share_one(i->name)); return (SA_OK); } /* * Checks whether the specified SMB share options are syntactically correct. */ static int smb_validate_shareopts(const char *shareopts) { /* TODO: Accept 'name' and sec/acl (?) */ if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0)) return (SA_OK); return (SA_SYNTAX_ERR); } /* * Checks whether a share is currently active. */ static boolean_t smb_is_share_active(sa_share_impl_t impl_share) { if (!smb_available()) return (B_FALSE); /* Retrieve the list of (possible) active shares */ smb_retrieve_shares(); for (const smb_share_t *i = smb_shares; i != NULL; i = i->next) if (strcmp(impl_share->sa_mountpoint, i->path) == 0) return (B_TRUE); return (B_FALSE); } static int smb_update_shares(void) { /* Not implemented */ return (0); } const sa_fstype_t libshare_smb_type = { .enable_share = smb_enable_share, .disable_share = smb_disable_share, .is_shared = smb_is_share_active, .validate_shareopts = smb_validate_shareopts, .commit_shares = smb_update_shares, }; /* * Provides a convenient wrapper for determining SMB availability */ static boolean_t smb_available(void) { static int avail; if (!avail) { struct stat statbuf; if (access(NET_CMD_PATH, F_OK) != 0 || lstat(SHARE_DIR, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) avail = -1; else avail = 1; } return (avail == 1); } diff --git a/tests/zfs-tests/cmd/mmapwrite.c b/tests/zfs-tests/cmd/mmapwrite.c index ca55d730fd34..a18609898485 100644 --- a/tests/zfs-tests/cmd/mmapwrite.c +++ b/tests/zfs-tests/cmd/mmapwrite.c @@ -1,161 +1,153 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include /* * -------------------------------------------------------------------- * Bug Issue Id: #7512 * The bug time sequence: * 1. context #1, zfs_write assign a txg "n". * 2. In the same process, context #2, mmap page fault (which means the mm_sem * is hold) occurred, zfs_dirty_inode open a txg failed, and wait previous * txg "n" completed. * 3. context #1 call zfs_uiomove to write, however page fault is occurred in * zfs_uiomove, which means it needs mm_sem, but mm_sem is hold by * context #2, so it stuck and can't complete, then txg "n" will not * complete. * * So context #1 and context #2 trap into the "dead lock". * -------------------------------------------------------------------- */ #define NORMAL_WRITE_TH_NUM 2 static void * normal_writer(void *filename) { char *file_path = filename; int fd = -1; ssize_t write_num = 0; int page_size = getpagesize(); fd = open(file_path, O_RDWR | O_CREAT, 0777); if (fd == -1) { err(1, "failed to open %s", file_path); } char buf = 'z'; while (1) { write_num = write(fd, &buf, 1); if (write_num == 0) { err(1, "write failed!"); break; } if (lseek(fd, page_size, SEEK_CUR) == -1) { err(1, "lseek failed on %s: %s", file_path, strerror(errno)); break; } } } static void * map_writer(void *filename) { int fd = -1; int ret = 0; char *buf = NULL; int page_size = getpagesize(); - int op_errno = 0; char *file_path = filename; while (1) { - ret = access(file_path, F_OK); - if (ret) { - op_errno = errno; - if (op_errno == ENOENT) { + fd = open(file_path, O_RDWR); + if (fd == -1) { + if (errno == ENOENT) { fd = open(file_path, O_RDWR | O_CREAT, 0777); if (fd == -1) { err(1, "open file failed"); } - ret = ftruncate(fd, page_size); if (ret == -1) { err(1, "truncate file failed"); } } else { - err(1, "access file failed!"); - } - } else { - fd = open(file_path, O_RDWR, 0777); - if (fd == -1) { err(1, "open file failed"); } } if ((buf = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { err(1, "map file failed"); } if (fd != -1) close(fd); char s[10] = {0, }; memcpy(buf, s, 10); ret = munmap(buf, page_size); if (ret != 0) { err(1, "unmap file failed"); } } } int main(int argc, char **argv) { pthread_t map_write_tid; pthread_t normal_write_tid[NORMAL_WRITE_TH_NUM]; int i = 0; if (argc != 3) { (void) printf("usage: %s " "\n", argv[0]); exit(1); } for (i = 0; i < NORMAL_WRITE_TH_NUM; i++) { if (pthread_create(&normal_write_tid[i], NULL, normal_writer, argv[1])) { err(1, "pthread_create normal_writer failed."); } } if (pthread_create(&map_write_tid, NULL, map_writer, argv[2])) { err(1, "pthread_create map_writer failed."); } pthread_join(map_write_tid, NULL); return (0); } diff --git a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c index 424231d112b2..906b81b4d9b3 100644 --- a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c +++ b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c @@ -1,84 +1,81 @@ #include #include #include #include #include #include #include #include #include /* backward compat in case it's not defined */ #ifndef O_TMPFILE #define O_TMPFILE (020000000|O_DIRECTORY) #endif /* * DESCRIPTION: * Verify we can link tmpfile. * * STRATEGY: * 1. open(2) with O_TMPFILE. * 2. linkat(2). * 3. freeze the pool, export and re-import the pool. * 3. stat(2) the path to verify it has been created. * */ static void run(const char *op) { int ret; char buf[50]; sprintf(buf, "sudo -E zpool %s $TESTPOOL", op); if ((ret = system(buf)) != 0) { if (ret == -1) err(4, "system \"zpool %s\"", op); else errx(4, "zpool %s exited %d\n", op, WEXITSTATUS(ret)); } } int main(void) { int i, fd; char spath[1024], dpath[1024]; const char *penv[] = {"TESTDIR", "TESTFILE0"}; - struct stat sbuf; (void) fprintf(stdout, "Verify O_TMPFILE file can be linked.\n"); /* * Get the environment variable values. */ for (i = 0; i < ARRAY_SIZE(penv); i++) if ((penv[i] = getenv(penv[i])) == NULL) errx(1, "getenv(penv[%d])", i); fd = open(penv[0], O_RDWR|O_TMPFILE, 0666); if (fd < 0) err(2, "open(%s)", penv[0]); snprintf(spath, 1024, "/proc/self/fd/%d", fd); snprintf(dpath, 1024, "%s/%s", penv[0], penv[1]); if (linkat(AT_FDCWD, spath, AT_FDCWD, dpath, AT_SYMLINK_FOLLOW) < 0) err(3, "linkat"); run("freeze"); close(fd); run("export"); run("import"); - if (stat(dpath, &sbuf) < 0) { - perror("stat"); - unlink(dpath); + if (unlink(dpath) == -1) { + perror("unlink"); exit(5); } - unlink(dpath); return (0); } diff --git a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_stat_mode.c b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_stat_mode.c index 4c34aec8bdb4..1a934a8b1852 100644 --- a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_stat_mode.c +++ b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_stat_mode.c @@ -1,105 +1,108 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2019 by Tomohiro Kusumi. All rights reserved. */ #include #include #include #include #include #include #include /* backward compat in case it's not defined */ #ifndef O_TMPFILE #define O_TMPFILE (020000000|O_DIRECTORY) #endif /* * DESCRIPTION: * Verify stat(2) for O_TMPFILE file considers umask. * * STRATEGY: * 1. open(2) with O_TMPFILE. * 2. linkat(2). * 3. fstat(2)/stat(2) and verify .st_mode value. */ static void test_stat_mode(mode_t mask) { - struct stat st, fst; + struct stat fst; int i, fd; char spath[1024], dpath[1024]; const char *penv[] = {"TESTDIR", "TESTFILE0"}; mode_t masked = 0777 & ~mask; mode_t mode; /* * Get the environment variable values. */ for (i = 0; i < ARRAY_SIZE(penv); i++) if ((penv[i] = getenv(penv[i])) == NULL) errx(1, "getenv(penv[%d])", i); umask(mask); fd = open(penv[0], O_RDWR|O_TMPFILE, 0777); if (fd == -1) err(2, "open(%s)", penv[0]); if (fstat(fd, &fst) == -1) - err(3, "open"); + err(3, "fstat(%s)", penv[0]); snprintf(spath, sizeof (spath), "/proc/self/fd/%d", fd); snprintf(dpath, sizeof (dpath), "%s/%s", penv[0], penv[1]); unlink(dpath); if (linkat(AT_FDCWD, spath, AT_FDCWD, dpath, AT_SYMLINK_FOLLOW) == -1) err(4, "linkat"); close(fd); - if (stat(dpath, &st) == -1) - err(5, "stat"); - unlink(dpath); - - /* Verify fstat(2) result */ + /* Verify fstat(2) result at old path */ mode = fst.st_mode & 0777; if (mode != masked) - errx(6, "fstat(2) %o != %o\n", mode, masked); + errx(5, "fstat(2) %o != %o\n", mode, masked); + + fd = open(dpath, O_RDWR); + if (fd == -1) + err(6, "open(%s)", dpath); - /* Verify stat(2) result */ - mode = st.st_mode & 0777; + if (fstat(fd, &fst) == -1) + err(7, "fstat(%s)", dpath); + + /* Verify fstat(2) result at new path */ + mode = fst.st_mode & 0777; if (mode != masked) - errx(7, "stat(2) %o != %o\n", mode, masked); + errx(8, "fstat(2) %o != %o\n", mode, masked); } int main(void) { fprintf(stdout, "Verify stat(2) for O_TMPFILE file considers umask.\n"); test_stat_mode(0022); test_stat_mode(0077); return (0); }