/*
 * nasd_cheops_mgr_bsdir.c
 *
 * Storage manager byte-segment directory services
 */
/*
 * Copyright (c) 1996,1997,1998,1999 Carnegie Mellon University.
 * All rights reserved.
 *
 * Author: Khalil Amiri, CMU SCS/ECE, July 18 1997
 */
/*
 * 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 <nasd/nasd_options.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <nasd/nasd_types.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_general.h>

#include <nasd/nasd_cheops_types.h>
#include <nasd/nasd_cheops_common.h>
#include <nasd/nasd_cheops_types.h>
#include <nasd/nasd_cheops_cache.h>
#include <nasd/nasd_cheops_mgr_structs.h>
#include <nasd/nasd_cheops_mgr_common.h>
#include <nasd/nasd_cheops_mgr_internal.h>

/*
 * XXX this should be fixed. It will not be correctly included
 * as a dependency on many platforms. Also, the relative path
 * thing makes it hard for alternatively-organized trees, and
 * just give up on including something like this in the kernel.
 */
#include "../cheops/shared/nasd_cheops_cl_drive.h"

#define MAX_NUM_PAGES 10
#define NASD_CHEOPS_OP_DEBUG 1
#define NASD_CHEOPS_DEMO 0              /* XXX ??? AIEEE */

/* extern proto stuff */
_nasd_cheops_cache_t *_nasd_cheops_dr_handle_cachep;

/* local proto stuff */
static int bs_instantiate_layout(
  nasd_cheops_bs_layout_t layout,
  nasd_cheops_bs_handle_t *bs_handle);
static int bsdir_inode_read(
  _nasd_cheops_cache_key_t key,
  caddr_t bp);
static int bsdir_inode_writeback(
  _nasd_cheops_cache_ent_t *e,
  int bsize);

/* nasty globals */
extern nasd_cheops_dmap_t *dmap;
nasd_cheops_supermap_t *smap;

_nasd_cheops_cache_t *Bsdir_cachep;
int Nasd_bsdir_cache_size = 80;
int Nasd_bsdir_fd;
int lastID;

/* initialize the byte-segment directory cache */
int
nasd_cheops_bsdir_cache_init(
  char          *imap_fname)
{
  int             fd, i, rc;
	int             nb, fsize, totalb;
	int 	        Bsdir_cache_size = 100;
	nasd_boolean_t  found;
	char            *bp;

  if (imap_fname == NULL)
    imap_fname = NASD_CHEOPS_DEFAULT_IMAP_FILE;

	NASD_Malloc(smap, sizeof(nasd_cheops_supermap_t),
              (nasd_cheops_supermap_t *) );
	bzero((char *)smap, sizeof(nasd_cheops_supermap_t));
	bp = (char *) smap;

	if ((Nasd_bsdir_fd = open(imap_fname, O_RDWR, 0666)) >0 ) {
	  lseek(Nasd_bsdir_fd, 0, SEEK_SET);
	  nb = read(Nasd_bsdir_fd, bp, sizeof(nasd_cheops_supermap_t) );
	  if (nb != sizeof(nasd_cheops_supermap_t)) {
	    (cheops_Msg "error reading imap file...exiting! \n");
	    nasd_cheops_mgr_shutdown();
	    exit(1);
	  }
	  lastID = smap->lastID;
  } else {
	  if ((Nasd_bsdir_fd = open(imap_fname, O_RDWR|O_CREAT, 0666)) < 0) {
	    return _NASD_CHEOPS_ERR_CANT_CREATE_FILE;
	  }
    bzero((char *)smap, sizeof(nasd_cheops_supermap_t));
	  smap->lastID = lastID = 0;
    rc = write(Nasd_bsdir_fd, bp, sizeof(nasd_cheops_supermap_t));
  }

	/* create byte-segment directory cache */
	rc = _nasd_cheops_cache_create(Nasd_bsdir_cache_size, 
                                 sizeof(nasd_cheops_bs_inode_t),
                                 _NASD_CHEOPS_CACHE_TYPE_IMAP, &Bsdir_cachep);
	Bsdir_cachep->handle_read_fault = bsdir_inode_read;
	Bsdir_cachep->writeback = bsdir_inode_writeback;

	return rc;
}

int
nasd_cheops_bsdir_cache_shutdown(
  void)
{
  int rc;

	rc = _nasd_cheops_cache_destroy(Bsdir_cachep);
	return rc;
}

static int 
bsdir_inode_read(
  _nasd_cheops_cache_key_t key,
  caddr_t bp)
{
  int nb;
  int inum;

  inum = key.record_id;
  if (inum <= lastID) {
    lseek(Nasd_bsdir_fd,
          sizeof(nasd_cheops_supermap_t) + inum*sizeof(nasd_cheops_bs_inode_t),
          SEEK_SET);
    bzero(bp, sizeof(nasd_cheops_bs_inode_t));
    nb = read(Nasd_bsdir_fd, bp, sizeof(nasd_cheops_bs_inode_t));
  }
  return 0;
}

static int 
bsdir_inode_writeback(
  _nasd_cheops_cache_ent_t *e,
  int bsize)
{
  int i, fd, nb;
  int inum, oid;
	nasd_cheops_bs_inode_t *ip;

  oid = e->key.object_id;
  inum = e->key.record_id;
  lseek(Nasd_bsdir_fd,
        sizeof(nasd_cheops_supermap_t) + inum*sizeof(nasd_cheops_bs_inode_t),
        SEEK_SET);
  nb = write(Nasd_bsdir_fd, e->data, sizeof(nasd_cheops_bs_inode_t));
  if (nb != sizeof(nasd_cheops_bs_inode_t)) {
    (cheops_Msg "inode write back failed!: wrote %d bytes out of %d\n", nb,
	   (int)sizeof(nasd_cheops_bs_inode_t));
    return -1;
  } else
    fsync(Nasd_bsdir_fd);

  return 0;
}

static int
bs_newinode(
  int   *new_inode_num)
{
  *new_inode_num = ++lastID;
  return 0;
}

int
nasd_cheops_bs_create(
  nasd_boolean_t            do_qos,
  nasd_cheops_qos_req_t    *qos_req, 
  nasd_cheops_bs_handle_t  *bs_handle)
{
  nasd_cheops_bs_layout_t layout;
	_nasd_cheops_cache_key_t key;
	_nasd_cheops_cache_ent_t *e;
	nasd_cheops_bs_inode_t *ip;
	int inum;
	int nb, i, rc;

	/* insert a new inode in the inode map */
	bs_newinode(&inum);	  
	bs_handle->bsid = inum;

	/* map the byte-segment requirements onto a logical layout */
	if (do_qos) {
	  nasd_cheops_bs_req_to_layout(qos_req, &layout);
	} else {
	  layout.raid_level = Default_RAID_Level; 
	  layout.stripe_unit_size = Default_Stripe_Unit_Size;
	  
	  if (Default_Stripe_Width <= NASD_CHEOPS_MAX_STRIPE_SIZE ) {
	    layout.num_col = layout.num_data_col = Default_Stripe_Width;
	  } else {
	    layout.num_col = layout.num_data_col = NASD_CHEOPS_MAX_STRIPE_SIZE;	  
	  }

	}

	/* instantiate the logical layout by mapping it onto physical drives */
	rc = bs_instantiate_layout(layout, bs_handle);	    
	if (rc)
	  return rc;	

	key.object_id = 0;
	key.record_id = inum;
	rc = _nasd_cheops_cache_hold_ent(Bsdir_cachep, key, &e);
	if (rc)
	  return rc;

	ip = (nasd_cheops_bs_inode_t *) e->data;
	ip->flags = NASD_CHEOPS_BSINODE_USED;
	ip->bsg_id = 0;
	ip->bsid = (nasd_identifier_t)inum;
	bzero((char *)&ip->create_cookie, sizeof(nasd_cookie_t));
	ip->raid_level = bs_handle->raid_level;
	ip->num_col = bs_handle->num_col;
	ip->num_data_col = bs_handle->num_data_col;
	ip->stripe_unit_size = bs_handle->stripe_unit_size;

 	for (i=0; i<ip->num_col; i++) {
	  ip->object_id[i] = bs_handle->identifier_list[i].nasd_identifier;
	  ip->drive_id[i] = bs_handle->identifier_list[i].disk_identifier;
	}	

	_nasd_cheops_cache_mark_dirty(Bsdir_cachep, key); /* for now, synchronous */
	rc = _nasd_cheops_cache_release_ent(Bsdir_cachep, e);	

	lseek(Nasd_bsdir_fd, 0, SEEK_SET);
	smap->lastID = lastID;
	nb = write(Nasd_bsdir_fd, (char *)smap, sizeof(nasd_cheops_supermap_t) );
	fsync(Nasd_bsdir_fd);
	if (nb != sizeof(nasd_cheops_supermap_t))
	  return EBADF;
  return 0; /* success */
}

static int
bs_instantiate_layout(
  nasd_cheops_bs_layout_t layout,
  nasd_cheops_bs_handle_t *bs_handle)
{
  int i, g, rc, failures;
	nasd_identifier_t ni;
	nasd_disk_ident_t di;
	nasd_drive_handle_t h;
	int partnum;
	nasd_key_t req_key;
	nasd_status_t nasd_status;
	nasd_rpc_status_t status;
	nasd_attribute_t na; /* new attribute */
	nasd_timespec_t now;
	int now_sec;
	_nasd_cheops_attribute_t nca; /* new cheops attribute */

	nasd_disk_ident_t dr_id[NASD_CHEOPS_MAX_STRIPE_SIZE];
	int dr_index[NASD_CHEOPS_MAX_STRIPE_SIZE];

	bs_handle->raid_level = layout.raid_level;
	bs_handle->stripe_unit_size = layout.stripe_unit_size;
	bs_handle->num_col = g = layout.num_col;
	bs_handle->num_data_col = layout.num_data_col;	

	rc = dmap_alloc_chunk(dmap, g, dr_id, dr_index);
	if (rc) {
	  (cheops_Msg "allocation failed!\n");
	  return rc;
	}

	for (i=0; i<bs_handle->num_col; i++) {
	  bs_handle->identifier_list[i].disk_identifier = dr_id[i];
	  h = dmap->dinfo[dr_index[i]].h;	
	  partnum = dmap->dinfo[dr_index[i]].partnum;
	  rc = _nasd_cheops_create_h_dr(h, partnum, req_key, &ni, &nasd_status,
                                  &status);
	  if (rc)
	    return rc; /* FIX -- change to handle errors */
	  bs_handle->identifier_list[i].nasd_identifier = ni; 
	  nca.ni[i] = ni;
	  nca.di[i] = dr_id[i];
	  /* write cheops specific attributes only if the object
	     is striped (across more than one NASD object) */
	  if ((i > 0) && (i == (bs_handle->num_col - 1))) {
	    nasd_gettime(&now);
	    now_sec = now.ts_sec;
	    nca.object_len = 0;
	    nca.block_size = layout.stripe_unit_size;
	    nca.raid_level = layout.raid_level;
	    nca.object_create_time.ts_sec = now_sec;
	    nca.object_modify_time.ts_sec = now_sec;
	    nca.attr_modify_time.ts_sec = now_sec;
	    bcopy((char *)&nca, (char *)na.fs_specific,
            sizeof(_nasd_cheops_attribute_t));
#if 0	   
# if NASD_CHEOPS_OP_DEBUG > 0
	    (cheops_Msg
       "sending set attr to (%" NASD_BIGd_FMT ", %d)\n", ni, dr_id[i]);
# endif /* NASD_CHEOPS_OP_DEBUG > 0 */
	    rc = _nasd_cheops_setattr_h_dr(h, partnum, in_cookie, ni, &na,
                                     &nasd_status, &status);
	    if(rc)
	      (cheops_Msg "setattr failed!(rc=%d)\n", rc);
# if NASD_CHEOPS_OP_DEBUG > 0
	    if (!rc)
        (cheops_Msg "setattr success!(status=%d/%d)\n", nasd_status,status);
# endif /* NASD_CHEOPS_OP_DEBUG > 0 */
#endif /* 0 */
	  }
	}
	return 0;
}

int 
nasd_cheops_bs_remove(
  nasd_identifier_t    bsid)
{
	_nasd_cheops_cache_key_t key;
	_nasd_cheops_cache_ent_t *e;
	nasd_cheops_bs_inode_t *ip;
	int inum;
	int rc;     
        
  key.object_id = 0;
	key.record_id = bsid;
#if NASD_CHEOPS_OP_DEBUG > 0
	(cheops_Msg
   "processing remove for cheops object (0x%" NASD_ID_FMT ") ...\n",
	 bsid);
#endif
	rc = _nasd_cheops_cache_hold_ent(Bsdir_cachep, key, &e);
	if (rc) {
	  (cheops_Msg
     "remove: can not hold entry in cache for object 0x%" NASD_ID_FMT "\n",
     bsid);
	  return rc;      
	}
	ip = (nasd_cheops_bs_inode_t *) e->data;
	ip->flags = ip->flags & ~NASD_CHEOPS_BSINODE_USED;
	ip->flags = ip->flags | NASD_CHEOPS_BSINODE_REMOVED;	

	_nasd_cheops_cache_mark_dirty(Bsdir_cachep, key);	
	rc = _nasd_cheops_cache_release_ent(Bsdir_cachep, e);	

	/* FIX -- update the supermap to reflect removal */
	return 0; /* success */
}

int
nasd_cheops_bs_lookup(
  nasd_identifier_t         bsid,
  nasd_cheops_bs_handle_t  *bs_handle)
     
{
	_nasd_cheops_cache_key_t key;
	_nasd_cheops_cache_ent_t *e;
	nasd_cheops_bs_inode_t *ip;
  int rc, i;

#if NASD_CHEOPS_OP_DEBUG > 0
	(cheops_Msg
   "processing lookup for cheops object (0x%" NASD_ID_FMT ") ...\n",
	 bsid);
#endif
	key.object_id = 0;
	key.record_id = bsid;
	rc = _nasd_cheops_cache_hold_ent(Bsdir_cachep, key, &e);

	ip = (nasd_cheops_bs_inode_t *) e->data;
	if (!(ip->flags & NASD_CHEOPS_BSINODE_USED)) {
	  rc = _nasd_cheops_cache_release_ent(Bsdir_cachep, e);
#if NASD_CHEOPS_OP_DEBUG > 0
    (cheops_Msg
     "lookup to cheops object (0x%" NASD_ID_FMT 
     ") failed, object does not exist ..\n", bsid);
#endif /* NASD_CHEOPS_OP_DEBUG > 0 */
	  return _NASD_CHEOPS_ERR_DOESNOTEXIST; 
	}
	if ((ip->flags & NASD_CHEOPS_BSINODE_REMOVED)) {
	  rc = _nasd_cheops_cache_release_ent(Bsdir_cachep, e);
#if NASD_CHEOPS_OP_DEBUG > 0
    (cheops_Msg
     "lookup to cheops object (0x%" NASD_ID_FMT 
     ") failed, object removed ..\n", bsid);
#endif /* NASD_CHEOPS_OP_DEBUG > 0 */
	  return _NASD_CHEOPS_ERR_DOESNOTEXIST; 
	}
	  
	bs_handle->bsid = bsid;
	bs_handle->raid_level = ip->raid_level;
	bs_handle->num_col = ip->num_col;
	bs_handle->num_data_col = ip->num_data_col;
	bs_handle->stripe_unit_size = ip->stripe_unit_size;
	for (i=0; i< ip->num_col; i++) { 
	  bs_handle->identifier_list[i].nasd_identifier = ip->object_id[i];
	  bs_handle->identifier_list[i].disk_identifier = ip->drive_id[i]; 
	}		

	rc = _nasd_cheops_cache_release_ent(Bsdir_cachep, e);
#if NASD_CHEOPS_OP_DEBUG > 0
	(cheops_Msg
   "lookup to cheops object (0x%" NASD_ID_FMT
   ") returning map =( ", bs_handle->bsid);
	for (i=0; i<bs_handle->num_col; i++)
	  (cheops_Msg "0x%" NASD_ID_FMT "*%d, ",
     bs_handle->identifier_list[i].nasd_identifier,
     bs_handle->identifier_list[i].disk_identifier);
	(cheops_Msg " )\n");
#endif /* NASD_CHEOPS_OP_DEBUG > 0 */
  return rc; /* success */
}

int
nasd_cheops_bs_getattr(
  nasd_identifier_t      bsid,
  nasd_attribute_t      *attr)
{
  return 0; /* success */
}

int
nasd_cheops_bs_setattr(
  nasd_identifier_t      bsid,
  nasd_attribute_t       attr)
{
  return 0; /* success */
}

int
nasd_cheops_dr_lookup(
  nasd_disk_ident_t        dr_id,
  nasd_cheops_cl_dinfo_t  *clinfo)
{
  int i=0;
  nasd_boolean_t found;

  found = NASD_FALSE;
  while (i < NumDrives && (dmap->dinfo[i].dr_id != dr_id) ) 
    i++;
  if (i==NumDrives)
    return EINVAL;

  clinfo->partnum = dmap->dinfo[i].partnum;
  strcpy(clinfo->dr_name, dmap->dinfo[i].dr_name);
  strcpy(clinfo->dr_port, dmap->dinfo[i].dr_port); 
  clinfo->dr_id = dr_id;

  return 0; /* success */
}

int
nasd_cheops_mgr_lookup(
  nasd_identifier_t              bsid,
  nasd_cheops_mgr_info_t        *manager_info)
{
  return 0; /* success */
}

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