/*
 * nasd_edrfs_name.c
 *
 * Name management for NASD EDRFS.
 *
 * 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_edrfs_server_internal.h>

nasd_freelist_t *nasd_edrfs_diskdirent_freelist = NULL;
#define NASD_MAX_FREE_DISKDIRENT 1536
#define NASD_DISKDIRENT_INC        64
#define NASD_DISKDIRENT_INITIAL   768

nasd_freelist_t *nasd_edrfs_attr_freelist = NULL;
#define NASD_MAX_FREE_ATTR 2048
#define NASD_ATTR_INC       256
#define NASD_ATTR_INITIAL   512

#define GET_DIRENT(_d_) \
  NASD_FREELIST_GET(nasd_edrfs_diskdirent_freelist,_d_,hnext,(nasd_edrfs_diskdirent_t *))

/*
 * Low 5 bits of each char
 */

int
nasd_edrfs_name_hash(
  char  *str)
{
  int l, i;
  long n;

  n = 0;
  l = strlen(str);
  if (l > 10) { l = 10; }

  for(i=0;i<l;i++) {
    n |= str[i]&0x1f;
    n <<= 5;
  }

  n %= NASD_EDRFS_NAMEHASH_BUCKETS;
  if(n < 0) { n = -n; }

  return((int)n);
}

void
nasd_edrfs_name_shutdown_freelist(
  void  *arg)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_diskdirent_freelist,hnext,(nasd_edrfs_diskdirent_t *));
}

void
nasd_edrfs_attr_shutdown_freelist(
  void  *arg)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_attr_freelist,next,(nasd_edrfs_attr_t *));
}

nasd_status_t
nasd_edrfs_name_init()
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_edrfs_diskdirent_freelist,
                       NASD_MAX_FREE_DISKDIRENT,
                       NASD_DISKDIRENT_INC, sizeof(nasd_edrfs_diskdirent_t));
  if (nasd_edrfs_diskdirent_freelist == NULL) { return(NASD_NO_MEM); }

  rc = nasd_shutdown_proc(nasd_edrfs_shutdown,
                          nasd_edrfs_name_shutdown_freelist, NULL);
  if (rc) {
    nasd_edrfs_name_shutdown_freelist(NULL);
    return(rc);
  }
  NASD_FREELIST_PRIME(nasd_edrfs_diskdirent_freelist,
                      NASD_DISKDIRENT_INITIAL,hnext,
                      (nasd_edrfs_diskdirent_t *));

  NASD_FREELIST_CREATE(nasd_edrfs_attr_freelist, NASD_MAX_FREE_ATTR,
                       NASD_ATTR_INC, sizeof(nasd_edrfs_attr_t));
  if (nasd_edrfs_attr_freelist == NULL) { return(NASD_NO_MEM); }

  rc = nasd_shutdown_proc(nasd_edrfs_shutdown,
                          nasd_edrfs_attr_shutdown_freelist, NULL);
  if (rc) {
    nasd_edrfs_attr_shutdown_freelist(NULL);
    return(rc);
  }
  NASD_FREELIST_PRIME(nasd_edrfs_attr_freelist, NASD_ATTR_INITIAL,next,
                      (nasd_edrfs_attr_t *));

  return(NASD_SUCCESS);
}

/*
 * call with dir LOCKED, dircache LOCKED
 * discard all namecache entries for dir
 */
void
nasd_edrfs_name_cleandir(
  nasd_edrfs_core_dir_t  *dir)
{
  nasd_edrfs_diskdirent_t *de;
  int i;

  NASD_FREELIST_DO_LOCK(nasd_edrfs_diskdirent_freelist);

  for(i = 0; i < NASD_EDRFS_NAMEHASH_BUCKETS; i++) {
    while(dir->buckets[i]) {
      de = dir->buckets[i];
      dir->buckets[i] = de->hnext;
      if (de->attr) {
        nasd_edrfs_release_attr(de->attr);
        de->attr = NULL;
      }
      NASD_FREELIST_FREE_NOLOCKING(nasd_edrfs_diskdirent_freelist,de,hnext);
    }
  }
  /* already freed entries above */
#if 0
  /*
   * This is actually extraneous, since nasd_edrfs_dir_get_coredir()
   * will zero the buckets. Callers of this function should be aware
   * that the resulting state of dir is inconsistent, however.
   */
  bzero((char *)dir->idbuckets, sizeof(dir->idbuckets));
#endif
  NASD_FREELIST_DO_UNLOCK(nasd_edrfs_diskdirent_freelist);
}

/* call with dir LOCKED */
nasd_status_t
nasd_edrfs_name_hash_ins(
  nasd_edrfs_core_dir_t    *dir,
  nasd_edrfs_diskdirent_t  *de)
{
  int h;

  h = nasd_edrfs_name_hash(de->name);
  de->hprev = NULL;
  de->hnext = dir->buckets[h];
  if (de->hnext) { de->hnext->hprev = de; }
  dir->buckets[h] = de;

  h = nasd_edrfs_obj_hash(de->data->nasdid);
  de->ihprev = NULL;
  de->ihnext = dir->idbuckets[h];
  if (de->ihnext) { de->ihnext->ihprev = de; }
  dir->idbuckets[h] = de;

  return(NASD_SUCCESS);
}

/* call with dir LOCKED */
nasd_edrfs_diskdirent_t *
nasd_edrfs_name_add(
  nasd_edrfs_core_dir_t      *dir,
  nasd_edrfs_core_dirpage_t  *page,
  int                         slot)
{
  nasd_edrfs_diskdirent_t *de;
  int h;

  GET_DIRENT(de);
  if (de == NULL) { return(NULL); }

  nasd_edrfs_dir_extract_name(page->page, slot, de->name);
  de->page   = page;
  de->data   = &(page->page->data_slots[slot]);
  de->slot   = slot;
  de->attr   = NULL;
  de->dir    = NULL;

  h = nasd_edrfs_name_hash(de->name);
  de->hprev  = NULL;
  de->hnext  = dir->buckets[h];
  if (de->hnext) { de->hnext->hprev = de; }
  dir->buckets[h]   = de;

  h = nasd_edrfs_obj_hash(de->data->nasdid);
  de->ihprev = NULL;
  de->ihnext = dir->idbuckets[h];
  if (de->ihnext) { de->ihnext->ihprev = de; }
  dir->idbuckets[h] = de;

  dir->nentries++;

  return(de);
}

/* call with dir LOCKED */
nasd_edrfs_diskdirent_t *
nasd_edrfs_name_lookup(
  nasd_edrfs_core_dir_t  *dir,
  char                   *name)
{
  nasd_edrfs_diskdirent_t *r;
  int h, l;

  h = nasd_edrfs_name_hash(name);
  l = strlen(name);

  for(r=dir->buckets[h];r;r=r->hnext) {
    if (r->data->name_len != l) { continue; }
    if (!bcmp(r->name, name, l)) { return(r); }
  }

  return(NULL);
}

/*
 * call with dir LOCKED
 *
 * deallocates de
 */
nasd_status_t
nasd_edrfs_name_remove(
  nasd_edrfs_core_dir_t    *dir,
  nasd_edrfs_diskdirent_t  *de,
  int                       unhash_only)
{
  int h;

  h = nasd_edrfs_name_hash(de->name);
  if (de->hprev) { de->hprev->hnext = de->hnext; }
  else {
    NASD_ASSERT(dir->buckets[h] == de);
    dir->buckets[h] = de->hnext;
    if (dir->buckets[h]) { dir->buckets[h]->hprev = NULL; }
  }

  if (de->hnext) { de->hnext->hprev = de->hprev; }
  de->hnext = NULL;
  de->hprev = NULL;

  h = nasd_edrfs_obj_hash(de->data->nasdid);
  if (de->ihprev) { de->ihprev->ihnext = de->ihnext; }
  else {
    NASD_ASSERT(dir->idbuckets[h] == de);
    dir->idbuckets[h] = de->ihnext;
    if (dir->idbuckets[h]) { dir->idbuckets[h]->ihprev = NULL; }
  }

  if (de->ihnext) { de->ihnext->ihprev = de->ihprev; }
  de->ihnext = NULL;
  de->ihprev = NULL;

  if (unhash_only == 0) {
    if (de->attr) {
      nasd_edrfs_release_attr(de->attr);
      de->attr = NULL;
    }

    NASD_FREELIST_FREE(nasd_edrfs_diskdirent_freelist,de,hnext);
    dir->nentries--;
  }

  return(NASD_SUCCESS);
}

nasd_edrfs_attr_t *
nasd_edrfs_get_attr()
{
  nasd_edrfs_attr_t *attr;

  NASD_FREELIST_GET(nasd_edrfs_attr_freelist,attr,next,(nasd_edrfs_attr_t *));
  attr->refcnt = 1;
  attr->next = NULL;
  return(attr);
}

/*
 * call with dircache lock HELD
 */
void
nasd_edrfs_release_attr(
  nasd_edrfs_attr_t  *attr)
{
  attr->refcnt--;
  if (attr->refcnt == 0) { NASD_FREELIST_FREE(nasd_edrfs_attr_freelist,
                                              attr, next);              }
}

void
nasd_edrfs_debug_name_cache(
  nasd_edrfs_core_dir_t  *dir)
{
  nasd_edrfs_diskdirent_t *rr, *ss, *r1;
  unsigned long slot;
  int h;

  for(h = 0; h < NASD_EDRFS_NAMEHASH_BUCKETS; h++) {
    for(ss = NULL, rr = dir->buckets[h]; rr; ss = rr, rr = rr->hnext) {
      if (ss) {
        NASD_ASSERT(ss->hnext == rr);
        if (rr->hprev != ss) {
          nasd_printf("caught rr->hprev != ss\n");
          for(r1 = dir->buckets[h]; r1; r1 = r1->hnext) {
            /* eeeevil */
            slot  = (unsigned long)  r1->data;
            slot -= (unsigned long) &r1->page->page->data_slots[0];
            slot /= sizeof(nasd_edrfs_dirdata_t);
            nasd_printf("[ 0x%16lx 0x%16lx 0x%16lx %s slot %03lu slotused=%d]\n",
                        (unsigned long) r1->hprev, (unsigned long)r1,
                        (unsigned long) r1->hnext, r1->name, slot,
                        NASD_EDRFS_DIRMAP_TEST_SLOT(r1->page->page->header.usemap, slot));
          }
        }
        NASD_ASSERT(rr->hprev == ss);
      }
      else {
        NASD_ASSERT(dir->buckets[h] == rr);
        NASD_ASSERT(rr->hprev == NULL);
      }
    }
  }
}

nasd_status_t
nasd_edrfs_valid_name(
  char  *name)
{
  int i, len;

  len = strlen(name);
  if (len == 0) { return(NASD_EDRFS_BAD_NAME); }
  if (len > NASD_EDRFS_MAX_NAME_LEN) { return(NASD_EDRFS_BAD_NAME); }

  for(i=0;i<len;i++) {
    if (name[i] == '/') { return(NASD_EDRFS_BAD_NAME); }
    if (name[i] & 0x80) { return(NASD_EDRFS_BAD_NAME); }
    if (name[i] == 127) { return(NASD_EDRFS_BAD_NAME); }
    if (name[i]  < ' ') { return(NASD_EDRFS_BAD_NAME); }
  }

  return(NASD_SUCCESS);
}

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