/*
 * Dump out the contents of the AFS cache.
 * Written by John Carr.
 * Based on a program by Bill Sommerfeld.
 * 
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <arpa/inet.h>

#ifdef SHOW_NAMES
#include <afs/stds.h>
#include <ubik.h>
#include <afs/vlserver.h>
#include <afs/cellconfig.h>
extern int UV_SetSecurity();
extern int VL_GetEntryByID();
extern struct ubik_client *cstruct;
#endif

struct afs_fheader {
    long magic;
    long firstCSize;
    long otherCSize;
    long spare;
};

struct ViceFid {
	u_long Volume;
	u_long Vnode;
	u_long Unique;
};

struct VenusFid {
    long Cell;
    struct ViceFid Fid;
};

struct fcache {
#ifdef OLD_AFS
    short hvNextp;		/* Next in vnode hash table, or freeDCList */
    short hcNextp;		/* Next index in [fid, chunk] hash table */
    short chunkNextp;		/* File queue of all chunks for a single vnode */
#else
    long hvNextp, hcNextp;
#endif
    struct VenusFid fid;	/* Fid for this file */
    long modTime;		/* last time this entry was modified */
#ifdef OLD_AFS
    long versionNo;		/* Associated data version number */
#else
    long versionNo[2];
#endif
    long chunk;			/* Relative chunk number */
    long inode;			/* Unix inode for this chunk */
    long chunkBytes;		/* Num bytes in this chunk */
    char states;		/* Has this chunk been modified? */
};

#define	DWriting	8

void get_local_cell(char *cell, int len
#ifdef SHOW_NAMES
		    , struct afsconf_dir *adir
#endif
		    )
{
  int fd;
#ifdef SHOW_NAMES
  if (adir && afsconf_GetLocalCell(adir, cell, len) == 0)
    return;
#endif
  fd = open(AFSCONF_CLIENTNAME "/ThisCell", O_RDONLY);

  if (fd == -1)
    {
      strcpy(cell, "<unknown>");
      return;
    }
  if (read(fd, cell, len) <= 0)
    strcpy(cell, "unknown");
  close(fd);
  return;
}

#ifdef SHOW_NAMES

static char remotecellname[MAXCELLCHARS];
static long remotecelladdr;

int find_cell(struct afsconf_cell *cellinfo, char *info,
	      struct afsconf_dir *adir)
{
  int i;

  for (i = 0; i < cellinfo->numServers; i++)
    if (*(long *)info == cellinfo->hostAddr[i].sin_addr.s_addr)
      {
	remotecelladdr = *(long *)info;
	strcpy(remotecellname, cellinfo->name);
	return 1;
      }
  return 0;
}
#endif

main(int argc, char *argv[])
{
#ifdef SHOW_NAMES
  struct afsconf_dir *adir;
#endif
  char localcell[64];
  char cache[128];
  int fd;
  struct fcache *fcp;
  struct stat st;
  int nfcp;
  register int i;
  char *cp;
  long code;

  if (argc > 1)
    sprintf(cache, "%s/CacheItems", argv[1]);
  else
    strcpy (cache, "/usr/vice/cache/CacheItems");

  fd = open (cache, O_RDONLY, 0);
  if (fd < 0)
    {
      perror(cache);
      return 2;
    }
  fstat (fd, &st);
    
  cp = malloc (st.st_size);
  if (cp == 0)
    {
      fprintf (stderr, "Can't allocate space to read cache index.\n");
      return 3;
    }
  fcp = (struct fcache *)(cp + sizeof(struct afs_fheader));
    
  nfcp = (st.st_size-sizeof(struct afs_fheader)) / sizeof (struct fcache);

  printf("%d cache entries\n", nfcp);
  if (read (fd, cp, st.st_size) != st.st_size)
    {
      fprintf (stderr, "Unable to read cache index file.\n");
      return 4;
    }
  close(fd);

#ifdef SHOW_NAMES
  adir = afsconf_Open(AFSCONF_CLIENTNAME);
  get_local_cell(localcell, sizeof(localcell), adir);
#else
  get_local_cell(localcell, sizeof(localcell));
#endif

  printf("Local cell is %s\n", localcell);
#ifdef SHOW_NAMES

  /* args:
       int		unauth
       char *		config dir
       char *		cell name
       long 		server auth
       struct ubik_client **
       int (*)()	security proc
   */
  code = vsu_ClientInit(1, AFSCONF_CLIENTNAME, localcell, 0, &cstruct,
			UV_SetSecurity);
  if (code != 0)
    {
      fprintf(stderr, "VLDB init failed code %lu\n", code);
      return 1;
    }
#endif

  puts("index [    cell    :   volume :  vnode : unique]");

  for (i = 0; i < nfcp; i++)
    {
      struct in_addr celladdr;
#ifdef SHOW_NAMES
      struct vldbentry vldbentry;
#endif
      if (fcp[i].fid.Cell == 0 || fcp[i].fid.Fid.Volume == 0)
	continue;
#ifdef SHOW_NAMES
      if (fcp[i].fid.Cell == 1
	  && ubik_Call(VL_GetEntryByID, cstruct, 0,
		       fcp[i].fid.Fid.Volume, -1, &vldbentry) == 0)
	{
	  printf("%4d: [%22s : %6x : %6x] ino %4d chunk %3d len %6x%s\n",
		 i, vldbentry.name, fcp[i].fid.Fid.Vnode,
		 fcp[i].fid.Fid.Unique, fcp[i].inode,
		 fcp[i].chunk, fcp[i].chunkBytes,
		 (fcp[i].states) & DWriting ? ", writing" : "");
	  continue;
	}
#endif
      if (fcp[i].fid.Cell == 1)
	{
	  printf ("%4d: [              %8x : %6x : %6x] ino %4d chunk %3d len %6x%s\n",
		  i, fcp[i].fid.Fid.Volume, fcp[i].fid.Fid.Vnode,
		  fcp[i].fid.Fid.Unique, fcp[i].inode, fcp[i].chunk,
		  fcp[i].chunkBytes,
		  fcp[i].states & DWriting ? ", writing" : "");
	  continue;
	}
      /* Look for the cell name */
      if (remotecelladdr != fcp[i].fid.Cell &&
	  afsconf_CellApply(adir, find_cell, (char *)&fcp[i].fid.Cell) != 1)
	{
	  remotecelladdr = celladdr.s_addr = fcp[i].fid.Cell;
	  strcpy(remotecellname, inet_ntoa(celladdr));
	}
      printf ("%4d: [%12s: %8x : %6x : %6x] ino %4d chunk %3d len %6x%s\n",
	      i, remotecellname, fcp[i].fid.Fid.Volume,
	      fcp[i].fid.Fid.Vnode, fcp[i].fid.Fid.Unique, fcp[i].inode,
	      fcp[i].chunk, fcp[i].chunkBytes,
	      fcp[i].states & DWriting ? ", writing" : "");
    }
  return !ferror (stdout);
}
    
     
