X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fexportfs%2Fexportfs.c;h=9f7954130ee2adf9579f933ec44134d25ed141da;hp=b78957f249f44ae6c2226984e241ed6a2eb0fded;hb=HEAD;hpb=38861db18a0687aaf483ea0bf03bf8c928ca0980 diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index b78957f..9f79541 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include "sockaddr.h" #include "misc.h" @@ -38,9 +40,45 @@ static void unexportfs(char *arg, int verbose); static void exports_update(int verbose); static void dump(int verbose); static void error(nfs_export *exp, int err); -static void usage(const char *progname); +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 export_d_read(const char *dname); +static void grab_lockfile(void); +static void release_lockfile(void); + +static const char *lockfile = EXP_LOCKFILE; +static int _lockfd = -1; + +/* + * 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() +{ + _lockfd = open(lockfile, O_CREAT|O_RDWR, 0666); + if (_lockfd != -1) + lockf(_lockfd, F_LOCK, 0); +} +static void +release_lockfile() +{ + if (_lockfd != -1) + lockf(_lockfd, F_ULOCK, 0); +} int main(int argc, char **argv) @@ -67,11 +105,17 @@ main(int argc, char **argv) export_errno = 0; - while ((c = getopt(argc, argv, "aio:ruvf")) != EOF) { + while ((c = getopt(argc, argv, "afhio:ruv")) != EOF) { switch(c) { case 'a': f_all = 1; break; + case 'f': + force_flush = 1; + break; + case 'h': + usage(progname, 0); + break; case 'i': f_ignore = 1; break; @@ -88,11 +132,8 @@ main(int argc, char **argv) case 'v': f_verbose = 1; break; - case 'f': - force_flush = 1; - break; default: - usage(progname); + usage(progname, 1); break; } } @@ -127,8 +168,17 @@ main(int argc, char **argv) return 0; } } - if (f_export && ! f_ignore) + + /* + * Serialize things as best we can + */ + grab_lockfile(); + atexit(release_lockfile); + + if (f_export && ! f_ignore) { export_read(_PATH_EXPORTS); + export_d_read(_PATH_EXPORTS_D); + } if (f_export) { if (f_all) export_all(f_verbose); @@ -246,7 +296,7 @@ static void exportfs(char *arg, char *options, int verbose) { struct exportent *eep; - nfs_export *exp; + nfs_export *exp = NULL; struct addrinfo *ai = NULL; char *path; char *hname = arg; @@ -397,7 +447,7 @@ validate_export(nfs_export *exp) int fs_has_fsid = 0; if (stat(path, &stb) < 0) { - xlog(L_ERROR, "Failed to stat %s: %m \n", path); + xlog(L_ERROR, "Failed to stat %s: %m", path); return; } if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { @@ -444,6 +494,36 @@ is_hostname(const char *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); + 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) { @@ -460,10 +540,10 @@ matchhostname(const char *hostname1, const char *hostname2) if (!is_hostname(hostname1) || !is_hostname(hostname2)) return 0; - results1 = host_addrinfo(hostname1); + results1 = address_list(hostname1); if (results1 == NULL) goto out; - results2 = host_addrinfo(hostname2); + results2 = address_list(hostname2); if (results2 == NULL) goto out; @@ -485,6 +565,62 @@ out: return result; } +/* Based on mnt_table_parse_dir() in + util-linux-ng/shlibs/mount/src/tab_parse.c */ +static void +export_d_read(const char *dname) +{ + int n = 0, i; + struct dirent **namelist = NULL; + + + n = scandir(dname, &namelist, NULL, versionsort); + if (n < 0) { + if (errno == ENOENT) + /* Silently return */ + return; + xlog(L_NOTICE, "scandir %s: %s", dname, strerror(errno)); + } else if (n == 0) + return; + + for (i = 0; i < n; i++) { + struct dirent *d = namelist[i]; + size_t namesz; + char fname[PATH_MAX + 1]; + int fname_len; + + + if (d->d_type != DT_UNKNOWN + && d->d_type != DT_REG + && d->d_type != DT_LNK) + continue; + if (*d->d_name == '.') + continue; + +#define _EXT_EXPORT_SIZ (sizeof(_EXT_EXPORT) - 1) + namesz = strlen(d->d_name); + if (!namesz + || namesz < _EXT_EXPORT_SIZ + 1 + || strcmp(d->d_name + (namesz - _EXT_EXPORT_SIZ), + _EXT_EXPORT)) + continue; + + fname_len = snprintf(fname, PATH_MAX +1, "%s/%s", dname, d->d_name); + if (fname_len > PATH_MAX) { + xlog(L_WARNING, "Too long file name: %s in %s", d->d_name, dname); + continue; + } + + export_read(fname); + } + + for (i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + + return; +} + static char dumpopt(char c, char *fmt, ...) { @@ -585,13 +721,13 @@ dump(int verbose) static void error(nfs_export *exp, int err) { - xlog(L_ERROR, "%s:%s: %s\n", exp->m_client->m_hostname, + xlog(L_ERROR, "%s:%s: %s", exp->m_client->m_hostname, exp->m_export.e_path, strerror(err)); } static void -usage(const char *progname) +usage(const char *progname, int n) { - fprintf(stderr, "usage: %s [-aruv] [host:/path]\n", progname); - exit(1); + fprintf(stderr, "usage: %s [-afhioruv] [host:/path]\n", progname); + exit(n); }