/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1987 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors:                                          BBS:      */
/* | o  | ||   John Toebes     Dave Baker     John Mainwaring                */
/* |  . |//                                                                  */
/* ======                                                                    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* Lock.c - lock manipulation */
/* ActLock, ActDupLock, ActUnLock */

/*---------------------------------------------------------------------------*/
/* Structure of a Lock:                                                      */
/*   struct FileLock {                                                       */
/*      BPTR fl_Link;             Next lock in the chain of device locks     */
/*      LONG fl_Key;              Block number of directory or file header   */
/*      LONG fl_Access;           Shared Read (-2) or Exclusive Write (-1)   */
/*      struct MsgPort * fl_Task; Handler process for Lock (Us)              */
/*      BPTR fl_Volume;           Node in DevInfo structure for Lock         */
/*      };                                                                   */
/*---------------------------------------------------------------------------*/
#include "handler.h"

long GetKey(global,lock)
struct global *global;
struct FileLock *lock;
{
   if (global->volume == NULL)
      {
      global->pkt->dp_Res2 = ERROR_DEVICE_NOT_MOUNTED;
      return(0);
      }

   if (lock == NULL)
      return(global->Root);
   return(lock->fl_Key);
}

/* create a lock to be returned */
struct FileLock *CreateLock(global, key, mode)
GLOBAL global;
KEY key;
int mode;
{
struct FileLock *lock;
int access;

   lock = NULL;

   if (global->volume == NULL)
      {
      global->pkt->dp_Res2 = ERROR_DEVICE_NOT_MOUNTED;
      key = 0;
      }

   if (key)
      {
      /* if we are creating a write lock, make sure we can lock the disk */
      access = 0;

      BUG(("Searching for locks from %08lx\n", BADDR(global->volume->dl_Lock)));

      Forbid();
      /* Now run through the list of locks for this volume to figure out */
      /* What level of access it is currently locked for                 */
      for (lock = (struct FileLock *)BADDR(global->volume->dl_Lock);
           lock != NULL && access == 0;
           lock = (struct FileLock *)BADDR(lock->fl_Link))
         {
         /* Does it match what we want to access? */
         if (lock->fl_Key == key)
            access = lock->fl_Access;
         }
      Permit();

      BUG(("Access outstanding is %ld\n", access));

      /* Given the prevailing access mode, see if we will allow the new lock */
      switch(mode)
         {
         case SHARED_LOCK:
            if (access == EXCLUSIVE_LOCK)
               {
               /* Not legal, tell them so... */
               global->pkt->dp_Res2 = ERROR_OBJECT_IN_USE;
               return(NULL);
               }
            break;

         case EXCLUSIVE_LOCK:
            /* Does anyone else have it open? */
            if (access)
               {
               /* Not legal, tell them so... */
               global->pkt->dp_Res2 = ERROR_OBJECT_IN_USE;
               return(NULL);
               }
            break;

         default:
            BUG(("Invalid access %ld requested for key %ld\n", mode, key));
            global->pkt->dp_Res2 = ERROR_INVALID_LOCK;
            return(NULL);
         }


      if ((lock = (struct FileLock *)
                  DosAllocMem(global, sizeof(struct FileLock))) != NULL)
         {
         BUG(("Creating the lock %08lx for key=%ld\n", lock, key));

         /* now fill in the lock */
         lock->fl_Key = key;
         lock->fl_Access = mode;
         lock->fl_Task = global->port;
         lock->fl_Volume = MKBADDR(global->volume);

         Forbid();
         lock->fl_Link = global->volume->dl_Lock;
         global->volume->dl_Lock = MKBADDR(lock);
         Permit();

         BUG(("Task=%08lx Vol=%08lx\n", lock->fl_Task, lock->fl_Volume));
         }
      }
   return(lock);
}

void freelock(global, lock)
GLOBAL global;
struct FileLock *lock;
{
struct FileLock *this;

   /* freeing NULL (root lock) is a bad move    */
   if (lock == NULL)
      {
      BUG(("NULL Lock\n"));
      return;
      }

   if (global->volume == NULL)
      {
      global->pkt->dp_Res2 = ERROR_DEVICE_NOT_MOUNTED;
      return;
      }

   /* locate the lock on the global volume list */
   Forbid();

   if ((this = (struct FileLock *)BADDR(global->volume->dl_Lock)) == lock)
      global->volume->dl_Lock = lock->fl_Link;
   else
      {
      while(this != NULL && (struct FileLock *)BADDR(this->fl_Link) != lock)
         this = (struct FileLock *)BADDR(this->fl_Link);
      if (this == NULL)
         /* lock wasn't in chain - don't drop through to free memory */
         {
         Permit();
         BUG(("Lock not found in chain\n"));
         return;
         }
      else
         this->fl_Link = lock->fl_Link;
      }
   Permit();

   DosFreeMem((char *)lock);
}

void ActLock(global, pkt)
GLOBAL global;
struct DosPacket      *pkt;      /* a pointer to the dos packet sent       */
/* Arg1: Lock */
/* Arg2: Name */
/* Arg3: Mode: ACCESS_READ, ACCESS_WRITE */
{
   KEY key;
   struct FileLock *lock;

   BUG(("Action Lock\n"));
   BUGBSTR("File to lock: ", pkt->dp_Arg2);

   key = GetKey(global,(struct FileLock *)pkt->dp_Arg1);

   key = LocateEntry(global, key, (char *)pkt->dp_Arg2);

   lock = CreateLock(global, key, pkt->dp_Arg3);

   BUG(("Lock created at %08lx\n", lock));

   pkt->dp_Res1 = MKBADDR(lock);
}

void ActDupLock(global,pkt)
GLOBAL global;
struct DosPacket      *pkt;      /* a pointer to the dos packet sent       */
{
   struct FileLock *lock;

   BUG(("Action DupLock\n"));

   lock = (struct FileLock *)pkt->dp_Arg1;

   lock = CreateLock(global, lock->fl_Key, lock->fl_Access);

   pkt->dp_Res1 = MKBADDR(lock);
}

void ActUnLock(global, pkt)
GLOBAL global;
struct DosPacket      *pkt;      /* a pointer to the dos packet sent       */
{
   struct FileLock *lock;

   lock = (struct FileLock *)pkt->dp_Arg1;

   BUG(("Action UnLock\n"));

   freelock(global, lock);

   pkt->dp_Res1 = DOS_TRUE;
}
