
#include <_sio_internal.h>
#include <stdio.h>

#define	MAX_FSNAME_LEN	64

static sio_return_t
_sio_find_fs_name(const char *linkcontents, struct _sio_mntpoint **mntp,
    char *mntnamep)
{
	struct _sio_mntpoint *mnt;
	const char *linkmntname, *linkfilename;
	int mntnamelen;

	if (strncmp("cmu-sio ", linkcontents, 8) != 0)
		return (SIO_ERR_INVALID_FILENAME);

	linkmntname = linkcontents + 8;
	linkfilename = strchr(linkmntname, ' ');
	if (linkfilename == NULL)
		return (SIO_ERR_INVALID_FILENAME);
	mntnamelen = linkfilename - linkmntname;
	linkfilename++;
	if (strlen(linkfilename) > MAX_FSNAME_LEN)
		return (SIO_ERR_INVALID_FILENAME);

	strcpy(mntnamep, linkfilename);

	for (mnt = _sio_mntpoint_list.tqh_first; mnt != NULL;
	    mnt = mnt->mnt_link.tqe_next) {
		if (strncmp(mnt->mnt_name, linkmntname, mntnamelen) == 0 &&
		    mnt->mnt_name[mntnamelen] == '\0') {
			*mntp = mnt;
			return SIO_SUCCESS;
		}
	}

	return (SIO_ERR_OP_UNSUPPORTED);
}

/* sio_open: done */
sio_return_t
sio_open(int *extfdp, const char *name, sio_mode_t mode,
    sio_control_t *controls, sio_count_t controlcount)
{
	struct _sio_mntpoint *mnt, *maxmnt;
	int score, maxscore;
	sio_return_t rv;
	char fsname[MAX_FSNAME_LEN + 1], linkcontents[PATH_MAX + 1];
	void *mpfilecookie;
	int osrv, created = 0, symlinked = 0;
	struct _sio_fdesc *intfd;

	_sio_init();

	if (mode & ~(SIO_MODE_READ | SIO_MODE_WRITE | SIO_MODE_CREATE))
		return SIO_ERR_VEND_INVALID_FLAGS;

	/* XXX SANITY CHECK THE CONTROLS */

	if (mode & SIO_MODE_CREATE) {
		/*
		 * try to create the file
		 */

		/* first, find out where to create it */
		maxscore = 0;
		maxmnt = NULL;
		mnt = _sio_mntpoint_list.tqh_first;
		rv = SIO_ERR_OP_UNSUPPORTED;	/* in case no file systems */
		while (mnt != NULL) {
			rv = mnt->mnt_createtest(mnt->mnt_cookie, controls,
			    controlcount, &score);

			if (rv == SIO_SUCCESS && score > maxscore) {
				maxscore = score;
				maxmnt = mnt;
			}

			mnt = mnt->mnt_link.tqe_next;
		}
		if (maxmnt == NULL)
			return (rv);
		mnt = maxmnt;

		/* now create it */
		rv = mnt->mnt_create(mnt->mnt_cookie, controls, controlcount,
		    fsname, sizeof fsname - 1);
		fsname[sizeof fsname - 1] = '\0';		/* sanity */
		if (rv != SIO_SUCCESS)
			return (rv);

		created = 1;

		/* and put it into the user's filesystem namespace */
		sprintf(linkcontents, "cmu-sio %s %s", mnt->mnt_name, fsname);
		osrv = symlink(linkcontents, name);
		if (osrv) {
			rv = SIO_ERR_VEND_OSERR_FIRST + errno;
			goto bad;
		}

		symlinked = 1;

		/* OK, the file has been created. */
	}
	mode &= ~SIO_MODE_CREATE;

	osrv = readlink(name, linkcontents, sizeof linkcontents - 1);
	if (osrv == -1) {
		rv = SIO_ERR_VEND_OSERR_FIRST + errno;
		goto bad;
	}
	linkcontents[osrv] = '\0';

	rv = _sio_find_fs_name(linkcontents, &mnt, fsname);
	if (rv != SIO_SUCCESS) {
		goto bad;
	}

	rv = mnt->mnt_open(mnt->mnt_cookie, fsname, mode, controls,
	    controlcount, &mpfilecookie);
	if (rv != SIO_SUCCESS) {
		goto bad;
	}

	intfd = _sio_fdesc_create(mnt, mpfilecookie);
	_sio_exttable_register(_sio_exttable_fdescs, intfd);
	_sio_rcobj_ref(intfd);		/* XXX */
	*extfdp = _sio_exttable_externalize(_sio_exttable_fdescs, intfd);

	return (SIO_SUCCESS);

bad:
	if (created) {
		sio_return_t tmprv;

		tmprv = mnt->mnt_unlink(mnt->mnt_cookie, fsname);
		if (tmprv != SIO_SUCCESS)
			_sio_warning("_sio_open: couldn't unlink %s:%s",
			    mnt->mnt_name, fsname);
	}
	if (symlinked)
		(void)unlink(name);
	return (rv);

}

#if 0
/* sio_test: XXX */
sio_return_t
sio_test(XXX)
{
}
#endif

/* sio_rename: done */
sio_return_t
sio_rename(const char *old, const char *new)
{
	char linkcontents[PATH_MAX + 1], fsname[MAX_FSNAME_LEN + 1];
	struct _sio_mntpoint *mnt;
	sio_return_t rv;
	int osrv;

	_sio_init();

	osrv = readlink(old, linkcontents, sizeof linkcontents - 1);
	if (osrv == -1)
		goto bad;
	linkcontents[osrv] = '\0';

	rv = _sio_find_fs_name(linkcontents, &mnt, fsname);
	if (rv != SIO_SUCCESS)
		return (rv);

	osrv = symlink(linkcontents, new);
	if (osrv == -1)
		goto bad;

	osrv = unlink(old);
	if (osrv == -1)
		goto bad;

	return (SIO_SUCCESS);	

bad:
	rv = SIO_ERR_VEND_OSERR_FIRST + errno;
	return (rv);
}

/* sio_unlink: done */
sio_return_t
sio_unlink(const char *file)
{
	char linkcontents[PATH_MAX + 1], fsname[MAX_FSNAME_LEN + 1];
	struct _sio_mntpoint *mnt;
	sio_return_t rv;
	int osrv;

	_sio_init();

	osrv = readlink(file, linkcontents, sizeof linkcontents - 1);
	if (osrv == -1)
		goto bad;
	linkcontents[osrv] = '\0';

	rv = _sio_find_fs_name(linkcontents, &mnt, fsname);
	if (rv != SIO_SUCCESS)
		return (rv);

	rv = mnt->mnt_unlink(mnt->mnt_cookie, fsname);
	if (rv != SIO_SUCCESS)
		return (rv);

	rv = unlink(file);
	if (rv == 0 )
	  return (rv);

bad:
	rv = SIO_ERR_VEND_OSERR_FIRST + errno;
	return (rv);
}

/* sio_control: done (except XXX) */
sio_return_t
sio_control(int extfd, sio_control_t *controls, sio_count_t len)
{
	struct _sio_fdesc *intfd;
	sio_return_t rv;

	_sio_init();

	/* internalize file desc. structure (refcnt++) */
	intfd = _sio_exttable_internalize(_sio_exttable_fdescs, extfd);
	if (intfd == NULL)
		return (SIO_ERR_INVALID_DESCRIPTOR);

	/* XXX SANITY CHECK THE CONTROLS */
	
	if (!intfd->fd_mp->mnt_control) {
	  fprintf(stderr, "sio_control: couldn't resolve function call, not implented\n");
	  return (SIO_ERR_CONTROL_FAILED);
	}
	rv = (*intfd->fd_mp->mnt_control)(intfd->fd_mp_cookie, controls, len);

bad:
	_sio_rcobj_unref(intfd);
	return (rv);
}

/* sio_close: done */
sio_return_t
sio_close(int extfd)
{
	struct _sio_fdesc *intfd;
	struct _sio_mntpoint *mp;
	void *mp_cookie;
	sio_return_t rv;

	_sio_init();

	/* internalize file desc. structure (refcnt++) */
	intfd = _sio_exttable_internalize(_sio_exttable_fdescs, extfd);
	if (intfd == NULL)
		return (SIO_ERR_INVALID_DESCRIPTOR);

	/* remove the file descriptor from the external table. */
	_sio_exttable_unregister(_sio_exttable_fdescs, intfd);

	/* wait until we're holding the last reference */
	_sio_rcobj_waitrefcnt(intfd, 2);

	rv = (*intfd->fd_mp->mnt_close)(intfd->fd_mp_cookie);

	_sio_rcobj_unref(intfd);
	_sio_fdesc_destroy(intfd);
	return (rv);
}
