Add build and install of the mount.fedfs binary. Signed-off-by: Ian Kent <raven@xxxxxxxxxx> --- CHANGELOG | 1 Makefile | 3 Makefile.rules | 2 autofs.spec | 1 fedfs/Makefile | 36 +++ fedfs/fedfs-gpl-boiler.h | 40 ++++ fedfs/fedfs-nls.h | 38 ++++ fedfs/mount.fedfs.c | 485 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 605 insertions(+), 1 deletion(-) create mode 100644 fedfs/Makefile create mode 100644 fedfs/fedfs-gpl-boiler.h create mode 100644 fedfs/fedfs-nls.h create mode 100644 fedfs/mount.fedfs.c diff --git a/CHANGELOG b/CHANGELOG index 8d6c737c..88992147 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ xx/xx/2018 autofs-5.1.5 - dont allow trailing slash in master map mount points. - fix libresolv configure check. - add fedfs-getsrvinfo.c. +- add mount.fedfs.c. 19/12/2017 autofs-5.1.4 - fix spec file url. diff --git a/Makefile b/Makefile index e560a7cb..99db2c75 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,9 @@ kernel: samples: set -e; if [ -d samples ]; then $(MAKE) -C samples all; fi +fedfs: + set -e; if [ -d fedfs ]; then $(MAKE) -C fedfs all; fi + clean: for i in $(SUBDIRS) samples; do \ if [ -d $$i ]; then $(MAKE) -C $$i clean; fi; done diff --git a/Makefile.rules b/Makefile.rules index 4deab3b9..fc9f6019 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -3,7 +3,7 @@ # # Root directory contents -SUBDIRS = lib daemon modules man +SUBDIRS = lib daemon fedfs modules man INCDIRS = include INCFILES = COPYING COPYRIGHT NEWS README* TODO Makefile Makefile.rules \ Makefile.conf.in .version .autofs-* configure.in aclocal.m4 \ diff --git a/autofs.spec b/autofs.spec index 2464e741..2cc0e38f 100644 --- a/autofs.spec +++ b/autofs.spec @@ -191,6 +191,7 @@ fi %config(noreplace) /etc/sysconfig/autofs %config(noreplace) /etc/autofs_ldap_auth.conf %{_sbindir}/automount +%{_sbindir}/mount.fedfs %dir %{_libdir}/autofs %{_libdir}/autofs/* %{_mandir}/*/* diff --git a/fedfs/Makefile b/fedfs/Makefile new file mode 100644 index 00000000..cb325bed --- /dev/null +++ b/fedfs/Makefile @@ -0,0 +1,36 @@ +# +# Makefile for mount.fedfs +# + +-include ../Makefile.conf +include ../Makefile.rules + +SRCS = mount.fedfs.c +HDRS = fedfs-getsrvinfo.h fedfs-gpl-boiler.h fedfs-nls.h + +fedfs-getsrvinfo_OBJ = fedfs-getsrvinfo.o + +mount_fedfs_OBJ = mount.fedfs.o + +version := $(shell cat ../.version) + +CFLAGS += -rdynamic $(DAEMON_CFLAGS) -D_GNU_SOURCE -I../include +CFLAGS += -DVERSION_STRING=\"$(version)\" +LDFLAGS += -rdynamic + +all: mount.fedfs + +mount.fedfs: $(mount_fedfs_OBJ) $(fedfs-getsrvinfo_OBJ) $(HDRS) + $(CC) -o mount.fedfs \ + $(mount_fedfs_OBJ) $(fedfs-getsrvinfo_OBJ) \ + $(LDFLAGS) $(LIBRESOLV) $(LIBS) + +clean: + rm -f *.o *.s *~ mount.fedfs + +install: all + install -d -m 755 $(INSTALLROOT)$(sbindir) + if ! test -x $(INSTALLROOT)$(sbindir)/mount.fedfs; \ + then \ + install -c mount.fedfs -m 755 $(INSTALLROOT)$(sbindir); \ + fi diff --git a/fedfs/fedfs-gpl-boiler.h b/fedfs/fedfs-gpl-boiler.h new file mode 100644 index 00000000..1353e736 --- /dev/null +++ b/fedfs/fedfs-gpl-boiler.h @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#ifndef _GPL_BOILER_H_ +#define _GPL_BOILER_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +/** + * GPLv2 boilerplate for usage messages + */ +static const char *fedfs_gpl_boilerplate = + "\nCopyright 2010, 2011, 2012, 2013 Oracle. " + "All rights reserved.\n\n" + "License GPLv2: " + "<http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>\n" + "This is free software. " + "You are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"; + +#endif /* !_GPL_BOILER_H_ */ diff --git a/fedfs/fedfs-nls.h b/fedfs/fedfs-nls.h new file mode 100644 index 00000000..92f968d3 --- /dev/null +++ b/fedfs/fedfs-nls.h @@ -0,0 +1,38 @@ +/* + * From util-linux/include/nls.h (GPLv2) + */ + +#ifndef UTIL_LINUX_NLS_H +#define UTIL_LINUX_NLS_H + +int main(int argc, char *argv[]); + +#ifndef LOCALEDIR +#define LOCALEDIR "/usr/share/locale" +#endif + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#else +# undef setlocale +# define setlocale(Category, Locale) ({}) /* empty */ +#endif + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(Text) gettext (Text) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# undef bindtextdomain +# define bindtextdomain(Domain, Directory) /* empty */ +# undef textdomain +# define textdomain(Domain) /* empty */ +# define _(Text) (Text) +# define N_(Text) (Text) +#endif + +#endif /* UTIL_LINUX_NLS_H */ diff --git a/fedfs/mount.fedfs.c b/fedfs/mount.fedfs.c new file mode 100644 index 00000000..bd434395 --- /dev/null +++ b/fedfs/mount.fedfs.c @@ -0,0 +1,485 @@ +/* + * 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 <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <sys/wait.h> + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <libgen.h> +#include <errno.h> +#include <getopt.h> +#include <locale.h> +#include <netdb.h> +#include <langinfo.h> + +#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); +} -- To unsubscribe from this list: send the line "unsubscribe autofs" in