/* CFS.c - main packet loop.
   Copyright (C) 1991, 1992, 1993 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.            */

/* $Log:	CFS.c,v $
 * Revision 1.2  93/01/11  18:00:14  Kristian
 * Added RCS keywords.
 * 
 */

/* 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 <libraries/filehandler.h>

#include <proto/exec.h>       /* These actually refer to 'inline/*.h' */
#include <proto/dos.h>	      /* However, 'proto' is used for compatibility. */

#ifdef __GNUC__
#include "my_alib_protos.h"	/* Because of bug in inline/exec.h */
#else
#include <clib/alib_protos.h>
#endif

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

#include <dossupport.h>

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

#ifdef __GNUC__
/* Make sure first address in module is jump to entry point. */
asm ("
	.text
	jmp pc@(_NotReallyMain-.+2)
     ");
#endif    /* __GNUC__ */

const char versiontag[] = "\0$VER: XFH-Handler " VERSION " (" DATE ")";
const char cporighttag[]
      = "\0$COPYRIGHT: Copyright (C) 1991, 1992, 1993 Kristian Nielsen";
const char RCSid[] = "$Id: CFS.c,v 1.2 93/01/11 18:00:14 Kristian Exp Locker: Kristian $";

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

int NotReallyMain(void){
   glb glob;
   struct DosPacket *pkt;
   int ret=0;
   struct FileLock *parent;
   static void handle_packet(glb glob, struct DosPacket *pkt);
   
   SysBase=(void *)*(ULONG *)4L;
   if(!(glob=AllocMem(sizeof(struct glob),MEMF_CLEAR))) return(100);
   glob->ioerr = ERROR_ACTION_NOT_KNOWN;  /* Default error code. */
#ifdef DEBUG
   opendebug();
   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)));
#endif

   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){
      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))){
      debug(("Error copying device name - exiting.\n"));
      OUTOFMEM;
      returnpkt(pkt,DOSFALSE,glob->ioerr,glob);
      ret=100;
      goto exitthis;
   }
   if(!glob->devname[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))){
      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))){
      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 ){
      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);
#ifdef DEBUG
   closedebug();
#endif
   
/*
 * 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);
}


static 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 ? (void *)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 ? (void *)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 ? (void *)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 && fl1->fl_Task != glob->dosport){
            res = FALSE;
         }else if(fl2 && fl2->fl_Task!=glob->dosport){
            res = FALSE;
         }else{
            struct CFSLock *f1,*f2;
            f1 = fl1 ? (void *)fl1->fl_Key : glob->rootlock;
            f2 = fl2 ? (void *)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 ? (void *)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 ? (void *)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 ? (void *)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 ? (void *)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 ? (void *)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 ? (void *)mylock1->fl_Key : glob->rootlock,
                  bstr2c(pkt->dp_Arg2,glob->pktstringbuf),
                  mylock2 ? (void *)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 ? (void *)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 ? (void *)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 ? (void *)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;
      }

      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)1 << 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;
};

#ifdef __GNUC__
void CallDeviceProc(void){
  register struct devprocmsg *rega0    asm("a0");
  struct devprocmsg *msg=rega0;
#else    /* __GNUC__ */
void __asm CallDeviceProc(register __a0 struct devprocmsg *msg){
#endif     /* __GNUC__ */

  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=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)
      dprintf("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;
      }
      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;
      }
      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;
      }
      strcpy(bcplname, fixroot ? XROOTNAME "_" : "");
      strcat(bcplname, &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;
}

/* End of CFS.c */
