/*
 * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Kungliga Tekniska
 *      Högskolan and its contributors.
 * 
 * 4. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Manage the priority of the files
 */

#include "arla_local.h"
#include <kafs.h>
RCSID("$Id: fprio.c,v 1.4 1999/01/10 18:42:09 assar Exp $");

/* Hashtable of entries by name */
static Hashtab *fpriohashtab;

/*
 * fprio - hash help functions
 */

static int
fpriocmp (void *a, void *b)
{
    struct fpriorityentry *n1 = (struct fpriorityentry *)a;
    struct fpriorityentry *n2 = (struct fpriorityentry *)b;

    return n1->fid.Cell != n2->fid.Cell || 
	n1->fid.fid.Volume != n2->fid.fid.Volume ||
	n1->fid.fid.Vnode != n2->fid.fid.Vnode ||
	n1->fid.fid.Unique != n2->fid.fid.Unique;
}

static unsigned
fpriohash (void *a)
{
     struct fpriorityentry *n = (struct fpriorityentry *)a;

     return n->fid.Cell ^ n->fid.fid.Volume ^ 
	 n->fid.fid.Vnode ^ n->fid.fid.Unique;
}

/*
 * fprio_init
 * 
 *  Just create the hashtab. Leave the smartness to the user.
 */

void
fprio_init (char *file)
{
    fpriohashtab = hashtabnew (FPRIOCACHE_SIZE, fpriocmp, fpriohash);
    if (fpriohashtab == NULL)
	arla_errx (1, ADEBERROR, "fprio_init: hashtabnew failed");

    if (file)
	fprio_readin(file);
}

/*
 * Cleanout unwanted enteries
 */

static Bool
cleanupfpriohash(void *ptr, void *arg)
{
    struct fpriorityentry *n = (struct fpriorityentry *)ptr;
    struct fpriorityentry *a = (struct fpriorityentry *)arg;

    /* Clean out if
     *
     *   NULL cleanout argument
     *   cleanout argument is in the same Cell and
     *      Volume == Vnode == 0  (ie, when whole cell), or
     *      Volume == the victim entry's Volume
     *             && Vnode == 0  (ie, whole volume), or
     *      the Vnode and Unique also match (ie the file/direntry)
     *
     *  This means that memset(&myarg, 0, sizeof(stuct fprioentry))
     *  is probably not what you want. (Cleaning out the localcell's
     *  all entries)
     */

    if (a == NULL ||
	(a->fid.Cell == n->fid.Cell && 
	 ((a->fid.fid.Volume == 0 && a->fid.fid.Vnode ==0) ||
	  (a->fid.fid.Volume == n->fid.fid.Volume &&
	   (a->fid.fid.Vnode == 0 ||
	    (a->fid.fid.Vnode == n->fid.fid.Vnode &&
	     a->fid.fid.Unique == n->fid.fid.Unique)))))) {
	
	AFSCallBack broken_callback = {0, 0, CBDROPPED};
	
	fcache_stale_entry (n->fid, broken_callback);
	free(n);
	
	return TRUE;
    }
    
    return FALSE;
}


int
fprio_clear(void)
{
    hashtabcleantab(fpriohashtab, cleanupfpriohash, NULL);
    return 0;
}

/*
 * zapp the `fid'
 */

void
fprio_remove(VenusFid fid)
{
    struct fpriorityentry key; 

    key.fid = fid;
    hashtabfree(fpriohashtab, &key);
    return;
}

/*
 * set a `fid' with `prio' to the hashtab
 */

void
fprio_set(VenusFid fid, unsigned prio)
{
    struct fpriorityentry *e; 
    struct fpriorityentry key; 

    key.fid = fid;

    e = hashtabsearch(fpriohashtab, &key);
    if (e) {
	e->priority = prio;
	return;
    }

    e = calloc(1, sizeof(*e));
    if (e == NULL) {
	arla_warn(ADEBFCACHE, 1, "fprio_set: Out of memory");
	return;
    }
    e->fid = fid;
    e->priority = prio;
    
    hashtabadd(fpriohashtab, e);
}

/*
 * Read in new data from the file
 */

#define MAXFPRIOLINE 1024

int
fprio_readin(char *file)
{
    FILE *f;
    char line[MAXFPRIOLINE];
    unsigned prio;
    char cell[MAXFPRIOLINE];
    int32_t cellnum;
    VenusFid fid;
    int lineno = 0 ;

    f = fopen(file, "r");
    if (f == NULL)
	return -1;

    while(fgets(line, sizeof(line)-1, f) != NULL) {
	lineno++;

	if (line[0] == '#')
	    continue;

	if (sscanf(line, "%d:%s:%u:%u:%u", &prio, 
		   cell,
		   &fid.fid.Volume, 
		   &fid.fid.Vnode, 
		   &fid.fid.Unique) != 5) {
	    arla_warn(ADEBFCACHE, 1, 
		      "fprio_readin: %s:%d contain error(s)", 
		      file, lineno);
	    continue;
	}
	
	cellnum = cell_name2num(cell);
	if (cellnum == -1) {
	    arla_warn(ADEBFCACHE, 1, 
		      "fprio_readin: the cell %s does not exist", cell);
	    continue;
	}

	fid.Cell = cellnum;
	fprio_set(fid, prio);
    }
    return 0;
}

/*
 * Find the priority of a fid
 */

int 
fprio_get(VenusFid fid)
{
    struct fpriorityentry a;
    struct fpriorityentry *b;

    a.fid = fid;

    b = hashtabsearch(fpriohashtab, &a);
    if (b)
	return b->priority;
    return 0;
}

/*
 * Print the entry `ptr' to the FILE `arg'
 */

static Bool
fprio_print_entry (void *ptr, void *arg)
{
    struct fpriorityentry *n = (struct fpriorityentry *)ptr;
    FILE *f = (FILE *) ptr;
    const char *cell = cell_num2name(n->fid.Cell);
    char *comment;

    if (cell == NULL)  /* If we cant find the cell comment it out */
	comment = "#";
    else
	comment = "";

    fprintf(f, "%s%d:%s:%d:%d:%d", 
	    comment, n->priority, cell?cell:"unknowncell", n->fid.fid.Volume, 
	    n->fid.fid.Vnode, n->fid.fid.Unique);

    return FALSE;
}

/*
 * Print the status of the fprio module in some strange format...
 */

void
fprio_status (FILE *f)
{
    time_t the_time = time(NULL);

    fprintf (f, "#fprio entries\n#\n#  Date: %s\n#\n"
	     "#priority range from %d to %d\n#\n"
	     "#Syntax: (# means comment)\n"
	     "#priority:cell:volume:vnode:unique\n",
	     ctime(&the_time), FPRIO_MIN, FPRIO_MAX);
    hashtabforeach (fpriohashtab, fprio_print_entry, f);
}



