/*
 * nasd_pdrive_client_colocate.c
 *
 * RPC-specific client code for colocating with drive
 *
 * Author: Jim Zelenka
 */
/*
 * 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_pdrive_client.h>
#include <nasd/nasd_pdrive_client_colocate.h>
#include <nasd/nasd_pipe.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_security.h>
#include <nasd/nasd_security_dr.h>

#ifdef KERNEL
#define NASD_OD_RPC_NASD_RET res->nasd_status
#include <nasd/nasd_od_rpc.h>
#include <nasd/nasd_od.h>
#include <nasd/nasd_cache.h>
#include <nasd/nasd_keymgmt_dr.h>
#endif /* KERNEL */

#define NASD_OD_RPC_RETURN(_statval_) { \
  *status = _statval_; \
  return; \
}
/* Don't use __FILE__ here to avoid getting kernel-mangled names */
#define DOBEGIN(_opname_) NASD_OD_RPC_DOBEGIN(_opname_,"nasd_pdrive_client_colocate.c")
#define DORETURN(_opname_) NASD_OD_RPC_DORETURN(_opname_,"nasd_pdrive_client_colocate.c")

nasd_status_t
nasd_cl_colocate_bind(
  nasd_drive_handle_t   handle,
  char                 *drive_name,
  char                 *portnum,
  int                   binding_type,
  void                 *binding_param,
  int                   binding_param_len)
{
    /* bail if the drive name doesn't refer to the local machine. */
  if (nasd_hostlocal(drive_name) != NASD_SUCCESS) { return(NASD_FAIL);    }
  else                                            { return(NASD_SUCCESS); }
}

nasd_status_t
nasd_cl_colocate_unbind(
  nasd_drive_handle_t  handle)
{
  return(NASD_SUCCESS);
}

/* If we were going to implement security the same way here that we've
   done it for the other RPC interfaces, we would generate a message
   digest and nonce using the client's private capability key.  Then
   we would generating the drive's private capability key from the
   drive's private keys and the capability, and calculate the message
   digest and nonce using that capability key.  If the two keys are
   the same, then the digests will match.  If the digests don't match,
   then the capability or the arguments have been altered, or the
   nonces don't match.

   However, here we are both the drive *and* the client, and we have
   access to both the client's privacy capability key and the drive's
   private keys.  Therefore there is no need to go through the extra
   steps of calculating two digests -- we can just generate the
   drive's private capability key and check it against the client's key.
   If they match, we know the capability is intact.

   If you are taking numbers, you should know that the colocation
   interface is not doing the same amount of work as the RPC layers
   would.  Compile with NASD_COLOCATE_CALC_DIGESTS=1 to turn on
   the code below that calculates the digests. */
nasd_status_t
nasd_cl_colocate_verify_key(nasd_security_param_t *sec_param,
                            nasd_capability_t *capability,
                            void *args,
                            int args_len,
                            nasd_key_t cl_key)
{
  nasd_key_t dr_key, ikey, pkey;
  nasd_capability_otw_t capability_otw;
  nasd_status_t rc;
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_digest_nonce_otw_t digest_otw;
  nasd_digest_nonce_t digest;
  nasd_timespec_t now;
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */

  if(sec_param->actual_protection == NASD_NO_PROTECTION)
    return NASD_SUCCESS;


  /* we will need the OTW version of the capability to recalculate
     the key */
  if(NASD_SEC_IS_CAPABILITY(sec_param->type)) {
    if(!capability)
      return NASD_BAD_KEYTYPE;
    nasd_capability_t_marshall(capability, capability_otw);
  }

  /* calculate our idea of what the key should be */
  rc = nasd_sec_get_capability_keys(sec_param, capability,
                                    capability_otw, dr_key, ikey, pkey);
  if(rc)
    return rc;

#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_printf("colocate_verify_key: generating nonce\n");
  /* calculate the client's digest/nonce */
  nasd_sec_fill_nonce(sec_param, cl_key, NULL, 0, args, args_len,
                      &now, digest_otw, NULL);

  /* calculate drive's digest/nonce & verify it */
  nasd_digest_nonce_t_unmarshall(digest_otw, &digest);
  nasd_printf("colocate_verify_key: calling verify_args\n");
  rc = nasd_sec_verify_args(sec_param, capability, digest_otw, &digest,
                            args, args_len, dr_key, ikey, NULL);
#else /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  /* shortcut: just compare keys */
  if(bcmp(cl_key, dr_key, sizeof(nasd_key_t)) != 0)
    return NASD_BAD_DIGEST_REQ;
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */

    /* Generic security checks.  We do these here (after verifying args
     and capability) so as to not expose information about the drive
     if the MAC was incorrect. */
    /* make sure client has specified a valid partition number */
  if(!NASD_OD_PARTNUM_VALID(sec_param->partnum)) {
    return NASD_BAD_PARTITION;
  }
  /* if we got a capability, do some capability-specific checks */
  if(NASD_SEC_IS_CAPABILITY(sec_param->type)) {
    /* make sure this capability is for a valid partition (we don't
       trust the FM) */
    if(!NASD_OD_PARTNUM_VALID(capability->partnum)) {
      return NASD_BAD_PARTITION;
    }
    /* make sure the client is at least as secure as the FM wants it to be.
       we need to mask off the bits that we turned off earlier to avoid
       triggering false alarms. */
    if((sec_param->actual_protection & capability->min_protection) !=
       capability->min_protection) {
      return NASD_CAP_PROTECTION;
    }
    if((sec_param->actual_protection &
        PART(capability->partnum).min_protection) !=
       PART(capability->partnum).min_protection) {
      return NASD_PART_PROTECTION;
    }
  }
  return NASD_SUCCESS;
}

void
nasd_cl_colocate_calc_reply_digest(nasd_security_param_t *sec_param,
                                   nasd_key_t op_key,
                                   void *res,
                                   int res_len)
{
  nasd_timespec_t now;
  nasd_digest_nonce_otw_t digest_otw;
  nasd_key_t ikey;

  bzero(ikey, sizeof(nasd_key_t));
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, ikey, NULL, 1, res, res_len, &now,
                      digest_otw, NULL);
}

void
nasd_cl_colocate_null_dr(
  nasd_drive_handle_t   handle,
  nasd_res_t           *res,
  nasd_rpc_status_t    *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(null);
  res->nasd_status = NASD_SUCCESS;
  DORETURN(null);
}

void
nasd_cl_colocate_sync_dr(
  nasd_drive_handle_t   handle,
  nasd_res_t           *res,
  nasd_rpc_status_t    *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(sync);
  res->nasd_status = nasd_odc_flush_dirty(1);
  DORETURN(sync);
}

void
nasd_cl_colocate_part_creat_dr(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t            *capability,
  nasd_p_part_creat_dr_args_t  *args,
  nasd_p_part_creat_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  nasd_identifier_t nid;
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(part_creat);

  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_od_create_partition(args->in_partnum,
      args->in_blkcnt, args->in_min_protection, args->in_partition_key,
      args->in_red_key, args->in_black_key, &nid);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(part_creat);
}

void
nasd_cl_colocate_create_dr__otw_provided(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
  nasd_security_param_t    *sec_param,
  nasd_capability_t        *capability,
  nasd_p_create_dr_args_t  *args,
  nasd_p_create_dr_res_t   *res,
  nasd_cl_p_otw_buf_t      *otw,
  nasd_rpc_status_t        *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(create);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_create(args->in_partnum,
      &args->in_attribute, args->in_fieldmask, &res->out_identifier,
      &res->out_attribute, 0);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(create);
}

void
nasd_cl_colocate_getattr_dr(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t         *capability,
  nasd_p_getattr_dr_args_t  *args,
  nasd_p_getattr_dr_res_t   *res,
  nasd_rpc_status_t         *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(getattr);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_getattr(args->in_partnum,
      args->in_identifier, &res->out_attribute);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(getattr);
}

void
nasd_cl_colocate_read_simple_dr(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t         *capability,
  int                        is_read2,
  nasd_p_smpl_op_dr_args_t  *args,
  void                      *buf,
  nasd_p_fastread_dr_res_t  *res,
  nasd_rpc_status_t         *status)
{
  nasd_co_pipe_state_t state;
  nasd_procpipe_t procpipe;
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(read_simple);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    state.orig_buf = state.cur_buf = (nasd_byte_t *)buf;
    state.orig_buf_len = state.buf_len_remain = args->in_len;
    procpipe.state = &state;
    procpipe.push = nasd_co_push;
    procpipe.pull = NULL;
    res->out_datalen = 0;

    res->nasd_status = nasd_obj_read_simple(args->in_partnum,
      args->in_identifier, args->in_offset, args->in_len, NULL,
      is_read2, 0, &procpipe, &res->out_datalen, NULL);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(read_simple);
}

void
nasd_cl_colocate_tread_simple_dr(
  nasd_drive_handle_t         handle,
  nasd_key_t                  in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t          *capability,
  int                         is_read2,
  nasd_p_thrtl_op_dr_args_t  *args,
  void                       *buf,
  nasd_p_fastread_dr_res_t   *res,
  nasd_rpc_status_t          *status)
{
  nasd_co_pipe_state_t state;
  nasd_procpipe_t procpipe;
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(tread_simple);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    state.orig_buf = state.cur_buf = (nasd_byte_t *)buf;
    state.orig_buf_len = state.buf_len_remain = args->in_len;
    procpipe.state = &state;
    procpipe.push = nasd_co_push;
    procpipe.pull = NULL;
    res->out_datalen = 0;

    res->nasd_status = nasd_obj_read_simple(args->in_partnum,
      args->in_identifier, args->in_offset, args->in_len, &args->in_bms_targ,
      is_read2, 0, &procpipe, &res->out_datalen, NULL);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(tread_simple);
}

void
nasd_cl_colocate_write_simple_dr(
  nasd_drive_handle_t         handle,
  nasd_key_t                  in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t          *capability,
  nasd_p_smpl_op_dr_args_t   *args,
  void                       *buf,
  nasd_p_fastwrite_dr_res_t  *res,
  nasd_rpc_status_t          *status)
{
  nasd_co_pipe_state_t state;
  nasd_procpipe_t procpipe;
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(write_simple);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    state.orig_buf = state.cur_buf = (nasd_byte_t *)buf;
    state.orig_buf_len = state.buf_len_remain = args->in_len;
    procpipe.state = &state;
    procpipe.push = NULL;
    procpipe.pull = nasd_co_pull;
    res->out_datalen = 0;

    res->nasd_status = nasd_obj_write_simple(args->in_partnum,
      args->in_identifier, args->in_offset, args->in_len, &procpipe,
      NULL, &res->out_datalen);

  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(write_simple);
}

void
nasd_cl_colocate_setattr_dr__otw_provided(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t         *capability,
  nasd_p_setattr_dr_args_t  *args,
  nasd_p_setattr_dr_res_t   *res,
  nasd_cl_p_otw_buf_t       *otw,
  nasd_rpc_status_t         *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(setattr);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_setattr(args->in_partnum,
      args->in_identifier, &args->in_attribute, args->in_fieldmask,
      &res->out_attribute);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(setattr);
}

void
nasd_cl_colocate_flush_obj_dr(
  nasd_drive_handle_t          handle,
  nasd_key_t                   in_key,
  nasd_security_param_t       *sec_param,
  nasd_capability_t           *capability,
  nasd_p_flush_obj_dr_args_t  *args,
  nasd_p_flush_obj_dr_res_t   *res,
  nasd_rpc_status_t           *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(flush_obj);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_flush(args->in_partnum,
      args->in_identifier);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(flush_obj);
}

void
nasd_cl_colocate_eject_obj_dr(
  nasd_drive_handle_t          handle,
  nasd_key_t                   in_key,
  nasd_security_param_t       *sec_param,
  nasd_capability_t           *capability,
  nasd_p_eject_obj_dr_args_t  *args,
  nasd_p_eject_obj_dr_res_t   *res,
  nasd_rpc_status_t           *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(eject_obj);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_eject(args->in_partnum,
      args->in_identifier);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(eject_obj);
}

void
nasd_cl_colocate_remove_dr(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
  nasd_security_param_t    *sec_param,
  nasd_capability_t        *capability,
  nasd_p_remove_dr_args_t  *args,
  nasd_p_remove_dr_res_t   *res,
  nasd_rpc_status_t        *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(remove);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_remove(args->in_partnum,
      args->in_identifier);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(remove);
}

void
nasd_cl_colocate_initialize_dr(
  nasd_drive_handle_t           handle,
  nasd_p_initialize_dr_args_t  *args,
  nasd_p_initialize_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(initialize);
  res->nasd_status = nasd_sec_initialize_drive(args->in_master_key,
    args->in_drive_key);
  DORETURN(initialize);
}

void
nasd_cl_colocate_strt_iread_dr(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t            *capability,
  nasd_p_strt_iread_dr_args_t  *args,
  nasd_p_strt_iread_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(strt_iread);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_start_iread(args->in_partnum,
      args->in_index_identifier, args->in_data_identifier,
      args->in_interval, args->in_offset, args->in_flownum,
      args->in_earliest_start, args->in_latest_start,
      args->in_client_addr, &res->out_stream_id);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(strt_iread);
}

void
nasd_cl_colocate_stop_iread_dr(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t            *capability,
  nasd_p_stop_iread_dr_args_t  *args,
  nasd_p_stop_iread_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(stop_iread);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_stop_iread(args->in_stream_id);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(stop_iread);
}

void
nasd_cl_colocate_remote_attach_dr(
  nasd_drive_handle_t             handle,
  nasd_key_t                      in_key,
  nasd_security_param_t          *sec_param,
  nasd_capability_t              *capability,
  nasd_p_remote_attach_dr_args_t *args,
  void                           *buf,
  nasd_p_remote_attach_dr_res_t  *res,
  nasd_rpc_status_t              *status)
{
  nasd_co_pipe_state_t state;
  nasd_procpipe_t procpipe;
  NASD_OD_RPC_OP_STATS_DECL;
  DOBEGIN(remote_attach);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param,capability,
                                                 args,sizeof(*args),in_key);
  if (!res->nasd_status) {
    state.orig_buf = state.cur_buf = (nasd_byte_t * )buf;
    state.orig_buf_len = state.buf_len_remain = args->in_args_len;
    procpipe.state = &state;
    procpipe.push = NULL;
    procpipe.pull = nasd_co_pull;

    res->nasd_status = nasd_obj_remote_attach(args->in_partnum, args->in_identifier,
                                              args->in_name, args->in_args_len,
                                              &procpipe, NULL);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(remote_attach);
}

void
nasd_cl_colocate_remote_detach_dr(
  nasd_drive_handle_t             handle,
  nasd_key_t                      in_key,
  nasd_security_param_t          *sec_param,
  nasd_capability_t              *capability,
  nasd_p_remote_detach_dr_args_t *args,
  nasd_p_remote_detach_dr_res_t  *res,
  nasd_rpc_status_t              *status)
{
  NASD_OD_RPC_OP_STATS_DECL;

  DOBEGIN(remote_detach);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_obj_remote_detach(args->in_partnum,args->in_identifier);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(remote_detach);
}

void
nasd_cl_colocate_remote_invoke_dr(
  nasd_drive_handle_t             handle,
  nasd_key_t                      in_key,
  nasd_security_param_t          *sec_param,
  nasd_capability_t              *capability,
  nasd_p_smpl_op_dr_args_t       *args,
  void                           *buf,
  nasd_p_fastread_dr_res_t       *res,
  nasd_rpc_status_t              *status)
{
  nasd_co_pipe_state_t state;
  nasd_procpipe_t       procpipe;
  NASD_OD_RPC_OP_STATS_DECL;
  DOBEGIN(remote_invoke);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param,capability,
                                                 args,sizeof(*args),in_key);
  if (!res->nasd_status) {
    state.orig_buf = state.cur_buf = (nasd_byte_t * )buf;
    state.orig_buf_len = state.buf_len_remain = args->in_len;
    procpipe.state = &state;
    procpipe.push = nasd_co_push;
    procpipe.pull = NULL;
    res->out_datalen = 0;
    
    res->nasd_status = nasd_obj_read_simple(args->in_partnum,
      args->in_identifier, args->in_offset, args->in_len, NULL,
      0, 1, &procpipe, &res->out_datalen, NULL);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(remote_invoke);
}

void
nasd_cl_colocate_rshutdown_dr(
  nasd_drive_handle_t          handle,
  nasd_key_t                   in_key,
  nasd_security_param_t       *sec_param,
  nasd_capability_t           *capability,
  nasd_p_rshutdown_dr_args_t  *args,
  nasd_p_rshutdown_dr_res_t   *res,
  nasd_rpc_status_t           *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(rshutdown);
  res->nasd_status = nasd_cl_colocate_verify_key(sec_param, capability,
                                                 args, sizeof(*args),
                                                 in_key);
  if(!res->nasd_status) {
    res->nasd_status = nasd_drive_rshutdown(args->in_flags);
  }
#if NASD_COLOCATE_CALC_DIGESTS > 0
  nasd_cl_colocate_calc_reply_digest(sec_param, in_key, res, sizeof(*res));
#endif /* NASD_COLOCATE_CALC_DIGESTS > 0 */
  DORETURN(rshutdown);
}

void
nasd_cl_colocate_getinfo_dr(
  nasd_drive_handle_t       handle,
  nasd_p_getinfo_dr_res_t  *res,
  nasd_rpc_status_t        *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(getinfo);
  res->nasd_status = nasd_drive_getinfo(&res->info);
  DORETURN(getinfo);
}

void
nasd_cl_colocate_error_string(
  nasd_drive_handle_t   handle,
  nasd_rpc_status_t     status,
  nasd_error_string_t   str,
  char                 *file,
  int                   line)
{
  switch(status) {
    case 0:
      sprintf(str, "Success");
      return;
    default:
      sprintf(str, "%d:%u", handle->type, status);
  }
}

nasd_cl_p_rpcmod_tab_t nasd_cl_colocate_mod = {
  NULL,
  NASD_RPCMOD_INIT_FAIL,

  nasd_cl_colocate_bind,
  nasd_cl_colocate_unbind,

  nasd_cl_colocate_null_dr,
  nasd_cl_colocate_sync_dr,
  nasd_cl_colocate_part_creat_dr,
  nasd_cl_colocate_getattr_dr,
  nasd_cl_colocate_write_simple_dr,
  NULL,
  nasd_cl_colocate_read_simple_dr,
  NULL,
  nasd_cl_colocate_tread_simple_dr,
  NULL,
  nasd_cl_colocate_flush_obj_dr,
  nasd_cl_colocate_eject_obj_dr,
  nasd_cl_colocate_remove_dr,
  nasd_cl_colocate_initialize_dr,
  nasd_cl_colocate_strt_iread_dr,
  nasd_cl_colocate_stop_iread_dr,
  nasd_cl_colocate_rshutdown_dr,
  nasd_cl_colocate_getinfo_dr,

  nasd_cl_colocate_remote_attach_dr,
  nasd_cl_colocate_remote_detach_dr,
  nasd_cl_colocate_remote_invoke_dr,

  nasd_cl_colocate_create_dr__otw_provided,
  nasd_cl_colocate_setattr_dr__otw_provided,

  nasd_cl_colocate_error_string,

  NASD_BIND_COLOCATE,
  "Colocate",
  0,

  NULL
};

void
nasd_cl_p_colocate_register(
  int  counter_lock_held)
{
  nasd_cl_p_mod_register(&nasd_cl_colocate_mod, counter_lock_held);
}

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