blob: c888a80aa74143a6ba18e8296467552f9c2f7847 [file] [log] [blame]
/*
* support/export/xtab.c
*
* Interface to the etab/exports file.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>
#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;
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()
{
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);
}