/*
 * Copyright 2009 Oracle.  All rights reserved.
 * Copyright 2017 Red Hat, Inc.  All rights reserved.
 *
 * This file is part of nfs-utils.
 *
 * nfs-utils is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * nfs-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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sys/stat.h>

#include <string.h>
#include <libgen.h>
#include <stdio.h>
#include <errno.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>

#include "xlog.h"
#include "misc.h"

/*
 * 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).
 */
__attribute__((__malloc__))
char *
generic_make_pathname(const char *base, const char *leaf)
{
	size_t size;
	char *path;
	int len;

	size = strlen(base) + strlen(leaf) + 2;
	if (size > PATH_MAX)
		return NULL;

	path = malloc(size);
	if (path == NULL)
		return NULL;

	len = snprintf(path, size, "%s/%s", base, leaf);
	if ((len < 0) || ((size_t)len >= size)) {
		free(path);
		return NULL;
	}

	return path;
}


/**
 * generic_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
 * @base: character buffer to contain the basedir that is set up
 * @baselen: size of @base in bytes
 *
 * 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
generic_setup_basedir(const char *progname, const char *parentdir, char *base,
		      const size_t baselen)
{
	static char buf[PATH_MAX];
	struct stat st;
	char *path;

	/* First: test length of name and whether it exists */
	if ((strlen(parentdir) >= baselen) || (strlen(parentdir) >= PATH_MAX)) {
		(void)fprintf(stderr, "%s: Directory name too long: %s",
				progname, parentdir);
		return false;
	}
	if (lstat(parentdir, &st) == -1) {
		(void)fprintf(stderr, "%s: Failed to stat %s: %s",
				progname, parentdir, strerror(errno));
		return false;
	}

	/* Ensure we have a clean directory pathname */
	strncpy(buf, parentdir, sizeof(buf)-1);
	path = dirname(buf);
	if (*path == '.') {
		(void)fprintf(stderr, "%s: Unusable directory %s",
				progname, parentdir);
		return false;
	}

	xlog(D_CALL, "Using %s as the state directory", parentdir);
	strcpy(base, parentdir);
	return true;
}
