/*
 * nasd_edrfs_removefile.c
 *
 * Remove a directory entry from an EDRFS directory.
 *
 * Author: Nat Lanza
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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_getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdrive_client_kpdev.h>
#include <nasd/nasd_edrfs_dir.h>
#include <nasd/nasd_edrfs_types.h>
#include <nasd/nasd_edrfs_types_marshall.h>

char *progname;

int                        binding_type;
int                        binding_args_len;
void                      *binding_args;
char                      *binding_port = NASD_PDRIVE_PORT;
nasd_drive_param_kpdev_t   kpdev_args;
int                        nondefault_binding = 0;

nasd_sec_keyring_t         keys;
nasd_error_string_t error_text;

void usage() {
  fprintf(stderr, "USAGE: %s [options] servername partnum nasdid password name\n",
	  progname);
  fprintf(stderr, "  No options supported.\n");
  fprintf(stderr, "  We have eliminated your need to make decisions.\n");
  fprintf(stderr, "  We have eliminated your need for choice.\n");
  fprintf(stderr, "  Have a nice day. (You have no choice.)\n");
  fflush(stderr);
  exit(1);
}

int main(int argc, char **argv) {
  nasd_p_smpl_op_dr_args_t   write_args, read_args;
  nasd_p_fastread_dr_res_t   read_res;
  nasd_p_fastwrite_dr_res_t  write_res;
  nasd_p_getattr_dr_args_t   getattr_args;
  nasd_p_getattr_dr_res_t    getattr_res;

  nasd_edrfs_dirpage_t       page;
  char                       raw_page[NASD_EDRFS_DIRPAGE_SIZE];

  nasd_rpc_status_t          status;
  nasd_drive_handle_t        h;
  nasd_cookie_t              cookie;
  nasd_security_param_t      sp;
  nasd_timespec_t            tm;
  nasd_status_t              rc;
  int                        i, npages, added, partnum, sec_level=0;
  char                       c;
  char                      *server_name, *master_password, *name;
  nasd_identifier_t          nasdid;
  nasd_uint16                protection;

  nasd_edrfs_attributes_t    edrfsattr;
  nasd_attribute_t          *attr;

  nasd_edrfs_dirdata_t      *dd;

  progname = argv[0];

  binding_type = NASD_BIND_DEFAULT;
  binding_args = NULL;
  binding_args_len = 0;

  while (nasd_getopt(argc, argv, "ks", &c)) {
    switch(c) {
      case 'k':
	if (nondefault_binding) { usage(); }
	nondefault_binding = 1;
	binding_type = NASD_BIND_KPDEV_DEFAULT;
	binding_args = &kpdev_args;
	binding_args_len = sizeof(kpdev_args);
	strcpy(kpdev_args.devname, "/dev/nasdkp0");
	break;
      case 's':
	if (sscanf(nasd_optarg, "%d", &sec_level) != 1) {
	  usage();
	}
	break;
      default:
	fprintf(stderr, "Unknown option '%c'\n", c);
	usage();
    }
  }

  if (nasd_optind >= argc) { usage(); }
  server_name = argv[nasd_optind];
  nasd_optind++;

  if (nasd_optind >= argc) { usage(); }
  if (sscanf(argv[nasd_optind], "%d", &partnum) != 1) { usage(); }
  nasd_optind++;

  if (nasd_optind >= argc) { usage(); }
  if (nasd_str_to_nasd_id(argv[nasd_optind], &nasdid)) { usage(); }
  nasd_optind++;

  if (nasd_optind >= argc) { usage(); }
  master_password = argv[nasd_optind];
  nasd_optind++;

  if (nasd_optind >= argc) { usage(); }
  name = argv[nasd_optind];
  nasd_optind++;

  if (nasd_optind < argc) { usage(); }

  rc = nasd_sec_seclevel_to_protection(sec_level, &protection);
  if(rc) {
    fprintf(stderr, "ERROR: invalid security level %d\n", sec_level);
    usage();
  }

  rc = nasd_cl_p_init();
  if (rc) {
    fprintf(stderr, "ERROR: cannot init client library, rc=0x%x (%s)\n",
      rc, nasd_error_string(rc));
    exit(1);
  }

  rc = nasd_bind_to_drive(server_name, binding_port,
			  binding_type, binding_args, binding_args_len, &h);
  if (rc) {
    fprintf(stderr, "ERROR: cannot bind to server %s\n", server_name);
    fflush(stderr);
    exit(1);
  }

  /* Fill in capability and security param */
  nasd_sec_password_to_keys(master_password, partnum, &keys);

  nasd_drive_handle_get_time(h, &tm);
  tm.ts_sec += (60*60);
  nasd_sec_build_capability(partnum, nasdid,
    NASD_ACCESS_RIGHTS_READ | NASD_ACCESS_RIGHTS_WRITE,
    0, tm.ts_sec, protection, NASD_BLACK_CAPABILITY,
    0, 0, 7, keys.black_key, &cookie);
  
  sp.type = NASD_BLACK_CAPABILITY; 
  sp.partnum = partnum;
  sp.actual_protection = protection;

  /* read attributes */

  getattr_args.in_identifier = nasdid;
  getattr_args.in_partnum    = partnum;
  
  nasd_cl_p_getattr_dr(h, cookie.key, &sp, &cookie.capability,
		       &getattr_args, &getattr_res, &status);

  if (getattr_res.nasd_status || status) {
    fprintf(stderr, "ERROR: nasd_status=0x%x (%s) status=0x%x (%s)\n",
      getattr_res.nasd_status, nasd_error_string(getattr_res.nasd_status),
      status, nasd_cl_error_string(h, status, error_text));
    exit(1);
  }

  attr = &getattr_res.out_attribute;
  nasd_edrfs_attributes_t_unmarshall((void *) attr->fs_specific,
				     &edrfsattr);

  if (edrfsattr.type != NASD_EDRFS_TYPE_DIR) {
    fprintf(stderr, "Unexpected type 0x%02x\n", edrfsattr.type);
    exit(1);
  }
  
  if (attr->object_len % NASD_EDRFS_DIRPAGE_SIZE) {
    fprintf(stderr, "ERROR: object len %" NASD_64u_FMT
      " not a multiple of %d\n", attr->object_len, NASD_EDRFS_DIRPAGE_SIZE);
    exit(1);
  }

  npages = attr->object_len / NASD_EDRFS_DIRPAGE_SIZE;
  if (npages < 1) {
    fprintf(stderr, "ERROR: %d pages\n", npages);
    exit(1);
  }

  /* read in pages */
  read_args.in_partnum    = write_args.in_partnum    = partnum;
  read_args.in_identifier = write_args.in_identifier = nasdid;
  read_args.in_len        = write_args.in_len        = NASD_EDRFS_DIRPAGE_SIZE;
  read_args.in_offset     = write_args.in_offset     = 0;

  added = 0;

  for (i = 0; i < npages; i++) {
    printf("Reading in page %d\n", i);

    
    nasd_cl_p_read_simple_dr(h, cookie.key, &sp, &cookie.capability,
			     &read_args, raw_page, &read_res, &status);

    if (status || read_res.nasd_status) {
      fprintf(stderr, "ERROR: nasd_status=0x%x (%s) status=0x%x (%s)\n",
	      read_res.nasd_status, nasd_error_string(read_res.nasd_status),
	      status, nasd_cl_error_string(h, status, error_text));
      exit(1);
    }

    printf("Parsing page %d\n", i);

    nasd_edrfs_dir_parse_page(raw_page, &page);

    printf("Trying to remove entry '%s' from page %d\n", name, i);

    rc = nasd_edrfs_dir_remove_entry(&page, name);
    
    if (rc == NASD_SUCCESS) {
      printf("Entry removed from page %d\n", i);
      
      nasd_edrfs_dir_marshall_page(&page, raw_page);

      write_args.in_offset = read_args.in_offset;

      nasd_cl_p_write_simple_dr(h, cookie.key, &sp, &cookie.capability,
				&write_args, raw_page, &write_res, &status);

      if (status || write_res.nasd_status) {
	fprintf(stderr, "ERROR: nasd_status=0x%x (%s) status=0x%x (%s)\n",
		write_res.nasd_status,
		nasd_error_string(write_res.nasd_status), status, 
		nasd_cl_error_string(h, status, error_text));
	exit(1);
      }
      
      break;
    } else {
      printf("Couldn't remove entry from page %d\n", i);
    }

    read_args.in_offset += NASD_EDRFS_DIRPAGE_SIZE;
  }
  /* clean up and exit */

  rc = nasd_unbind_drive(&h);
  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) unbinding drive\n",
      rc, nasd_error_string(rc));
    exit(1);
  }
  nasd_cl_p_shutdown();

  exit(0);

}
