/*
 * nasd_edrfs_fsck.c
 *
 * Check the validity and consistency of a NASD EDRFS partition
 *
 * Author: 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_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_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_dir.h>
#include <nasd/nasd_edrfs_internal.h>
#include <nasd/nasd_edrfs_types.h>
#include <nasd/nasd_edrfs_types_marshall.h>

#define SET_BIT(word,bit) ((word) |= (1 << (bit)))
#define UNSET_BIT(word, bit) ((word) &= ~(1 << (bit)))
#define CHECK_BIT(word, bit) (((word) & (1 << (bit))) >> (bit))

typedef struct edrfs_fsck_dir_ent_s {
  char *name;
  nasd_identifier_t nid;
  struct edrfs_fsck_dir_ent_s *next;
} edrfs_fsck_dir_ent_t;

typedef struct edrfs_fsck_obj_s {
  nasd_identifier_t nid;
  struct edrfs_fsck_obj_s *next;
} edrfs_fsck_obj_t;

char *progname;
char *server_name;
int partnum;
int verbose = 0;
int debug = 0;
int autofix = 0;
int nofix = 0;
nasd_drive_handle_t h;
nasd_identifier_t root_nid;
edrfs_fsck_obj_t *object_list = NULL;

void usage(void);
void parse_options(int argc, char **argv);
void binary_byte(char b, char *s);
int count_bits(nasd_uint8 c);
void init_drive(void);
nasd_identifier_t get_root_objectid(void);
nasd_attribute_t *get_object_attributes(nasd_identifier_t nid);
int read_directory(nasd_identifier_t nid,
                    nasd_edrfs_dirpage_t **dirpage);
int write_dirpage(nasd_identifier_t nid, int n, nasd_edrfs_dirpage_t *dirpage);
void print_edrfsattr(nasd_edrfs_attributes_t *edrfsattr);
void print_dirdata(nasd_edrfs_dirdata_t *dirent);
void print_usemap(nasd_uint8 *usemap);
void print_dirpage(nasd_edrfs_dirpage_t *dirpage);

int add_object_to_obj_list(nasd_identifier_t nid, edrfs_fsck_obj_t **list, int checkuniq);
void build_usemap_from_dirpage(nasd_edrfs_dirpage_t *dirpage, nasd_uint8 *usemap);

int check_dirpage(nasd_edrfs_dirpage_t *dirpage);
int check_dirent(nasd_edrfs_dirdata_t *dirent);
int check_object_list(void);

int process_directory(nasd_identifier_t nid, char *name,
                    int do_recurse, nasd_identifier_t parent_nid);

int do_fix(void);
int do_fix_freemap(nasd_identifier_t nid, int pageno, nasd_edrfs_dirpage_t *dirpage);

int
main(int argc, char **argv) {
  int status = 0;
  
  parse_options(argc, argv);
  init_drive();

  root_nid = get_root_objectid();

  status += !process_directory(root_nid, "/", 1, root_nid);
  status += !check_object_list();
  
  return status;
}


void usage(void) {
  fprintf(stderr, "USAGE: %s servername partnum\n", progname);
  fflush(stderr);
  exit(1);
}


void binary_byte(char b, char *s) {
  int i, mask;

  mask = 0x01;

  for (i = 0; i < 8; i++) {
    s[i] = (mask&b)?'1':'0';
    mask<<=1;
  }
  s[8] = '\0';
}


int count_bits(nasd_uint8 c) {
  int i = 0, n = 0;

  for (i = 0; i < 8; i++) {
    n += CHECK_BIT(c, i);
  }

  return n;
}


void parse_options(int argc, char **argv) {
  char c;

  progname = argv[0];
  partnum = (-1);

  while (nasd_getopt(argc, argv, "vanDp:", &c)) {
    switch(c) {
    case 'p':
      if (sscanf(nasd_optarg, "%d", &partnum) != 1) {
        usage();
      }
      break;
    case 'v':
      verbose = 1;
      break;
    case 'D':
      debug = 1;
      verbose = 1;
      break;
    case 'a':
      autofix = 1;
      break;
    case 'n':
      nofix = 1;
      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) {
    fprintf(stderr, "\"%s\" is not a valid partition number\n", argv[nasd_optind]);
    usage();
  }

  nasd_optind++;

  if (nasd_optind < argc) {
    fprintf(stderr, "Extra arguments detected on command line\n");
    usage();
  }

  if (partnum < 0) {
    fprintf(stderr, "%s: bad partition number %d\n", progname, partnum);
    fflush(stderr);
    exit(1);
  }
}


void init_drive(void) {
  nasd_status_t rc;

 rc =nasd_cl_p_init();

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

  rc = nasd_bind_to_drive(server_name, NASD_PDRIVE_PORT,
			  NASD_BIND_DEFAULT, NULL, 0, &h);

  if (rc) {
    fprintf(stderr, "ERROR: cannot bind to server %s: error 0x%x (%s)\n",
            server_name, rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }

}


nasd_identifier_t get_root_objectid(void) {
  nasd_status_t rc;
  nasd_ctrl_part_info_t ptinfo;
  nasd_cookie_t cookie;
  nasd_security_param_t sp;

  bzero((char *)&cookie, sizeof(cookie));
  bzero(&sp, sizeof(nasd_security_param_t));
  sp.actual_protection = NASD_NO_PROTECTION;

  /* get partition info */
  rc = nasd_cl_p_ctrl_get_part_info(h, cookie.key, &sp, &cookie.capability,
				    partnum, &ptinfo);

  if ((rc == NASD_CTRL_ID_CHECK_FAILED) && (ptinfo.ctrl_id == 0)) {
    /* got back a page of zeroes - no such partition */
    fprintf(stderr, "ERROR: partition %d does not exist\n", partnum);
    fflush(stderr);
    exit(1);
  }

  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) getting partition info\n",
      rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }

  return ptinfo.first_obj;
}


nasd_attribute_t *get_object_attributes(nasd_identifier_t nid) {
  nasd_rpc_status_t status;
  nasd_error_string_t err_str;
  nasd_p_getattr_dr_args_t ga_args;
  nasd_p_getattr_dr_res_t ga_res;
  nasd_attribute_t *attr = NULL;
  nasd_cookie_t cookie;
  nasd_security_param_t sp;

  bzero((char *)&cookie, sizeof(cookie));
  bzero(&sp, sizeof(nasd_security_param_t));

  ga_args.in_partnum = partnum;
  ga_args.in_identifier = nid;

  nasd_cl_p_getattr_dr(h, cookie.key, &sp, &cookie.capability,
                       &ga_args, &ga_res, &status);
  if (ga_res.nasd_status || status) {
    if (verbose) {
      fprintf(stderr,
              "ERROR: got nasd_status 0x%x (%s) status 0x%x (%s) getting object status for 0x%" NASD_ID_FMT "\n",
              ga_res.nasd_status, nasd_error_string(ga_res.nasd_status),
              status, nasd_cl_error_string(h, status, err_str), nid);
      fflush(stderr);
    }
    return NULL;
  }

  attr = (nasd_attribute_t *) malloc(sizeof(nasd_attribute_t));
  if (attr == NULL) { return NULL; }

  bcopy(&ga_res.out_attribute, attr, sizeof(nasd_attribute_t));
  return attr;
}


int read_directory(nasd_identifier_t nid,
                    nasd_edrfs_dirpage_t **result) {
  nasd_p_smpl_op_dr_args_t rd_args;
  nasd_p_fastread_dr_res_t rd_res;
  nasd_error_string_t err_str;
  nasd_rpc_status_t status;
  nasd_status_t rc;
  nasd_cookie_t cookie;
  nasd_security_param_t sp;
  nasd_attribute_t *attr = NULL;
  nasd_uint64 dirsize;
  nasd_uint32 num_pages;
  nasd_edrfs_dirpage_t *dirpage;

  bzero((char *)&cookie, sizeof(cookie));

  attr = get_object_attributes(nid);
  if (attr == NULL) { return 0; }

  if (verbose) {
    nasd_edrfs_attributes_t edrfsattr;
    nasd_edrfs_attributes_t_unmarshall((nasd_otw_base_t *) attr->fs_specific,
                                       &edrfsattr);
    fprintf(stderr, "  directory attributes: ");
    print_edrfsattr(&edrfsattr);
  }

  dirsize = attr->object_len;
  num_pages = dirsize / sizeof(nasd_edrfs_dirpage_t);

  dirpage = NULL;

  dirpage = (nasd_edrfs_dirpage_t *) malloc(num_pages *
                                         sizeof(nasd_edrfs_dirpage_t));
  rd_args.in_partnum = partnum;
  rd_args.in_identifier = nid;
  rd_args.in_offset = 0;
  rd_args.in_len = dirsize;
  sp.type = cookie.capability.type;
  sp.partnum = partnum;
  sp.actual_protection = cookie.capability.min_protection;

  nasd_cl_p_read_simple_dr(h, cookie.key, &sp, &cookie.capability,
                           &rd_args, dirpage, &rd_res, &status);

  rc = rd_res.nasd_status;

  if (rc || status) {
    fprintf(stderr,
            "ERROR: got nasd_status 0x%x (%s) status 0x%x (%s) reading root object\n",
            rc, nasd_error_string(rc),
            status, nasd_cl_error_string(h, status, err_str));
    fflush(stderr);
    return 0;
  }

  *result = dirpage;

  return 1;
}


int write_dirpage(nasd_identifier_t nid, int n, nasd_edrfs_dirpage_t *dirpage) {
  nasd_p_smpl_op_dr_args_t wr_args;
  nasd_p_fastwrite_dr_res_t wr_res;
  nasd_error_string_t err_str;
  nasd_rpc_status_t status;
  nasd_status_t rc;
  nasd_cookie_t cookie;
  nasd_security_param_t sp;

  bzero((char *) &cookie, sizeof(cookie));
  bzero(&sp, sizeof(nasd_security_param_t));

  wr_args.in_partnum = partnum;
  wr_args.in_identifier = nid;
  wr_args.in_offset = n * sizeof(nasd_edrfs_dirpage_t);
  wr_args.in_len = sizeof(nasd_edrfs_dirpage_t);
  sp.type = cookie.capability.type;
  sp.partnum = partnum;
  sp.actual_protection = cookie.capability.min_protection;

  nasd_cl_p_write_simple_dr(h, cookie.key, &sp, &cookie.capability,
                            &wr_args, dirpage, &wr_res, &status);

  rc = wr_res.nasd_status;

  if (rc || status) {
    fprintf(stderr, "ERROR: got nasd_status 0x%x (%s) status 0x%x (%s) writing dirpage\n",
            rc, nasd_error_string(rc), status, nasd_cl_error_string(h, status, err_str));
    fflush(stderr);
    return 0;
  } else {
    return 1;
  }
}


void print_edrfsattr(nasd_edrfs_attributes_t *edrfsattr) {
  printf("%03o %03d %d %d %d %d\n",
         edrfsattr->mode,
         edrfsattr->type,
         edrfsattr->nlink,
         edrfsattr->uid,
         edrfsattr->gid,
         edrfsattr->clean);
}


void print_dirdata(nasd_edrfs_dirdata_t *dirent) {
  int t;

  fprintf(stderr, "0x%016" NASD_64x_FMT " \"%-43s\" (",
         dirent->nasdid, dirent->name);
  t = NASD_EDRFS_DIRD_TYPEOF(dirent);
  
  switch (t) {
  case NASD_EDRFS_DIRD_TYPE_BOGUS:
    fprintf(stderr, "bogus)\n");
    break;
  case NASD_EDRFS_DIRD_TYPE_DIR:
    fprintf(stderr, "directory)\n");
    break;
  case NASD_EDRFS_DIRD_TYPE_LINK:
    fprintf(stderr, "link)\n");
    break;
  case NASD_EDRFS_DIRD_TYPE_DATA:
    fprintf(stderr, "data)\n");
    break;
  default:
    fprintf(stderr, "unknown)\n");
    break;
  }
}


void print_usemap(nasd_uint8 *usemap) {
  int i, j;
  char b[9];

  for (i = 0; i < 2; i++) {
    printf("    ");
    for (j = 0; j < 8; j++) {
      binary_byte(usemap[(8*i) + j], b);
      fprintf(stderr, "%s ", b);
    }
    fprintf(stderr, "\n");
  }
}


void print_dirpage(nasd_edrfs_dirpage_t *dirpage) {
  int i;

  for (i = 0; i < 127; i++) {
    fprintf(stderr, "%03d: ", i);
    print_dirdata(&dirpage->data_slots[i]);
  }
}

/* add an object ID to our global list, checking to see if
   it's already there. return zero if it is, one if it's unique. */
int add_object_to_obj_list(nasd_identifier_t nid, edrfs_fsck_obj_t **list, int checkuniq) {
  edrfs_fsck_obj_t *tmp = NULL;
  int flag = 0;

  if (checkuniq) {
    tmp = *list;
    while (tmp != NULL) {
      if (tmp->nid == nid) {
        flag = 1;
        break;
      }
      tmp = tmp->next;
    }
  }

  if (flag) {
    return 0;
  } else {
    tmp = (edrfs_fsck_obj_t *) malloc(sizeof(edrfs_fsck_obj_t));
    tmp->nid = nid;
    tmp->next = *list;
    *list = tmp;
    return 1;
  }
}


void build_usemap_from_dirpage(nasd_edrfs_dirpage_t *dirpage, nasd_uint8 *usemap) {
  nasd_edrfs_dirdata_t *dirent = dirpage->data_slots;
  int i, j, cells;

  for (i = 0; i < 16; i++) { usemap[i] = 0; }

  for (i = 0; i < 127; i++) {
    if (dirent[i].nasdid != 0) {
      SET_BIT(usemap[i / 8], (i % 8));

      if (NASD_EDRFS_DIRD_EXLEN(&dirent[i]) > 0) {
        cells = NASD_EDRFS_DIRD_EXLEN(&dirent[i]);
        for (j = i+1; j < (i + cells + 1); j++) {
          SET_BIT(usemap[j/8], (j%8));
        }
        i += cells;
      }
    }
  }
}


int check_dirpage(nasd_edrfs_dirpage_t *dirpage) {
  nasd_uint8 usemap[16];
  int i, j, flag, changed = 0;

  if (verbose) { fprintf(stderr, "    checking usemap validity... "); }

  build_usemap_from_dirpage(dirpage, usemap);
  
  flag = 1;
  for (i = 0; i < 16; i++) {
    if (usemap[i] != dirpage->header.usemap[i]) { flag = 0; }
  }
 
  if (flag) {
    if (verbose) { fprintf(stderr, "passed\n"); }
  } else {
    if (verbose) {
      fprintf(stderr, "failed: usemap does not match free slots\n");
      print_usemap(usemap);
      fprintf(stderr, "\n");
      print_usemap(dirpage->header.usemap);
    } else {
      printf("usemap validity test failed!\n");
    }
    
    if (do_fix()) {
      for (i = 0; i < 16; i++) { dirpage->header.usemap[i] = usemap[i]; }
      changed = 1;
    }
  }
    
  if (verbose) { fprintf(stderr, "    checking free count... "); }
  
  j = 0;
  for (i = 0; i < 16; i++) { j += count_bits(usemap[i]); }
  
  if ((127 - j) != dirpage->header.freecnt) {
    if (verbose) {
      fprintf(stderr, "failed: %d of 127 slots marked free, but %d actually used\n",
             dirpage->header.freecnt, j);
    } else {
      printf("free count check failed: %d of 127 slots marked free, but %d used\n",
             dirpage->header.freecnt, j);
    }
    
    if (do_fix()) {
      dirpage->header.freecnt = (127 - j);
      changed = 1;
    }
    
  } else {
    if (verbose) {
      fprintf(stderr, "passed\n");
    }
  }

  return changed;
}


int check_dirent(nasd_edrfs_dirdata_t *dirent) {
  nasd_attribute_t *attr = NULL;
  int valid = 0;

  if (debug) { print_dirdata(dirent); }
  
  attr = get_object_attributes(dirent->nasdid);

  if (attr == NULL) {
    /* entry is invalid */
    valid = 0;
    if (verbose) {
      fprintf(stderr, "Entry points to invalid nasdid 0x%016" NASD_64x_FMT "!\n",
              dirent->nasdid);
    }
  } else {
    /* entry refers to a valid object */
    valid = 1;

    if (!add_object_to_obj_list(dirent->nasdid, &object_list, 1)) {
      return 0;
    }

    switch(NASD_EDRFS_DIRD_TYPEOF(dirent)) {
    case NASD_EDRFS_DIRD_TYPE_BOGUS:
      if (verbose) { fprintf(stderr, "      entry type is bogus. literally.\n"); }
      break;
    case NASD_EDRFS_DIRD_TYPE_DIR:
      break;
    case NASD_EDRFS_DIRD_TYPE_LINK:
      break;
    case NASD_EDRFS_DIRD_TYPE_DATA:
      break;
    default:
      if (!verbose) {
        printf("entry has bad entry type 0x%" NASD_64x_FMT ":\n",
               NASD_EDRFS_DIRD_TYPEOF(dirent));
        print_dirdata(dirent);
      } else {
        fprintf(stderr, "      bad entry type 0x%" NASD_64x_FMT "!\n",
               NASD_EDRFS_DIRD_TYPEOF(dirent));
      }
      valid = 0;
      break;
    }
  }

  return valid;
}


int check_object_list(void) {
  nasd_identifier_t objs[10];
  nasd_p_smpl_op_dr_args_t rd_args;
  nasd_p_fastread_dr_res_t rd_res;
  nasd_error_string_t err_str;
  nasd_rpc_status_t status;
  nasd_cookie_t cookie;
  nasd_security_param_t sp;
  nasd_offset_t off;
  nasd_len_t len;
  int i;

  edrfs_fsck_obj_t *driveobjs = NULL;
  edrfs_fsck_obj_t *tmp = NULL;

  bzero((char *)&cookie, sizeof(cookie));
  bzero(&sp, sizeof(nasd_security_param_t));
  off = 0;

  do {
    rd_args.in_partnum = partnum;
    rd_args.in_identifier = (nasd_identifier_t)NASD_CTRL_PART_OBJS;
    rd_args.in_offset = off;
    rd_args.in_len = sizeof(objs);
    sp.type = cookie.capability.type;
    sp.partnum = partnum;
    sp.actual_protection = cookie.capability.min_protection;
    nasd_cl_p_read_simple_dr(h, cookie.key, &sp, &cookie.capability,
                             &rd_args, objs, &rd_res, &status);
    
    len = rd_res.out_datalen;
    
    if (status || rd_res.nasd_status) {
      fprintf(stderr,
              "Error issuing read, off=%" NASD_64u_FMT " status=0x%x (%s) nasd_status=0x%x (%s)\n",
              off, status, nasd_cl_error_string(h, status, err_str),
              rd_res.nasd_status, nasd_error_string(rd_res.nasd_status));
      fflush(stderr);
      return 0;
    }
  
    for (i = 0; i < len/sizeof(nasd_identifier_t); i++) {
      add_object_to_obj_list(objs[i], &driveobjs, 0);
    }

    off += len;

  }  while(len);

  if (debug) {
    tmp = driveobjs;

    while (tmp != NULL) {
      printf("0x%016" NASD_64x_FMT "\n", tmp->nid);
      tmp = tmp->next;
    }
  }
  
  return 1;
}


int process_directory(nasd_identifier_t nid, char *name,
                      int do_recurse, nasd_identifier_t parent_nid) {
  nasd_edrfs_dirpage_t *dirpage = NULL;
  nasd_edrfs_dirdata_t *dirent;
  edrfs_fsck_dir_ent_t *dirs = NULL;
  edrfs_fsck_dir_ent_t *tempdir = NULL;
  int i, j, k, num_pages, entrylen;
  int oldchanged = 0, changed = 0;
  char *dirname = NULL;

  if ((num_pages = read_directory(nid, &dirpage)) == 0) {
    fprintf(stderr, "Could not read directory '%s' at nasdid 0x%016" NASD_64x_FMT "!\n",
            name, nid);
    return 0;
  }

  for (i = 0; i < num_pages; i++) {
    if (verbose) { fprintf(stderr, "  page %d of \"%s\":\n", i, name); }

    changed += check_dirpage(&dirpage[i]);

    if (verbose) {
      fprintf(stderr, "    checking data slots: ");
      oldchanged = changed;
    }

    for (j = 0; j < 127; j++) {
      dirent = &dirpage[i].data_slots[j];
      entrylen = NASD_EDRFS_DIRD_EXLEN(dirent);
      if (dirent->nasdid != 0) {
        if ((dirent->nasdid != nid) &&
            (dirent->nasdid != parent_nid)) {
          if (!check_dirent(dirent)) {
            fprintf(stderr, "\nEntry \"%s\" in directory \"%s\" is invalid!\n",
                    dirent->name, name);
            if (do_fix()) {
              for (k = 0; k < (entrylen + 1); k++) {
                UNSET_BIT(dirpage[i].header.usemap[((j + k)/8)], ((j+k)%8));
                dirpage[i].header.freecnt++;
                memset(&(dirpage[i].data_slots[j+k]), 0, sizeof(nasd_edrfs_dirdata_t));
              }
              changed++;
            }
          }
        }

        if (NASD_EDRFS_DIRD_TYPEOF(dirent) == NASD_EDRFS_DIRD_TYPE_DIR) {
          tempdir = malloc(sizeof(edrfs_fsck_dir_ent_t));
          tempdir->next = dirs;
          tempdir->nid = dirent->nasdid;
          tempdir->name = strdup(dirent->name);
          dirs = tempdir;
        }
      }

      /* if this entry spanned multiple slots, skip over them. */
      if (entrylen > 0) { j += entrylen; }
    }
    
    if (verbose && (changed == oldchanged)) { fprintf(stderr, "passed\n"); }

    if (changed) { write_dirpage(nid, i, &dirpage[i]); }
  }

  tempdir = dirs;
  while (tempdir != NULL) {
    if ((do_recurse) && (tempdir->nid != nid) &&
        (tempdir->nid != parent_nid)) {
      dirname = malloc(sizeof(char) * (strlen(tempdir->name) + strlen(name) + 2));
      strcpy(dirname, name);
      if (strcmp(dirname, "/")) {
        strcat(dirname, "/");
      }
      strcat(dirname, tempdir->name);

      if (verbose) {
        fprintf(stderr, "checking directory \"%s\" at nasdid 0x%016" NASD_64x_FMT "...\n",
                dirname, tempdir->nid);
      } else {
        printf("%s\n", dirname);
      }

      process_directory(tempdir->nid, dirname, 1, nid);
      free(dirname);
    }
    tempdir = tempdir->next;
  }

  while (dirs->next != NULL) {
    tempdir = dirs->next;
    free(dirs->name);
    free(dirs);
    dirs = tempdir;
  }
  free(dirs->name);
  free(dirs);

  return 1;
}

int do_fix(void) {
  char c[80];

  if (nofix) { return 0; }
  if (autofix) { return 1; }

  while (1) {
    printf("Fix (y/n)? ");
    fgets(c, 79, stdin);
    
    switch(c[0]) {
    case 'Y':
    case 'y':
      printf("\nFixing...\n");
      return 1;
      /*NOTREACHED*/
      break;
    case 'N':
    case 'n':
      printf("\nSkipping...\n");
      return 0;
      /*NOTREACHED*/
      break;
    default:
      break;
    }
  }
}

int do_fix_freemap(nasd_identifier_t nid, int pageno,
                   nasd_edrfs_dirpage_t *directorypage) {
  nasd_edrfs_dirpage_t *directory = NULL;
  nasd_edrfs_dirpage_t *page = NULL;
  int error, i, mask;

  if (directorypage != NULL) {
    page = directorypage;
  } else {
    error = read_directory(nid, &directory);
    page = &directory[pageno];
  }

  if (verbose) { fprintf(stderr, "fixing freemap...\n"); }

  for (i = 0; i < 127; i++) {
    mask = 1 << (i % 8);

    if (((page->data_slots[i].nasdid == 0) &&
         (page->header.usemap[i/8] & mask)) ||
        ((page->data_slots[i].nasdid == 0) &&
         (page->header.usemap[i/8] & mask))) {
      if (verbose) { fprintf(stderr, "mismatch at entry %d\n", i); }
    }
  }

  if (verbose) { fprintf(stderr, "...done\n"); }

  if (directory != NULL) { free(directory); }
  
  return 1;
}

int do_fix_freecnt(nasd_identifier_t nid, int pageno,
                   nasd_edrfs_dirpage_t *directorypage) {
  nasd_edrfs_dirpage_t *directory = NULL;
  nasd_edrfs_dirpage_t *page = NULL;
  int error, i, count;

  if (directorypage != NULL) {
    page = directorypage;
  } else {
    error = read_directory(nid, &directory);
    page = &directory[pageno];
  }

  if (verbose) { fprintf(stderr, "fixing free count...\n"); }

  count = 127;
  for(i = 0; i < 127; i++) {
    if (page->data_slots[i].nasdid != 0) { count--; }
  }

  if (page->header.freecnt != count) {
    if (verbose) {
      fprintf(stderr, "incorrect free count %d corrected to %d.\n",
              page->header.freecnt, count);
    }
    page->header.freecnt = count;
  }

  if (verbose) { fprintf(stderr, "...done\n"); }

  if (directory != NULL) { free(directory); }
  
  return 1;
}

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