/* 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
 *
 * afversions.c - operations on revisions
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afvers.c[7.0] Thu Jan 20 12:05:48 1994 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"

/*====================================================================
 *    af_savecache
 *====================================================================*/

EXPORT int af_savecache (aso, outKey, uniqAttr, mode)
     Af_key *aso, *outKey;
     char* uniqAttr;
     int mode;
{
  if (afAccessAso (aso, AF_DATA))
    FAIL ("savecache", "", AF_EINVKEY, ERROR);
  
  if (VATTR(aso).af_state != AF_BUSY)
    FAIL ("savecache", "", AF_ENOTBUSY, ERROR);

  if (!S_ISREG (VATTR(aso).af_mode))
    FAIL ("savecache", "", AF_ENOTREGULAR, ERROR);

  if (af_checkperm (aso, AF_WORLD) == ERROR)
    return (ERROR);

  if ((outKey->af_ldes = afInitObjCache (CATTR(aso).af_syspath)) == NULL)
    return (ERROR);

  if (afObjCacheAdd (aso, outKey, uniqAttr) == ERROR)
    return (ERROR);

  outKey->af_ldes->af_access++;
  return (AF_OK);
}


/*====================================================================
 *    af_saverev
 *====================================================================*/

EXPORT int af_saverev (busykey, savekey, generation, storeType)
     Af_key *busykey;
     Af_key *savekey; /* out */
     int    generation;
     int    storeType;
{
  register Af_key *lastkey, *lastgenkey = NULL;
  Af_key predkey;
  register Af_user *author;
  struct stat ibuf;
  
  if (afAccessAso (busykey, AF_ATTRS))
    FAIL ("saverev", "", AF_EINVKEY, ERROR);
  
  if (VATTR(busykey).af_state != AF_BUSY)
    FAIL ("saverev", "", AF_ENOTBUSY, ERROR);

  if (!S_ISREG (VATTR(busykey).af_mode))
    FAIL ("saverev", "", AF_ENOTREGULAR, ERROR);

  /* check for "AtFS"-file */
  if (!strcmp (VATTR(busykey).af_name, AF_SUBDIR) && !VATTR(busykey).af_type)
    FAIL ("saverev", "cannot save file named 'AtFS'", AF_EMISC, ERROR);

  if (busykey->af_ldes->af_nrevs > 1) /* there are saved versions */ {
    lastkey = af_glastkey (busykey->af_ldes);
    if (generation != AF_LASTVERS) {
      if (generation < 0)
	FAIL ("saverev", "invalid generation number", AF_EMISC, ERROR);
      /* if generation is current generation */
      if (VATTR(lastkey).af_gen == generation)
	generation = AF_LASTVERS;
      /* if generation is unknown */
      if ((lastgenkey = af_glastgenkey (busykey->af_ldes, generation)) == NULL)
	FAIL ("saverev", "invalid generation number", AF_EMISC, ERROR);
    }

    if (generation != AF_LASTVERS) {
      if (af_checkperm (lastgenkey, AF_LOCKHOLDER) == ERROR)
	FAIL ("saverev", "", AF_ENOTLOCKED, ERROR); }
    else {
      if (af_checkperm (lastkey, AF_LOCKHOLDER) == ERROR)
	FAIL ("saverev", "", AF_ENOTLOCKED, ERROR);
    }
  }

  /* get attribute buffer (pointed to by "savekey") for new version */
  /* invalidates "lastkey" */
  if (afNewAso (busykey->af_ldes, savekey, AF_CLASS_SOURCE, generation) == ERROR)
    return (ERROR);

  /* set key and attributes of new version */
  VATTR(savekey).af_name = VATTR(busykey).af_name;
  VATTR(savekey).af_type = VATTR(busykey).af_type;
  VATTR(savekey).af_lckname = VATTR(busykey).af_lckname;
  VATTR(savekey).af_lckhost = VATTR(busykey).af_lckhost;
  VATTR(savekey).af_lckdomain = VATTR(busykey).af_lckdomain;
  VATTR(savekey).af_ltime = af_acttime ();
  
  if (busykey->af_ldes->af_nrevs == 1) {
    /* there is only one (the busy-) revision (..nrevs == 1) */
    VATTR(savekey).af_gen = AF_INITGEN;
    VATTR(savekey).af_rev = AF_INITREV;
    VATTR(savekey).af_predgen = AF_NOVNUM;
    VATTR(savekey).af_predrev = AF_NOVNUM;
    VATTR(savekey).af_succgen = AF_NOVNUM;
    VATTR(savekey).af_succrev = AF_NOVNUM;
    VATTR(busykey).af_predgen = AF_INITGEN;
    VATTR(busykey).af_predrev = AF_INITREV;
    if (af_nodelta (busykey, savekey) != AF_OK)
      return (ERROR);
  }
  else { /* get some attributes from preceding revision */
    lastkey = af_glastkey (busykey->af_ldes);
    if (generation == AF_LASTVERS) {
      VATTR(savekey).af_gen = VATTR(lastkey).af_gen; 
      VATTR(savekey).af_rev = VATTR(lastkey).af_rev+1;
    }
    else {
      VATTR(savekey).af_gen = VATTR(lastgenkey).af_gen;
      VATTR(savekey).af_rev = VATTR(lastgenkey).af_rev+1;;
    }
    VATTR(savekey).af_succgen = AF_NOVNUM;
    VATTR(savekey).af_succrev = AF_NOVNUM;
    if (storeType == AF_STORE_COMPLETE) {
      VATTR(savekey).af_predgen = AF_NOVNUM;
      VATTR(savekey).af_predrev = AF_NOVNUM;
      if (af_nodelta (busykey, savekey) != AF_OK)
	return (ERROR);
    }
    else {
      if (VATTR(busykey).af_predgen == AF_NOVNUM) {
	VATTR(savekey).af_predgen = VATTR(lastkey).af_gen;
	VATTR(savekey).af_predrev = VATTR(lastkey).af_rev;
      }
      else {
	VATTR(savekey).af_predgen = VATTR(busykey).af_predgen;
	VATTR(savekey).af_predrev = VATTR(busykey).af_predrev;
      }
      if (af_buildkey (busykey->af_ldes, VATTR(savekey).af_predgen, VATTR(savekey).af_predrev, &predkey) == ERROR)
	predkey.af_ldes = NULL;
      if (af_dodelta (busykey, &predkey, savekey) == ERROR)
	FAIL ("saverev", "", AF_EDELTA, ERROR);
    }
    VATTR(busykey).af_predgen = VATTR(savekey).af_gen;
    VATTR(busykey).af_predrev = VATTR(savekey).af_rev;
  }

  VATTR(savekey).af_state = AF_SAVED;
  VATTR(savekey).af_class = VATTR(busykey).af_class;
  author = af_afuser ((uid_t) geteuid());
  VATTR(savekey).af_auname = af_entersym (author->af_username);
  VATTR(savekey).af_auhost = af_enterhost (author->af_userhost);
  VATTR(savekey).af_audomain = af_enterdomain (author->af_userdomain);
  stat (busykey->af_ldes->af_busyfilename, &ibuf);	
  VATTR(savekey).af_mode = ibuf.st_mode;
  VATTR(savekey).af_mtime = ibuf.st_mtime;
  VATTR(savekey).af_atime = af_acttime ();
  VATTR(savekey).af_ctime = ibuf.st_ctime;
  VATTR(savekey).af_stime = af_acttime ();
  VATTR(savekey).af_notesize = 0;
  VATTR(savekey).af_note = NULL;
  VATTR(savekey).af_nrefs = 1;

  afInitUdas (savekey);
  afCopyUdas (busykey, savekey);
  
  VATTR(savekey).af_hashname = NULL;
  
  /* update list descriptor */
  busykey->af_ldes->af_nrevs++;
  busykey->af_ldes->af_datasize += VATTR(savekey).af_notesize;
  
  /* save changes */
  if (afAddAso (savekey) == ERROR)
    return (ERROR);
  
  return (AF_OK);
} /* af_saverev */



/*====================================================================
 *    af_newgen
 *====================================================================*/

EXPORT int af_newgen (key, newkey)
     Af_key *key;
     Af_key *newkey; /* out */
{
  register Af_key *lastkey, *busykey;

  if (afAccessAso (key, AF_ATTRS))
    FAIL ("newgen", "", AF_EINVKEY, ERROR);

/* Hmm, I think this is not necessary, Andy 18.3.93.
  if (key->af_ldes->af_nrevs == 1)
    FAIL ("newgen", "", AF_ENOTVERS, ERROR);
*/
  if (VATTR(key).af_state == AF_DERIVED)
    FAIL ("newgen", "", AF_EDERIVED, ERROR);

  if (!S_ISREG (VATTR(key).af_mode))
    FAIL ("newgen", "", AF_ENOTREGULAR, ERROR);

  lastkey = af_glastkey (key->af_ldes);
  if (af_checkperm (lastkey, AF_LOCKHOLDER) == ERROR)
    FAIL ("newgen", "", AF_ENOTLOCKED, ERROR);

  if (afNewAso (key->af_ldes, newkey, AF_CLASS_SOURCE, AF_LASTVERS) == ERROR)
    return (ERROR);

  if ((VATTR(key).af_predgen == VATTR(lastkey).af_gen) && (VATTR(key).af_predrev == VATTR(lastkey).af_rev)) {
    VATTR(key).af_predgen++;
    VATTR(key).af_predrev = 0;
  }

  /* duplicate last revision */

  VATTR(newkey).af_gen = VATTR(lastkey).af_gen+1; 
  VATTR(newkey).af_rev = 0;
  VATTR(newkey).af_predgen = VATTR(lastkey).af_gen;
  VATTR(newkey).af_predrev = VATTR(lastkey).af_rev;

  if (af_dodelta (NULL, lastkey, newkey) == ERROR)
    FAIL ("newgen", "", AF_EDELTA, ERROR);

  VATTR(newkey).af_name = VATTR(lastkey).af_name;
  VATTR(newkey).af_type = VATTR(lastkey).af_type;
  VATTR(newkey).af_state = AF_SAVED;
  VATTR(newkey).af_class = VATTR(lastkey).af_class;
  VATTR(newkey).af_auname = VATTR(lastkey).af_auname;
  VATTR(newkey).af_auhost = VATTR(lastkey).af_auhost;
  VATTR(newkey).af_audomain = VATTR(lastkey).af_audomain;
  VATTR(newkey).af_mode = VATTR(lastkey).af_mode;
  VATTR(newkey).af_mtime = VATTR(lastkey).af_mtime;
  VATTR(newkey).af_atime = VATTR(lastkey).af_atime;
  VATTR(newkey).af_ctime = VATTR(lastkey).af_ctime;
  VATTR(newkey).af_stime = VATTR(lastkey).af_stime;
  VATTR(newkey).af_notesize = VATTR(lastkey).af_notesize;
  VATTR(newkey).af_note = VATTR(lastkey).af_note;
  VATTR(newkey).af_lckname = NULL;
  VATTR(newkey).af_lckhost = NULL;
  VATTR(newkey).af_lckdomain = NULL;
  VATTR(newkey).af_nrefs = 1;

  afInitUdas (newkey);
  afCopyUdas (lastkey, newkey);

  VATTR(newkey).af_succgen = AF_NOVNUM;
  VATTR(newkey).af_succrev = AF_NOVNUM;

  /* update list descriptor */
  key->af_ldes->af_nrevs++;
  key->af_ldes->af_datasize += VATTR(newkey).af_notesize;

  /* update predecessor of busy version */
  busykey = af_gbuskey (key->af_ldes);
  if  ((VATTR(busykey).af_predgen == VATTR(lastkey).af_gen) && (VATTR(busykey).af_predrev == VATTR(lastkey).af_rev)) {
    VATTR(busykey).af_predgen = VATTR(newkey).af_gen;
    VATTR(busykey).af_predrev = VATTR(newkey).af_rev;
  }

  if (afAddAso (newkey) == ERROR)
    return (ERROR);

  return (AF_OK);
}


/*====================================================================
 *    af_setbusy
 *====================================================================*/

EXPORT int af_setbusy (busykey, key, prevkey)
     Af_key *busykey, *key, *prevkey;
{
  int mtKey=FALSE;

  if ((key->af_ldes == NULL) && (key->af_lpos == -1))
    mtKey = TRUE;
  else {
    if (afAccessAso (key, AF_ATTRS))
      FAIL ("setbusy", "", AF_EINVKEY, ERROR);
    if (VATTR(key).af_state == AF_BUSY)
      FAIL ("setbusy", "", AF_EBUSY, ERROR);
  }

  if (busykey) {
    if (afAccessAso (busykey, AF_ATTRS))
      FAIL ("setbusy", "", AF_EINVKEY, ERROR);
    if (VATTR(busykey).af_state != AF_BUSY)
      FAIL ("setbusy", "", AF_ENOTBUSY, ERROR);
    if (VATTR(busykey).af_state == AF_DERIVED)
      FAIL ("setbusy", "", AF_EDERIVED, ERROR);
  }
  else
    busykey = af_gbuskey (key->af_ldes);

  if (af_checkperm (busykey, AF_AUTHOR) == ERROR)
    return (ERROR);

  /* determine previous predecessor of busy version */
  prevkey->af_ldes = NULL;
  prevkey->af_lpos = -1;
  if (VATTR(busykey).af_predgen != AF_NOVNUM) {
    if (af_buildkey (busykey->af_ldes, VATTR(busykey).af_predgen, VATTR(busykey).af_predrev, prevkey) == ERROR)
      af_wng ("setbusy", "corrupted predecessor information");
  }

  if (mtKey || (busykey->af_ldes->af_busyfilename != key->af_ldes->af_busyfilename)) {
    VATTR(busykey).af_predgen = AF_NOVNUM;
    VATTR(busykey).af_predrev = AF_NOVNUM;
  }
  else {
    VATTR(busykey).af_predgen = VATTR(key).af_gen;
    VATTR(busykey).af_predrev = VATTR(key).af_rev;
  }

  if (afUpdateAso (busykey, AF_CHANGE) == ERROR)
    return (ERROR);

  return (AF_OK);
}


/*====================================================================
 *    af_svnum
 *====================================================================*/

EXPORT int af_svnum (key, gen, rev)
     Af_key *key;
     int gen, rev;
{
  Af_key *lastkey, *busykey;
  Af_key predkey;

  if (afAccessAso (key, AF_ATTRS))
    FAIL ("svnum", "", AF_EINVKEY, ERROR);

  if ((VATTR(key).af_state == AF_BUSY) || (VATTR(key).af_state > AF_PROPOSED))
    FAIL ("svnum", "", AF_EWRONGSTATE, ERROR);

  if (VATTR(key).af_state != AF_DERIVED) { /* derived files can get any vnum */
    if (af_checkperm (key, AF_LOCKHOLDER) == ERROR)
      FAIL ("svnum", "", AF_ENOTLOCKED, ERROR);

    /* only the version number of the last version in a generation can be modified */
    lastkey = af_glastgenkey (key->af_ldes, VATTR(key).af_gen);
    if (af_keycmp (key, lastkey))
      FAIL ("svnum", "can set version number only for last version of a generation", AF_EMISC, ERROR);

    /* if version number is from another generation, key must be last version at all */
    if (VATTR(key).af_gen != gen) {
      lastkey = af_glastkey (key->af_ldes);
      if (af_keycmp (key, lastkey))
	FAIL ("svnum", "can set version number only within generation", AF_EMISC, ERROR);
    }

    /* if new version number is smaller than the old one */
    if (gen < VATTR(key).af_gen)
      FAIL ("svnum", "", AF_EINVVNUM, ERROR);
    if (gen == VATTR(key).af_gen) {
      if (rev < VATTR(key).af_rev)
	FAIL ("svnum", "", AF_EINVVNUM, ERROR);
    }

    /* read data file in order to get it updated */
    if (afAccessAso (key, AF_DATA) == ERROR)
      return (ERROR);
  }

  /* check if version was predecessor of busy version */
  busykey = af_gbuskey (key->af_ldes);
  if ((VATTR(busykey).af_predgen == VATTR(key).af_gen) &&
      (VATTR(busykey).af_predrev == VATTR(key).af_rev)) {
    VATTR(busykey).af_predgen = (short)gen;
    VATTR(busykey).af_predrev = (short)rev;
  }

  VATTR(key).af_gen = (short)gen;
  VATTR(key).af_rev = (short)rev;

  /* if a predecessor exists, update its successor field */
  if (af_buildkey (key->af_ldes, VATTR(key).af_predgen, VATTR(key).af_predrev, &predkey) == AF_OK) {
    VATTR((&predkey)).af_succgen = (short)gen;
    VATTR((&predkey)).af_succrev = (short)rev;
  }

  /* the predecessor is updated implicitely by af_updtvers (key) */
  if (afUpdateAso (key, AF_CHANGE) == ERROR)
    return (ERROR);
  
  return (AF_OK);
}

