/*
 * nasd_edrfs_drive.c
 *
 * Interfaces to NASD drive for NASD EDRFS server.
 *
 * Authors: Jim Zelenka, Nat Lanza
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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 <nasd/nasd_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_control.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_edrfs_server_internal.h>
#include <nasd/nasd_edrfs_types_marshall.h>
#include <nasd/nasd_timer.h>

#include <sys/errno.h>

NASD_DECLARE_MUTEX(nasd_edrfs_mount_lock)
#define LOCK_MOUNT()   NASD_LOCK_MUTEX(nasd_edrfs_mount_lock)
#define UNLOCK_MOUNT() NASD_UNLOCK_MUTEX(nasd_edrfs_mount_lock)

nasd_edrfs_drive_t *nasd_edrfs_mountbuckets[NASD_EDRFS_NAMEHASH_BUCKETS];
nasd_edrfs_drive_t *nasd_edrfs_dimountbuckets[NASD_EDRFS_DIHASH_BUCKETS];

#define NASD_EDRFS_DISKIDENT_BASE_FIRST 1
nasd_disk_ident_t nasd_edrfs_diskident_base = NASD_EDRFS_DISKIDENT_BASE_FIRST;

#define DEBUG_WRITE_DIRTY_DETAIL 0

nasd_status_t
nasd_edrfs_drsys_init()
{
  nasd_status_t rc;

  bzero((char *)nasd_edrfs_mountbuckets, sizeof(nasd_edrfs_mountbuckets));
  bzero((char *)nasd_edrfs_dimountbuckets, sizeof(nasd_edrfs_dimountbuckets));
  nasd_edrfs_diskident_base = NASD_EDRFS_DISKIDENT_BASE_FIRST;

  rc = nasd_mutex_init(&nasd_edrfs_mount_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_mutex(nasd_edrfs_shutdown, &nasd_edrfs_mount_lock);
  if (rc) {
    return(rc);
  }

  return(NASD_SUCCESS);
}

/*
 * Called when shutting down to re-mark directories as "clean"
 */
nasd_status_t
nasd_edrfs_mark_dirs_clean()
{
  return(NASD_SUCCESS);
}

/*
 * Clean up one mounted drive
 */
void
nasd_edrfs_shutdown_drive(
  nasd_edrfs_drive_t *dr)
{
  nasd_error_string_t err_str;
  nasd_rpc_status_t status;
  nasd_timespec_t ts;
  nasd_status_t rc;
  nasd_timer_t tm;

  nasd_edrfs_name_cleandir(dr->root);
  nasd_edrfs_release_attr(dr->root->attr);
  dr->root->attr = NULL;
  nasd_edrfs_ditch_coredir(dr->root, 0);
  NASD_TM_START(&tm);
  nasd_cl_p_sync_dr(dr->handle, &rc, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (rc || status) {
    nasd_printf("ERROR: status=0x%x (%s) nasd_status=0x%x (%s) on sync of %s\n",
      status, nasd_cl_error_string(dr->handle, status, err_str),
      rc, nasd_error_string(rc), dr->drname);
  }
  nasd_unbind_drive(&dr->handle);
  NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
}

/*
 * Cleanup mount table state
 */
void
nasd_edrfs_shutdown_drives()
{
  nasd_edrfs_drive_t *dr, *next;
  int i;

  for(i=0;i<NASD_EDRFS_NAMEHASH_BUCKETS;i++) {
    for(dr=nasd_edrfs_mountbuckets[i];dr;dr=next) {
      next = dr->next;
      /* cleanup drive dr */
      nasd_edrfs_shutdown_drive(dr);
    }
    nasd_edrfs_mountbuckets[i] = NULL;
  }
}

/*
 * Note that this is not protected against simultaneous mount
 * adds. If mounts are dynamically added, the dynamic adder
 * should just serialize all such requests.
 */
nasd_status_t
nasd_edrfs_add_mount(
  char         *path,
  char         *drive,
  char         *drive_bind_name,
  int           portnum,
  int           partnum,
  nasd_uint32   net_addr)
{
  nasd_edrfs_attributes_t *edrfsattr;
  nasd_ctrl_drive_info_t *drinfo;
  nasd_ctrl_part_info_t *ptinfo;
  nasd_p_getattr_dr_args_t args;
  nasd_error_string_t err_str;
  nasd_rpc_status_t status;
  nasd_edrfs_core_dir_t *cd;
  nasd_drive_handle_t dh;
  nasd_security_param_t sp;
  nasd_sec_keyring_t keys;
  nasd_edrfs_drive_t *dr;
  nasd_p_getattr_dr_res_t *attr_res;
  nasd_timespec_t ts;
  nasd_status_t rc;
  nasd_timer_t tm;
  int h, l;

  nasd_printf("Add mount %s from %s partnum %d\n", path, drive, partnum);

  rc = nasd_edrfs_do_drive_bind(drive_bind_name, &dh);
  if (rc) {
    nasd_printf("EDRFS: cannot bind to %s\n", drive);
    return(rc);
  }
  NASD_TM_START(&tm);
  nasd_cl_p_null_dr(dh, &rc, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (status || rc) {
    nasd_printf("EDRFS: cannot talk to %s status=0x%x (%s) nasd_status=0x%x (%s)\n",
      drive, status, nasd_cl_error_string(dh, status, err_str),
      rc, nasd_error_string(rc));
    nasd_unbind_drive(&dh);
    return(NASD_OFFLINE);
  }
  NASD_Malloc(dr,sizeof(nasd_edrfs_drive_t),(nasd_edrfs_drive_t *));
  if (dr == NULL) {
    nasd_unbind_drive(&dh);
    return(NASD_NO_MEM);
  }
  dr->handle = dh;

  LOCK_MOUNT();
  dr->di = nasd_edrfs_diskident_base;
  nasd_edrfs_diskident_base++;
  UNLOCK_MOUNT();

  NASD_Malloc(ptinfo, sizeof(nasd_ctrl_part_info_t),
    (nasd_ctrl_part_info_t *));
  if (ptinfo == NULL) {
    nasd_printf("EDRFS: could not allocate info page to mount %s\n", drive);
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(NASD_NO_MEM);
  }
  drinfo = (nasd_ctrl_drive_info_t *)ptinfo;

  /* @@@ add security here */
  bzero(&sp, sizeof(nasd_security_param_t));
  sp.actual_protection = NASD_NO_PROTECTION;
  sp.type = NASD_BLACK_KEY;
    
        
  NASD_TM_START(&tm);
  rc = nasd_cl_p_ctrl_get_drive_info(dh, keys.black_key, &sp, NULL, drinfo);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (rc) {
    nasd_printf("EDRFS: nasd_cl_p_get_drive_info got 0x%x (%s)\n",
      rc, nasd_error_string(rc));
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(rc);
  }
  dr->blocksize = drinfo->blocksize;
  NASD_TM_START(&tm);
  rc = nasd_cl_p_ctrl_get_part_info(dh, keys.black_key, &sp, NULL, partnum, ptinfo);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (rc) {
    nasd_printf("EDRFS: nasd_cl_p_get_part_info got 0x%x (%s)\n",
      rc, nasd_error_string(rc));
    if ((rc == NASD_CTRL_ID_CHECK_FAILED) && (ptinfo->ctrl_id == 0)) {
      nasd_printf(
        "EDRFS: got bad control id 0, suggesting partition %d does not exist on %s\n",
        partnum, drive);
      return(NASD_FAIL);
    }
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(rc);
  }

  cd = nasd_edrfs_server_dir_get_coredir(0);
  NASD_ASSERT(cd != NULL);

  dr->root = cd;
  dr->portnum = portnum;
  dr->net_addr = net_addr;
  dr->partnum = partnum;
  l = NASD_MIN(NASD_EDRFS_DRIVENAME_LEN-1, strlen(drive));
  bcopy(drive, dr->drname, l);
  dr->drname[l] = '\0';
  l = NASD_MIN(NASD_EDRFS_DRPATH_LEN-1, strlen(path));
  bcopy(path, dr->pname, l);
  dr->pname[l] = '\0';

  cd->drive = dr;
  cd->nid = ptinfo->first_obj;
  cd->di = dr->di;
  /* @@@ init cd->cookie here */
  NASD_ASSERT(cd->refcnt == 1);
  cd->busy = 1;
  cd->ndirty = 0;
  cd->attr = nasd_edrfs_get_attr();
  NASD_ASSERT(cd->attr != NULL);
  cd->isroot = 1;

  NASD_Free(ptinfo, sizeof(nasd_ctrl_part_info_t));

  args.in_partnum = partnum;
  args.in_identifier = cd->nid;

  NASD_TM_START(&tm);
  /* security param is still initialized to NASD_NO_PROTECTION from
     earlier */
  nasd_cl_p_getattr_dr(dh, keys.black_key, &sp, NULL, &args,
                       &cd->attr->attr_res, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  attr_res = &cd->attr->attr_res;
  if (status || attr_res->nasd_status) {
    nasd_printf(
      "EDRFS: cannot get attribute root %s nid=0x%" NASD_ID_FMT " status=0x%x (%s) nasd_status=0x%x (%s)\n",
      drive, (nasd_identifier_t) cd->nid,
      status, nasd_cl_error_string(dh, status, err_str),
      attr_res->nasd_status,
      nasd_error_string(attr_res->nasd_status));
    nasd_edrfs_release_attr(cd->attr);
    cd->attr = NULL;
    nasd_edrfs_ditch_coredir(cd, 0);
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(rc);
  }

  if (attr_res->out_attribute.object_len%sizeof(nasd_edrfs_dirpage_t)) {
    nasd_printf("EDRFS: root object drive %s partition %d has bad length %lu\n",
                drive, partnum, (u_long)attr_res->out_attribute.object_len);
    nasd_edrfs_release_attr(cd->attr);
    cd->attr = NULL;
    nasd_edrfs_ditch_coredir(cd, 0);
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(NASD_FAIL);
  }

  edrfsattr = &cd->attr->edrfsattr;
  nasd_edrfs_attributes_t_unmarshall((nasd_otw_base_t *)
                                     attr_res->out_attribute.fs_specific,
                                     edrfsattr);

  if (edrfsattr->clean != NASD_EDRFS_CLEAN) {
    nasd_printf("EDRFS: filesystem on %s partition %d is not clean\n",
      drive, partnum);
    nasd_edrfs_release_attr(cd->attr);
    cd->attr = NULL;
    nasd_edrfs_ditch_coredir(cd, 0);
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(NASD_FAIL);
  }
  if (edrfsattr->type != NASD_EDRFS_TYPE_DIR) {
    nasd_printf("EDRFS: filesystem on %s partition %d is not dir-rooted!\n",
      drive, partnum);
    nasd_edrfs_release_attr(cd->attr);
    cd->attr = NULL;
    nasd_edrfs_ditch_coredir(cd, 0);
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(NASD_FAIL);
  }

  NASD_EDRFS_LOCK_DIRCACHE();
  NASD_EDRFS_LOCK_DIR(cd,1);

  nasd_edrfs_server_dir_hash_ins(cd);

  NASD_EDRFS_UNLOCK_DIR(cd);
  NASD_EDRFS_UNLOCK_DIRCACHE();

  rc = nasd_edrfs_load_dir(cd);
  if (rc != NASD_SUCCESS) {
    nasd_printf("EDRFS: got 0x%x (%s) loading %s@%s (%s)\n", rc,
      nasd_error_string(rc), path, drive, drive_bind_name);
    cd->hnext->hprev = cd->hprev;
    cd->hprev->hnext = cd->hnext;
    nasd_edrfs_name_cleandir(cd);
    nasd_edrfs_release_attr(cd->attr);
    cd->attr = NULL;
    nasd_edrfs_ditch_coredir(cd, 0);
    nasd_unbind_drive(&dh);
    NASD_Free(dr, sizeof(nasd_edrfs_drive_t));
    return(NASD_FAIL);
  }

  /*
   * XXX mark directory dirty here
   */

  NASD_EDRFS_LOCK_DIRCACHE();
  NASD_EDRFS_LOCK_DIR(cd,1);

  cd->busy = 0;
  NASD_BROADCAST_COND(cd->cond);

  NASD_EDRFS_UNLOCK_DIR(cd);
  nasd_edrfs_server_dir_release(cd);
  NASD_EDRFS_UNLOCK_DIRCACHE();

  LOCK_MOUNT();

  h = nasd_edrfs_name_hash(dr->pname);
  dr->next = nasd_edrfs_mountbuckets[h];
  nasd_edrfs_mountbuckets[h] = dr;

  h = NASD_EDRFS_DIHASH(dr->di);
  dr->dinext = nasd_edrfs_dimountbuckets[h];
  nasd_edrfs_dimountbuckets[h] = dr;

  UNLOCK_MOUNT();

  return(NASD_SUCCESS);
}

nasd_status_t
nasd_edrfs_mount_lookup(
  char                     *dirpath,
  nasd_list_len_t           in_listlen,
  nasd_net_address_t        out_drivelist[],
  nasd_list_len_t          *out_listlen,
  nasd_edrfs_identifier_t  *out_identifier)
{
  nasd_edrfs_drive_t *r, *dr;
  nasd_status_t rc;
  int h;

  if (in_listlen < 1) {
    return(NASD_BAD_LEN);
  }

  rc = NASD_SUCCESS;
  LOCK_MOUNT();

  h = nasd_edrfs_name_hash(dirpath);
  for(dr=NULL,r=nasd_edrfs_mountbuckets[h];r;r=r->next) {
    if (!strcmp(dirpath, r->pname)) {
      dr = r;
      break;
    }
  }
  if (dr == NULL) {
    rc = NASD_EDRFS_BAD_NAME;
    goto done;
  }
  out_drivelist[0].disk_identifier = dr->di;
  out_drivelist[0].network_address = dr->net_addr;
  out_drivelist[0].port_number = dr->portnum;
  out_drivelist[0].partnum = dr->partnum;
  *out_listlen = 1;
  out_identifier->disk_identifier = dr->di;
  out_identifier->nasd_identifier = dr->root->nid;
  out_identifier->partnum=dr->partnum;
  out_identifier->pad1 = 0;


done:
  UNLOCK_MOUNT();
  return(rc);
}

#define NASD_EDRFS_SERVER_DIR_WRITER_FLS() { \
  nasd_security_param_t _sp; \
  nasd_p_smpl_op_dr_args_t _wr_args; \
  nasd_p_fastwrite_dr_res_t _wr_res; \
  nasd_timespec_t _ts; \
  nasd_timer_t _tm; \
  NASD_EDRFS_CSINC(dir_writer_fls); \
  memlist[i-1].next = NULL; \
  _sp.type = dir->cookie.capability.type; \
  _sp.partnum = dir->drive->partnum; \
  _sp.actual_protection = dir->cookie.capability.min_protection; \
  _wr_args.in_identifier = dir->nid; \
  _wr_args.in_offset = off; \
  _wr_args.in_len = len; \
  _wr_args.in_partnum = dir->drive->partnum; \
  NASD_TM_START(&_tm); \
  nasd_cl_p_range_write_dr(dir->drive->handle, dir->cookie.key, &_sp, \
    &dir->cookie.capability, &_wr_args, memlist, &_wr_res, &status); \
  NASD_TM_STOP(&_tm); \
  NASD_TM_ELAPSED_TS(&_tm, &_ts); \
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &_ts); \
  nasd_status = _wr_res.nasd_status; \
  out_len = _wr_res.out_datalen; \
  if (nasd_status || status) { \
    nasd_printf( \
      "EDRFS: writing directory drive %s partnum %d nasd_status 0x%x (%s) status 0x%x (%s)\n", \
      dir->drive->drname, dir->drive->partnum, nasd_status, \
      nasd_error_string(nasd_status), status, \
      nasd_cl_error_string(dir->drive->handle, status, err_str)); \
    NASD_PANIC(); \
  } \
  if (out_len != len) { \
    nasd_printf( \
      "EDRFS: writing directory drive %s partnum %d, got partial %d/%d\n", \
      dir->drive->drname, dir->drive->partnum, (int)out_len, (int)len); \
    NASD_PANIC(); \
  } \
  if (dir->attr) { \
    nasd_status = nasd_edrfs_server_dir_load_attr(dir); \
    if (nasd_status) { \
      nasd_printf("EDRFS: failed attribute readback\n"); \
    } \
  } \
  i = 0; \
  len = 0; \
  next_off = 0; \
  off = 0; \
}

/*
 * call with dir already marked BUSY
 */
nasd_status_t
nasd_edrfs_server_dir_write_dirty(
  nasd_edrfs_core_dir_t  *dir)
{
  nasd_mem_list_t memlist[NASD_EDRFS_MEMLIST_LEN];
  nasd_edrfs_core_dirpage_t *cdp;
  nasd_offset_t next_off, off;
  nasd_error_string_t err_str;
  nasd_rpc_status_t status;
  nasd_status_t nasd_status;
  nasd_len_t len, out_len;
  int i;

#if DEBUG_WRITE_DIRTY_DETAIL
  nasd_printf("writing dirty dir 0x%" NASD_ID_FMT "\n", dir->nid);
#endif /* DEBUG_WRITE_DIRTY_DETAIL */

  i = 0;
  len = 0;
  next_off = 0;
  off = 0;
  for(cdp=dir->first_page;cdp;cdp=cdp->next) {
    if (cdp->dirty == 0) { continue; }
    if ((i != 0) &&
        (cdp->offset != next_off)) { NASD_EDRFS_SERVER_DIR_WRITER_FLS(); }

    if (i == 0) { off = cdp->offset; }
    NASD_EDRFS_CSINC(dir_writer_pages);

#if DEBUG_WRITE_DIRTY_DETAIL
    nasd_printf("marshalling page in nid 0x%" NASD_ID_FMT "\n", dir->nid);
#endif /* DEBUG_WRITE_DIRTY_DETAIL */

    nasd_edrfs_dir_marshall_page(cdp->page, cdp->raw_page);
    memlist[i].addr = (void *)cdp->raw_page;
    memlist[i].len = sizeof(nasd_edrfs_dirpage_t);
    memlist[i].stride = 0;
    memlist[i].nelem = 1;
    memlist[i].next = &memlist[i+1];
    next_off += sizeof(nasd_edrfs_dirpage_t);
    len += sizeof(nasd_edrfs_dirpage_t);
    i++;
    if (i >= NASD_EDRFS_MEMLIST_LEN) { NASD_EDRFS_SERVER_DIR_WRITER_FLS(); }
    cdp->dirty = 0;
  }

  if (i) { NASD_EDRFS_SERVER_DIR_WRITER_FLS(); }
  dir->ndirty = 0;

  return(NASD_SUCCESS);
}

/* call with dir LOCKED */
nasd_status_t
nasd_edrfs_server_dir_load_attr(
  nasd_edrfs_core_dir_t  *dir)
{
  nasd_security_param_t sp;
  nasd_sec_keyring_t keys;
  nasd_p_getattr_dr_args_t args;
  nasd_error_string_t err_str;
  nasd_rpc_status_t status;
  nasd_drive_handle_t dh;
  nasd_timespec_t ts;
  nasd_timer_t tm;

  NASD_EDRFS_CSINC(dir_load_attr);
  if (dir->attr == NULL) {
    dir->attr = nasd_edrfs_get_attr();
  }
  dh = dir->drive->handle;

  /* @@@ init security here */
  bzero(&sp, sizeof(nasd_security_param_t));
  sp.actual_protection = NASD_NO_PROTECTION;
  sp.type = NASD_BLACK_KEY;

  args.in_partnum = dir->drive->partnum;
  args.in_identifier = dir->nid;
  NASD_TM_START(&tm);
  nasd_cl_p_getattr_dr(dh, keys.black_key, &sp, NULL, &args,
                       &dir->attr->attr_res, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (status || dir->attr->attr_res.nasd_status) {
    nasd_printf(
      "EDRFS: cannot get attribute drive %s nid=0x%" NASD_ID_FMT " status=0x%x (%s) nasd_status=0x%x (%s)\n",
      dir->drive->drname, dir->nid, status,
      nasd_cl_error_string(dh, status, err_str),
      dir->attr->attr_res.nasd_status,
      nasd_error_string(dir->attr->attr_res.nasd_status));
    nasd_edrfs_release_attr(dir->attr);
    dir->attr = NULL;
    return(NASD_FAIL);
  }
  /* Unmarshall edrfs attributes */
  nasd_edrfs_attributes_t_unmarshall((nasd_otw_base_t *) NASD_EDRFS_ATTR_ATTR
                                     (dir->attr).fs_specific,
                                     &dir->attr->edrfsattr);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_edrfs_do_getattr(
  nasd_edrfs_drive_t         *dr,
  nasd_identifier_t           nasdid,
  nasd_p_getattr_dr_res_t    *ga_res)
{
  nasd_security_param_t sp;
  nasd_sec_keyring_t keys;
  nasd_p_getattr_dr_args_t args;
  nasd_rpc_status_t status;
  nasd_timespec_t ts;
  nasd_timer_t tm;

  NASD_EDRFS_CSINC(do_getattr);
  bzero(&sp, sizeof(nasd_security_param_t));
  sp.actual_protection = NASD_NO_PROTECTION;
  sp.type = NASD_BLACK_KEY;

  args.in_partnum = dr->partnum;
  args.in_identifier = nasdid;
  NASD_TM_START(&tm);
  nasd_cl_p_getattr_dr(dr->handle, keys.black_key, &sp, NULL,
                       &args, ga_res, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (status)
    return(NASD_FAIL);

  return(ga_res->nasd_status);
}

nasd_status_t
nasd_edrfs_do_setattr(
  nasd_edrfs_drive_t          *dr,
  nasd_p_setattr_dr_args_t    *args,
  nasd_p_setattr_dr_res_t     *res,
  nasd_cl_p_otw_buf_t         *otw)
{
  nasd_security_param_t sp;
  nasd_sec_keyring_t keys;
  nasd_rpc_status_t status;
  nasd_timespec_t ts;
  nasd_timer_t tm;

  NASD_EDRFS_CSINC(do_setattr);

  /* @@@ init security */
  bzero(&sp, sizeof(nasd_security_param_t));
  sp.actual_protection = NASD_NO_PROTECTION;
  sp.type = NASD_BLACK_KEY;

  NASD_TM_START(&tm);
  nasd_cl_p_setattr_dr__otw_provided(dr->handle, keys.black_key, &sp, NULL,
    args, res, otw, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (status)
    return(NASD_FAIL);
  return(res->nasd_status);
}

nasd_status_t
nasd_edrfs_get_drive(
  nasd_edrfs_identifier_t    in_identifier,
  nasd_edrfs_drive_t       **drp)
{
  nasd_edrfs_drive_t *r;
  nasd_status_t rc;
  int h;

  rc = NASD_BAD_IDENTIFIER;
  *drp = NULL;
  LOCK_MOUNT();

  h = NASD_EDRFS_DIHASH(in_identifier.disk_identifier);
  for(r=nasd_edrfs_dimountbuckets[h];r;r=r->dinext) {
    if (r->di == in_identifier.disk_identifier) {
      *drp = r;
      rc = NASD_SUCCESS;
      break;
    }
  }

  UNLOCK_MOUNT();
  return(rc);
}

nasd_status_t
nasd_edrfs_do_create(
  nasd_edrfs_drive_t         *dr,
  nasd_p_create_dr_args_t    *cr_args,
  nasd_p_create_dr_res_t     *cr_res,
  nasd_cl_p_otw_buf_t        *otw)
{
  nasd_security_param_t sp;
  nasd_sec_keyring_t keys;
  nasd_rpc_status_t status;
  nasd_timespec_t ts;
  nasd_timer_t tm;

  /* @@@ init security & fill in keys */
  bzero(&sp, sizeof(nasd_security_param_t));
  sp.actual_protection = NASD_NO_PROTECTION;
  sp.type = NASD_BLACK_KEY;

  cr_args->in_partnum = dr->partnum;

  NASD_TM_START(&tm);
  nasd_cl_p_create_dr__otw_provided(dr->handle, keys.black_key, &sp, NULL,
    cr_args, cr_res, otw, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (status)
    return(NASD_FAIL);
  if (cr_res->nasd_status)
    return(cr_res->nasd_status);

  return(NASD_SUCCESS);
}

nasd_status_t
nasd_edrfs_do_remove(
  nasd_edrfs_drive_t   *dr,
  nasd_identifier_t     nasdid)
{
  nasd_security_param_t sp;
  nasd_sec_keyring_t keys;
  nasd_p_remove_dr_args_t rm_args;
  nasd_p_remove_dr_res_t rm_res;
  nasd_rpc_status_t status;
  nasd_timespec_t ts;
  nasd_timer_t tm;

  /* @@@ init security */
  bzero(&sp, sizeof(nasd_security_param_t));
  sp.actual_protection = NASD_NO_PROTECTION;
  sp.type = NASD_BLACK_KEY;

  rm_args.in_identifier = nasdid;
  rm_args.in_partnum = dr->partnum;

  NASD_TM_START(&tm);
  nasd_cl_p_remove_dr(dr->handle, keys.black_key, &sp, NULL,
                      &rm_args, &rm_res, &status);
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_edrfs_cache_stats.drive_time, &ts);
  if (status)
    return(NASD_FAIL);

  return(rm_res.nasd_status);
}

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