/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
 */
/*
 * AtFS -- Attribute Filesystem
 *
 * afobjcache.c -- manage derived object cache
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afobjcache.c[7.0] Sun Jan 23 16:05:57 1994 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"
#include "afarchive.h"

LOCAL int afObjCacheNameNum = AF_DEFAULT_CACHED_PER_NAME;
LOCAL int afObjCacheAttrNum = AF_DEFAULT_CACHED_PER_ATTR;
LOCAL int afObjCacheMax = AF_DEFAULT_CACHED_MAX;

/*====================
 * Object Cache Size
 *====================*/

extern int afCurTransaction;

EXPORT int af_cachesize (path, totalMax, maxPerName, maxPerAttr,
			 oldTotalMax, oldMaxPerName, oldMaxPerAttr)
     char *path;
     int totalMax, maxPerName, maxPerAttr;
     int *oldTotalMax, *oldMaxPerName, *oldMaxPerAttr;
{
  char errMsg[512];
  Af_revlist *list;

  if ((list = afInitObjCache (af_entersym (path))) == NULL)
    return (ERROR);

  *oldTotalMax = afObjCacheMax;
  *oldMaxPerName = afObjCacheNameNum;
  *oldMaxPerAttr = afObjCacheAttrNum;

  if (totalMax <= 0)
    totalMax = afObjCacheMax;
  if (maxPerName <= 0)
    maxPerName = afObjCacheNameNum;
  if (maxPerAttr <= 0)
    maxPerAttr = afObjCacheAttrNum;

  if (maxPerName < maxPerAttr) {
    sprintf (errMsg, "maxPerAttr (%d) may not be bigger than maxPerName (%d)", maxPerAttr, maxPerName);
    FAIL ("cachesize", errMsg, AF_EMISC, ERROR);
  }
  if (maxPerName > totalMax) {
    sprintf (errMsg, "maxPerName (%d) may not be bigger than totalMax (%d)", maxPerName, totalMax);
    FAIL ("cachesize", errMsg, AF_EMISC, ERROR);
    }
  if (maxPerName > AF_MAX_CACHED_PER_NAME) {
    sprintf (errMsg, "maxPerName (%d) may not be bigger than %d (system limit)", maxPerName, AF_MAX_CACHED_PER_NAME);
    FAIL ("cachesize", errMsg, AF_EMISC, ERROR);
  }

  if ((totalMax < afObjCacheMax) || (maxPerName < afObjCacheNameNum)
      || (maxPerAttr < afObjCacheAttrNum )) {
    if (list->af_nrevs > 0) {
      /* ToDo: reorganize derived object cache */
      sprintf (errMsg, "cannot shrink derived object cache (yet)\n\t\t(current cache size is %d)", list->af_nrevs);
      FAIL ("cachesize", errMsg, AF_EMISC, ERROR);
    }
  }

  afObjCacheMax = totalMax;
  afObjCacheNameNum = maxPerName;
  afObjCacheAttrNum = maxPerAttr;

  if (afCurTransaction)
    return (afAddToTransactionList (list));

  return (afObjCacheWrite (list));
}


/*====================
 * Read Object Cache
 *====================*/

EXPORT int afObjCacheRead (list)
     Af_revlist *list;
{ 
  char	  idStr[AF_SEGSTRLEN+1], line[AF_LINESIZ], *itemPtr;
  char    cacheName[PATH_MAX], *udaBuf;
  struct stat cacheIbuf;
  FILE    *cacheFile;
  int     i, j, objCacheVersion, tmpMode;
  Af_key  tmpKey;

  strcpy (cacheName, afCacheFileName (list->af_arpath, AF_CACHENAME));

  /* if object cache archive file has been modified */
  if (stat (cacheName, &cacheIbuf) == -1)
    cacheIbuf.st_mtime = (time_t) 0;
  if (list->af_lastmod != cacheIbuf.st_mtime) {
    if (list->af_access > 0) {
      af_wng ("ObjCacheRead", "object cache has changed");
      list->af_access = 0;
    }
    list->af_extent &= ~AF_SEGMASK; /* invalidate data */
  }

  if (list->af_extent & AF_COMPLETE)
    return (AF_OK);

  af_frmemlist (list);

  /* open cache control file */
  if ((cacheFile = afOpenLock (cacheName, AF_READ, list))) {
    idStr[AF_SEGSTRLEN] = '\0';
    fgets (idStr, AF_SEGSTRLEN+1, cacheFile);
    if (strncmp (idStr, AF_CACHEHEADER, AF_SEGSTRLEN)) {
      afCloseUnlock (cacheFile, AF_READ, list);
      FATAL ("ObjCacheRead", "wrong header in cache control file", AF_EINCONSIST, ERROR);
    }

    /* read header */
    fgets (line, AF_LINESIZ, cacheFile);
    itemPtr = afFirstItem (line);
    objCacheVersion = atoi (itemPtr);
    if (objCacheVersion != AF_CACHECURVERS) {
      afCloseUnlock (cacheFile, AF_READ, list);
      FATAL ("ObjCacheRead", "unknown cache format version", AF_EINCONSIST, ERROR);
    }

    itemPtr = afNextItem (itemPtr);
    list->af_nrevs = atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    afObjCacheMax = atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    afObjCacheNameNum = atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    afObjCacheAttrNum = atoi (itemPtr);
  }
  else {
    list->af_nrevs = 0;
  }

  /* alloc memory for revision list (plus space for new revs) */
  list->af_listlen = afObjCacheMax;
  if ((list->af_list = (Af_vattrs *)af_malloc (list, (unsigned) (afObjCacheMax * sizeof(Af_vattrs)))) == NULL) {
    if (cacheFile)
      afCloseUnlock (cacheFile, AF_READ, list);
    return (ERROR);
  }
  memset ((char *) list->af_list, 0, list->af_listlen * sizeof (Af_vattrs));
    
  if (list->af_nrevs == 0) {
    if (cacheFile)
      afCloseUnlock (cacheFile, AF_READ, list);
    list->af_extent |= AF_COMPLETE;
    return (AF_OK);
  }

  tmpKey.af_ldes = list;
  if ((udaBuf = malloc ((unsigned) AF_UDASEGSIZ)) == NULL) {
    if (cacheFile)
      afCloseUnlock (cacheFile, AF_READ, list);
    FAIL ("ObjCacheRead", "malloc", AF_ESYSERR, ERROR);
  }

  for (i=0; i < list->af_nrevs; i++) {

    /* read name and revision ID */
    fgets (idStr, AF_IDSTRLEN+1, cacheFile);
    if (strcmp (idStr, AF_NAMEID)) {
      afCloseUnlock (cacheFile, AF_READ, list);
      FATAL ("ObjCacheRead", "wrong name-ID in cache control file", AF_EINCONSIST, ERROR);
    }
    fgets (line, AF_LINESIZ, cacheFile);
    itemPtr = afFirstItem (line);
    list->af_list[i].af_name = af_entersym (itemPtr);
    itemPtr = afNextItem (itemPtr);
    if (strcmp (itemPtr, AF_NOSTRING))
      list->af_list[i].af_type = af_entersym (itemPtr);
    else
      list->af_list[i].af_type = NULL;
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_gen = atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_rev = atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    sscanf (itemPtr, "%o", &tmpMode);
    list->af_list[i].af_mode = (mode_t) tmpMode;
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_hashname = af_entersym (itemPtr);
    list->af_list[i].af_state = AF_DERIVED;
    list->af_list[i].af_class = AF_CLASS_DERIVED | AF_VALID;

    /* read author*/
    fgets (idStr, AF_IDSTRLEN+1, cacheFile);
    fgets (line, AF_LINESIZ, cacheFile);
    itemPtr = afFirstItem (line);
    list->af_list[i].af_auname = af_entersym (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_auhost = af_enterhost (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_audomain = af_enterdomain (itemPtr);
    list->af_list[i].af_lckname = NULL;
    list->af_list[i].af_lckhost = NULL;
    list->af_list[i].af_lckdomain = NULL;

    /* read dates */
    fgets (idStr, AF_IDSTRLEN+1, cacheFile);
    fgets (line, AF_LINESIZ, cacheFile);
    itemPtr = afFirstItem (line);
    list->af_list[i].af_mtime = (time_t) atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_atime = (time_t) atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_ctime = (time_t) atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_stime = (time_t) atoi (itemPtr);
    list->af_list[i].af_ltime = AF_NOTIME;
    list->af_list[i].af_notesize = 0;
    list->af_list[i].af_note = NULL;
    
    /* read kind of representation and size */
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_repr = (short) atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_fsize = (size_t) atoi (itemPtr);
    itemPtr = afNextItem (itemPtr);
    list->af_list[i].af_dsize = (size_t) atoi (itemPtr);
    list->af_list[i].af_data = NULL;
    list->af_list[i].af_nrefs = 0;
    list->af_list[i].af_succgen = AF_NOVNUM;
    list->af_list[i].af_succrev = AF_NOVNUM;
    list->af_list[i].af_predgen = AF_NOVNUM;
    list->af_list[i].af_predrev = AF_NOVNUM;

    /* read user defined attributes */
    fgets (idStr, AF_IDSTRLEN+1, cacheFile);
    if (strcmp (idStr, AF_UDAID)) {
      afCloseUnlock (cacheFile, AF_READ, list);
      FATAL ("ObjCacheRead", "wrong uda-ID in cache control file", AF_EINCONSIST, ERROR);
    }
    fgetc (cacheFile); /* skip newline char */

    tmpKey.af_lpos = i;
    afInitUdas (&tmpKey);

    j = 0;
    while (TRUE) {
      char c;
      if ((udaBuf[j] = fgetc (cacheFile)) == '\0') {
	if (j != 0) {
	  tmpKey.af_lpos = i;
	  afEnterUda (&tmpKey, udaBuf);
	}
	/* a second nullbyte indicates the end of the list of udas */
	if ((c = fgetc (cacheFile)) == '\0')
	  break;
	udaBuf[0] = c;
	j = 1;
      }
      else if (udaBuf[j] == EOF) {
	afCloseUnlock (cacheFile, AF_READ, list);
	FATAL ("ObjCacheRead", "unexpected end of file", AF_EINCONSIST, ERROR);
      }
      else {
	j++;
	if ((j % AF_UDASEGSIZ) == 0) { /* if segment is full */
	  if ((udaBuf = realloc (udaBuf, (unsigned) ((j + AF_UDASEGSIZ) * sizeof (char)))) == NULL) {
	    afCloseUnlock (cacheFile, AF_READ, list);
	    FAIL ("ObjCacheRead", "realloc", AF_ESYSERR, ERROR);
	  }
	}
      }
    }
    fgetc (cacheFile); /* skip trailing newline char */
  } /* end for */

  afCloseUnlock (cacheFile, AF_READ, list);
  free (udaBuf);
  list->af_lastmod = cacheIbuf.st_mtime;
  list->af_extent |= AF_COMPLETE;
  return (AF_OK);
}

/*====================
 * Write Object Cache
 *====================*/

EXPORT int afObjCacheWrite (list)
     Af_revlist *list;
{
  int    i, j, maxIndex, err;
  size_t siz;
  FILE	 *tmpFile, *cacheFile;
  char   *tmpName, cacheName[PATH_MAX], *attrList[AF_MAXUDAS+1], buf[BUFSIZ];
  Af_key tmpKey;
  struct stat ibuf;

  tmpKey.af_ldes = list;
  strcpy (cacheName, afCacheFileName (list->af_arpath, AF_CACHENAME));

  tmpName = af_gtmpname (list->af_arpath);
  af_regtmpfile (tmpName);
  /* open tmpfile */
  if ((tmpFile = fopen (tmpName, "w")) == NULL)
    return (ERROR);

  /* write header */
  fprintf (tmpFile, "%s %d %d %d %d %d\n", AF_CACHEHEADER, AF_CACHECURVERS,
	   list->af_nrevs, afObjCacheMax, afObjCacheNameNum, afObjCacheAttrNum);

  /* write list of ASO attributes */
  maxIndex = list->af_nrevs-1;
  for (i=0; i <= maxIndex; i++) {
    /* skip deleted ASOs */
    if (!(list->af_list[i].af_class & AF_VALID)) {
      if (++maxIndex == list->af_listlen) {
	fclose (tmpFile);
	af_unregtmpfile (tmpName);
	unlink (tmpName);
	FATAL ("ObjCacheWrite", "revision count", AF_EINCONSIST, ERROR);
      }
      continue;
    }

    /* write name and version number */
    fprintf (tmpFile, "%s %s %s %d %d %o %s\n", AF_NAMEID,
	     list->af_list[i].af_name, 
	     NOTMT(list->af_list[i].af_type),
	     list->af_list[i].af_gen,
	     list->af_list[i].af_rev,
	     list->af_list[i].af_mode,
	     list->af_list[i].af_hashname);
    /* write author */
    fprintf (tmpFile, "%s %s %s %s\n", AF_AUTHORID,
	     list->af_list[i].af_auname,
	     list->af_list[i].af_auhost,
	     list->af_list[i].af_audomain);
    /* write dates and kind of representation */
    fprintf (tmpFile, "%s %ld %ld %ld %ld %d %lu %lu\n", AF_DATEID,
	     list->af_list[i].af_mtime,
	     list->af_list[i].af_atime,
	     list->af_list[i].af_stime,
	     list->af_list[i].af_ctime,
	     list->af_list[i].af_repr,
	     list->af_list[i].af_fsize,
	     list->af_list[i].af_dsize);

    /* write user defined attributes */
    fprintf (tmpFile, "%s\n", AF_UDAID);
    tmpKey.af_lpos = i;
    afListUdas (&tmpKey, attrList);
    j=0;
    while (attrList[j])
      fprintf (tmpFile, "%s%c", attrList[j++], '\0');
    if (j==0) /* if no user defined attribute has been written */
      fputc ('\0', tmpFile);
    fputc ('\0', tmpFile);
    fputc ('\n', tmpFile);
    fflush (tmpFile);
  }
  fclose (tmpFile);

  if ((tmpFile = fopen (tmpName, "r")) == NULL) {
    af_unregtmpfile (tmpName);
    unlink (tmpName);
    FATAL ("ObjCacheWrite", "temporary archive file lost", AF_EINTERNAL, ERROR);
  }

  disableSig (SIGINT);
  disableSig (SIGQUIT);

  /* set lock on old object cache file -- tmp file *is* still locked */
  if ((cacheFile = afOpenLock (cacheName, AF_WRITE, list)) == NULL) {
    af_unregtmpfile (tmpName);
    unlink (tmpName);
    return (ERROR);
  }

  /* copy contents of temporary file to archive file */
  err = FALSE;
  while ((siz = fread (buf, sizeof(char), (size_t) BUFSIZ, tmpFile)) == BUFSIZ) {
    if (fwrite (buf, sizeof(char), (size_t) BUFSIZ, cacheFile) != BUFSIZ)
      err = TRUE;
  }
  if ((fwrite (buf, sizeof(char), (size_t) siz, cacheFile) != siz) || err) {
    fclose (tmpFile);
    af_unregtmpfile (tmpName);
    unlink (tmpName);
    enableSig();
    afCloseUnlock (cacheFile, AF_WRITE, list);
    FAIL ("ObjCacheWrite", "fwrite", AF_ESYSERR, ERROR);
  }
  fclose (tmpFile);
  af_unregtmpfile (tmpName);
  unlink (tmpName);
  enableSig();
  afCloseUnlock (cacheFile, AF_WRITE, list);

  /* update list descriptor */
  if (stat (cacheName, &ibuf) != ERROR)
    list->af_lastmod = ibuf.st_mtime;

  return (AF_OK);
}

/*==============================================
 *   afObjCacheAdd -- add object cache entry
 *==============================================*/

EXPORT int afObjCacheAdd (aso, outKey, cacheAttr)
     Af_key *aso, *outKey;
     char   *cacheAttr;
{
  char   *cacheAttrValue = NULL, *tmpAttr;
  int    maxIndex, i, equalNameCount = 0, hasAttrCount = 0;
  time_t curAttrAtime, curNameAtime, curAtime;
  int    freeIndex = -1, oldestAttrIndex = -1, oldestNameIndex = -1;
  Af_revlist *cacheList = outKey->af_ldes;
  Af_key tmpKey, *tmpKeyPtr = &tmpKey;
  int    nameIndexList[AF_MAX_CACHED_PER_NAME];
  int    addAso = FALSE;
  char   *cachedFileName, *uniqName = NULL;
  struct utimbuf timeBuf;
  struct stat    iBuf;

  outKey->af_lpos = -1;

  if (cacheAttr && cacheAttr[0]) {
    if ((cacheAttrValue = strchr (cacheAttr, AF_UDANAMDEL)))
      cacheAttrValue++;
    else
      cacheAttrValue = "";
  }

  /* assemble list of equally named objects and remember
   * - oldest ASO carrying the given attribute and
   * - oldest equally named ASO
   */
  maxIndex = cacheList->af_nrevs-1;
  curAttrAtime = curNameAtime = af_acttime ();
  tmpKey.af_ldes = cacheList;
  for (i=0; i <= maxIndex; i++) {
    /* skip deleted ASOs */
    if (!(cacheList->af_list[i].af_class & AF_VALID)) {
      if (++maxIndex == cacheList->af_listlen)
	FATAL ("ObjCacheAdd", "revision count", AF_EINCONSIST, ERROR);
      if (freeIndex == -1)
	freeIndex = i;
      continue;
    }

    /* check name */
    if ((VATTR(aso).af_name == cacheList->af_list[i].af_name) &&
	(VATTR(aso).af_type == cacheList->af_list[i].af_type)) {
      nameIndexList[equalNameCount++] = i;
      if (!cacheList->af_list[i].af_nrefs &&
	  (cacheList->af_list[i].af_atime < curNameAtime)) {
	oldestNameIndex = i;
	curNameAtime = cacheList->af_list[i].af_atime;
      }

      /* check attribute */
      tmpKey.af_lpos = i;
      tmpAttr = af_retattr (&tmpKey, cacheAttr);
      if (tmpAttr && !strcmp (cacheAttrValue, tmpAttr)) {
	hasAttrCount++;
	if (!cacheList->af_list[i].af_nrefs &&
	    (cacheList->af_list[i].af_atime < curAttrAtime)) {
	  oldestAttrIndex = i;
	  curAttrAtime = cacheList->af_list[i].af_atime;
	}
      } /* if attr equal */
    } /* if name equal */
  } /* for */
  if ((freeIndex == -1) && (++maxIndex < cacheList->af_listlen))
    freeIndex = maxIndex;

  /* check if there is space for a new ASO */
  if ((equalNameCount < afObjCacheNameNum) && (hasAttrCount < afObjCacheAttrNum) && (cacheList->af_nrevs < afObjCacheMax)) {
    if (freeIndex == -1)
      FATAL ("ObjCacheAdd", "derived object cache ASO count", AF_EINCONSIST, ERROR);
    outKey->af_lpos = freeIndex;
    addAso = TRUE;
  }
  else { /* we have to clean out an ASO */
    if (oldestAttrIndex != -1)
      outKey->af_lpos = oldestAttrIndex;
    else if (oldestNameIndex != -1)
      outKey->af_lpos = oldestNameIndex;
  }
  
  /* if outKey is still not defined, clean out the oldest ASO of all */
  if (outKey->af_lpos == -1) {
    maxIndex = cacheList->af_nrevs-1;
    curAtime = af_acttime ();
    for (i=0; i <= maxIndex; i++) {
      if (!(cacheList->af_list[i].af_class & AF_VALID)) {
	maxIndex++;
	continue;
      }
      tmpKey.af_lpos = i;
      if ((VATTR(tmpKeyPtr).af_atime < curAtime) && !VATTR(tmpKeyPtr).af_nrefs) {
	outKey->af_lpos = tmpKey.af_lpos;
	curAtime = VATTR(outKey).af_atime;
      }
    } /* for */
  }
  
  if (outKey->af_lpos == -1)
    FAIL ("ObjCacheAdd", "cannot find space in derived object cache", AF_EMISC, ERROR);

  uniqName = VATTR(outKey).af_hashname;
  /*** create attributes ***/
  VATTR(outKey) = VATTR(aso);
  VATTR(outKey).af_state = AF_DERIVED;
  VATTR(outKey).af_class |= AF_CLASS_DERIVED;
  VATTR(outKey).af_lckname = NULL;
  VATTR(outKey).af_lckhost = NULL;
  VATTR(outKey).af_lckdomain = NULL;
  VATTR(outKey).af_stime = af_acttime ();
  VATTR(outKey).af_ltime = AF_NOTIME;
  VATTR(outKey).af_notesize = 0;
  VATTR(outKey).af_note = NULL;
  VATTR(outKey).af_nrefs = 1;
  VATTR(outKey).af_succgen = AF_NOVNUM;
  VATTR(outKey).af_succrev = AF_NOVNUM;
  VATTR(outKey).af_predgen = AF_NOVNUM;
  VATTR(outKey).af_predrev = AF_NOVNUM;

  /*** copy user defined attributes ***/
  afInitUdas (outKey);
  afCopyUdas (aso, outKey);

  /*** copy file ***/
  if (!uniqName) {
    char *unixName = af_gbusname (NULL, VATTR(outKey).af_name, VATTR(outKey).af_type);
    char uniqChar = 'A';
    int  notUnique = TRUE;
    while (notUnique) {
      uniqName = afCacheUniqueName (unixName, uniqChar);
      /* check Name for uniqueness */
      notUnique = FALSE;
      for (i=0; i < equalNameCount; i++) {
	tmpKey.af_lpos = nameIndexList[i];
	if (VATTR(tmpKeyPtr).af_hashname == uniqName) {
	  notUnique = TRUE;
	  break;
	}
      } /* for */
      if (uniqChar == 'Z')
	uniqChar = 'a';
      else
	uniqChar++;
    } /* while */
  }
  cachedFileName = afCacheFileName (outKey->af_ldes->af_arpath, uniqName);
  if (af_cpfile (aso->af_ldes->af_busyfilename, VATTR(aso).af_fsize, cachedFileName) == ERROR) {
    VATTR(outKey).af_class &= ~AF_VALID;
    cacheList->af_nrevs--;
    FAIL ("ObjCacheAdd", "cpfile", AF_ESYSERR, ERROR);
  } 
  if (addAso)
    cacheList->af_nrevs++;
  VATTR(outKey).af_hashname = uniqName;
  stat (cachedFileName, &iBuf);
  VATTR(outKey).af_ctime = iBuf.st_ctime;

  /*** set modification and access date ***/
  timeBuf.actime = VATTR(outKey).af_atime;
  timeBuf.modtime = VATTR(outKey).af_mtime;
  if (utime (cachedFileName, &timeBuf) == ERROR)
    af_wng ("ObjCacheAdd", "cannot set UNIX access and modification date");

  return (afObjCacheWrite (outKey->af_ldes));
}

/*================================================
 * afObjCacheRestore -- restore object cache file
 *================================================*/

EXPORT int afObjCacheRestore (aso, fileName)
     Af_key *aso;
     char   *fileName;
{
  if (af_cpfile (afCacheFileName (aso->af_ldes->af_arpath, VATTR(aso).af_hashname), VATTR(aso).af_fsize, fileName) == ERROR)
    FAIL ("ObjCacheResore", "cpfile", AF_ESYSERR, ERROR);
  return (AF_OK);
}
