/*
 * fop.c: iml file operation - interface
 */

#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>

#include "iml_fop.h"
#include "fop.h"

#define URI(uri, path)	((NULL != (uri)) ? (uri) : (path))

typedef enum {
	FOP__FOPC_CREATE = 0,
	FOP__FOPC_FREE,

	FOP_OPEN,
	FOP_CLOSE,
	FOP_CREAT,
	FOP_READ,
	FOP_READV,
	FOP_WRITE,
	FOP_WRITEV,
	FOP_LSEEK,
	FOP_STAT,
	FOP_LSTAT,
	FOP_FSTAT,
	FOP_SYMLINK,
	FOP_READLINK,
	FOP_LINK,
	FOP_UNLINK,
	FOP_RENAME,
	FOP_FCNTL,
	FOP_TRUNCATE,
	FOP_FTRUNCATE,
	FOP_MKDIR,
	FOP_RMDIR,
	FOP_OPENDIR,
	FOP_READDIR,
	FOP_CLOSEDIR,
	FOP_ACCESS,
	FOP_CHMOD,
	FOP_FCHMOD,
	FOP_CHOWN,
#if 0
	FOP_LCHOWN,
#endif /* 0 */
	FOP_FCHOWN,
	FOP_PATHCONF,
	FOP_FPATHCONF,
	FOP_UTIME,
	FOP_UTIMES,

	FOP_FUNC_LAST		/* last element */
} fop_func_type_t;


typedef struct _fop_scheme {
    char *			name;
    int				id;
    void **			function;
    struct _fop_scheme *	next;
} fop_scheme_t;


typedef struct {
    fop_func_type_t	type;
    char *		name;
    void *		function;
} fop_name_function_t;


struct _fop_file {
    int			id;
    int			scheme;	/* scheme */
    void *		value;	/* scheme specific value */
    struct _fop_file *	next;
};


static void	fop_scheme_initialize(void);

static fop_scheme_t *	fop_scheme;


static fop_file_t *
fop_file_insert(
    fop_file_t *	head,
    fop_file_t *	file
)
{
    int			id;
    fop_file_t *	cur;
    fop_file_t *	prev;

    if (NULL == head) {
	file->id = 0;
	file->next = NULL;
	return file;
    }

    id = 0;
    prev = NULL;
    for (cur = head; NULL != cur; cur = cur->next, id += 1) {
	if (id != cur->id) {
	    file->id = id;
	    file->next = cur;
	    if (NULL == prev) {
		return file;
	    } else {
		prev->next = file;
		return head;
	    }
	}
	prev = cur;
    }

    file->id = id;
    prev->next = file;
    file->next = NULL;

    return head;
}


static fop_file_t *
fop_file_remove_id(
    fop_file_t *	head,
    int			id
)
{
    fop_file_t *	cur;
    fop_file_t *	prev;

    if (NULL == head) {
	return NULL;
    }

    prev = NULL;
    for (cur = head; NULL != cur; cur = cur->next) {
	if (id == cur->id) {
	    if (NULL == prev) return cur->next;

	    prev->next = cur->next;
	    return head;
	}
	prev = cur;
    }

    return head;
}


static fop_file_t *
fop_file_remove_value(
    fop_file_t *	head,
    void *		value
)
{
    fop_file_t *	cur;
    fop_file_t *	prev;

    if (NULL == head) {
	return NULL;
    }

    prev = NULL;
    for (cur = head; NULL != cur; cur = cur->next) {
	if (value == cur->value) {
	    if (NULL == prev) return cur->next;

	    prev->next = cur->next;
	    return head;
	}
	prev = cur;
    }

    return head;
}


static fop_file_t *
fop_file_search_id(
    fop_file_t *	head,
    int			id
)
{
    fop_file_t *	cur;

    if (NULL == head) {
	return NULL;
    }

    for (cur = head; NULL != cur; cur = cur->next) {
	if (id == cur->id) return cur;
    }

    return NULL;
}


static fop_file_t *
fop_file_search_value(
    fop_file_t *	head,
    void *		value
)
{
    fop_file_t *	cur;

    if (NULL == head) {
	return NULL;
    }

    for (cur = head; NULL != cur; cur = cur->next) {
	if (value == cur->value) return cur;
    }

    return NULL;
}


static int
fop_path_to_uri(
    fopc_t *		fopc,
    const char *	path,
    char **		uri
)
{
    /*
     * experimental implementation.
     * configuration file is not consulted.
     */

    int			scheme;
    char *		u;
    int			length;
    static char *	base_dir;
    static char *	var_dir;
    static int		base_dir_len;
    static int		var_dir_len;
    static int		initialized;

    /*
     * hard-coded mapping.  experimental purpose.
     */

    if (0 == initialized) {
	initialized = 1;

	var_dir = getenv("IIIMD_OPTION_VARDIR");
	if (NULL != var_dir) {
	    var_dir = strdup(var_dir);
	    if (NULL == var_dir) return -1;
	    var_dir_len = strlen(var_dir);
	    base_dir = (char *)malloc(PATH_MAX);
	    if (NULL == base_dir) {
		free(var_dir);
		return -1;
	    }

	    snprintf(base_dir, PATH_MAX, "/var/lib/iiim/le/%s", fopc->le);
	    base_dir_len = strlen(base_dir);
	}
    }

    scheme = 0;

    if (NULL == var_dir) {
	*uri = NULL;
    } else {
	if ((0 == strncmp(path, base_dir, base_dir_len)) &&
	    (('/' == *(path + base_dir_len)) ||
	     ('\0' == *(path + base_dir_len)))) {
	    length = var_dir_len;
	    length += 1;
	    length += strlen(fopc->le);
	    length += 1;
	    if ('/' == *(path + base_dir_len)) {
		length += (strlen(path) - strlen(base_dir));
		length += 1;
	    }
	    u = (char *)malloc(length);
	    if (NULL == u) return -1;
	    snprintf(u, length, "%s/%s%s",
		     var_dir, fopc->le, path + base_dir_len);
	    *uri = u;
	} else {
	    *uri = NULL;
	}
    }

    return scheme;
}


static void *
fop_scheme_func(
    int			scheme,
    fop_func_type_t	func_type
)
{
    fop_scheme_t *	s;

    if (NULL == fop_scheme) fop_scheme_initialize();

    for (s = fop_scheme; NULL != s; s = s->next) {
	if (scheme == s->id) return *(s->function + func_type);
    }

    return NULL;
}


static void *
fop__fopc_create(
    const char *	le,
    int			type,
    void *		owner
)
{
    fopc_t *		fopc;

    fopc = (fopc_t *)malloc(sizeof (fopc_t));
    if (NULL == fopc) return NULL;

    fopc->le = strdup(le);
    if (NULL == fopc->le) {
	free(fopc);
	return NULL;
    }
    fopc->type = type;
    fopc->owner = owner;
    fopc->file = NULL;

    return fopc;
}


static void
fop__fopc_free(
    void *	fopc
)
{
    if (NULL != fopc) free(((fopc_t *)fopc)->le);
    free(fopc);

    return;
}


static int
fop_open(
    void *		fopc,
    const char *	path,
    int			oflag,
    ...
)
{
    va_list		ap;
    int			scheme;
    char *		uri;
    mode_t		mode;
    fop_open_t		func;
    int			fop_errno;
    void *		value;
    fop_file_t *	file;

    if (0 != (O_CREAT & oflag)) {
	va_start(ap, oflag);
	mode = va_arg(ap, mode_t);
	va_end(ap);
    } else {
	mode = 0;
    }

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_open_t)fop_scheme_func(scheme, FOP_OPEN);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    file = (fop_file_t *)malloc(sizeof (fop_file_t));
    if (NULL == file) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
	return -1;
    }

    value = func(fopc, URI(uri, path), oflag, mode);
    if (NULL == value) {
	fop_errno = errno;
	free(uri);
	free(file);
	errno = fop_errno;
        return -1;
    }

    file->scheme = scheme;
    file->value = value;

    ((fopc_t *)fopc)->file = fop_file_insert(((fopc_t *)fopc)->file, file);

    free(uri);

    return file->id;
}


static int
fop_close(
    void *		fopc,
    int			fd
)
{
    fop_close_t		func;
    int			rv;
    int			fop_errno;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fd);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_close_t)fop_scheme_func(file->scheme, FOP_CLOSE);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, file->value);

    ((fopc_t *)fopc)->file = fop_file_remove_id(((fopc_t *)fopc)->file, fd);

    fop_errno = errno;
    free(file);
    errno = fop_errno;

    return rv;
}


static int
fop_creat(
    void *		fopc,
    const char *	path,
    mode_t		mode
)
{
    return fop_open(fopc, path, (O_WRONLY | O_CREAT | O_TRUNC), mode);

#if 0
    int			scheme;
    char *		uri;
    fop_creat_t		func;
    int			fop_errno;
    void *		value;
    fop_file_t *	file;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_creat_t)fop_scheme_func(scheme, FOP_CREAT);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    file = (fop_file_t *)malloc(sizeof (fop_file_t));
    if (NULL == file) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
	return -1;
    }

    value = func(fopc, URI(uri, path), mode);
    if (NULL == value) {
	fop_errno = errno;
	free(uri);
	free(file);
	errno = fop_errno;
        return -1;
    }

    file->scheme = scheme;
    file->value = value;

    ((fopc_t *)fopc)->file = fop_file_insert(((fopc_t *)fopc)->file, file);

    free(uri);

    return file->id;
#endif /* 0 */
}


static ssize_t
fop_read(
    void *		fopc,
    int			fildes,
    void *		buf,
    size_t		nbyte
)
{
    fop_read_t		func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_read_t)fop_scheme_func(file->scheme, FOP_READ);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, buf, nbyte);
}


static ssize_t
fop_readv(
    void *			fopc,
    int				fildes,
    const struct iovec *	iov,
    int				iovcnt
)
{
    fop_readv_t		func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_readv_t)fop_scheme_func(file->scheme, FOP_READV);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, iov, iovcnt);
}


static ssize_t
fop_write(
    void *		fopc,
    int			fildes,
    const void *	buf,
    size_t		nbyte
)
{
    fop_write_t		func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_write_t)fop_scheme_func(file->scheme, FOP_WRITE);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, buf, nbyte);
}


static ssize_t
fop_writev(
    void *			fopc,
    int				fildes,
    const struct iovec *	iov,
    int				iovcnt
)
{
    fop_writev_t	func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_writev_t)fop_scheme_func(file->scheme, FOP_WRITEV);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, iov, iovcnt);
}


static off_t
fop_lseek(
    void *		fopc,
    int			fildes,
    off_t		offset,
    int			whence
)
{
    fop_lseek_t		func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_lseek_t)fop_scheme_func(file->scheme, FOP_LSEEK);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, offset, whence);
}


static int
fop_stat(
    void *		fopc,
    const char *	path,
    struct stat *	buf
)
{
    int			scheme;
    char *		uri;
    fop_stat_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_stat_t)fop_scheme_func(scheme, FOP_STAT);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), buf);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_lstat(
    void *		fopc,
    const char *	path,
    struct stat *	buf
)
{
    int			scheme;
    char *		uri;
    fop_lstat_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_lstat_t)fop_scheme_func(scheme, FOP_LSTAT);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), buf);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_fstat(
    void *		fopc,
    int			fildes,
    struct stat *	buf
)
{
    fop_fstat_t		func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_fstat_t)fop_scheme_func(file->scheme, FOP_FSTAT);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, buf);
}


static int
fop_symlink(
    void *		fopc,
    const char *	name1,
    const char *	name2
)
{
    int			scheme1;
    int			scheme2;
    char *		uri1;
    char *		uri2;
    fop_symlink_t	func;
    int			rv;
    int			fop_errno;

    scheme1 = fop_path_to_uri(fopc, name1, &uri1);
    if (scheme1 < 0) return -1;

    scheme2 = fop_path_to_uri(fopc, name2, &uri2);
    if (scheme2 < 0) {
	free(uri1);
	return -1;
    }

    if (scheme1 != scheme2) {
	free(uri1);
	free(uri2);
	errno = EPERM;
	return -1;
    }

    func = (fop_symlink_t)fop_scheme_func(scheme1, FOP_SYMLINK);
    if (NULL == func) {
	free(uri1);
	free(uri2);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri1, name1), URI(uri2, name2));
    fop_errno = errno;
    free(uri1);
    free(uri2);
    errno = fop_errno;

    return rv;
}


static int
fop_readlink(
    void *		fopc,
    const char *	path,
    char *		buf,
    size_t		bufsiz
)
{
    int			scheme;
    char *		uri;
    fop_readlink_t	func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_readlink_t)fop_scheme_func(scheme, FOP_READLINK);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), buf, bufsiz);
    fop_errno = errno;
    free(uri);
    errno = fop_errno;

    return rv;
}


static int
fop_link(
    void *		fopc,
    const char *	existing,
    const char *	new
)
{
    int			scheme1;
    int			scheme2;
    char *		uri1;
    char *		uri2;
    fop_link_t		func;
    int			rv;
    int			fop_errno;

    scheme1 = fop_path_to_uri(fopc, existing, &uri1);
    if (scheme1 < 0) return -1;

    scheme2 = fop_path_to_uri(fopc, new, &uri2);
    if (scheme2 < 0) {
	free(uri1);
	return -1;
    }

    if (scheme1 != scheme2) {
	free(uri1);
	free(uri2);
	errno = EPERM;
	return -1;
    }

    func = (fop_link_t)fop_scheme_func(scheme1, FOP_LINK);
    if (NULL == func) {
	free(uri1);
	free(uri2);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri1, existing), URI(uri2, new));
    fop_errno = errno;
    free(uri1);
    free(uri2);
    errno = fop_errno;

    return rv;
}


static int
fop_unlink(
    void *		fopc,
    const char *	path
)
{
    int			scheme;
    char *		uri;
    fop_unlink_t	func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_unlink_t)fop_scheme_func(scheme, FOP_UNLINK);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path));
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_rename(
    void *		fopc,
    const char *	old,
    const char *	new
)
{
    int			scheme1;
    int			scheme2;
    char *		uri1;
    char *		uri2;
    fop_rename_t	func;
    int			rv;
    int			fop_errno;

    scheme1 = fop_path_to_uri(fopc, old, &uri1);
    if (scheme1 < 0) return -1;

    scheme2 = fop_path_to_uri(fopc, new, &uri2);
    if (scheme2 < 0) {
	free(uri1);
	return -1;
    }

    if (scheme1 != scheme2) {
	free(uri1);
	free(uri2);
	errno = EPERM;
	return -1;
    }

    func = (fop_rename_t)fop_scheme_func(scheme1, FOP_RENAME);
    if (NULL == func) {
	free(uri1);
	free(uri2);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri1, old), URI(uri2, new));
    if ((NULL != uri1) || (NULL != uri2)) {
	fop_errno = errno;
	free(uri1);
	free(uri2);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_fcntl(
    void *		fopc,
    int			fildes,
    int			cmd,
    ...
)
{
    va_list		ap;
    fop_fcntl_t		func;
    fop_file_t *	file;
    int			arg_int;
    struct flock *	arg_flock;
#if defined(F_FREESP64) || defined(F_GETLK64) || \
    defined(F_SETLK64) || defined(F_SETLKW64)
    struct flock64 *	arg_flock64;
#endif /* F_FREESP64 || F_GETLK64 || F_SETLK64 || F_SETLKW64 */
    int			rv;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_fcntl_t)fop_scheme_func(file->scheme, FOP_FCNTL);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    va_start(ap, cmd);
    switch(cmd) {
    case F_DUPFD:
#if defined(F_DUP2FD)
    case F_DUP2FD:
#endif /* F_DUP2FD */
	/* not supported */
	errno = EPERM;
	rv = -1;
	break;
    case F_SETFD:
    case F_SETFL:
    case F_SETOWN:
	arg_int = va_arg(ap, int);
	rv = func(fopc, file->value, cmd, arg_int);
	break;
#if defined(F_FREESP)
    case F_FREESP:
#endif /* F_FREESP */
    case F_GETLK:
    case F_SETLK:
    case F_SETLKW:
	arg_flock = va_arg(ap, struct flock *);
	rv = func(fopc, file->value, cmd, arg_flock);
	break;
#if __WORDSIZE != 64 && !defined(__USE_FILE_OFFSET64)
	/* When __WORDSIZE is 64 or __USE_FILE_OFFSET64 is defined,
	   F_*64 has similar number to F_*(non-64) */
#if defined(F_FREESP64)
    case F_FREESP64:
#endif /* F_FREESP64 */
#if defined(F_GETLK64)
    case F_GETLK64:
#endif /* F_GETLK64 */
#if defined(F_SETLK64)
    case F_SETLK64:
#endif /* F_SETLK64 */
#if defined(F_SETLKW64)
    case F_SETLKW64:
#endif /* F_SETLKW64 */

#if defined(F_FREESP64) || defined(F_GETLK64) || defined(F_SETLK64) || defined(F_SETLKW64)
	arg_flock64 = va_arg(ap, struct flock64 *);
	rv = func(fopc, file->value, cmd, arg_flock64);
	break;
#endif /* F_FREESP64 || F_GETLK64 || F_SETLK64 || F_SETLKW64 */
#endif
    default:
	rv = func(fopc, file->value, cmd, 0);
	break;
    }
    va_end(ap);

    return rv;
}


static int
fop_truncate(
    void *		fopc,
    const char *	path,
    off_t		length
)
{
    int			scheme;
    char *		uri;
    fop_truncate_t	func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_truncate_t)fop_scheme_func(scheme, FOP_TRUNCATE);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), length);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_ftruncate(
    void *		fopc,
    int			fildes,
    off_t		length
)
{
    fop_ftruncate_t	func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_ftruncate_t)fop_scheme_func(file->scheme, FOP_FTRUNCATE);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, length);
}


static int
fop_mkdir(
    void *		fopc,
    const char *	path,
    mode_t		mode
)
{
    int			scheme;
    char *		uri;
    fop_mkdir_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_mkdir_t)fop_scheme_func(scheme, FOP_MKDIR);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), mode);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_rmdir(
    void *		fopc,
    const char *	path
)
{
    int			scheme;
    char *		uri;
    fop_rmdir_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_rmdir_t)fop_scheme_func(scheme, FOP_RMDIR);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path));
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static DIR *
fop_opendir(
    void *		fopc,
    const char *	dirname
)
{
    int			scheme;
    char *		uri;
    fop_opendir_t	func;
    int			fop_errno;
    void *		value;
    fop_file_t *	file;

    scheme = fop_path_to_uri(fopc, dirname, &uri);
    if (scheme < 0) return NULL;

    func = (fop_opendir_t)fop_scheme_func(scheme, FOP_OPENDIR);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return NULL;
    }

    file = (fop_file_t *)malloc(sizeof (fop_file_t));
    if (NULL == file) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
	return NULL;
    }

    value = func(fopc, URI(uri, dirname));
    if (NULL == value) {
	fop_errno = errno;
	free(uri);
	free(file);
	errno = fop_errno;
        return NULL;
    }

    file->scheme = scheme;
    file->value = value;

    ((fopc_t *)fopc)->file = fop_file_insert(((fopc_t *)fopc)->file, file);

    free(uri);

    return (DIR *)(file->value);
}


static struct dirent *
fop_readdir(
    void *		fopc,
    DIR *		dirp
)
{
    fop_readdir_t	func;
    fop_file_t *	file;

    file = fop_file_search_value(((fopc_t *)fopc)->file, (void *)dirp);
    if (NULL == file) {
	errno = EBADF;
	return NULL;
    }

    func = (fop_readdir_t)fop_scheme_func(file->scheme, FOP_READDIR);
    if (NULL == func) {
	errno = EPERM;
	return NULL;
    }

    return func(fopc, file->value);
}


static int
fop_closedir(
    void *		fopc,
    DIR *		dirp
)
{
    fop_closedir_t	func;
    fop_file_t *	file;
    int			rv;
    int			fop_errno;

    file = fop_file_search_value(((fopc_t *)fopc)->file, dirp);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_closedir_t)fop_scheme_func(file->scheme, FOP_CLOSEDIR);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, file->value);

    ((fopc_t *)fopc)->file = fop_file_remove_value(((fopc_t *)fopc)->file, dirp);

    fop_errno = errno;
    free(file);
    errno = fop_errno;

    return rv;
}


static int
fop_access(
    void *		fopc,
    const char *	path,
    int			amode
)
{
    int			scheme;
    char *		uri;
    fop_access_t	func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_access_t)fop_scheme_func(scheme, FOP_ACCESS);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), amode);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_chmod(
    void *		fopc,
    const char *	path,
    mode_t		mode
)
{
    int			scheme;
    char *		uri;
    fop_chmod_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_chmod_t)fop_scheme_func(scheme, FOP_CHMOD);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), mode);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_fchmod(
    void *		fopc,
    int			fildes,
    mode_t		mode
)
{
    fop_fchmod_t	func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_fchmod_t)fop_scheme_func(file->scheme, FOP_FCHMOD);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, mode);
}


static int
fop_chown(
    void *		fopc,
    const char *	path,
    uid_t		owner,
    gid_t		group
)
{
    int			scheme;
    char *		uri;
    fop_chown_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_chown_t)fop_scheme_func(scheme, FOP_CHOWN);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), owner, group);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


#if 0
static int
fop_lchown(
    void *		fopc,
    const char *	path,
    uid_t		owner,
    gid_t		group
)
{
    int			scheme;
    char *		uri;
    fop_chown_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_chown_t)fop_scheme_func(scheme, FOP_LCHOWN);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), owner, group);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}
#endif /* 0 */


static int
fop_fchown(
    void *		fopc,
    int			fildes,
    uid_t		owner,
    gid_t		group
)
{
    fop_fchown_t	func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_fchown_t)fop_scheme_func(file->scheme, FOP_FCHOWN);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, owner, group);
}


static long
fop_pathconf(
    void *		fopc,
    const char *	path,
    int			name
)
{
    int			scheme;
    char *		uri;
    fop_pathconf_t	func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_pathconf_t)fop_scheme_func(scheme, FOP_PATHCONF);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), name);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static long
fop_fpathconf(
    void *		fopc,
    int			fildes,
    int			name
)
{
    fop_fpathconf_t	func;
    fop_file_t *	file;

    file = fop_file_search_id(((fopc_t *)fopc)->file, fildes);
    if (NULL == file) {
	errno = EBADF;
	return -1;
    }

    func = (fop_fpathconf_t)fop_scheme_func(file->scheme, FOP_FPATHCONF);
    if (NULL == func) {
	errno = EPERM;
	return -1;
    }

    return func(fopc, file->value, name);
}


static int
fop_utime(
    void *			fopc,
    const char *		path,
    const struct utimbuf *	times
)
{
    int			scheme;
    char *		uri;
    fop_utime_t		func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_utime_t)fop_scheme_func(scheme, FOP_UTIME);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), times);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static int
fop_utimes(
    void *			fopc,
    const char *		path,
    const struct timeval *	times
)
{
    int			scheme;
    char *		uri;
    fop_utimes_t	func;
    int			rv;
    int			fop_errno;

    scheme = fop_path_to_uri(fopc, path, &uri);
    if (scheme < 0) return -1;

    func = (fop_utimes_t)fop_scheme_func(scheme, FOP_UTIMES);
    if (NULL == func) {
	free(uri);
	errno = EPERM;
	return -1;
    }

    rv = func(fopc, URI(uri, path), times);
    if (NULL != uri) {
	fop_errno = errno;
	free(uri);
	errno = fop_errno;
    }

    return rv;
}


static fop_name_function_t fop_name_function_list[] = {
	{FOP__FOPC_CREATE,	"_fopc_create",	(void *)fop__fopc_create},
	{FOP__FOPC_FREE,	"_fopc_free",	(void *)fop__fopc_free},

	{FOP__FOPC_CREATE,	"_nsc_create",	(void *)fop__fopc_create},
	{FOP__FOPC_FREE,	"_nsc_free",	(void *)fop__fopc_free},

	{FOP_OPEN,		"open",		(void *)fop_open},
	{FOP_CLOSE,		"close",	(void *)fop_close},
	{FOP_READ,		"read",		(void *)fop_read},
	{FOP_WRITE,		"write",	(void *)fop_write},
	{FOP_LSEEK,		"lseek",	(void *)fop_lseek},
	{FOP_LSTAT,		"lstat",	(void *)fop_lstat},
	{FOP_TRUNCATE,		"truncate",	(void *)fop_truncate},
	{FOP_FTRUNCATE,		"ftruncate",	(void *)fop_ftruncate},
	{FOP_OPENDIR,		"opendir",	(void *)fop_opendir},
	{FOP_READDIR,		"readdir",	(void *)fop_readdir},
	{FOP_CLOSEDIR,		"closedir",	(void *)fop_closedir},
	{FOP_MKDIR,		"mkdir",	(void *)fop_mkdir},
	{FOP_RMDIR,		"rmdir",	(void *)fop_rmdir},
	{FOP_SYMLINK,		"symlink",	(void *)fop_symlink},
	{FOP_READLINK,		"readlink",	(void *)fop_readlink},
	{FOP_CREAT,		"creat",	(void *)fop_creat},
	{FOP_UNLINK,		"unlink",	(void *)fop_unlink},
	{FOP_RENAME,		"rename",	(void *)fop_rename},
	{FOP_FCNTL,		"fcntl",	(void *)fop_fcntl},
	{FOP_STAT,		"stat",		(void *)fop_stat},
	{FOP_FSTAT,		"fstat",	(void *)fop_fstat},
	{FOP_ACCESS,		"access",	(void *)fop_access},
	{FOP_CHMOD,		"chmod",	(void *)fop_chmod},
	{FOP_CHOWN,		"chown",	(void *)fop_chown},
	{FOP_FCHMOD,		"fchmod",	(void *)fop_fchmod},
#if 0
	{FOP_FCHOWN,		"lchown",	(void *)fop_lchown},
#endif /* 0 */
	{FOP_FCHOWN,		"fchown",	(void *)fop_fchown},
	{FOP_PATHCONF,		"pathconf",	(void *)fop_pathconf},
	{FOP_FPATHCONF,		"fpathconf",	(void *)fop_fpathconf},
	{FOP_READV,		"readv",	(void *)fop_readv},
	{FOP_WRITEV,		"writev",	(void *)fop_writev},
	{FOP_UTIME,		"utime",	(void *)fop_utime},
	{FOP_UTIMES,		"utimes",	(void *)fop_utimes},
	{FOP_LINK,		"link",		(void *)fop_link},

	{FOP_FUNC_LAST,		NULL,		NULL}
};


void *
fop_get_function(
    const char *	operation
)
{
    fop_name_function_t *	f;

    for (f = fop_name_function_list; NULL != f->name; f += 1) {
	if (0 == strcmp(operation, f->name)) {
	    return f->function;
	}
    }

    if ((0 == strcmp(operation, "_fop_basicfopset")) ||
	(0 == strcmp(operation, "_nsc_basicfioset"))) {
	/*
	 * This interface is not defined yet.
	 */
	typedef struct _iml_fop_basic_fopset {
	    iml_fop_open_t	fop_open;
	    iml_fop_read_t	fop_read;
	    iml_fop_stat_t	fop_stat;
	    iml_fop_write_t	fop_write;
	    iml_fop_close_t	fop_close;
	    iml_fop_mkdir_t	fop_mkdir;
	    iml_fop_rmdir_t	fop_rmdir;
	    iml_fop_symlink_t	fop_symlink;
	    iml_fop_lstat_t	fop_lstat;
	    iml_fop_creat_t	fop_creat;
	    iml_fop_lseek_t	fop_lseek;
	    iml_fop_unlink_t	fop_unlink;
	    iml_fop_rename_t	fop_rename;
	    iml_fop_fcntl_t	fop_fcntl;
	    iml_fop_truncate_t	fop_truncate;
	    iml_fop_opendir_t	fop_opendir;
	    iml_fop_readdir_t	fop_readdir;
	    iml_fop_closedir_t	fop_closedir;
	    iml_fop_fstat_t	fop_fstat;
	    iml_fop_ftruncate_t	fop_ftruncate;
	} iml_fop_basic_fopset_t;
	iml_fop_basic_fopset_t *	bfopset;

	bfopset = malloc(sizeof (iml_fop_basic_fopset_t));
	if (NULL == bfopset) return NULL;

	bfopset->fop_open = fop_open;
	bfopset->fop_read = fop_read;
	bfopset->fop_stat = fop_stat;
	bfopset->fop_write = fop_write;
	bfopset->fop_close = fop_close;
	bfopset->fop_mkdir = fop_mkdir;
	bfopset->fop_rmdir = fop_rmdir;
	bfopset->fop_symlink = fop_symlink;
	bfopset->fop_lstat = fop_lstat;
	bfopset->fop_creat = fop_creat;
	bfopset->fop_lseek = fop_lseek;
	bfopset->fop_unlink = fop_unlink;
	bfopset->fop_rename = fop_rename;
	bfopset->fop_fcntl = fop_fcntl;
	bfopset->fop_truncate = fop_truncate;
	bfopset->fop_opendir = fop_opendir;
	bfopset->fop_readdir = fop_readdir;
	bfopset->fop_closedir = fop_closedir;
	bfopset->fop_fstat = fop_fstat;
	bfopset->fop_ftruncate = fop_ftruncate;

	return bfopset;
    }

    return NULL;
}


static void
fop_scheme_initialize(
)
{
    fop_name_function_t *	f;
    void **			v;
    extern void *		fop_scheme_file_get_function(const char *);

    if (NULL != fop_scheme) return;

    fop_scheme = (fop_scheme_t *)malloc(sizeof (fop_scheme_t));
    if (NULL == fop_scheme) return;

    /*
     * load ``file'' scheme.
     */
    fop_scheme->name = strdup("file");
    fop_scheme->id = 0;
    fop_scheme->function =
	(void **)calloc(FOP_FUNC_LAST, (sizeof (void *)));
    if (NULL == fop_scheme->function) {
	free(fop_scheme);
	return;
    }
    v = fop_scheme->function;
    for (f = fop_name_function_list; NULL != f->name; f += 1) {
	*(v + f->type) = fop_scheme_file_get_function(f->name);
    }

    fop_scheme->next = NULL;

    /*
     * Other schemes can be loaded here.
     */

    return;
}


/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
