/*
 * _sio_mnt_nasdpath.c
 *
 * SIO NASD Virtual Op definitions
 *
 * Authors: Khalil Amiri, CMU SCS/ECE, July 18 1997
 *          Sean Levy, CMU SCS, August 1999
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */

#include "_sio_internal.h"
#include "../clerk/nasd_cheops_cl_vnasd.h"
#include <nasd/nasd_pdrive_client.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifndef _NASD_MNT_DEBUG
# define _NASD_MNT_DEBUG 0
#endif

struct _nasdpath_open_file {
  sio_mode_t mode;
  int	datafd;
  int	labelfd;
  nasd_cookie_t cookie;
  nasd_partnum_t partnum;
};

static NASD_DECLARE_ONCE(_nasdpath_init_once_block);
static int _nasdpath_initted;

static u_int32_t _nasdpath_pid;
static u_int32_t _nasdpath_uniq;
NASD_DECLARE_STATIC_MUTEX(_nasdpath_uniq_mutex);

sio_return_t	_sio_mnt_nasdpath_createtest(void *fscookie,
		    sio_control_t *controls, sio_count_t controlcount,
		    int *score);
sio_return_t	_sio_mnt_nasdpath_create(void *fscookie,
		    sio_control_t *controls, sio_count_t controlcount,
		    char *buf, int buflen);
sio_return_t	_sio_mnt_nasdpath_open(void *fscookie, const char *fsfilename,
		    sio_mode_t mode, sio_control_t *controls,
		    sio_count_t controlcount, void **fsfilecookie);
/* XXX test */
sio_return_t	_sio_mnt_nasdpath_unlink(void *fscookie,
		    const char *fsfilename);
/* XXX control */
sio_return_t	_sio_mnt_nasdpath_close(void *filecookie);
void		_sio_mnt_nasdpath_doio(void *filecookie,
		    struct _sio_iodesc *iod);

void
_nasdpath_init(void)
{
  int rc=0;

	_nasdpath_pid = getpid();

  rc = nasd_mutex_init(&_nasdpath_uniq_mutex);
  if (rc) {
    NASD_PANIC();
  }
	_nasdpath_uniq = 0;
	rc = _nasd_cheops_cl_init(NULL, NASD_BIND_DEFAULT, 0, 0);
	if (rc) {
	  _sio_warning ("cheops init failure!...bailing out...\n");
	  exit(rc);
	}
	_nasdpath_initted = 1;

}

struct _sio_mntpoint *
_sio_mnt_nasdpath_init(const char *name, const char *path)
{
	struct _sio_mntpoint *nmp = NULL;
	sio_return_t err;
	int fd = -1;
	struct stat sb;
	nasd_status_t nasd_status;
	nasd_rpc_status_t status;

	nasd_once(&_nasdpath_init_once_block, &_nasdpath_init);
  if (!_nasdpath_initted) {
    NASD_PANIC();
  }

	nmp = malloc(sizeof(*nmp));
	if (nmp == NULL) {
		err = SIO_ERR_VEND_OSERR_FIRST + ENOMEM;
		goto bad;
	}
	memset(nmp, '0', sizeof *nmp);

	nmp->mnt_name = strdup(name);
	if (nmp->mnt_name == NULL) {
		err = SIO_ERR_VEND_OSERR_FIRST + ENOMEM;
		goto bad;
	}
	
	_nasd_null_cl(NULL, &nasd_status, &status);
	if ( (nasd_status!=NASD_SUCCESS) || status ) {
    err = SIO_ERR_VEND_OSERR_FIRST + ENODEV;
    goto bad;
	}
	/* get cookie to give to cheops clerk (from file manager?) */
	nmp->mnt_cookie = (void *)(long)0;

	/* fill in the function switch */
	nmp->mnt_createtest = _sio_mnt_nasdpath_createtest;
	nmp->mnt_create = _sio_mnt_nasdpath_create;
	nmp->mnt_open = _sio_mnt_nasdpath_open;
	nmp->mnt_test = NULL;
	nmp->mnt_unlink = _sio_mnt_nasdpath_unlink;
	nmp->mnt_control = NULL;
	nmp->mnt_close = _sio_mnt_nasdpath_close;
	nmp->mnt_doio = _sio_mnt_nasdpath_doio;

#if _NASD_MNT_DEBUG > 0
	_sio_warning ("successful nasd initialization\n");
#endif
	return (nmp);

bad:
	if (nmp != NULL) {
		if (nmp->mnt_name != NULL)
			free(nmp->mnt_name);
		if (nmp->mnt_cookie != NULL)
			free(nmp->mnt_name);
		free(nmp);
		if (fd != -1)
			close(fd);
	}
	_sio_warning("_sio_mnt_nasdpath_init: %s (%s): %s\n", name, path,
               sio_error_string(err));
	return (NULL);
}

sio_return_t
_sio_mnt_nasdpath_createtest(void *fscookie, sio_control_t *controls,
                             sio_count_t controlcount, int *score)
{

	/* XXX do something with controls */
	if (controlcount > 0)
		return (SIO_ERR_OP_UNSUPPORTED);

	/*
	 * XXX make sure there's some minimal amount of disk space
	 * available, etc.
	 */

	*score = 1;
	return (SIO_SUCCESS);
}

sio_return_t
_sio_mnt_nasdpath_create(void *fscookie, sio_control_t *controls,
                         sio_count_t controlcount, char *buf, int buflen)
{
	char fn[(3 * 8) + 3 + 5 + 1], labelbuf[SIO_MAX_LABEL_LEN];
	u_int32_t uniq, time32;
	int cwdfd, tmp;
	sio_return_t rv;
	nasd_cookie_t       in_cookie;
	nasd_attribute_t    in_attribute;
	nasd_fieldmask_t    in_fieldmask;
	nasd_partnum_t      in_partnum;
	nasd_identifier_t   out_identifier;
	nasd_attribute_t    out_attribute;
	nasd_status_t       nasd_status;
	nasd_rpc_status_t      status;
	
	time32 = time(NULL);
  NASD_LOCK_MUTEX(_nasdpath_uniq_mutex);
	uniq = _nasdpath_uniq++;
  NASD_UNLOCK_MUTEX(_nasdpath_uniq_mutex);

	if (buflen < (3 * 8) + 3)
		_sio_panic("_sio_mnt_nasdpath_create: buffer too short (%d)",
               buflen);

	/* todo : the cookie should be stored in mnt (size should be enlarged) */
	
	_nasd_create_cl(NULL, in_cookie, in_attribute, in_fieldmask, in_partnum,
                  &out_identifier, &out_attribute, &nasd_status, &status);
	if ( (nasd_status != NASD_SUCCESS) || status ) {
    _sio_warning("_sio_mnt_nasdpath_create: couldn't create cheops object");
		return SIO_ERR_VEND_OSERR_FIRST + nasd_status + status;
	}
#if _NASD_MNT_DEBUG > 0
	_sio_warning ("created object %" SIO_NASDID_FMT "\n", out_identifier);
#endif	
	sprintf(buf, "%" SIO_NASDID_FMT, out_identifier);
	
	rv = SIO_SUCCESS;

out:
	return (rv);

}

sio_return_t
_sio_mnt_nasdpath_open(void *fscookie, const char *fsfilename, sio_mode_t mode,
                       sio_control_t *controls, sio_count_t controlcount, void **fsfilecookie)
{
	char fn[(3 * 8) + 3 + 5 + 1];
	struct _nasdpath_open_file *of;
	sio_return_t rv;
	int cwdfd = -1, nasdmode;
	nasd_cookie_t in_cookie, out_cookie;
	nasd_status_t nasd_status;
	nasd_rpc_status_t status;
	nasd_identifier_t in_id;

	/* XXX do something with controls */
	if (controlcount > 0)
		return (SIO_ERR_OP_UNSUPPORTED);

	of = malloc(sizeof *of);
	if (of == NULL)
		return (SIO_ERR_VEND_OSERR_FIRST + ENOMEM);

	sscanf(fsfilename, "%" SIO_NASDID_FMT, &in_id);
#if _NASD_MNT_DEBUG > 0
	_sio_warning("in nasdpath_open id=%" SIO_NASDID_FMT "\n", in_id);
#endif
	_nasd_access_cl (NULL, in_cookie, in_id, &out_cookie, &nasd_status, &status);

	of->mode = mode;
	of->datafd = in_id;

	if ((mode & SIO_MODE_READ|SIO_MODE_WRITE) ==
	    (SIO_MODE_READ|SIO_MODE_WRITE))
		nasdmode = O_RDWR;
	else if ((mode & SIO_MODE_READ) == SIO_MODE_READ)
		nasdmode = O_RDONLY;
	else if ((mode & SIO_MODE_WRITE) == SIO_MODE_WRITE)
		nasdmode = O_WRONLY;

	*fsfilecookie = of;
	rv = SIO_SUCCESS;

out:
	return (rv);
}

/* XXX test */

sio_return_t
_sio_mnt_nasdpath_unlink(void *fscookie,
                         const char *fsfilename)
{
	char fn[(3 * 8) + 3 + 5 + 1];
	sio_return_t rv;
	int cwdfd;
	nasd_identifier_t in_id;
	nasd_cookie_t in_cookie;
	nasd_status_t nasd_status;
	nasd_rpc_status_t status;
	

	fprintf(stderr, "nasdpath_unlink: removing file %s\n", fsfilename);
	sscanf(fsfilename, "%d", &in_id);

#if _NASD_MNT_DEBUG > 0
	_sio_warning("in nasdpath_unlink id= %d\n", in_id);
#endif
	_nasd_remove_cl (NULL, in_cookie, in_id, &nasd_status, &status);
	
	if (nasd_status != NASD_SUCCESS) {
		_sio_warning("_sio_mnt_nasdpath_unlink: couldn't remove object");
		rv = SIO_ERR_VEND_OSERR_FIRST + nasd_status;
		goto out;
	}
	
	rv = SIO_SUCCESS;
out:       
	return (rv);
}

/* XXX control */

sio_return_t
_sio_mnt_nasdpath_close(void *filecookie)
{
	struct _nasdpath_open_file *of = filecookie;

	free(of);

	return (SIO_SUCCESS);
}

void
_sio_mnt_nasdpath_doio(void *filecookie, struct _sio_iodesc *iod)
{
	struct _nasdpath_open_file *of = filecookie;
	sio_transfer_len_t fbytes, mbytes, f_elem_left, m_elem_left, f_chunk_left, m_chunk_left;
	sio_count_t fi, mi, f_chunk_index, m_chunk_index;
	off_t f_elem_off, f_chunk_off;
	ssize_t m_elem_off, m_chunk_off, iofn_rv;
	size_t todo, done;
	char *io_addr;
	nasd_status_t nasd_status;
	nasd_rpc_status_t op_status;
	nasd_offset_t io_offset;
	nasd_len_t in_len, out_len;

	void (*iofn)(nasd_cheops_handle_t, nasd_cookie_t, nasd_identifier_t,
               nasd_offset_t, nasd_len_t, nasd_partnum_t, nasd_len_t *, 
               nasd_byte_t *, nasd_status_t *, nasd_rpc_status_t *);


	if (((iod->iod_flags & _SIO_IOD_READ) != 0 &&
	     (of->mode & SIO_MODE_READ) == 0) ||
	    ((iod->iod_flags & _SIO_IOD_WRITE) != 0 &&
	     (of->mode & SIO_MODE_WRITE) == 0)) {
		iod->iod_transferlen = 0;
		iod->iod_result = SIO_ERR_INCORRECT_MODE;
		goto out;
	}

	if ((iod->iod_flags & _SIO_IOD_READ) != 0)
	  iofn = (void (*)(nasd_cheops_handle_t, nasd_cookie_t, nasd_identifier_t,
                     nasd_offset_t, nasd_len_t, nasd_partnum_t, nasd_len_t *, 
                     nasd_byte_t *, nasd_status_t *, nasd_rpc_status_t *)) _nasd_read_cl;
	else
	  iofn = (void (*)(nasd_cheops_handle_t, nasd_cookie_t, nasd_identifier_t,
                     nasd_offset_t, nasd_len_t, nasd_partnum_t, nasd_len_t *, 
                     nasd_byte_t *, nasd_status_t *, nasd_rpc_status_t *)) _nasd_write_cl;

	for (fbytes = 0, fi = 0; fi < iod->iod_fllen; fi++) {
	  if (iod->iod_fl[fi].size < 0) {
			iod->iod_transferlen = 0;
			iod->iod_result = SIO_ERR_INVALID_FILE_LIST;
			goto out;
		}
		fbytes += iod->iod_fl[fi].size * iod->iod_fl[fi].element_cnt;
	}
	for (mbytes = 0, mi = 0; mi < iod->iod_mllen; mi++) {
		if (iod->iod_ml[mi].size < 0) {
			iod->iod_transferlen = 0;
			iod->iod_result = SIO_ERR_INVALID_MEMORY_LIST;
			goto out;
		}
		mbytes += iod->iod_ml[mi].size * iod->iod_ml[mi].element_cnt;
	}
	if (mbytes != fbytes) {
		iod->iod_transferlen = 0;
		iod->iod_result = SIO_ERR_UNEQUAL_LISTS;
		goto out;
	}

	done = 0;
	fi = mi = -1;
	f_elem_left = m_elem_left = 0;
	while (done < fbytes) {
		if (f_elem_left == 0) {
			fi++;
			if (fi == iod->iod_fllen)
				break;
			f_elem_left = iod->iod_fl[fi].size *
        iod->iod_fl[fi].element_cnt;
			f_elem_off = 0 - iod->iod_fl[fi].stride;
			f_chunk_index = -1;
			f_chunk_left = 0;
		}
		if (f_chunk_left == 0) {
			f_chunk_index++;
			f_elem_off += iod->iod_fl[fi].stride;
			f_chunk_left = iod->iod_fl[fi].size;
			f_chunk_off = 0;
		}
		if (m_elem_left == 0) {
			mi++;
			if (mi == iod->iod_mllen)
				break;
			m_elem_left = iod->iod_ml[mi].size *
        iod->iod_ml[mi].element_cnt;
			m_elem_off = 0 - iod->iod_ml[mi].stride;
			m_chunk_index = -1;
			m_chunk_left = 0;
		}
		if (m_chunk_left == 0) {
			m_chunk_index++;
			m_elem_off += iod->iod_ml[mi].stride;
			m_chunk_left = iod->iod_ml[mi].size;
			m_chunk_off = 0;
		}

		todo = f_chunk_left > m_chunk_left ?
      m_chunk_left : f_chunk_left;

		io_offset = iod->iod_fl[fi].offset + f_elem_off + f_chunk_off;
		io_addr = (char *)iod->iod_ml[mi].addr + m_elem_off +
      m_chunk_off;

#if _NASD_MNT_DEBUG > 0
    _sio_warning("nasdpath_doio: issuing io obj=%d, off=%d, size=%d\n", of->datafd, io_offset, todo);
#endif

    out_len = 0;
    in_len = todo;
    (*iofn)(NULL, of->cookie, of->datafd, io_offset, in_len,
            of->partnum, &out_len, (nasd_byte_t *) io_addr, &nasd_status, &op_status);

    /* nasd_status is actually a cheops return code */
    if ( nasd_status == _NASD_CHEOPS_ERR_READINCOMP) {
      iod->iod_result = SIO_ERR_VEND_EOF;  
#if _NASD_MNT_DEBUG > 0
      _sio_warning("nasdpath_doio: io incomplete [returning eof] to obj=%d, off=%d, size=%d (out_len=%d)\n", of->datafd, io_offset, todo, out_len);
#endif
      done += out_len;
      goto io_done;
    }

    if ( (op_status) || nasd_status ) {
      iod->iod_result = SIO_ERR_VEND_OSERR_FIRST + 
        SIO_ERR_VEND_INVALID_FLAGS;
			/* op_status + nasd_status; */
		    
      _sio_warning("io failed: nasd_status/stats=%d/%d\n",
                   nasd_status, op_status);
      /* fix -- returned error values should be more consistent */
      goto io_done;
    }

    done += out_len;
    f_chunk_left -= todo;
    f_elem_left -= todo;
    f_chunk_off += todo;
    m_chunk_left -= todo;
    m_elem_left -= todo;
    m_chunk_off += todo;

#if _NASD_MNT_DEBUG > 0
    _sio_warning("nasdpath_doio: io done to obj=%d, off=%d, size=%d (out_len=%d)\n", of->datafd, 
                 io_offset, todo, out_len);
#endif
	}
	iod->iod_result = SIO_SUCCESS;

io_done:
	iod->iod_transferlen = done;
  /*
    _sio_rcobj_unref(&iod->iod_rcobj);
  */

out:
  NASD_LOCK_MUTEX(iod->iod_rcobj.rco_mutex);
	iod->iod_flags |= _SIO_IOD_DONE;
  NASD_UNLOCK_MUTEX(iod->iod_rcobj.rco_mutex);
#if _NASD_MNT_DEBUG > 0
	_sio_warning("nasdpath_doio: io done to obj=%d, off=%d, total transferred=%d\n", of->datafd, io_offset, done);
#endif
	return;
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
