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

/* 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"

/* create a lock to be returned */
struct FileLock *CreateLock(global, nlock, RLock, Access)
GLOBAL global;
NETPTR nlock;
RPTR RLock;
LONG Access;
{
   struct FileLock *flock;
   NETPTR newnlock;

   if(!(flock=(struct FileLock *)
              DosAllocMem(global, sizeof(struct FileLock))) ||
      !(newnlock=(NETPTR) DosAllocMem(global, sizeof(struct NetPtr)))
      )
      return(NULL);

   flock->fl_Key = (LONG)newnlock;
   flock->fl_Access = Access;
   flock->fl_Task = global->n.port;
   flock->fl_Volume = MKBADDR(global->volume);
   newnlock->NetNode = nlock->NetNode;
   newnlock->RDevice = nlock->RDevice;
   newnlock->RPtr    = RLock;

   /* Link the lock into the lock chain */
   Forbid();
   flock->fl_Link = global->volume->dl_Lock;
   global->volume->dl_Lock = MKBADDR(flock);
   Permit();

   return(flock);
}

void FreeLock(global, flock)
GLOBAL global;
struct FileLock *flock;
{
   struct FileLock *current, *next;

   if(!flock) return;

   Forbid();
   if((current=(struct FileLock *)BADDR(global->volume->dl_Lock)) == flock)
   {
      global->volume->dl_Lock = flock->fl_Link;
      Permit();
      BUG(("FreeLock: Head lock freed, new head = %lx\n", 
         global->volume->dl_Lock));
   }
   else
   {
      for(next=NULL; 
          current && 
             (next=(struct FileLock *)BADDR(current->fl_Link)) != flock;
          current = next);

      if(!next)
      {
         Permit();
         BUG(("****************************************\n"));
         BUG(("******* Lock not found in chain ********\n"));
         BUG(("****************************************\n"));
         return;
      }
      else
      {
         current->fl_Link = next->fl_Link;
         Permit();
         BUG(("Lock found and removed from chain\n"));
      }
   }

   /* If RDevice == NULL, we have a dummy nlock, so don't free it */
   /* Otherwise, free it                                          */
   if(((NETPTR)flock->fl_Key)->RDevice)
      DosFreeMem((char *)flock->fl_Key);  /* Free the net lock   */

   DosFreeMem((char *)flock);          /* Free the local 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 */
{
   NETPTR nlock;
   struct FileLock *flock;

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

   /**********************************************************************/
   /* If the given lock is on the local root, call the ParseName routine */
   /* to determine if we need to call the remote nodes.  If ParseName    */
   /* returns 1, we have a name of the form "NET:" and we should return  */
   /* a lock on the root of NET:.  If it returns 0, we have a name of    */
   /* the form "NET:FOO" or "NET:FOO/BAR", etc and we need to call the   */
   /* remote node specified in the pseudo-lock returned.                 */
   /**********************************************************************/
   
   if((!(flock=(struct FileLock *)pkt->dp_Arg1) || 
       !(nlock=(NETPTR)flock->fl_Key)->RDevice))
   {
      /* ParseName returns 1 if we are locking the root.  It fills in */
      /* nlock with a pointer to the pseudolock for the node.  The    */
      /* pseudolock is kept in the NetNode struct.                    */
      /* Any leftover parts of the name are left in RP.Data as a      */
      /* BCPL string.                                                 */
      if(ParseName(global, (char *)pkt->dp_Arg2, &nlock, global->RP.Data))
      {
         BUG(("ActLock: Returning lock on root\n"));
         /* Return lock on root */
         /* ParseName has put the pseudolock for the root into nlock */
         pkt->dp_Res1 = 
               MKBADDR(CreateLock(global, nlock, NULL, pkt->dp_Arg3));
         return;
      }
      else if(!nlock)
      {
         BUG(("ActLock: Couldn't find node\n"));
         pkt->dp_Res1 = NULL;
         pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
         return;
      }
#if DEBUG
      else
         BUG(("ActLock: got nlock, falling through\n"));
#endif
   }
   else
   {
      BUGBSTR("ActLock: Relative open, node ", nlock->NetNode->name);
      /* We are doing a relative open - put filename in Data */
      MBSTR(pkt->dp_Arg2, global->RP.Data);
   }

   global->RP.Type = pkt->dp_Type;
   global->RP.Arg1 = (LONG)nlock->RPtr;
   global->RP.Arg3 = pkt->dp_Arg3;
   global->RP.DLen = BSTRLEN(global->RP.Data)+1;
   if(global->RP.DLen == 1) global->RP.DLen = 0;

   if(!RemotePacket(global, nlock) && pkt->dp_Res1)
   {
      BUG(("ActLock: Returned RPTR is %lx\n", pkt->dp_Res1));
      pkt->dp_Res1 = MKBADDR(CreateLock(global, nlock, 
                                        (RPTR)pkt->dp_Res1, pkt->dp_Arg3));
   }
}

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

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

   /* If this is a lock on the local root, it is a special case */
   if((!(flock=(struct FileLock *)pkt->dp_Arg1) || 
       !(nlock=(NETPTR)flock->fl_Key)->RDevice))
   {
      pkt->dp_Res1 = NULL;
      if(!flock) return;    /* Dup of null lock is null lock            */
      goto GETLOCK;         /* Dup of real lock on root is real lock (?)*/
   }

   global->RP.Type = pkt->dp_Type;
   global->RP.Arg1 = (LONG)nlock->RPtr;
   global->RP.DLen = 0;

   if(!RemotePacket(global, nlock) && pkt->dp_Res1)
   {
GETLOCK:
      pkt->dp_Res1 = MKBADDR(CreateLock(global, nlock,
                                 (RPTR)pkt->dp_Res1, flock->fl_Access));
   }
}

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

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

   if(!(flock = (struct FileLock *)pkt->dp_Arg1))
   {
      /* No lock - do nothing */
      pkt->dp_Res1 = DOS_TRUE;
      return;
   }

   /* If this lock exists on a remote node, delete it */
   if((nlock=(NETPTR)flock->fl_Key)->RDevice)
   {
      global->RP.Type = pkt->dp_Type;
      global->RP.Arg1 = (LONG)nlock->RPtr;
      global->RP.DLen = 0;
      RemotePacket(global, nlock);
   }
   else
      pkt->dp_Res1 = DOS_TRUE;

   FreeLock(global, flock);
}

int ParseName(global, name, nlock, outname)
GLOBAL global;
char *name;
NETPTR *nlock;
char *outname;
{
   int len, cur;
   struct NetNode *node;

BUGBSTR("ParseName: Parsing name ", name);

   for(len=*(name++), cur=0; len && *name != '/'; len--, name++)
   {
      if(*name==':')
         cur=0;
      else
         outname[cur++] = *name;
   }
   outname[cur] = '\0';
   if(len>0 && *name == '/') name++, len--;

   if(len <= 1 && cur == 0)
   {
      /* It is the root of NET: */
      *nlock = &global->netchain.RootLock;
      return(1);
   }

   /* We have a network node name - find it */
   node = FindNode(global, outname);

   /* OK, check to see if it is a .info file for a node */
   if(!node && cur >= 6 && !stricmp(outname+cur-5, ".info"))
   {
      outname[cur-5] = '\0';
      if(node = FindNode(global, outname))
      {
         BUG(("Got a .info file for node '%s'\n", outname));
         name = "Node.rinfo";
         len = 10;
      }
   }

   /* No node found - return null nlock */
   if(!node)
   {
      BUG(("ParseName: No node found, nlock is NULL\n"));
      *nlock = NULL; 
   }
   else
   {
      /* Node found - return template lock */
      *nlock = &node->RootLock;
      BUGBSTR("ParseName: Sending packet to node ", node->name);
   }

   /* Move the rest of the name into outname as a BSTR */
   outname[0] = (char)len+1;
   outname[1] = ':';
   if(len) MQ(name, outname+2, len);
   outname[len+2] = '\0';
   BUG(("Remote filename: len %d text '%s'\n", (int)*outname, outname+1));
   return(0);
}

struct NetNode *FindNode(global, name)
GLOBAL global;
char *name;
{
   struct NetNode *node;

   for(node=global->netchain.next; node; node=node->next)
      if(!stricmp(name, node->name+1)) break;

   return(node);
}

