/* * Copyright 2011 Oracle. All rights reserved. * * This file is part of fedfs-utils. * * fedfs-utils is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2.0 as * published by the Free Software Foundation. * * fedfs-utils is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License version 2.0 for more details. * * You should have received a copy of the GNU General Public License * version 2.0 along with fedfs-utils. If not, see: * * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fedfs-nls.h" #include "fedfs-getsrvinfo.h" #include "fedfs-gpl-boiler.h" /** * Top-level directory on client under which we mount NFSv4 domain roots */ #define FEDFS_NFS4_TLDIR "nfs4" /** * Name of SRV record containing NFSv4 FedFS root */ #define FEDFS_NFS_DOMAINROOT "_nfs-domainroot._tcp" /** * Export path of NFSv4 FedFS root */ #define FEDFS_NFS_EXPORTPATH "/.domainroot" /** * Pathname to mount.nfs */ #define MOUNT_NFS_EXECUTABLE "/sbin/mount.nfs" /** * Mount status values, lifted from util-linux */ enum { EX_SUCCESS = 0, EX_USAGE = 1, EX_FAIL = 32, }; static char *progname; static int nomtab; static int verbose; static _Bool readonly; static _Bool sloppy; static _Bool fake; /** * Short form command line options */ static const char fedfs_opts[] = "fhno:rsvVw"; /** * Long form command line options */ static const struct option fedfs_longopts[] = { { "fake", 0, NULL, 'f' }, { "help", 0, NULL, 'h' }, { "no-mtab", 0, NULL, 'n' }, { "options", 1, NULL, 'o' }, { "read-only", 0, NULL, 'r' }, { "read-write", 0, NULL, 'w' }, { "ro", 0, NULL, 'r' }, { "rw", 0, NULL, 'w' }, { "sloppy", 0, NULL, 's' }, { "verbose", 0, NULL, 'v' }, { "version", 0, NULL, 'V' }, { NULL, 0, NULL, 0 } }; /** * Display mount.fedfs usage message */ static void mount_usage(void) { printf(_("\nUsage: %s remotedir localdir [-fhnrsvVw]\n\n"), progname); printf(_("options:\n")); printf(_("\t-f\t\tFake mount, do not actually mount\n")); printf(_("\t-h\t\tPrint this help\n")); printf(_("\t-n\t\tDo not update /etc/mtab\n")); printf(_("\t-r\t\tMount file system readonly\n")); printf(_("\t-s\t\tTolerate sloppy mount options\n")); printf(_("\t-v\t\tVerbose\n")); printf(_("\t-V\t\tPrint version\n")); printf(_("\t-w\t\tMount file system read-write\n")); printf("%s", fedfs_gpl_boilerplate); } /** * Concatenate three strings * * @param s NUL-terminated C string * @param t NUL-terminated C string * @param u NUL-terminated C string * @return pointer to NUL-terminated C string or NULL * * Caller must free the returned string with free(3). Always frees * its first arg - typical use: s = xstrconcat3(s,t,u); * * Lifted from util-linux. */ static char * xstrconcat3(char *s, const char *t, const char *u) { _Bool free_s = true; char *result; if (s == NULL) { s = ""; free_s = false; } if (t == NULL) t = ""; if (u == NULL) u = ""; result = malloc(strlen(s) + strlen(t) + strlen(u) + 1); if (result == NULL) goto out; strcpy(result, s); strcat(result, t); strcat(result, u); out: if (free_s) free(s); return result; } /** * Exec mount.nfs * * @param server NUL-terminated C string containing name of NFS server * @param port server port to use when mounting * @param domainname NUL-terminated C string containing FedFS domain name * @param export_path NUL-terminated C string containing server export path * @param mounted_on_dir NUL-terminated C string containing local mounted-on directory * @param text_options NUL-terminated C string containing user's mount options * */ static void exec_mount_nfs4(const char *server, unsigned short port, const char *domainname, const char *export_path, const char *mounted_on_dir, const char *text_options) { static char special[2048]; static char options[2048]; char *args[16]; int count = 0; snprintf(special, sizeof(special), "%s:%s/%s%s", server, FEDFS_NFS_EXPORTPATH, domainname, export_path); if (text_options != NULL && strcmp(text_options, "") != 0) snprintf(options, sizeof(options), "%s,vers=4,fg,port=%u", text_options, port); else snprintf(options, sizeof(options), "vers=4,fg,port=%u", port); if (verbose) { printf(_("%s: Special device: %s\n"), progname, special); printf(_("%s: Mounted-on directory: %s\n"), progname, mounted_on_dir); printf(_("%s: Mount options: %s\n"), progname, options); } args[count++] = MOUNT_NFS_EXECUTABLE; args[count++] = special; args[count++] = (char *)mounted_on_dir; if (verbose) args[count++] = "-v"; if (fake) args[count++] = "-f"; if (nomtab) args[count++] = "-n"; if (readonly) args[count++] = "-r"; if (sloppy) args[count++] = "-s"; args[count++] = "-o"; args[count++] = options; args[count] = NULL; execv(args[0], args); } /** * Mount a FedFS domain root via NFSv4 * * @param domainname NUL-terminated C string containing FedFS domain name * @param export_path NUL-terminated C string containing server export path * @param mounted_on_dir NUL-terminated C string containing local mounted-on directory * @param text_options NUL-terminated C string containing user's mount options * @return exit status code from the mount.nfs command * * Construct the server:/export string and the mounted-on directory string * based on the DNS SRV query results, then fork and exec mount.nfs to do * the actual mount request. */ static int nfs4_mount(const char *domainname, const char *export_path, const char *mounted_on_dir, const char *text_options) { struct srvinfo *tmp, *si = NULL; int error, status; status = EX_FAIL; error = getsrvinfo(FEDFS_NFS_DOMAINROOT, domainname, &si); switch (error) { case ESI_SUCCESS: break; case ESI_NONAME: fprintf(stderr, _("%s: Domain name %s not found\n"), progname, domainname); goto out; case ESI_SERVICE: fprintf(stderr, _("%s: No FedFS domain root available for %s\n"), progname, domainname); goto out; default: fprintf(stderr, _("%s: Failed to resolve %s: %s\n"), progname, domainname, gsi_strerror(error)); goto out; } /* * The srvinfo list is already in RFC 2782 sorted order. Try each * SRV record once, in the foreground. Go with the first one that * works. */ for (tmp = si; tmp != NULL; tmp = tmp->si_next) { pid_t pid; pid = fork(); switch (pid) { case 0: exec_mount_nfs4(tmp->si_target, tmp->si_port, domainname, export_path, mounted_on_dir, text_options); /*NOTREACHED*/ fprintf(stderr, _("%s: Failed to exec: %s\n"), progname, strerror(errno)); exit(EX_FAIL); case -1: fprintf(stderr, _("%s: Failed to fork: %s\n"), progname, strerror(errno)); goto out; default: waitpid(pid, &status, 0); if (status == EX_SUCCESS) goto out; } } out: freesrvinfo(si); return status; } /** * Try one mount request * * @param source NUL-terminated C string containing name of "special device" * @param target NUL-terminated C string containing local mounted-on directory * @param text_options NUL-terminated C string containing user's mount options * @return an exit status code * * Parse the pathname in "source." It contains the file system protocol * and FedFS domain name. Then pass these arguments to the appropriate * mount helper subcommand. */ static int try_mount(const char *source, const char *target, const char *text_options) { char *global_name, *topdir, *domainname, *remaining; int result; remaining = NULL; result = EX_FAIL; global_name = strdup(source); if (global_name == NULL) { fprintf(stderr, _("%s: Unable to parse globally useful name\n"), progname); goto out; } topdir = strtok(global_name, "/"); if (topdir == NULL) { fprintf(stderr, _("%s: Invalid globally useful name: %s\n"), progname, source); goto out; } if (verbose) printf(_("%s: Top-level directory: %s\n"), progname, topdir); domainname = strtok(NULL, "/"); if (domainname == NULL) { fprintf(stderr, _("%s: Missing domain name in globally " "useful name: %s\n"), progname, source); goto out; } if (verbose) printf(_("%s: Domain name: %s\n"), progname, domainname); remaining = strtok(NULL, "/"); if (remaining == NULL) { remaining = strdup("/"); if (remaining == NULL) { fprintf(stderr, _("%s: No memory\n"), progname); goto out; } } else { char *tmp; tmp = malloc(strlen(remaining) + 1); if (tmp == NULL) { fprintf(stderr, _("%s: No memory\n"), progname); remaining = NULL; goto out; } strcpy(tmp, "/"); strcat(tmp, remaining); remaining = tmp; } if (verbose) printf(_("%s: Export path: %s\n"), progname, remaining); if (strcmp(topdir, FEDFS_NFS4_TLDIR) == 0) result = nfs4_mount(domainname, remaining, target, text_options); #if 0 /* example: SMB support plugs in here */ else if (strcmp(topdir, FEDFS_SMB_TLDIR) == 0) result = smb_mount(domainname, remaining, target, text_options); #endif else fprintf(stderr, _("%s: Unrecognized file system protocol\n"), progname); out: free(global_name); free(remaining); return result; } /** * Program entry point * * @param argc count of command line arguments * @param argv array of NUL-terminated C strings containing command line arguments * @return program exit status */ int main(int argc, char *argv[]) { char *source, *target, *text_options; int c, mnt_err; (void)setlocale(LC_ALL, ""); progname = basename(argv[0]); if (argv[1] && argv[1][0] == '-') { if(argv[1][1] == 'V') printf("%s (VERSION_STRING)\n", progname); else mount_usage(); exit(EX_SUCCESS); } if (argc < 3) { mount_usage(); exit(EX_USAGE); } source = argv[1]; target = argv[2]; mnt_err = EX_USAGE; text_options = NULL; readonly = false; sloppy = false; fake = false; argv[2] = argv[0]; /* so that getopt error messages are correct */ while ((c = getopt_long(argc - 2, argv + 2, fedfs_opts, fedfs_longopts, NULL)) != -1) { switch (c) { case 'f': fake = true; break; case 'n': ++nomtab; break; case 'o': /* Ugh. */ if (text_options != NULL) text_options = xstrconcat3(text_options, ",", optarg); else text_options = strdup(optarg); if (text_options == NULL) { fprintf(stderr, _("%s: No memory\n"), progname); goto out; } break; case 'r': readonly = true; break; case 's': sloppy = true; break; case 'v': ++verbose; break; case 'V': printf("%s: (VERSION_STRING)\n", progname); mnt_err = EX_SUCCESS; goto out; case 'w': readonly = false; break; case 'h': default: mount_usage(); goto out; } } /* Extra non-option words at the end are bogus... */ if (optind != argc - 2) { mount_usage(); goto out; } if (getuid() != 0 && geteuid() != 0) { fprintf(stderr, _("%s: Not installed setuid - " "\"user\" FedFS mounts are not supported\n"), progname); mnt_err = EX_FAIL; goto out; } mnt_err = try_mount(source, target, text_options); out: free(text_options); exit(mnt_err); }