diff --git a/Makefile b/Makefile index 68075ca..5715c8d 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ #******************************************************************************* #******************************************************************************* -# Functions +# Functions #******************************************************************************* #******************************************************************************* @@ -31,12 +31,12 @@ MULTI_CORES ?= $(shell grep -c ^processor /proc/cpuinfo) #******************************************************************************* # Compile configure #******************************************************************************* -export ARCH ?= arm -export CROSS_COMPILE ?= -CC := $(CROSS_COMPILE)gcc -LD := $(CROSS_COMPILE)ld -AR := $(CROSS_COMPILE)ar -STRIP := $(CROSS_COMPILE)strip +export ARCH ?= aarch64 +export CROSS_COMPILE ?= /home/gaoyang3513/Workspaces/ndk/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android- +export CC := /home/gaoyang3513/Workspaces/ndk/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/clang +export LD := $(CROSS_COMPILE)ld +export AR := $(CROSS_COMPILE)ar +export STRIP := $(CROSS_COMPILE)strip #******************************************************************************* # Targets @@ -72,7 +72,10 @@ nfs-utils: checkenv fi if [ ! -e $(NFS_DIR)/Makefile ]; then \ cd $(NFS_DIR); \ - ./configure --prefix=$(INSTALL_DIR) --disable-nfsv4 --disable-gss --disable-uuid --disable-mount --sbindir=$(INSTALL_DIR)/sbin/ --with-statedir=$(INSTALL_DIR)/var/lig/nfs/; \ + ./configure --prefix=$(INSTALL_DIR) \ + --target=aarch64-linux-android --host=aarch64-linux-android --build=x86_64-linux \ + --disable-nfsv4 --disable-gss --disable-uuid --disable-mount \ + --sbindir=$(INSTALL_DIR)/sbin/ --with-statedir=$(INSTALL_DIR)/var/lig/nfs/; \ fi $(MAKE) -C $(NFS_DIR) all install diff --git a/nfs-utils-2.5.2/support/export/xtab.c b/nfs-utils-2.5.2/support/export/xtab.c new file mode 100644 index 0000000..31ad37a --- /dev/null +++ b/nfs-utils-2.5.2/support/export/xtab.c @@ -0,0 +1,256 @@ +/* + * support/export/xtab.c + * + * Interface to the etab/exports file. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfslib.h" +#include "exportfs.h" +#include "xio.h" +#include "xlog.h" +#include "v4root.h" +#include "misc.h" + +static char state_base_dirname[PATH_MAX] = NFS_STATEDIR; +extern struct state_paths etab; + +int v4root_needed; +static void cond_rename(char *newfile, char *oldfile); + +static int +xtab_read(char *xtab, char *lockfn, int is_export) +{ + /* is_export == 0 => reading /proc/fs/nfs/exports - we know these things are exported to kernel + * is_export == 1 => reading /var/lib/nfs/etab - these things are allowed to be exported + */ + struct exportent *xp; + nfs_export *exp; + int lockid; + + if ((lockid = xflock(lockfn, "r")) < 0) + return 0; + setexportent(xtab, "r"); + if (is_export == 1) + v4root_needed = 1; + while ((xp = getexportent(is_export==0, 0)) != NULL) { + if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) && + !(exp = export_create(xp, is_export!=1))) { + if(xp->e_hostname) { + free(xp->e_hostname); + xp->e_hostname=NULL; + } + if(xp->e_uuid) { + free(xp->e_uuid); + xp->e_uuid=NULL; + } + continue; + } + switch (is_export) { + case 0: + exp->m_exported = 1; + break; + case 1: + exp->m_xtabent = 1; + exp->m_mayexport = 1; + if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0) + v4root_needed = 0; + break; + } + if(xp->e_hostname) { + free(xp->e_hostname); + xp->e_hostname=NULL; + } + if(xp->e_uuid) { + free(xp->e_uuid); + xp->e_uuid=NULL; + } + + } + endexportent(); + xfunlock(lockid); + + return 0; +} + +int +xtab_export_read(void) +{ + return xtab_read(etab.statefn, etab.lockfn, 1); +} + +/* + * mountd now keeps an open fd for the etab at all times to make sure that the + * inode number changes when the xtab_export_write is done. If you change the + * routine below such that the files are edited in place, then you'll need to + * fix the auth_reload logic as well... + */ +static int +xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export) +{ + struct exportent xe; + nfs_export *exp; + int lockid, i; + + if ((lockid = xflock(lockfn, "w")) < 0) { + xlog(L_ERROR, "can't lock %s for writing", xtab); + return 0; + } + setexportent(xtabtmp, "w"); + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (is_export && !exp->m_xtabent) + continue; + if (!is_export && ! exp->m_exported) + continue; + + /* write out the export entry using the FQDN */ + xe = exp->m_export; + xe.e_hostname = exp->m_client->m_hostname; + putexportent(&xe); + } + } + endexportent(); + + cond_rename(xtabtmp, xtab); + + xfunlock(lockid); + + return 1; +} + +int +xtab_export_write(void) +{ + return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1); +} + +/* + * rename newfile onto oldfile unless + * they are identical + */ +static void cond_rename(char *newfile, char *oldfile) +{ + int nfd, ofd; + char nbuf[4096], obuf[4096]; + int ncnt, ocnt; + + nfd = open(newfile, 0); + if (nfd < 0) + return; + ofd = open(oldfile, 0); + if (ofd < 0) { + close(nfd); + rename(newfile, oldfile); + return; + } + + do { + ncnt = read(nfd, nbuf, sizeof(nbuf)); + if (ncnt < 0) + break; + ocnt = read(ofd, obuf, sizeof(obuf)); + if (ocnt < 0) + break; + if (ncnt != ocnt) + break; + if (ncnt == 0) { + close(nfd); + close(ofd); + unlink(newfile); + return; + } + } while (memcmp(obuf, nbuf, ncnt) == 0); + + /* some mis-match */ + close(nfd); + close(ofd); + rename(newfile, oldfile); + return; +} + +/* + * Returns a dynamically allocated, '\0'-terminated buffer + * containing an appropriate pathname, or NULL if an error + * occurs. Caller must free the returned result with free(3). + */ +static char * +state_make_pathname(const char *tabname) +{ + return generic_make_pathname(state_base_dirname, tabname); +} + +/** + * state_setup_basedir - set up basedir + * @progname: C string containing name of program, for error messages + * @parentdir: C string containing pathname to on-disk state, or NULL + * + * This runs before logging is set up, so error messages are directed + * to stderr. + * + * Returns true and sets up our basedir, if @parentdir was valid + * and usable; otherwise false is returned. + */ +_Bool +state_setup_basedir(const char *progname, const char *parentdir) +{ + return generic_setup_basedir(progname, parentdir, state_base_dirname, + PATH_MAX); +} + +int +setup_state_path_names(const char *progname, const char *statefn, + const char *tmpfn, const char *lockfn, + struct state_paths *paths) +{ + paths->statefn = state_make_pathname(statefn); + if (!paths->statefn) { + fprintf(stderr, "%s: state_make_pathname(%s) failed\n", + progname, statefn); + goto out_err; + } + paths->tmpfn = state_make_pathname(tmpfn); + if (!paths->tmpfn) { + fprintf(stderr, "%s: state_make_pathname(%s) failed\n", + progname, tmpfn); + goto out_free_statefn; + } + paths->lockfn = state_make_pathname(lockfn); + if (!paths->lockfn) { + fprintf(stderr, "%s: state_make_pathname(%s) failed\n", + progname, lockfn); + goto out_free_tmpfn; + } + return 1; + +out_free_tmpfn: + free(paths->tmpfn); +out_free_statefn: + free(paths->statefn); +out_err: + return 0; + +} + +void +free_state_path_names(struct state_paths *paths) +{ + free(paths->statefn); + free(paths->tmpfn); + free(paths->lockfn); +} diff --git a/nfs-utils-2.5.2/support/nfs/xcommon.c b/nfs-utils-2.5.2/support/nfs/xcommon.c new file mode 100644 index 0000000..779b70f --- /dev/null +++ b/nfs-utils-2.5.2/support/nfs/xcommon.c @@ -0,0 +1,191 @@ +/* + * xcommon.c - various functions put together to avoid basic error checking. + * + * added fcntl locking by Kjetil T. (kjetilho@math.uio.no) - aeb, 950927 + * + * 1999-02-22 Arkadiusz Miskiewicz + * - added Native Language Support + * + * 2006-06-06 Amit Gud + * - Moved code snippets here from mount/sundries.c of util-linux + * and merged code from support/nfs/xmalloc.c by Olaf Kirch here. + */ + +#include +#include +#include +#include +#include + +#include "xcommon.h" +#include "nls.h" /* _() */ + +void (*at_die)(void ) = NULL; + +char * +xstrndup (const char *s, int n) { + char *t; + + if (s == NULL) + die (EX_SOFTWARE, _("bug in xstrndup call")); + + t = xmalloc(n+1); + strncpy(t,s,n); + t[n] = 0; + + return t; +} + +char * +xstrconcat2 (const char *s, const char *t) { + char *res; + + if (!s) s = ""; + if (!t) t = ""; + res = xmalloc(strlen(s) + strlen(t) + 1); + strcpy(res, s); + strcat(res, t); + return res; +} + +/* frees its first arg - typical use: s = xstrconcat3(s,t,u); */ +char * +xstrconcat3 (const char *s, const char *t, const char *u) { + char *res; + + int dofree = 1; + + if (!s) s = "", dofree=0; + if (!t) t = ""; + if (!u) u = ""; + res = xmalloc(strlen(s) + strlen(t) + strlen(u) + 1); + strcpy(res, s); + strcat(res, t); + strcat(res, u); + if (dofree) + free((void *) s); + return res; +} + +/* frees its first arg - typical use: s = xstrconcat4(s,t,u,v); */ +char * +xstrconcat4 (const char *s, const char *t, const char *u, const char *v) { + char *res; + + int dofree = 1; + + if (!s) s = "", dofree=0; + if (!t) t = ""; + if (!u) u = ""; + if (!v) v = ""; + res = xmalloc(strlen(s) + strlen(t) + strlen(u) + strlen(v) + 1); + strcpy(res, s); + strcat(res, t); + strcat(res, u); + strcat(res, v); + if (dofree) + free((void *) s); + return res; +} + +/* Non-fatal error. Print message and return. */ +/* (print the message in a single printf, in an attempt + to avoid mixing output of several threads) */ +void +nfs_error (const char *fmt, ...) { + va_list args; + char *fmt2; + + fmt2 = xstrconcat2 (fmt, "\n"); + va_start (args, fmt); +// vfprintf (stderr, fmt2, args); + va_end (args); + free (fmt2); +} + +/* Make a canonical pathname from PATH. Returns a freshly malloced string. + It is up the *caller* to ensure that the PATH is sensible. i.e. + canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' + is not a legal pathname for ``/dev/fd0''. Anything we cannot parse + we return unmodified. */ +char *canonicalize (const char *path) { + char canonical[PATH_MAX+2]; + + if (path == NULL) + return NULL; + +#if 1 + if (streq(path, "none") || + streq(path, "proc") || + streq(path, "devpts")) + return xstrdup(path); +#endif + if (realpath (path, canonical)) + return xstrdup(canonical); + + return xstrdup(path); +} + +/* Fatal error. Print message and exit. */ +void +die(int err, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + + if (at_die) + (*at_die)(); + + exit(err); +} + +static void +die_if_null(void *t) { + if (t == NULL) + die(EX_SYSERR, _("not enough memory")); +} + +void * +xmalloc (size_t size) { + void *t; + + if (size == 0) + return NULL; + + t = malloc(size); + die_if_null(t); + + return t; +} + +void * +xrealloc (void *p, size_t size) { + void *t; + + t = realloc(p, size); + die_if_null(t); + + return t; +} + +void +xfree(void *ptr) +{ + free(ptr); +} + +char * +xstrdup (const char *s) { + char *t; + + if (s == NULL) + return NULL; + + t = strdup(s); + die_if_null(t); + + return t; +} diff --git a/nfs-utils-2.5.2/utils/exportfs/exportfs.c b/nfs-utils-2.5.2/utils/exportfs/exportfs.c new file mode 100644 index 0000000..d8940f5 --- /dev/null +++ b/nfs-utils-2.5.2/utils/exportfs/exportfs.c @@ -0,0 +1,780 @@ +/* + * utils/exportfs/exportfs.c + * + * Export file systems to knfsd + * + * Copyright (C) 1995, 1996, 1997 Olaf Kirch + * + * Extensive changes, 1999, Neil Brown + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INT_TO_LONG_THRESHOLD_SECS (INT_MAX - (60 * 60 * 24)) + +#include "sockaddr.h" +#include "misc.h" +#include "nfsd_path.h" +#include "nfslib.h" +#include "exportfs.h" +#include "xlog.h" +#include "conffile.h" + +static void export_all(int verbose); +static void exportfs(char *arg, char *options, int verbose); +static void unexportfs(char *arg, int verbose); +static void dump(int verbose, int export_format); +static void usage(const char *progname, int n); +static void validate_export(nfs_export *exp); +static int matchhostname(const char *hostname1, const char *hostname2); +static void grab_lockfile(void); +static void release_lockfile(void); + +static const char *lockfile = EXP_LOCKFILE; +static int _lockfd = -1; + +struct state_paths etab; + +static ssize_t exportfs_write(int fd, const char *buf, size_t len) +{ + return nfsd_path_write(fd, buf, len); +} + +/* + * If we aren't careful, changes made by exportfs can be lost + * when multiple exports process run at once: + * + * exportfs process 1 exportfs process 2 + * ------------------------------------------ + * reads etab version A reads etab version A + * adds new export B adds new export C + * writes A+B writes A+C + * + * The locking in support/export/xtab.c will prevent mountd from + * seeing a partially written version of etab, and will prevent + * the two writers above from writing simultaneously and + * corrupting etab, but to prevent problems like the above we + * need these additional lockfile() routines. + */ +static void +grab_lockfile(void) +{ + _lockfd = open(lockfile, O_CREAT|O_RDWR, 0666); + if (_lockfd != -1) + lockf(_lockfd, F_LOCK, 0); +} +static void +release_lockfile(void) +{ + if (_lockfd != -1) { + lockf(_lockfd, F_ULOCK, 0); + close(_lockfd); + _lockfd = -1; + } +} + +int +main(int argc, char **argv) +{ + char *options = NULL; + char *progname = NULL; + int f_export = 1; + int f_all = 0; + int f_verbose = 0; + int f_export_format = 0; + int f_reexport = 0; + int f_ignore = 0; + int i, c; + int force_flush = 0; + char *s; + + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + + xlog_open(progname); + xlog_stderr(1); + xlog_syslog(0); + + conf_init_file(NFS_CONFFILE); + xlog_from_conffile("exportfs"); + nfsd_path_init(); + + /* NOTE: following uses "mountd" section of nfs.conf !!!! */ + s = conf_get_str("mountd", "state-directory-path"); + if (s && !state_setup_basedir(argv[0], s)) + exit(1); + + while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) { + switch(c) { + case 'a': + f_all = 1; + break; + case 'd': + xlog_sconfig(optarg, 1); + break; + case 'f': + force_flush = 1; + break; + case 'h': + usage(progname, 0); + break; + case 'i': + f_ignore = 1; + break; + case 'o': + options = optarg; + break; + case 'r': + f_reexport = 1; + f_all = 1; + break; + case 'u': + f_export = 0; + break; + case 'v': + f_verbose = 1; + break; + case 's': + f_export_format = 1; + break; + default: + usage(progname, 1); + break; + } + } + + if (optind != argc && f_all) { + xlog(L_ERROR, "extra arguments are not permitted with -a or -r"); + return 1; + } + if (f_ignore && (f_all || ! f_export)) { + xlog(L_ERROR, "-i not meaningful with -a, -r or -u"); + return 1; + } + if (f_reexport && ! f_export) { + xlog(L_ERROR, "-r and -u are incompatible"); + return 1; + } +printf("point 1\n"); + if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab)) + return 1; +printf("point 2\n"); + if (optind == argc && ! f_all) { + if (force_flush) { + cache_flush(1); + free_state_path_names(&etab); + return 0; + } else { + xtab_export_read(); + dump(f_verbose, f_export_format); + free_state_path_names(&etab); + export_freeall(); + return 0; + } + } +printf("point 3\n"); + + /* + * Serialize things as best we can + */ + grab_lockfile(); + atexit(release_lockfile); + + if (f_export && ! f_ignore) { + if (! (export_read(_PATH_EXPORTS, 0) + + export_d_read(_PATH_EXPORTS_D, 0))) { + if (f_verbose) + xlog(L_WARNING, "No file systems exported!"); + } + } + if (f_export) { + if (f_all) + export_all(f_verbose); + else + for (i = optind; i < argc ; i++) + exportfs(argv[i], options, f_verbose); + } + /* If we are unexporting everything, then + * don't care about what should be exported, as that + * may require DNS lookups.. + */ + if (! ( !f_export && f_all)) { + /* note: xtab_*_read does not update entries if they already exist, + * so this will not lose new options + */ + if (!f_reexport) + xtab_export_read(); + if (!f_export) + for (i = optind ; i < argc ; i++) + unexportfs(argv[i], f_verbose); + } + xtab_export_write(); + cache_flush(force_flush); + free_state_path_names(&etab); + export_freeall(); + + return export_errno; +} + +/* + * export_all finds all entries and + * marks them xtabent and mayexport so that they get exported + */ +static void +export_all(int verbose) +{ + nfs_export *exp; + int i; + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (verbose) + printf("exporting %s:%s\n", + exp->m_client->m_hostname, + exp->m_export.e_path); + exp->m_xtabent = 1; + exp->m_mayexport = 1; + exp->m_changed = 1; + exp->m_warned = 0; + validate_export(exp); + } + } +} + + +static void +exportfs_parsed(char *hname, char *path, char *options, int verbose) +{ + struct exportent *eep; + nfs_export *exp = NULL; + struct addrinfo *ai = NULL; + int htype; + + if ((htype = client_gettype(hname)) == MCL_FQDN) { + ai = host_addrinfo(hname); + if (ai != NULL) { + exp = export_find(ai, path); + hname = ai->ai_canonname; + } + } else + exp = export_lookup(hname, path, 0); + + if (!exp) { + if (!(eep = mkexportent(hname, path, options)) || + !(exp = export_create(eep, 0))) + goto out; + } else if (!updateexportent(&exp->m_export, options)) + goto out; + + if (verbose) + printf("exporting %s:%s\n", exp->m_client->m_hostname, + exp->m_export.e_path); + exp->m_xtabent = 1; + exp->m_mayexport = 1; + exp->m_changed = 1; + exp->m_warned = 0; + validate_export(exp); + +out: + nfs_freeaddrinfo(ai); +} + +static int exportfs_generic(char *arg, char *options, int verbose) +{ + char *path; + + if ((path = strchr(arg, ':')) != NULL) + *path++ = '\0'; + + if (!path || *path != '/') + return 1; + + exportfs_parsed(arg, path, options, verbose); + return 0; +} + +static int exportfs_ipv6(char *arg, char *options, int verbose) +{ + char *path, *c; + + arg++; + c = strchr(arg, ']'); + if (c == NULL) + return 1; + + /* no colon means this is a wildcarded DNS hostname */ + if (memchr(arg, ':', c - arg) == NULL) + return exportfs_generic(--arg, options, verbose); + + path = strstr(c, ":/"); + if (path == NULL) + return 1; + *path++ = '\0'; + + /* if there's anything between the closing brace and the + * path separator, it's probably a prefix length */ + memmove(c, c + 1, path - c); + + exportfs_parsed(arg, path, options, verbose); + return 0; +} + +static void +exportfs(char *arg, char *options, int verbose) +{ + int failed; + + if (*arg == '[') + failed = exportfs_ipv6(arg, options, verbose); + else + failed = exportfs_generic(arg, options, verbose); + if (failed) + xlog(L_ERROR, "Invalid export syntax: %s", arg); +} + +static void +unexportfs_parsed(char *hname, char *path, int verbose) +{ + nfs_export *exp; + struct addrinfo *ai = NULL; + int htype; + int success = 0; + + if ((htype = client_gettype(hname)) == MCL_FQDN) { + ai = host_addrinfo(hname); + if (ai) + hname = ai->ai_canonname; + } + + /* + * It's possible the specified path ends with a '/'. But + * the entry from exportlist won't has the trailing '/', + * so need to deal with it. + */ + size_t nlen = strlen(path); + while (path[nlen - 1] == '/') + nlen--; + + for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { + if (strlen(exp->m_export.e_path) != nlen) + continue; + if (path && strncmp(path, exp->m_export.e_path, nlen)) + continue; + if (htype != exp->m_client->m_type) + continue; + if (htype == MCL_FQDN + && !matchhostname(exp->m_export.e_hostname, + hname)) + continue; + if (htype != MCL_FQDN + && strcasecmp(exp->m_export.e_hostname, hname)) + continue; + if (verbose) { +#if 0 + if (exp->m_exported) { + printf("unexporting %s:%s from kernel\n", + exp->m_client->m_hostname, + exp->m_export.e_path); + } + else +#endif + printf("unexporting %s:%s\n", + exp->m_client->m_hostname, + exp->m_export.e_path); + } + exp->m_xtabent = 0; + exp->m_mayexport = 0; + success = 1; + } + if (!success) + xlog(L_ERROR, "Could not find '%s:%s' to unexport.", hname, path); + + nfs_freeaddrinfo(ai); +} + +static int unexportfs_generic(char *arg, int verbose) +{ + char *path; + + if ((path = strchr(arg, ':')) != NULL) + *path++ = '\0'; + + if (!path || *path != '/') + return 1; + + unexportfs_parsed(arg, path, verbose); + return 0; +} + +static int unexportfs_ipv6(char *arg, int verbose) +{ + char *path, *c; + + arg++; + c = strchr(arg, ']'); + if (c == NULL) + return 1; + + /* no colon means this is a wildcarded DNS hostname */ + if (memchr(arg, ':', c - arg) == NULL) + return unexportfs_generic(--arg, verbose); + + path = strstr(c, ":/"); + if (path == NULL) + return 1; + *path++ = '\0'; + + /* if there's anything between the closing brace and the + * path separator, it's probably a prefix length */ + memmove(c, c + 1, path - c); + + unexportfs_parsed(arg, path, verbose); + return 0; +} + +static void +unexportfs(char *arg, int verbose) +{ + int failed; + + if (*arg == '[') + failed = unexportfs_ipv6(arg, verbose); + else + failed = unexportfs_generic(arg, verbose); + if (failed) + xlog(L_ERROR, "Invalid export syntax: %s", arg); +} + +static int can_test(void) +{ + char buf[1024] = { 0 }; + int fd; + int n; + size_t bufsiz = sizeof(buf); + + fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY); + if (fd < 0) + return 0; + + /* + * We introduce tolerance of 1 day to ensure that we use a + * LONG_MAX for the expiry timestamp before it is actually + * needed. To use LONG_MAX, the kernel code must have + * commit 2f74f972 (sunrpc: prepare NFS for 2038). + */ + if (time(NULL) > INT_TO_LONG_THRESHOLD_SECS) + snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %ld -test-client-\n", LONG_MAX); + else + snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %d -test-client-\n", INT_MAX); + + n = write(fd, buf, strlen(buf)); + close(fd); + if (n < 0) + return 0; + + fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY); + if (fd < 0) + return 0; + close(fd); + return 1; +} + +static int test_export(nfs_export *exp, int with_fsid) +{ + char *path = exp->m_export.e_path; + int flags = exp->m_export.e_flags | (with_fsid ? NFSEXP_FSID : 0); + /* beside max path, buf size should take protocol str into account */ + char buf[NFS_MAXPATHLEN+1+64] = { 0 }; + char *bp = buf; + int len = sizeof(buf); + int fd, n; + + n = snprintf(buf, len, "-test-client- "); + bp += n; + len -= n; + qword_add(&bp, &len, path); + if (len < 1) + return 0; + snprintf(bp, len, " 3 %d 65534 65534 0\n", flags); + fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY); + if (fd < 0) + return 0; + n = exportfs_write(fd, buf, strlen(buf)); + close(fd); + if (n < 0) + return 0; + return 1; +} + +static void +validate_export(nfs_export *exp) +{ + /* Check that the given export point is potentially exportable. + * We just give warnings here, don't cause anything to fail. + * If a path doesn't exist, or is not a dir or file, give an warning + * otherwise trial-export to '-test-client-' and check for failure. + */ + struct stat stb; + char *path = exportent_realpath(&exp->m_export); + struct statfs64 stf; + int fs_has_fsid = 0; + + if (stat(path, &stb) < 0) { + xlog(L_ERROR, "Failed to stat %s: %m", path); + return; + } + if (!S_ISDIR(stb.st_mode)) { + xlog(L_ERROR, "%s is not a directory. " + "Remote access will fail", path); + return; + } + if (!can_test()) + return; + + if (!statfs64(path, &stf) && + (stf.f_fsid.__val[0] || stf.f_fsid.__val[1])) + fs_has_fsid = 1; + + if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid || + fs_has_fsid) { + if ( !test_export(exp, 1)) { + xlog(L_ERROR, "%s does not support NFS export", path); + return; + } + } else if ( !test_export(exp, 0)) { + if (test_export(exp, 1)) + xlog(L_ERROR, "%s requires fsid= for NFS export", path); + else + xlog(L_ERROR, "%s does not support NFS export", path); + return; + + } +} + +static _Bool +is_hostname(const char *sp) +{ + if (*sp == '\0' || *sp == '@') + return false; + + for (; *sp != '\0'; sp++) { + if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/') + return false; + if (*sp == '\\' && sp[1] != '\0') + sp++; + } + + return true; +} + +/* + * Take care to perform an explicit reverse lookup on presentation + * addresses. Otherwise we don't get a real canonical name or a + * complete list of addresses. + */ +static struct addrinfo * +address_list(const char *hostname) +{ + struct addrinfo *ai; + char *cname; + + ai = host_pton(hostname); + if (ai != NULL) { + /* @hostname was a presentation address */ + cname = host_canonname(ai->ai_addr); + nfs_freeaddrinfo(ai); + if (cname != NULL) + goto out; + } + /* @hostname was a hostname or had no reverse mapping */ + cname = strdup(hostname); + if (cname == NULL) + return NULL; + +out: + ai = host_addrinfo(cname); + free(cname); + return ai; +} + +static int +matchhostname(const char *hostname1, const char *hostname2) +{ + struct addrinfo *results1 = NULL, *results2 = NULL; + struct addrinfo *ai1, *ai2; + int result = 0; + + if (strcasecmp(hostname1, hostname2) == 0) + return 1; + + /* + * Don't pass export wildcards or netgroup names to DNS + */ + if (!is_hostname(hostname1) || !is_hostname(hostname2)) + return 0; + + results1 = address_list(hostname1); + if (results1 == NULL) + goto out; + results2 = address_list(hostname2); + if (results2 == NULL) + goto out; + + if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) { + result = 1; + goto out; + } + + for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next) + for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next) + if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) { + result = 1; + break; + } + +out: + nfs_freeaddrinfo(results1); + nfs_freeaddrinfo(results2); + return result; +} + +#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT +__attribute__((format (printf, 2, 3))) +#endif +static char +dumpopt(char c, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + printf("%c", c); + vprintf(fmt, ap); + va_end(ap); + return ','; +} + +static void +dump(int verbose, int export_format) +{ + /* buf[] size should >= sizeof(struct exportent->e_path) */ + char buf[NFS_MAXPATHLEN+1] = { 0 }; + char *bp; + int len; + nfs_export *exp; + struct exportent *ep; + int htype; + char *hname, c; + + for (htype = 0; htype < MCL_MAXTYPES; htype++) { + for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { + ep = &exp->m_export; + if (!exp->m_xtabent) + continue; /* neilb */ + if (htype == MCL_ANONYMOUS) + hname = (export_format) ? "*" : ""; + else + hname = ep->e_hostname; + if (strlen(ep->e_path) > 14 && !export_format) + printf("%-14s\n\t\t%s", ep->e_path, hname); + else + if (export_format) { + bp = buf; + len = sizeof(buf) - 1; + qword_add(&bp, &len, ep->e_path); + *bp = '\0'; + printf("%s %s", buf, hname); + } else { + printf("%-14s\t%s", ep->e_path, hname); + } + + if (!verbose && !export_format) { + printf("\n"); + continue; + } + c = '('; + if (ep->e_flags & NFSEXP_ASYNC) + c = dumpopt(c, "async"); + else + c = dumpopt(c, "sync"); + if (ep->e_flags & NFSEXP_GATHERED_WRITES) + c = dumpopt(c, "wdelay"); + else + c = dumpopt(c, "no_wdelay"); + if (ep->e_flags & NFSEXP_NOHIDE) + c = dumpopt(c, "nohide"); + else + c = dumpopt(c, "hide"); + if (ep->e_flags & NFSEXP_CROSSMOUNT) + c = dumpopt(c, "crossmnt"); + if (ep->e_flags & NFSEXP_NOSUBTREECHECK) + c = dumpopt(c, "no_subtree_check"); + if (ep->e_flags & NFSEXP_NOAUTHNLM) + c = dumpopt(c, "insecure_locks"); + if (ep->e_flags & NFSEXP_NOREADDIRPLUS) + c = dumpopt(c, "nordirplus"); + if (ep->e_flags & NFSEXP_SECURITY_LABEL) + c = dumpopt(c, "security_label"); + if (ep->e_flags & NFSEXP_NOACL) + c = dumpopt(c, "no_acl"); + if (ep->e_flags & NFSEXP_PNFS) + c = dumpopt(c, "pnfs"); + if (ep->e_flags & NFSEXP_FSID) + c = dumpopt(c, "fsid=%d", ep->e_fsid); + if (ep->e_uuid) + c = dumpopt(c, "fsid=%s", ep->e_uuid); + if (ep->e_mountpoint) + c = dumpopt(c, "mountpoint%s%s", + ep->e_mountpoint[0]?"=":"", + ep->e_mountpoint); + if (ep->e_anonuid != 65534) + c = dumpopt(c, "anonuid=%d", ep->e_anonuid); + if (ep->e_anongid != 65534) + c = dumpopt(c, "anongid=%d", ep->e_anongid); + switch(ep->e_fslocmethod) { + case FSLOC_NONE: + break; + case FSLOC_REFER: + c = dumpopt(c, "refer=%s", ep->e_fslocdata); + break; + case FSLOC_REPLICA: + c = dumpopt(c, "replicas=%s", ep->e_fslocdata); + break; +#ifdef DEBUG + case FSLOC_STUB: + c = dumpopt(c, "fsloc=stub"); + break; +#endif + } + secinfo_show(stdout, ep); + printf("%c\n", (c != '(')? ')' : ' '); + } + } +} + +static void +usage(const char *progname, int n) +{ + fprintf(stderr, "usage: %s [-adfhioruvs] [host:/path]\n", progname); + exit(n); +} diff --git a/nfs-utils-2.5.2/utils/mountd/auth.c b/nfs-utils-2.5.2/utils/mountd/auth.c new file mode 100644 index 0000000..cd77550 --- /dev/null +++ b/nfs-utils-2.5.2/utils/mountd/auth.c @@ -0,0 +1,316 @@ +/* + * utils/mountd/auth.c + * + * Authentication procedures for mountd. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "sockaddr.h" +#include "misc.h" +#include "nfslib.h" +#include "exportfs.h" +#include "mountd.h" +#include "v4root.h" + +enum auth_error +{ + bad_path, + unknown_host, + no_entry, + not_exported, + illegal_port, + success +}; + +static void auth_fixpath(char *path); +static nfs_export my_exp; +static nfs_client my_client; + +extern int use_ipaddr; + +extern struct state_paths etab; + +void +auth_init(void) +{ + auth_reload(); +} + +/* + * A client can match many different netgroups and it's tough to know + * beforehand whether it will. If the concatenated string of netgroup + * m_hostnames is >512 bytes, then enable the "use_ipaddr" mode. This + * makes mountd change how it matches a client ip address when a mount + * request comes in. It's more efficient at handling netgroups at the + * expense of larger kernel caches. + */ +static void +check_useipaddr(void) +{ + nfs_client *clp; + int old_use_ipaddr = use_ipaddr; + unsigned int len = 0; + + /* add length of m_hostname + 1 for the comma */ + for (clp = clientlist[MCL_NETGROUP]; clp; clp = clp->m_next) + len += (strlen(clp->m_hostname) + 1); + + if (len > (NFSCLNT_IDMAX / 2)) + use_ipaddr = 1; + else + use_ipaddr = 0; + + if (use_ipaddr != old_use_ipaddr) + cache_flush(1); +} + +unsigned int +auth_reload(void) +{ + struct stat stb; + static ino_t last_inode; + static int last_fd = -1; + static unsigned int counter; + int fd; + + if ((fd = open(etab.statefn, O_RDONLY)) < 0) { + xlog(L_FATAL, "couldn't open %s", etab.statefn); + } else if (fstat(fd, &stb) < 0) { + xlog(L_FATAL, "couldn't stat %s", etab.statefn); + close(fd); + } else if (last_fd != -1 && stb.st_ino == last_inode) { + /* We opened the etab file before, and its inode + * number hasn't changed since then. + */ + close(fd); + return counter; + } else { + /* Need to process entries from the etab file. Close + * the file descriptor from the previous open (last_fd), + * and keep the current file descriptor open to prevent + * the file system reusing the current inode number + * (last_inode). + */ + if (last_fd != -1) + close(last_fd); + last_fd = fd; + last_inode = stb.st_ino; + } + + export_freeall(); + memset(&my_client, 0, sizeof(my_client)); + xtab_export_read(); + check_useipaddr(); + v4root_set(); + + ++counter; + + return counter; +} + +static char *get_client_ipaddr_name(const struct sockaddr *caller) +{ + char buf[INET6_ADDRSTRLEN + 1]; + + buf[0] = '$'; + host_ntop(caller, buf + 1, sizeof(buf) - 1); + return strdup(buf); +} + +static char * +get_client_hostname(const struct sockaddr *caller, struct addrinfo *ai, + enum auth_error *error) +{ + char *n; + + if (use_ipaddr) + return get_client_ipaddr_name(caller); + n = client_compose(ai); + *error = unknown_host; + if (!n) + return NULL; + if (*n) + return n; + free(n); + return strdup("DEFAULT"); +} + +bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai) +{ + return client_check(exp->m_client, ai); +} + +bool namelist_client_matches(nfs_export *exp, char *dom) +{ + return client_member(dom, exp->m_client->m_hostname); +} + +bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai) +{ + if (is_ipaddr_client(dom)) + return ipaddr_client_matches(exp, ai); + return namelist_client_matches(exp, dom); +} + +/* return static nfs_export with details filled in */ +static nfs_export * +auth_authenticate_newcache(const struct sockaddr *caller, + const char *path, struct addrinfo *ai, + enum auth_error *error) +{ + nfs_export *exp; + int i; + + free(my_client.m_hostname); + + my_client.m_hostname = get_client_hostname(caller, ai, error); + if (my_client.m_hostname == NULL) + return NULL; + + my_client.m_naddr = 1; + set_addrlist(&my_client, 0, caller); + my_exp.m_client = &my_client; + + exp = NULL; + for (i = 0; !exp && i < MCL_MAXTYPES; i++) + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (strcmp(path, exp->m_export.e_path)) + continue; + if (!client_matches(exp, my_client.m_hostname, ai)) + continue; + if (exp->m_export.e_flags & NFSEXP_V4ROOT) + /* not acceptable for v[23] export */ + continue; + break; + } + *error = not_exported; + if (!exp) + return NULL; + + my_exp.m_export = exp->m_export; + exp = &my_exp; + return exp; +} + +static nfs_export * +auth_authenticate_internal(const struct sockaddr *caller, const char *path, + struct addrinfo *ai, enum auth_error *error) +{ + nfs_export *exp; + + exp = auth_authenticate_newcache(caller, path, ai, error); + if (!exp) + return NULL; + if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && + nfs_get_port(caller) >= IPPORT_RESERVED) { + *error = illegal_port; + return NULL; + } + *error = success; + + return exp; +} + +nfs_export * +auth_authenticate(const char *what, const struct sockaddr *caller, + const char *path) +{ + nfs_export *exp = NULL; + char epath[MAXPATHLEN+1]; + char *p = NULL; + char buf[INET6_ADDRSTRLEN]; + struct addrinfo *ai = NULL; + enum auth_error error = bad_path; + + if (path[0] != '/') { + xlog(L_WARNING, "Bad path in %s request from %s: \"%s\"", + what, host_ntop(caller, buf, sizeof(buf)), path); + return exp; + } + + strncpy(epath, path, sizeof (epath) - 1); + epath[sizeof (epath) - 1] = '\0'; + auth_fixpath(epath); /* strip duplicate '/' etc */ + + ai = client_resolve(caller); + if (ai == NULL) + return exp; + + /* Try the longest matching exported pathname. */ + while (1) { + exp = auth_authenticate_internal(caller, epath, ai, &error); + if (exp || (error != not_exported && error != no_entry)) + break; + /* We have to treat the root, "/", specially. */ + if (p == &epath[1]) break; + p = strrchr(epath, '/'); + if (p == epath) p++; + *p = '\0'; + } + + host_ntop(caller, buf, sizeof(buf)); + switch (error) { + case bad_path: + xlog(L_WARNING, "bad path in %s request from %s: \"%s\"", + what, buf, path); + break; + + case unknown_host: + xlog(L_WARNING, "refused %s request from %s for %s (%s): unmatched host", + what, buf, path, epath); + break; + + case no_entry: + xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry", + what, buf, path, epath); + break; + + case not_exported: + xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported", + what, buf, path, epath); + break; + + case illegal_port: + xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %u", + what, buf, path, epath, nfs_get_port(caller)); + break; + + case success: + xlog(L_NOTICE, "authenticated %s request from %s:%u for %s (%s)", + what, buf, nfs_get_port(caller), path, epath); + break; + default: + xlog(L_NOTICE, "%s request from %s:%u for %s (%s) gave %d", + what, buf, nfs_get_port(caller), path, epath, error); + } + + nfs_freeaddrinfo(ai); + return exp; +} + +static void +auth_fixpath(char *path) +{ + char *sp, *cp; + + for (sp = cp = path; *sp; sp++) { + if (*sp != '/' || sp[1] != '/') + *cp++ = *sp; + } + while (cp > path+1 && cp[-1] == '/') + cp--; + *cp = '\0'; +} diff --git a/nfs-utils-2.5.2/utils/mountd/v4root.c b/nfs-utils-2.5.2/utils/mountd/v4root.c new file mode 100644 index 0000000..670cd88 --- /dev/null +++ b/nfs-utils-2.5.2/utils/mountd/v4root.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2009 Red Hat + * + * support/export/v4root.c + * + * Routines used to support NFSv4 pseudo roots + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xlog.h" +#include "exportfs.h" +#include "nfslib.h" +#include "misc.h" +#include "v4root.h" +#include "pseudoflavors.h" + +static nfs_export pseudo_root = { + .m_next = NULL, + .m_client = NULL, + .m_export = { + .e_hostname = "*", + .e_path = "/", + .e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH + | NFSEXP_NOSUBTREECHECK | NFSEXP_FSID + | NFSEXP_V4ROOT, + .e_anonuid = 65534, + .e_anongid = 65534, + .e_squids = NULL, + .e_nsquids = 0, + .e_sqgids = NULL, + .e_nsqgids = 0, + .e_fsid = 0, + .e_mountpoint = NULL, + .e_ttl = DEFAULT_TTL, + }, + .m_exported = 0, + .m_xtabent = 1, + .m_mayexport = 1, + .m_changed = 0, + .m_warned = 0, +}; + +static void +set_pseudofs_security(struct exportent *pseudo, int flags) +{ + struct flav_info *flav; + int i; + + if (flags & NFSEXP_INSECURE_PORT) + pseudo->e_flags |= NFSEXP_INSECURE_PORT; + if ((flags & NFSEXP_ROOTSQUASH) == 0) + pseudo->e_flags &= ~NFSEXP_ROOTSQUASH; + for (flav = flav_map; flav < flav_map + flav_map_size; flav++) { + struct sec_entry *new; + + if (!flav->fnum) + continue; + + i = secinfo_addflavor(flav, pseudo); + new = &pseudo->e_secinfo[i]; + + if (flags & NFSEXP_INSECURE_PORT) + new->flags |= NFSEXP_INSECURE_PORT; + } +} + +/* + * Create a pseudo export + */ +static struct exportent * +v4root_create(char *path, nfs_export *export) +{ + nfs_export *exp; + struct exportent eep; + struct exportent *curexp = &export->m_export; + + dupexportent(&eep, &pseudo_root.m_export); + eep.e_hostname = curexp->e_hostname; + strncpy(eep.e_path, path, sizeof(eep.e_path)-1); + if (strcmp(path, "/") != 0) + eep.e_flags &= ~NFSEXP_FSID; + set_pseudofs_security(&eep, curexp->e_flags); + exp = export_create(&eep, 0); + if (exp == NULL) + return NULL; + xlog(D_CALL, "v4root_create: path '%s' flags 0x%x", + exp->m_export.e_path, exp->m_export.e_flags); + return &exp->m_export; +} + +/* + * Make sure the kernel has pseudo root support. + */ +static int +v4root_support(void) +{ + struct export_features *ef; + static int warned = 0; + + ef = get_export_features(); + + if (ef->flags & NFSEXP_V4ROOT) + return 1; + if (!warned) { + xlog(L_WARNING, "Kernel does not have pseudo root support."); + xlog(L_WARNING, "NFS v4 mounts will be disabled unless fsid=0"); + xlog(L_WARNING, "is specfied in /etc/exports file."); + warned++; + } + return 0; +} + +static int +pseudofs_update(char *hostname, char *path, nfs_export *source) +{ + nfs_export *exp; + + exp = export_lookup(hostname, path, 0); + if (exp && !(exp->m_export.e_flags & NFSEXP_V4ROOT)) + return 0; + if (!exp) { + if (v4root_create(path, source) == NULL) { + xlog(L_WARNING, "v4root_set: Unable to create " + "pseudo export for '%s'", path); + return -ENOMEM; + } + return 0; + } + /* Update an existing V4ROOT export: */ + set_pseudofs_security(&exp->m_export, source->m_export.e_flags); + return 0; +} + +static int v4root_add_parents(nfs_export *exp) +{ + char *hostname = exp->m_export.e_hostname; + char *path; + char *ptr; + int ret = 0; + + path = strdup(exp->m_export.e_path); + if (!path) { + xlog(L_WARNING, "v4root_add_parents: Unable to create " + "pseudo export for '%s'", exp->m_export.e_path); + return -ENOMEM; + } + for (ptr = path; ptr; ptr = strchr(ptr, '/')) { + char saved; + + saved = *ptr; + *ptr = '\0'; + ret = pseudofs_update(hostname, *path ? path : "/", exp); + if (ret) + break; + *ptr = saved; + ptr++; + } + free(path); + return ret; +} + +/* + * Create pseudo exports by running through the real export + * looking at the components of the path that make up the export. + * Those path components, if not exported, will become pseudo + * exports allowing them to be found when the kernel does an upcall + * looking for components of the v4 mount. + */ +void +v4root_set(void) +{ + nfs_export *exp; + int i; + + if (!v4root_needed) + return; + if (!v4root_support()) + return; + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (exp->m_export.e_flags & NFSEXP_V4ROOT) + /* + * We just added this one, so its + * parents are already dealt with! + */ + continue; + + if (strcmp(exp->m_export.e_path, "/") == 0 && + !(exp->m_export.e_flags & NFSEXP_FSID)) { + /* Force '/' to be exported as fsid == 0*/ + exp->m_export.e_flags |= NFSEXP_FSID; + exp->m_export.e_fsid = 0; + } + + v4root_add_parents(exp); + /* XXX: error handling! */ + } + } +}