X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=lib%2Flibshare%2Fnfs.c;h=56725d27c35665a3ff6a981ae89600875a591e97;hb=4b787d75c869a7f633607ecb17f3a7c482a553d2;hp=60edaa2c398f5f9e5746b7c99f31b28e16fde337;hpb=46e18b3f0fc13aa0859d0fef7dc829db20491ab6;p=zfs.git diff --git a/lib/libshare/nfs.c b/lib/libshare/nfs.c index 60edaa2..56725d2 100644 --- a/lib/libshare/nfs.c +++ b/lib/libshare/nfs.c @@ -36,12 +36,22 @@ static sa_fstype_t *nfs_fstype; static boolean_t nfs_available; +/* + * nfs_exportfs_temp_fd refers to a temporary copy of the output + * from exportfs -v. + */ +static int nfs_exportfs_temp_fd = -1; + typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value, void *cookie); typedef int (*nfs_host_callback_t)(const char *sharepath, const char *host, const char *security, const char *access, void *cookie); +/** + * Invokes the specified callback function for each Solaris share option + * listed in the specified string. + */ static int foreach_nfs_shareopt(const char *shareopts, nfs_shareopt_callback_t callback, void *cookie) @@ -105,6 +115,11 @@ typedef struct nfs_host_cookie_s { const char *security; } nfs_host_cookie_t; +/** + * Helper function for foreach_nfs_host. This function checks whether the + * current share option is a host specification and invokes a callback + * function with information about the host. + */ static int foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie) { @@ -158,6 +173,9 @@ foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie) return SA_OK; } +/** + * Invokes a callback function for all NFS hosts that are set for a share. + */ static int foreach_nfs_host(sa_share_impl_t impl_share, nfs_host_callback_t callback, void *cookie) @@ -176,6 +194,9 @@ foreach_nfs_host(sa_share_impl_t impl_share, nfs_host_callback_t callback, &udata); } +/** + * Converts a Solaris NFS host specification to its Linux equivalent. + */ static int get_linux_hostspec(const char *solaris_hostspec, char **plinux_hostspec) { @@ -200,34 +221,17 @@ get_linux_hostspec(const char *solaris_hostspec, char **plinux_hostspec) return SA_OK; } +/** + * Used internally by nfs_enable_share to enable sharing for a single host. + */ static int nfs_enable_share_one(const char *sharepath, const char *host, const char *security, const char *access, void *pcookie) { - pid_t pid; - int rc, status; + int rc; char *linuxhost, *hostpath, *opts; const char *linux_opts = (const char *)pcookie; - - pid = fork(); - - if (pid < 0) - return SA_SYSTEM_ERR; - - if (pid > 0) { - while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR) - ; /* empty loop body */ - - if (rc <= 0) - return SA_SYSTEM_ERR; - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - return SA_CONFIG_ERR; - - return SA_OK; - } - - /* child */ + char *argv[6]; /* exportfs -i -o sec=XX,rX, : */ @@ -262,18 +266,27 @@ nfs_enable_share_one(const char *sharepath, const char *host, fprintf(stderr, "sharing %s with opts %s\n", hostpath, opts); #endif - rc = execlp("/usr/sbin/exportfs", "exportfs", "-i", \ - "-o", opts, hostpath, NULL); + argv[0] = "/usr/sbin/exportfs"; + argv[1] = "-i"; + argv[2] = "-o"; + argv[3] = opts; + argv[4] = hostpath; + argv[5] = NULL; - if (rc < 0) { - free(hostpath); - free(opts); - exit(1); - } + rc = libzfs_run_process(argv[0], argv, 0); - exit(0); + free(hostpath); + free(opts); + + if (rc < 0) + return SA_SYSTEM_ERR; + else + return SA_OK; } +/** + * Adds a Linux share option to an array of NFS options. + */ static int add_linux_shareopt(char **plinux_opts, const char *key, const char *value) { @@ -306,6 +319,10 @@ add_linux_shareopt(char **plinux_opts, const char *key, const char *value) return SA_OK; } +/** + * Validates and converts a single Solaris share option to its Linux + * equivalent. + */ static int get_linux_shareopts_cb(const char *key, const char *value, void *cookie) { @@ -343,7 +360,7 @@ get_linux_shareopts_cb(const char *key, const char *value, void *cookie) strcmp(key, "root_squash") != 0 && strcmp(key, "no_root_squash") != 0 && strcmp(key, "all_squash") != 0 && - strcmp(key, "no_all_squash") != 0 && + strcmp(key, "no_all_squash") != 0 && strcmp(key, "fsid") != 0 && strcmp(key, "anonuid") != 0 && strcmp(key, "anongid") != 0) { return SA_SYNTAX_ERR; } @@ -353,6 +370,10 @@ get_linux_shareopts_cb(const char *key, const char *value, void *cookie) return SA_OK; } +/** + * Takes a string containing Solaris share options (e.g. "sync,no_acl") and + * converts them to a NULL-terminated array of Linux NFS options. + */ static int get_linux_shareopts(const char *shareopts, char **plinux_opts) { @@ -377,6 +398,9 @@ get_linux_shareopts(const char *shareopts, char **plinux_opts) return rc; } +/** + * Enables NFS sharing for the specified share. + */ static int nfs_enable_share(sa_share_impl_t impl_share) { @@ -404,33 +428,16 @@ nfs_enable_share(sa_share_impl_t impl_share) return rc; } +/** + * Used internally by nfs_disable_share to disable sharing for a single host. + */ static int nfs_disable_share_one(const char *sharepath, const char *host, const char *security, const char *access, void *cookie) { - pid_t pid; - int rc, status; + int rc; char *linuxhost, *hostpath; - - pid = fork(); - - if (pid < 0) - return SA_SYSTEM_ERR; - - if (pid > 0) { - while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR) - ; /* empty loop body */ - - if (rc <= 0) - return SA_SYSTEM_ERR; - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - return SA_CONFIG_ERR; - - return SA_OK; - } - - /* child */ + char *argv[4]; rc = get_linux_hostspec(host, &linuxhost); @@ -452,17 +459,24 @@ nfs_disable_share_one(const char *sharepath, const char *host, fprintf(stderr, "unsharing %s\n", hostpath); #endif - rc = execlp("/usr/sbin/exportfs", "exportfs", "-u", \ - hostpath, NULL); + argv[0] = "/usr/sbin/exportfs"; + argv[1] = "-u"; + argv[2] = hostpath; + argv[3] = NULL; - if (rc < 0) { - free(hostpath); - exit(1); - } + rc = libzfs_run_process(argv[0], argv, 0); - exit(0); + free(hostpath); + + if (rc < 0) + return SA_SYSTEM_ERR; + else + return SA_OK; } +/** + * Disables NFS sharing for the specified share. + */ static int nfs_disable_share(sa_share_impl_t impl_share) { @@ -477,6 +491,9 @@ nfs_disable_share(sa_share_impl_t impl_share) return foreach_nfs_host(impl_share, nfs_disable_share_one, NULL); } +/** + * Checks whether the specified NFS share options are syntactically correct. + */ static int nfs_validate_shareopts(const char *shareopts) { @@ -493,113 +510,73 @@ nfs_validate_shareopts(const char *shareopts) return SA_OK; } -/* - * TODO: Rather than invoking "exportfs -v" once for each share we should only - * call it once for all shares. +/** + * Checks whether a share is currently active. */ static boolean_t is_share_active(sa_share_impl_t impl_share) { - pid_t pid; - int rc, status; - int pipes[2]; - FILE *exportfs_stdout; char line[512]; char *tab, *cur; - boolean_t share_active = B_FALSE; + FILE *nfs_exportfs_temp_fp; - if (pipe(pipes) < 0) + if (nfs_exportfs_temp_fd < 0) return B_FALSE; - pid = fork(); + nfs_exportfs_temp_fp = fdopen(dup(nfs_exportfs_temp_fd), "r"); - if (pid < 0) + if (nfs_exportfs_temp_fp == NULL || + fseek(nfs_exportfs_temp_fp, 0, SEEK_SET) < 0) { + fclose(nfs_exportfs_temp_fp); return B_FALSE; + } - if (pid > 0) { - share_active = B_FALSE; - - exportfs_stdout = fdopen(pipes[0], "r"); - close(pipes[1]); + while (fgets(line, sizeof(line), nfs_exportfs_temp_fp) != NULL) { + /* + * exportfs uses separate lines for the share path + * and the export options when the share path is longer + * than a certain amount of characters; this ignores + * the option lines + */ + if (line[0] == '\t') + continue; - while (fgets(line, sizeof(line), exportfs_stdout) != NULL) { - if (share_active) - continue; + tab = strchr(line, '\t'); + if (tab != NULL) { + *tab = '\0'; + cur = tab - 1; + } else { /* - * exportfs uses separate lines for the share path - * and the export options when the share path is longer - * than a certain amount of characters; this ignores - * the option lines + * there's no tab character, which means the + * NFS options are on a separate line; we just + * need to remove the new-line character + * at the end of the line */ - if (line[0] == '\t') - continue; - - tab = strchr(line, '\t'); - - if (tab != NULL) { - *tab = '\0'; - cur = tab - 1; - } else { - /* - * there's no tab character, which means the - * NFS options are on a separate line; we just - * need to remove the new-line character - * at the end of the line - */ - cur = line + strlen(line) - 1; - } - - /* remove trailing spaces and new-line characters */ - while (cur >= line && - (*cur == ' ' || *cur == '\n')) - *cur-- = '\0'; - - if (strcmp(line, impl_share->sharepath) == 0) { - share_active = B_TRUE; - - /* - * We can't break here just yet, otherwise - * exportfs might die due to write() failing - * with EPIPE once we've closed the pipe file - * descriptor - we need to read until EOF - * occurs on the stdout pipe. - */ - } + cur = line + strlen(line) - 1; } - fclose(exportfs_stdout); - - while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR) - ; /* empty loop body */ - - if (rc <= 0) - return B_FALSE; + /* remove trailing spaces and new-line characters */ + while (cur >= line && (*cur == ' ' || *cur == '\n')) + *cur-- = '\0'; - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - return B_FALSE; - - return share_active; + if (strcmp(line, impl_share->sharepath) == 0) { + fclose(nfs_exportfs_temp_fp); + return B_TRUE; + } } - /* child */ + fclose(nfs_exportfs_temp_fp); - /* exportfs -v */ - - close(pipes[0]); - - if (dup2(pipes[1], STDOUT_FILENO) < 0) - exit(1); - - rc = execlp("/usr/sbin/exportfs", "exportfs", "-v", NULL); - - if (rc < 0) { - exit(1); - } - - exit(0); + return B_FALSE; } +/** + * Called to update a share's options. A share's options might be out of + * date if the share was loaded from disk (i.e. /etc/dfs/sharetab) and the + * "sharenfs" dataset property has changed in the meantime. This function + * also takes care of re-enabling the share if necessary. + */ static int nfs_update_shareopts(sa_share_impl_t impl_share, const char *resource, const char *shareopts) @@ -637,7 +614,10 @@ nfs_update_shareopts(sa_share_impl_t impl_share, const char *resource, return SA_OK; } - +/** + * Clears a share's NFS options. Used by libshare to + * clean up shares that are about to be free()'d. + */ static void nfs_clear_shareopts(sa_share_impl_t impl_share) { @@ -654,11 +634,40 @@ static const sa_share_ops_t nfs_shareops = { .clear_shareopts = nfs_clear_shareopts, }; +/* + * nfs_check_exportfs() checks that the exportfs command runs + * and also maintains a temporary copy of the output from + * exportfs -v. + * To update this temporary copy simply call this function again. + * + * TODO : Use /var/lib/nfs/etab instead of our private copy. + * But must implement locking to prevent concurrent access. + * + * TODO : The temporary file descriptor is never closed since + * there is no libshare_nfs_fini() function. + */ static int nfs_check_exportfs(void) { pid_t pid; - int rc, status, null_fd; + int rc, status; + static char nfs_exportfs_tempfile[] = "/tmp/exportfs.XXXXXX"; + + /* + * Close any existing temporary copies of output from exportfs. + * We have already called unlink() so file will be deleted. + */ + if (nfs_exportfs_temp_fd >= 0) + close(nfs_exportfs_temp_fd); + + nfs_exportfs_temp_fd = mkstemp(nfs_exportfs_tempfile); + + if (nfs_exportfs_temp_fd < 0) + return SA_SYSTEM_ERR; + + unlink(nfs_exportfs_tempfile); + + fcntl(nfs_exportfs_temp_fd, F_SETFD, FD_CLOEXEC); pid = fork(); @@ -680,14 +689,12 @@ nfs_check_exportfs(void) /* child */ - null_fd = open("/dev/null", O_RDONLY); + /* exportfs -v */ - if (null_fd < 0 || dup2(null_fd, 1) < 0 || dup2(null_fd, 2) < 0) + if (dup2(nfs_exportfs_temp_fd, STDOUT_FILENO) < 0) exit(1); - close(null_fd); - - rc = execlp("/usr/sbin/exportfs", "exportfs", NULL); + rc = execlp("/usr/sbin/exportfs", "exportfs", "-v", NULL); if (rc < 0) { exit(1); @@ -696,6 +703,9 @@ nfs_check_exportfs(void) exit(0); } +/** + * Initializes the NFS functionality of libshare. + */ void libshare_nfs_init(void) {