/*-
 * customsNorm.c --
 *	Normalize directory path for export
 *
 * Copyright (c) 1988, 1989 by the Regents of the University of California
 * Copyright (c) 1988, 1989 by Adam de Boor
 * Copyright (c) 1989 by Berkeley Softworks
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any non-commercial purpose
 * and without fee is hereby granted, provided that the above copyright
 * notice appears in all copies.  The University of California,
 * Berkeley Softworks and Adam de Boor make no representations about
 * the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 */
#ifndef lint
static char *rcsid =
"$Id: customsNorm.c,v 1.6 2000/07/05 00:59:43 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <string.h>
#include    <stdio.h>
#include    <sys/types.h>
#include    <netdb.h>
#include    <errno.h>
extern int  errno;
#include    <sys/param.h>

#include    "customs.h"

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif


#ifndef ORIG_CWD

#ifdef linux
#include    <sys/vfs.h>
#else /* !linux */
#ifdef SVR4
#include    <sys/statvfs.h>
#else /* !SVR4 */
#ifdef sgi
#include    <sys/statfs.h>
#else /* !sgi */
#ifdef bsdi
#include    <sys/mount.h>
#else /* !bsdi */
#include    <sys/stat.h>
#endif /* bsdi */
#endif  /* sgi */
#endif /* SVR4 */
#endif /* linux */

/*-
 *-----------------------------------------------------------------------
 * CustomsRemotePath --
 *	Check whether a directory reside on a remote file system.
 *
 * Results:
 *	1 if it does, 0 if is doesn't, -1 if a system call error occurred.
 *
 * Side Effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */
static int
CustomsRemotePath(path)
    char	*path;		/* pointer to path buffer */
{
    /*
     * XXX: This is a mess because there is not uniform system interface
     * to get this information.
     */

#ifdef linux
    struct statfs buf;

    /*
     * XXX: including <linux/nfs_fs.h> to define NFS_SUPER_MAGIC
     * causes no end of trouble...
     */
    if (statfs(path, &buf) < 0) {
	return -1;
    }
    return (buf.f_type == 0x6969 /* NFS_SUPER_MAGIC */);
#else /* !linux */
#ifdef SVR4
    struct statvfs buf;
    char *cp;

    /*
     * In SVR4 we can check the filesystem type directly.
     */
    if (statvfs(path, &buf) < 0) {
	return -1;
    }

    /* 
     * If the filesystem type is NFS assume it is a remote filesystem
     */
    if (strcmp(buf.f_basetype, "nfs") == 0) {
	return 1;
    }

    /*
     * If any path prefix is an automount point also assume the filesystem
     * can be accessed remotely.
     */
    for (cp = strchr(path + 1, '/'); cp != NULL;  cp = strchr(cp + 1, '/')) {
	*cp = '\0';

	if (statvfs(path, &buf) < 0) {
	    *cp = '/';
	    return -1;
	}

	if (strcmp(buf.f_basetype, "autofs") == 0) {
	    *cp = '/';
	    return 1;
	}

	*cp = '/';
    }
    return 0;
#else /* !SVR4 */
#ifdef sgi
    struct statfs buf;

    /*
     * IRIX has a non-standard statfs() call, but at least it returns
     * a useful filesystem type!
     */
    if (statfs(path, &buf, sizeof(buf), 0) < 0) {
	return -1;
    }
    return (buf.f_fstyp == 1);	/* 1 means NFS, empirically */
#else /* !SVR4 && !sgi */
#ifdef bsdi
    struct statfs buf;

    /*
     * 4.4BSD finally has statfs() that returns useful info in f_type.
     */
    if (statfs(path, &buf) < 0) {
	return -1;
    }
    return (buf.f_type == MOUNT_NFS);
#else /* !SVR4 && !sgi && !bsdi */
    struct stat buf;

    /*
     * On all other systems, it *seems* that NFS-mounted filesystems
     * always get a negative device number.
     */
    if (stat(path, &buf) < 0) {
	return -1;
    }
    return (buf.st_dev < 0);
#endif /* bsdi */
#endif /* sgi */
#endif /* SVR4 */
#endif /* linux */
}
#endif /* ORIG_CWD */


/*-
 *-----------------------------------------------------------------------
 * Customs_NormPath --
 *	Normalize path so that it can be used all over the network.
 *	This function has to be customized according to the local
 *	mounting conventions.
 *
 * Results:
 *	0 if goes well, -1 if an error occurs.
 *
 * Side Effects:
 *	The path string is modified in place. An error message is
 *	printed on stderr if normalization fails for some reason.
 *
 *-----------------------------------------------------------------------
 */
#ifndef TMPMNT
#define TMPMNT	"/tmp_mnt/"
#endif

/*ARGSUSED*/
int
Customs_NormPath(path, pathlen)
    char	*path;		/* pointer to path buffer */
    int		pathlen;	/* size of path buffer */
{
    /*
     * Relative paths cannot be normalized.
     */ 
    if (*path != '/') {
	errno = EINVAL;
	return -1;
    }

#ifndef ORIG_CWD
    /*
     * Also, local /tmp directories are usually not exported.  Assume that the
     * user wants to use the remote /tmp directory -- this is also convenient
     * for testing on hosts that don't have an automounter.
     */
    if (strncmp(path, "/tmp", pathlen) == 0 ||
	strncmp(path, "/usr/tmp", pathlen) == 0 ||
	strncmp(path, "/var/tmp", pathlen) == 0)
    {
	return 0;
    }

    /*
     * We fudge a uniform file name space here under the assumption that
     * we're running an automounter with a /n/host style mount map.
     */
    if (strncmp(path, TMPMNT, strlen(TMPMNT)) == 0) {
	/*
	 * strip tmp_mnt path
	 */
	memmove(path, path+sizeof(TMPMNT)-2, strlen(path+sizeof(TMPMNT)-2) + 1);
#ifdef NETMNT
    } else if (strncmp(path, NETMNT, strlen(NETMNT)) == 0) {
	/*
	 * Path has network automount prefix -- should work remotely, too.
	 */
	return 0;
    } else {
	int isRemote;
	char hname[MAXHOSTNAMELEN];
	char *dot;

	/*
	 * One more heuristic [pomeranz@aqm.com (Hal Pomeranz)]:
	 * If the filesystem is NFS mounted but doesn't have the automounter
	 * prefix, assume its uniformly mounted on all machines, hence
	 * doesn't need editing.
	 */
	isRemote = CustomsRemotePath(path);
	if (isRemote < 0) {
	    return -1;
	} else if (isRemote) {
	    return 0;
	}
		
	/*
	 * prepend /n/hostname path
	 */
	if (gethostname(hname, sizeof(hname)) == -1) {
	    return -1;
	}

	/* strip domain name */
	if (dot = strchr(hname, '.')) {
	    *dot = '\0';
	}

	if (sizeof(NETMNT)+strlen(hname)+strlen(path) > pathlen) {
	    errno = ERANGE;
	    return -1;
	}
	memmove(path+sizeof(NETMNT)-1+strlen(hname), path, strlen(path)+1);
	memcpy(path, NETMNT, strlen(NETMNT));
	memcpy(path+strlen(NETMNT), hname, strlen(hname));
#endif /* NETMNT */
    }
#endif /* ! ORIG_CWD */
    return 0;
}

#ifdef STANDALONE
main(argc, argv)
    int argc;
    char **argv;
{
    int i;
    char path[MAXPATHLEN];

    for (i = 1; i < argc; i++) {
	strncpy(path, argv[i], sizeof(path));
	if (Customs_NormPath(path, sizeof(path)) < 0) {
	    perror(argv[i]);
	} else {
	    printf("%s\n", path);
	}
    }
    exit (0);
}
#endif /* STANDALONE */
