/* CFS.c - main packet loop.
   Copyright (C) 1991, 1992, 1993, 1994 Kristian Nielsen.

   This file is part of XFH, the compressing file system handler.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            */

/* This file replaces the startup code! Make sure the object file
 * corresponding to this source is linked into the head of the final
 * executeable.
 */

#include "CFS.h"

#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/execbase.h>

#include <clib/alib_protos.h>

#include <string.h>
#include <stdarg.h>

#include "dossupport.h"

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

char Version[]   = "$VER: XFH-Handler " VERSION " " __AMIGADATE__ "";
char Copyright[] = "$COPYRIGHT: Copyright (C) 1991, 1992, 1993, 1994 Kristian Nielsen";

/**************************************************************************
 *                                                                        *
 * Handler entry point.                                                   *
 *                                                                        *
 * Do initialisation and allocation, then enter main loop after returning *
 *    our initial packet.                                                 *
 *                                                                        *
 **************************************************************************/

LONG __saveds NotReallyMain(void)

{
 glb glob;
 struct DosPacket *pkt;
 int ret=0;
 struct FileLock *parent;
 void handle_packet(glb glob, struct DosPacket *pkt);
   
 SysBase=*(struct ExecBase **)4L;
 if ((glob=AllocMem(sizeof(struct glob),MEMF_CLEAR))==NULL) return(100);

 glob->ioerr=ERROR_ACTION_NOT_KNOWN;  /* Default error code. */

 debug (("XFH/CFS compressing file system handler v" VERSION ".\n"
        "Copyright (C) 1991,1992,1993 Kristian Nielsen.\n"));
 debug (("Starting handler with task at 0x%lx.\n",FindTask(0L)));

 glob->dosport=&(glob->myproc=(struct Process *)(glob->mytask=FindTask(0L)))->pr_MsgPort;

 pkt=getpkt(glob);
 glob->devnode=(struct DeviceNode *)b2c(pkt->dp_Arg3);
 if (glob->devnode->dn_Name==NULL)
  {
   debug (("Error: NULL device name - exiting.\n"));
   glob->ioerr=ERROR_INVALID_COMPONENT_NAME;
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   goto exitthis;
  }

 if ((glob->devname=copybstr(glob->devnode->dn_Name))==NULL)
  {
   debug (("Error copying device name - exiting.\n"));
   OUTOFMEM;
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   goto exitthis;
  }
 if (glob->devname[0]=='\0')
  {
   debug (("Error: Empty device name - exiting.\n"));
   glob->ioerr=ERROR_INVALID_COMPONENT_NAME;
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   goto exitthis;
  }

 debug (("Using device name '%s'.\n",glob->devname));

 glob->bcplstartup=glob->devnode->dn_Startup;
   
 if ((glob->ioport=CreatePort(NULL,0L))==NULL)
  {
   OUTOFMEM;
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error creating message port - exiting.\n"));
   goto exitthis;
  }

 if ((glob->DOSBase=OpenLibrary("dos.library",0L))==NULL)
  {
   glob->ioerr=ERROR_INVALID_RESIDENT_LIBRARY;
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   debug (("Error opening dos.library - exiting.\n"));
   ret=100;
   goto exitthis;
  }
   DOSBase=glob->DOSBase;

 /* Set primary options (from S:.xfhrc.<unit>, if available). */
 if (!InitOptions(glob))
  {
   returnpkt (pkt,DOSFALSE,ERROR_BAD_TEMPLATE,glob);
   debug (("Error setting primary options - exiting.\n"));
   ret=100;
   goto exitthis;
  }

 if (!InitXpk(glob))
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   debug (("Error initialising Xpk routines - exiting.\n"));
   ret=100;
   goto exitthis;
  }

 debug (("Using '%s' as root directory...\n",glob->xRootName));
 glob->xprocid=DoDeviceProc( (LONG *)&glob->xrootlock,glob->xRootName,glob);
 if (!glob->xprocid)
  {
   glob->xrootlock=0L;                      /* Don't xUnLock() */
   returnpkt (pkt,DOSFALSE,ERROR_OBJECT_NOT_FOUND,glob);
   ret=100;
   debug (("Error doing DeviceProc(%s) - exiting.\n",glob->xRootName));
   goto exitthis;
  }
 glob->xrootlock=b2c(glob->xrootlock);
 debug (("DeviceProc(%s) returned: %lx %lx\n",glob->xRootName,
         glob->xprocid,glob->xrootlock));
 glob->xrootlock=xLock(glob,glob->xrootlock,glob->xRootName,ACCESS_READ);
 if (glob->xrootlock==NULL)
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error obtaining xrootlock: %ld - exiting.\n",glob->ioerr,
           glob->xRootName));
   goto exitthis;
  }
 debug (("Obtained xrootlock: %lx\n",glob->xrootlock ));

 if (!SetOptionsFromFile(glob,glob->xrootlock,ALTOPTIONPATH))
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error setting secondary options.\n"));
   goto exitthis;
  }

 if (!xInfo(glob, glob->xrootlock,&glob->infodata))
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error obtaining disk info: %ld\n",glob->ioerr));
   goto exitthis;
  }
 glob->bytesperblock=glob->infodata.id_BytesPerBlock;
   
 if (parent=xParentDir(glob,glob->xrootlock)) xUnLock(glob,parent);

 if (!xExamine(glob,glob->xrootlock,&glob->fib1))
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error finding name for volume: %ld\n",glob->ioerr));
   goto exitthis;
  }
   
 if (!(glob->rootlock=makerootlockdayone(glob)))
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error creating rootlock: %ld - exiting.\n",glob->ioerr));
   goto exitthis;
  }
   
 if (!createvolnode(glob, parent == NULL, &glob->fib1))
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error creating volume node - exiting.\n"));
   goto exitthis;
  }

 if (!InitArexx(glob))
  {
   returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
   ret=100;
   debug (("Error initialising AREXX stuff.\n"));
   goto exitthis;
  }

 if (!UpdateXFHNode(glob))
  {
   debug (("Error initialising XFHNode.\n"));
   /* Carry on... */
  }
   
 /* Handler opened succesfully. Patch in task address so that we will
 * stay in control for all future references.
 */
 glob->devnode->dn_Task=glob->dosport;
      
 debug (("1: Packet type received: %lx\n",pkt->dp_Type));
 returnpkt (pkt,DOSTRUE,0L,glob);

 glob->opencnt=0;        /* Just for good measure... */
 /* Ready to go for the main loop.
  * Lets pray that we get no new messages before the initial ReplyPkt()
  * (otherwise we may loose the signal for the message port).
  */
 for (glob->done=FALSE; !glob->done;)
  {
   ULONG signals=Wait(arexxsigmask(glob)|getpktsigmask(glob)|guisigmask(glob));

   if (signals&arexxsigmask(glob)) checkarexxmsg(glob);
   if (signals&guisigmask(glob))
    if (!UpdateXFHNode(glob))
     debug (("Error during UpdateXFHNode().\n"));

   if (signals&getpktsigmask(glob))
    while (!glob->done&&(pkt=checkpkt(glob)))
     handle_packet (glob,pkt);
  }
      
exitthis:
 debug (("CFS exiting...\n"));
 CleanupArexx (glob);
 if (glob->volnode) freevolnode (glob);
 if (glob->rootlock) CFSUnLock (glob,glob->rootlock);
 CleanupXpk (glob);
 CleanupOptions (glob);
 if (glob->ioport) DeletePort (glob->ioport);
 if (glob->devname) freestr (glob->devname);
   
/*
 * Unload the code. This is mostly to serve the ACTION_DIE packet.
 * Useful for debugging (otherwise countless copies might end up in memory.
 * It is rather ugly, however, and propably not really useful.
 */
 Forbid();
 UnLoadSeg (glob->devnode->dn_SegList);
 glob->devnode->dn_SegList=NULL;
   
 if (glob->DOSBase) CloseLibrary(glob->DOSBase);
 FreeMem (glob,sizeof(struct glob));

 return ret;
}

#ifndef ACTION_GET_DISK_FSSM
#define ACTION_GET_DISK_FSSM 4201
#endif

void handle_packet(glb glob,struct DosPacket *pkt)

{
 glob->ioerr=0L;
 switch(pkt->dp_Type)
  {
   case ACTION_LOCATE_OBJECT:
    {
     struct FileLock *mylock;
     struct CFSLock *mycfslock;
         
     debug (("ACTION_LOCATE_OBJECT(%lx,\"%s\",%ld)",
             pkt->dp_Arg1,
             bstr2c(pkt->dp_Arg2,glob->debugbuf1),
             pkt->dp_Arg3));
     mylock=b2c(pkt->dp_Arg1);
     mycfslock=mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock;
     mycfslock=CFSLock(glob, 
                       mycfslock,
                       bstr2c(pkt->dp_Arg2,glob->pktstringbuf),
                       pkt->dp_Arg3);
     if (mycfslock)
      if (mylock=CreateFileLock(glob,mycfslock)) glob->opencnt++;
      else
       {
        LONG saveioerr;

        saveioerr=glob->ioerr;
        CFSUnLock(glob, mycfslock);
        glob->ioerr=saveioerr;
       }
     else mylock=NULL;

     debug (("=%lx\n",c2b(mylock)));
     returnpkt (pkt,c2b(mylock),glob->ioerr,glob);
     break;
    }

   case ACTION_COPY_DIR:
    {
     struct FileLock *mylock;
     struct CFSLock *mycfslock;
        
     debug (("ACTION_COPY_DIR(%lx)",pkt->dp_Arg1));
     mylock=b2c(pkt->dp_Arg1);
     mycfslock=mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock;
     if (mycfslock=CFSDupLock(glob,mycfslock))
      {
       if (mylock=CreateFileLock(glob, mycfslock )) glob->opencnt++;
       else
        {
         LONG saveioerr;

         saveioerr=glob->ioerr;
         CFSUnLock(glob,mycfslock);
         glob->ioerr=saveioerr;
        }
      }
     else mylock=NULL;

     debug (("=%lx\n",c2b(mylock)));
     returnpkt (pkt, c2b(mylock), glob->ioerr, glob);
     break;
    }

   case ACTION_PARENT:
    {
     struct FileLock *mylock;
     struct CFSLock *mycfslock;

     debug (("ACTION_PARENT(%lx)",pkt->dp_Arg1));
     mylock=b2c(pkt->dp_Arg1);
     mycfslock=mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock;
     if (mycfslock=CFSParentDir(glob,mycfslock))
      {
       if (mylock=CreateFileLock(glob,mycfslock)) glob->opencnt++;
       else
        {
         LONG saveioerr;

         saveioerr=glob->ioerr;
         CFSUnLock(glob, mycfslock);
         glob->ioerr=saveioerr;
        }
      }
     else mylock=NULL;

     debug (("=%lx\n",c2b(mylock)));
     returnpkt (pkt, c2b(mylock), glob->ioerr, glob);
     break;
    }

   case ACTION_FREE_LOCK:
    {
     BOOL err;
     struct FileLock *mylock;
       
     debug (("ACTION_FREE_LOCK(%lx)\n",pkt->dp_Arg1));
     mylock=b2c(pkt->dp_Arg1);
     if (mylock)
      {
       err=CFSUnLock(glob,(void *)mylock->fl_Key);
       if (err)
        {
         dfree(mylock);
         glob->opencnt--;
        }
      }
     else err=ERROR_OBJECT_WRONG_TYPE;

     returnpkt (pkt,err,glob->ioerr,glob);
     break;
    }

   /* ToDo: Handle NULL locks correct (but what is correct?)
    * Now, a NULL lock is equal to out root lock.
    */
   case ACTION_SAME_LOCK:
    {
     struct FileLock *fl1,*fl2;
     BOOL res;
       
     fl1=b2c(pkt->dp_Arg1);
     fl2=b2c(pkt->dp_Arg2);
     debug (("ACTION_SAME_LOCK(%lx,%lx)",fl1,fl2));
     /* ToDo: This bit tests that both locks belong to us - this
      * is probably bogus.
      */
     if ((fl1!=NULL)&&(fl1->fl_Task!=glob->dosport)) res=FALSE;
     else
      if ((fl2!=NULL)&&(fl2->fl_Task!=glob->dosport)) res=FALSE;
      else
       {
        struct CFSLock *f1,*f2;

        f1=fl1?(struct CFSLock *)fl1->fl_Key:glob->rootlock;
        f2=fl2?(struct CFSLock *)fl2->fl_Key:glob->rootlock;
        res=CFSSameLock(glob,f1,f2);
       }

     debug (("=%ld\n",res));
     /* ToDo: DOCS mention error code in case of zero return.
      * Not currently implemented.
      */
     returnpkt (pkt,res,glob->ioerr,glob);
     break;
    }

   case ACTION_FINDINPUT:   /* (filehandle, lock, name)->BOOL */
   case ACTION_FINDOUTPUT:
   case ACTION_FINDUPDATE:
    {
     struct FileHandle *fh;
     struct CFSFH *cfsfh;
     char *name;
     struct FileLock *parentlock;
     struct CFSLock *cfsparentlock;
      
     debug (("%s(%lx,%lx,\"%s\")\n",
            pkt->dp_Type==ACTION_FINDINPUT ?"ACTION_FINDINPUT": 
            pkt->dp_Type==ACTION_FINDOUTPUT?"ACTION_FINDOUTPUT":
                                            "ACTION_FINDUPDATE",
            pkt->dp_Arg1,pkt->dp_Arg2,
            bstr2c(pkt->dp_Arg3,glob->debugbuf1)));
      
     fh=b2c(pkt->dp_Arg1);
     parentlock=b2c(pkt->dp_Arg2);
     cfsparentlock=parentlock?(struct CFSLock *)parentlock->fl_Key:glob->rootlock;
     name=bstr2c(pkt->dp_Arg3, glob->pktstringbuf);
     cfsfh=CFSOpen(glob,cfsparentlock,name,pkt->dp_Type);
     debug (("(=%lx)\n",cfsfh));
     if (cfsfh)
      {
       glob->opencnt++;
       fh->fh_Arg1=c2b(cfsfh);
       returnpkt (pkt,DOSTRUE,glob->ioerr,glob);
      }
     else returnpkt (pkt,DOSFALSE,glob->ioerr,glob);
     break;
    }

   case ACTION_READ:    /* (fh->arg1, buf, len)->actlen */
    {
     struct CFSFH *cfsfh;
     LONG len;
     void *buf;
         
     debug (("ACTION_READ(%lx,%lx,%ld)\n",
             pkt->dp_Arg1,pkt->dp_Arg2,pkt->dp_Arg3));
     cfsfh=b2c(pkt->dp_Arg1);
     buf=(void *)pkt->dp_Arg2;
     len=pkt->dp_Arg3;
     len=CFSRead(glob, cfsfh, buf, len);
     returnpkt (pkt, len, glob->ioerr, glob);
     break;
    }

   case ACTION_WRITE:    /* (fh->arg1, buf, len)->actlen */
    {
     struct CFSFH *cfsfh;
     LONG len;
     void *buf;
         
     debug (("ACTION_WRITE(%lx,%lx,%ld)\n",
             pkt->dp_Arg1,pkt->dp_Arg2,pkt->dp_Arg3));
     cfsfh=b2c(pkt->dp_Arg1);
     buf=(void *)pkt->dp_Arg2;
     len=pkt->dp_Arg3;
     len=CFSWrite(glob, cfsfh, buf, len);
     debug (("ACTION_WRITE returns %ld %ld.\n",len, glob->ioerr));
     returnpkt (pkt, len, glob->ioerr, glob);
     break;
    }

   case ACTION_SEEK:    /* (fh->arg1, pos, offset)->actlen */
    {
     struct CFSFH *cfsfh;
     LONG pos;
     LONG offset;
         
     debug (("ACTION_SEEK(%lx,%lx,%ld)\n",
             pkt->dp_Arg1,pkt->dp_Arg2,pkt->dp_Arg3));
     cfsfh=b2c(pkt->dp_Arg1);
     pos=pkt->dp_Arg2;
     offset=pkt->dp_Arg3;
     pos=CFSSeek(glob, cfsfh, pos, offset);
     returnpkt (pkt, pos, glob->ioerr, glob);
     break;
    }

   case ACTION_END:     /* (fh->arg1) -> BOOL */
    {
     struct CFSFH *cfsfh;
     BOOL res;
         
     cfsfh=b2c(pkt->dp_Arg1);
     debug (("ACTION_END(%lx)\n",cfsfh));
     res=CFSClose( glob, cfsfh );
     if (res ) glob->opencnt--;
     debug (("Closing file - still %ld files open.\n",glob->opencnt));
     returnpkt (pkt,res,pkt->dp_Res2,glob);
     break;
    }

   case ACTION_DIE:
    debug (("ACTION_DIE()\n"));
    Forbid();
    if (!glob->opencnt)
     {
      glob->done=TRUE;
      returnpkt (pkt,DOSTRUE,pkt->dp_Res2,glob);
      glob->devnode->dn_Task=NULL;
      debug (("No open files - Handler exiting.\n"));
     }
    else
     {
      returnpkt (pkt,DOSFALSE,ERROR_OBJECT_IN_USE,glob);
      debug (("Cannot end yet - still %ld open files.\n",glob->opencnt));
     }
    Permit();
    break;

   case ACTION_EXAMINE_OBJECT:
    {
     struct FileInfoBlock *fib;
     struct FileLock *mylock;
     BOOL err;
         
     debug (("ACTION_EXAMINE_OBJECT(%lx,%lx)\n",pkt->dp_Arg1,pkt->dp_Arg2));
     mylock=b2c(pkt->dp_Arg1);
     fib=b2c(pkt->dp_Arg2);
     err=CFSExamine(glob,mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,fib);
     debug (("NAME: %s DirEntryType: %ld err: %ld\n",
            fib->fib_FileName, fib->fib_DirEntryType,err));
     cstr2binplace(&fib->fib_FileName[0]);
     cstr2binplace(&fib->fib_Comment[0]);
     returnpkt (pkt,err,glob->ioerr,glob);
     break;
    }

   case ACTION_EXAMINE_NEXT:
    {
     struct FileInfoBlock *fib;
     struct FileLock *mylock;
     BOOL err;
         
     debug(("ACTION_EXAMINE_NEXT(%lx,%lx)\n",pkt->dp_Arg1,pkt->dp_Arg2));
     mylock=b2c(pkt->dp_Arg1);
     fib=b2c(pkt->dp_Arg2);
     bstr2cinplace(&fib->fib_FileName[0]);
     safebstr2cinplace(&fib->fib_Comment[0],80);
     err=CFSExamineNext(glob, 
                        mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                        fib);
     debug (("NAME: %s DirEntryType: %ld err: %ld\n",
              fib->fib_FileName, fib->fib_DirEntryType,err));
     cstr2binplace(&fib->fib_FileName[0]);
     cstr2binplace(&fib->fib_Comment[0]);
     returnpkt (pkt,err,glob->ioerr,glob);
     break;
    }

   case ACTION_CREATE_DIR:
    { /* (parentlock, name) -> lock */
     struct FileLock *mylock;
     struct CFSLock *lock;

     debug (("ACTION_CREATE_DIR(%lx,\"%s\") ",
             pkt->dp_Arg1,bstr2c(pkt->dp_Arg2,glob->debugbuf1)));
     mylock=b2c(pkt->dp_Arg1);
     lock=CFSCreateDir(glob, 
                       mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                       bstr2c(pkt->dp_Arg2,glob->pktstringbuf));
     if (lock)
      {
       if (mylock=CreateFileLock(glob,lock)) glob->opencnt++;
       else
        {
         LONG saveioerr;

         saveioerr=glob->ioerr;
         CFSUnLock (glob,lock);
         glob->ioerr=saveioerr;
        }
      }
     else mylock=NULL;

     debug (("= %lx\n",mylock));
     returnpkt (pkt, c2b(mylock), glob->ioerr, glob);
     break;
    }

   case ACTION_DELETE_OBJECT:
    { /* (parentlock, name) -> succes */
     struct FileLock *mylock;
     BOOL err;

     debug (("ACTION_DELETE_OBJECT(%lx,\"%s\")\n", pkt->dp_Arg1,
             bstr2c(pkt->dp_Arg2,glob->debugbuf1)));
     mylock=b2c(pkt->dp_Arg1);
     err=CFSDeleteFile(glob,
                       mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                       bstr2c(pkt->dp_Arg2,glob->pktstringbuf));
     if (!err) debug (("Error deleting file!\n"));
     returnpkt (pkt, err, glob->ioerr, glob);
     break;
    }

   case ACTION_RENAME_OBJECT:
    { /* (lock1,name1,lock2,name2) -> succes */
     struct FileLock *mylock1, *mylock2;
     BOOL err;

     /* ToDo: Check that both locks belong to us? */
     debug (("ACTION_RENAME_OBJECT(%lx,\"%s\", %lx,\"%s\")\n",pkt->dp_Arg1,
             bstr2c(pkt->dp_Arg2,glob->debugbuf1),
             pkt->dp_Arg3,
             bstr2c(pkt->dp_Arg4,glob->debugbuf2)));
     mylock1=b2c(pkt->dp_Arg1);
     mylock2=b2c(pkt->dp_Arg3);

     err=CFSRename(glob,
                   mylock1?(struct CFSLock *)mylock1->fl_Key:glob->rootlock,
                   bstr2c(pkt->dp_Arg2,glob->pktstringbuf),
                   mylock2?(struct CFSLock *)mylock2->fl_Key:glob->rootlock,
                   bstr2c(pkt->dp_Arg4,glob->pktstringbuf2));
     if (!err) debug (("Error renaming file!\n"));
     returnpkt (pkt, err, glob->ioerr, glob);
     break;
    }

   case ACTION_SET_PROTECT:
    { /* (dummy, parentlock, name, bits) -> succes */
     struct FileLock *mylock;
     BOOL err;

     debug (("ACTION_SET_PROTECT([%lx] %lx,\"%s\",%lx)\n",
             pkt->dp_Arg1,pkt->dp_Arg2,
             bstr2c(pkt->dp_Arg3,glob->debugbuf1),
             pkt->dp_Arg4));
     mylock=b2c(pkt->dp_Arg2);
     err=CFSSetProtection(glob,
                          mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                          bstr2c(pkt->dp_Arg3,glob->pktstringbuf),
                          pkt->dp_Arg4);

     if (!err) debug (("Error changing protection bits!\n"));
     returnpkt (pkt,err,glob->ioerr,glob);
     break;
    }

   case ACTION_SET_COMMENT:
    { /* (dummy, parentlock, name, comment) -> succes */
     struct FileLock *mylock;
     BOOL err;

     debug (("ACTION_SET_COMMENT([%lx] %lx,\"%s\",\"%s\")\n",
             pkt->dp_Arg1,pkt->dp_Arg2,
             bstr2c(pkt->dp_Arg3,glob->debugbuf1),
             bstr2c(pkt->dp_Arg4,glob->debugbuf2)));
     mylock=b2c(pkt->dp_Arg2);
     err=CFSSetComment(glob,
                       mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                       bstr2c(pkt->dp_Arg3,glob->pktstringbuf),
                       bstr2c(pkt->dp_Arg4,glob->pktstringbuf2));
     if (!err) debug (("Error changing file comment!\n"));
     returnpkt (pkt,err,glob->ioerr,glob);
     break;
    }

   case ACTION_SET_DATE:
    { /* (dummy, parentlock, name, datestamp) -> succes */
     struct FileLock *mylock;
     BOOL err;

     debug (("ACTION_SET_DATE([%lx] %lx,\"%s\",%ld,%ld,%ld)\n",
             pkt->dp_Arg1,pkt->dp_Arg2,
             bstr2c(pkt->dp_Arg3,glob->debugbuf1),
             ((struct DateStamp *)(pkt->dp_Arg4))->ds_Days,
             ((struct DateStamp *)(pkt->dp_Arg4))->ds_Minute,
             ((struct DateStamp *)(pkt->dp_Arg4))->ds_Tick));
     mylock=b2c(pkt->dp_Arg2);
     err=CFSSetDate(glob,
                    mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                    bstr2c(pkt->dp_Arg3,glob->pktstringbuf),
                    (struct DateStamp *)pkt->dp_Arg4);
     if (!err) debug (("Error changing objects date!\n"));
     returnpkt (pkt, err, glob->ioerr, glob);
     break;
    }

   case ACTION_DISK_INFO:
    /* (infodata)=BOOL */
    debug (("ACTION_DISK_INFO(%lx)\n",pkt->dp_Arg1));
    returnpkt (pkt,diskinfo(glob,b2c(pkt->dp_Arg1)),0L,glob);
    break;

   case ACTION_INFO:
    /* (lock,infodata)=BOOL */
    debug (("ACTION_INFO(%lx,%lx)\n",pkt->dp_Arg1,pkt->dp_Arg2));
    returnpkt (pkt,diskinfo(glob,b2c(pkt->dp_Arg2)),0L,glob);
    break;

   case ACTION_RENAME_DISK:
    {  /* (BCPLNAME)=BOOL */
     BOOL res;

     debug (("ACTION_RENAME_DISK(%s)\n",
             bstr2c(pkt->dp_Arg1,glob->debugbuf1)));
     res=SetOptionPermanent(glob, "VOLUMENAME",
                            bstr2c(pkt->dp_Arg1,glob->pktstringbuf));
     if (!res) debug (("Error during relabel: %ld.\n",glob->ioerr));
     returnpkt (pkt,res,glob->ioerr,glob);
     break;
    }

   case ACTION_IS_FILESYSTEM:
    debug (("ACTION_IS_FILESYSTEM\n"));
    returnpkt (pkt,DOSTRUE,0L,glob);
    break;

   case ACTION_PARENT_FH:
    {
     struct FileLock *mylock;
     struct CFSLock *mycfslock;
         
     debug (("ACTION_PARENT_FH(%lx)",pkt->dp_Arg1));
     if (mycfslock=CFSParentFH(glob,b2c(pkt->dp_Arg1)))
      if (mylock=CreateFileLock(glob, mycfslock )) glob->opencnt++;
      else
       {
        LONG saveioerr;

        saveioerr=glob->ioerr;
        CFSUnLock (glob,mycfslock);
        glob->ioerr=saveioerr;
        }
      else mylock=NULL;

     debug (("=%lx\n",c2b(mylock)));
     returnpkt (pkt, c2b(mylock), glob->ioerr, glob);
     break;
    }

   case ACTION_EXAMINE_FH:
    {
     struct FileInfoBlock *fib;
     struct CFSFH *fh;
     BOOL err;
         
     debug (("ACTION_EXAMINE_FH(%lx,%lx)\n",pkt->dp_Arg1,pkt->dp_Arg2));
     fh=b2c(pkt->dp_Arg1);
     fib=b2c(pkt->dp_Arg2);
     err=CFSExamineFH(glob, fh, fib);
     debug (("NAME: %s DirEntryType: %ld err: %ld\n",
             fib->fib_FileName, fib->fib_DirEntryType,err));
     cstr2binplace (&fib->fib_FileName[0]);
     cstr2binplace (&fib->fib_Comment[0]);
     returnpkt (pkt,err,glob->ioerr,glob);
     break;
    }

   case ACTION_MAKE_LINK:
    {
     struct FileLock *mylock;
     BOOL succ;

     debug (("ACTION_MAKE_LINK(%lx,\"%s\",%lx,%ld)\n",
             pkt->dp_Arg1,bstr2c(pkt->dp_Arg2,glob->debugbuf1),
             pkt->dp_Arg3,pkt->dp_Arg4));

     mylock=b2c(pkt->dp_Arg1);
     succ=CFSMakeLink(glob,
                      mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                      bstr2c(pkt->dp_Arg2,glob->pktstringbuf),
                      pkt->dp_Arg3,pkt->dp_Arg4);

#ifdef DEBUG
     if (!succ) debug (("Error making link!\n"));
#endif

     returnpkt (pkt,succ,glob->ioerr,glob);
     break;
    }

   case ACTION_READ_LINK:
    {
     struct FileLock *mylock;
     BOOL succ;

     debug (("ACTION_READ_LINK(%lx,\"%s\")\n",pkt->dp_Arg1,pkt->dp_Arg2));

     mylock=b2c(pkt->dp_Arg1);
     succ=CFSReadLink(glob,
                      mylock?(struct CFSLock *)mylock->fl_Key:glob->rootlock,
                      (char *)pkt->dp_Arg2,(char *)pkt->dp_Arg3,pkt->dp_Arg4);

#ifdef DEBUG
     if (succ) debug (("ReadLink: \"%s\"\n",pkt->dp_Arg3));
#endif

     returnpkt (pkt,succ,glob->ioerr,glob);
     break;
    }

   case ACTION_GET_DISK_FSSM:
    returnpkt (pkt,DOSFALSE,ERROR_OBJECT_WRONG_TYPE,glob);
    break;

   default:
    debug (("Unknown packet received: %ld\n",pkt->dp_Type));
    returnpkt (pkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN,glob);
  }
}

struct DosPacket *getpkt(glb glob)

{
 struct Message *msg;
   
 while (!(msg=GetMsg(glob->dosport))) WaitPort(glob->dosport);
 return (struct DosPacket *)msg->mn_Node.ln_Name;
}


ULONG getpktsigmask(glb glob)

{
 return (ULONG)(1L<<glob->dosport->mp_SigBit);
}


struct DosPacket *checkpkt(glb glob)

{
 struct Message *msg;
   
 if (msg=GetMsg(glob->dosport)) return (struct DosPacket *)msg->mn_Node.ln_Name;
 else return NULL;
}


void returnpkt (struct DosPacket *pkt,LONG res1,LONG res2,glb glob)

{
 struct MsgPort *port;
 struct Message *msg;
   
 port=pkt->dp_Port;
 msg=pkt->dp_Link;

 msg->mn_Node.ln_Succ=msg->mn_Node.ln_Pred=NULL;
 msg->mn_Node.ln_Name=(char *)pkt;
   
 pkt->dp_Res1=res1;
 pkt->dp_Res2=res2;
 pkt->dp_Port=glob->dosport;
   
 PutMsg(port,msg);
}

struct devprocmsg

{
 struct Message msg;
 void (*func)();
 char *filedesc;
 struct MsgPort *procid;
 LONG res2;
};

void __asm CallDeviceProc(register __a0 struct devprocmsg *msg)

{
 msg->procid=DeviceProc(msg->filedesc);
 msg->res2=IoErr();
}

struct MsgPort *DoDeviceProc(LONG *res2,char *filedesc,glb glob)

{
 struct devprocmsg msg,*msg2;
 extern void DoDOSSeg();
 struct MsgPort *procid;
   
 msg.msg.mn_Node.ln_Succ=NULL;
 msg.msg.mn_Node.ln_Pred=NULL;
 msg.msg.mn_Node.ln_Name=NULL;
 msg.msg.mn_Node.ln_Type=NT_MESSAGE;
 msg.msg.mn_Node.ln_Pri=0;
 msg.msg.mn_ReplyPort=glob->ioport;
 msg.msg.mn_Length=sizeof(msg);
 msg.func=(void (*)())CallDeviceProc;
 msg.filedesc=filedesc;
   
 if (!(procid=CreateProc("DoDOS",glob->mytask->tc_Node.ln_Pri,
                         (BPTR)((ULONG)DoDOSSeg>>2),4000L))) return 0L;
   
 PutMsg(procid,(struct Message *)&msg);
 do WaitPort(glob->ioport);
 while (!(msg2=(struct devprocmsg *)GetMsg(glob->ioport)));

#ifdef DEBUG
 if (msg2!=&msg) KPrintF ("ERROR: bogus return message: &msg=%lx msg2=%lx\n",&msg,msg2);
#endif
 if (res2) *res2=msg2->res2;
 return msg2->procid;
}

/* DOS device list locking.
 * ToDo: Use correct 2.0 locking calls.
 */

static void MyLockDosList(glb glob)

{
 Forbid();
}

static void MyUnlockDosList(glb glob)

{
 Permit();
}


void addvolnode(glb glob,struct DeviceList *volnode)

{
 struct DosInfo *dosinfo;

 MyLockDosList(glob);
 dosinfo=b2c( ((struct RootNode *)DOSBase->dl_Root)->rn_Info);
 volnode->dl_Next=dosinfo->di_DevInfo;
 dosinfo->di_DevInfo=c2b(volnode);
 MyUnlockDosList(glob);
}

BOOL removevolnode(glb glob,struct DeviceList *volnode)

{
 struct DosInfo *dosinfo;
 BPTR *p;
   
 /* ToDo: check for 2.0 device list locking. */
 MyLockDosList(glob);
 dosinfo=b2c( ((struct RootNode *)DOSBase->dl_Root)->rn_Info);
 p=&dosinfo->di_DevInfo;
   
 while(*p)
  if (b2c(*p) == volnode)
   {
    *p=volnode->dl_Next;
    MyUnlockDosList(glob);
    return TRUE;
   }
  else p=&( ((struct DeviceList *)b2c(*p)) -> dl_Next);

 MyUnlockDosList(glob);
 /* Hey - we didn't find the node in the list! */
 debug (("WARNING!:removevolnode(): volume node not found.\n"));
 return FALSE;
}

/* Used to update the 'Startup' field in our device node (used in option
 * 'KILLSTARTUP').
 */

void DevNode_Stuff_Startup_String(glb glob,BSTR value)

{
 MyLockDosList(glob);
 glob->devnode->dn_Startup=value;
 MyUnlockDosList (glob);
}


/* NOTE: this function assumes properly dos device list locking!
 * This function will fail if 'name' is NULL.
 * NOTE: createvolnode(), below does this in it's own way.
 */
static BOOL stuffvolnamevolnode(glb glob,char *name)

{
 UBYTE *bcplname,*oldbcplname;

 /* NASTY BUG: It seems that the volume name is expected to be zero 
  * terminated even though it is a BSTR. Hence strlen()+2 in alloc. */
 /* Check if user specified a volume name. */

  if (name)
   {
    if (!(bcplname=dosalloc(strlen(name)+2)))
     {
      OUTOFMEM;
      return FALSE;
     }
    /* Free any old name. */
    oldbcplname=b2c(glob->volnode->dl_Name);
    if (oldbcplname) dosfree (oldbcplname);

   strcpy(bcplname,name);
   cstr2binplace(bcplname);
   glob->volnode->dl_Name=c2b(bcplname);
   debug (("Using user suplied volumename '%s'.\n",bcplname+1));
   return TRUE;
  }
 else
  {
   glob->ioerr=ERROR_INVALID_COMPONENT_NAME;
   return FALSE;
  }
}


/* This function creates our volumenode. The argument 'fixroot' is
 * a hack, telling whether the volumename should be changed to prevent
 * name-clashing with the volume name of the UFS.
 */
BOOL createvolnode(glb glob,BOOL fixroot,struct FileInfoBlock *fib)

{
 UBYTE *bcplname;

 if (!dalloc(glob->volnode)) return FALSE;

 /* NASTY BUG: It seems that the volume name is expected to be zero 
  * terminated even though it is a BSTR. Hence strlen()+2 in alloc. */
 /* Check if user specified a volume name. */

 if (glob->uservolname)
  {
   if (!(bcplname=dosalloc(strlen(glob->uservolname)+2)))
    {
     dfree(glob->volnode);
     return FALSE;
    }

   (void)strcpy(bcplname,glob->uservolname);
   debug (("Using user suplied volumename '%s'.\n",bcplname));
  }
 else
  {
   if (!(bcplname=dosalloc(strlen(fib->fib_FileName)+2+
                           (fixroot?strlen(XROOTNAME)+1:0))))
    {
     dfree(glob->volnode);
     return FALSE;
    }

   (void)strcat(strcpy(bcplname,fixroot?XROOTNAME "_":""),&fib->fib_FileName[0]);
   debug (("Using default volume name '%s'.\n",bcplname));
  }
   
 glob->volnode->dl_Type=DLT_VOLUME;
 glob->volnode->dl_Task=glob->dosport;
 glob->volnode->dl_Lock=c2b(NULL);
 glob->volnode->dl_VolumeDate=fib->fib_Date;
 glob->volnode->dl_LockList=c2b(NULL);
 glob->volnode->dl_DiskType=ID_DOS_DISK; /*Wonder what would be right*/
 glob->volnode->dl_unused=0L;
 cstr2binplace (bcplname);
 glob->volnode->dl_Name=c2b(bcplname);
   
 /* Check if user requested that we create a volume node. */
 /* if (glob->createvolnode) */
 addvolnode (glob,glob->volnode);

 return TRUE;
}

/* Set the volume name in the volnode. */
BOOL SetVolumeNameVolNode(glb glob,char *name)

{
 BOOL res;

 MyLockDosList(glob);
 res=stuffvolnamevolnode(glob,name);
 MyUnlockDosList(glob);
 return res;
}

BOOL freevolnode(glb glob)

{
/* Check if user requested that we create a volume node. */
/* if (glob->createvolnode)*/
 if (!removevolnode(glob,glob->volnode)) return FALSE;

 dosfree (b2c(glob->volnode->dl_Name));
 dfree (glob->volnode);

 return TRUE;
}


BOOL diskinfo(glb glob,struct InfoData *infodata)

{
 if (!xInfo(glob,glob->xrootlock,infodata))
  {
   debug (("Error: diskinfo(): xInfo() returned zero (%ld).\n",glob->ioerr));
   return DOSFALSE;
  }

/*   infodata->id_NumSoftErrors=0;*/
/*   infodata->id_UnitNumber=glob->fsstartup ?*/
/*                             glob->fsstartup->fssm_Unit : 0;*/
/*   infodata->id_DiskState=ID_VALIDATED;*/
/*   infodata->id_NumBlocks=1;*/
/*   infodata->id_NumBlocksUsed=1;*/
/*   infodata->id_BytesPerBlock=1;*/
/*   infodata->id_DiskType=ID_DOS_DISK;*/

 infodata->id_VolumeNode=c2b(glob->volnode);
 infodata->id_InUse=glob->opencnt?1:0;
 debug (("diskinfo(): U=%ld DS=%lx #=%ld #u=%ld #b=%ld IU=%ld.\n",
         infodata->id_UnitNumber,infodata->id_DiskState,infodata->id_NumBlocks,
         infodata->id_NumBlocksUsed,infodata->id_BytesPerBlock,
         infodata->id_InUse));

 return DOSTRUE;
}

UWORD PutChar[2] = {0x16C0,0x4E75};

/* dirty hack to avoid assembler part :-)

   16C0: move.b d0,(a3)+
   4E75: rts
*/

void SPrintF(char *Buffer,char *FormatString,...)

{
 RawDoFmt (FormatString,(APTR)((LONG *)&FormatString+1L),(void *)PutChar,Buffer);
}

/* End of CFS.c */
