home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / disk / amicdrom-1.11.lha / AmiCDROM-1.11 / device.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-05-19  |  41.7 KB  |  1,491 lines

  1. /* device.c:
  2.  *
  3.  * Handler for ISO-9660 (+ Rock Ridge) + HFS CDROM filing system.
  4.  * Based on DOSDEV V1.10 (2 Nov 87) by Matthew Dillon.
  5.  *
  6.  * ----------------------------------------------------------------------
  7.  * This code is (C) Copyright 1993,1994 by Frank Munkert.
  8.  * All rights reserved.
  9.  * This software may be freely distributed and redistributed for
  10.  * non-commercial purposes, provided this notice is included.
  11.  * ----------------------------------------------------------------------
  12.  * History:
  13.  *
  14.  * 17-May-94   fmu   New option MAYBELOWERCASE (=ML).
  15.  * 20-Apr-94   fmu   Improved implementation of ACTION_INHIBIT.
  16.  * 17-Apr-94   fmu   Fixed bug concerning TRACKDISK disk change recognition.
  17.  * 12-Apr-94   fmu   Adapted ACTION_CURRENT_VOLUME to new filehandle
  18.  *             management.
  19.  * 09-Apr-94   fmu   Volume management: locks and filehandles will not
  20.  *                   be forgotten if a CDROM is removed from the drive.
  21.  * 06-Feb-94   dmb   - Full support for ACTION_INHIBIT
  22.  *                   - Change Check_Disk() for trackdisk support
  23.  * 05-Jan-93   fmu   - Retry displaying CD-DA icon if WB is not open.
  24.  *                   - id_UnitNumber of InfoData set to SCSI unit number.
  25.  *                   - Added Make_FSSM().
  26.  * 01-Jan-93   fmu   Support for symbolic links on RockRidge disks.
  27.  * 11-Dec-93   fmu   - ACTION_FLUSH always returns DOSTRUE.
  28.  *                   - ISO volume names are mapped to lowercase if
  29.  *                     the option LOWERCASE has been selected.
  30.  * 26-Nov-93   fmu   Some packets are now handled even if no disk
  31.  *                   is inserted.
  32.  * 21-Nov-93   fmu   - User programmable diskchange check interval.
  33.  *                   - Better support for ACTION_INHIBIT.
  34.  *                   - Handles filenames with ';'.
  35.  * 15-Nov-93   fmu   Missing return value for 'handler' inserted.
  36.  * 14-Nov-93   fmu   Added ACTION_USER packet for 'cdcontrol' program.
  37.  * 15-Oct-93   fmu   Adapted to new VOLUME structure.
  38.  * 10-Oct-93   fmu   - Creates volume node for 'no DOS' disks.
  39.  *             - Volume node now contains the correct volume
  40.  *               creation date.
  41.  * 09-Oct-93   fmu   - New format for mountlist startup field.
  42.  *             - SAS/C support.
  43.  *             - Debug process assembly tag adapted to small
  44.  *               memory model.
  45.  *             - Get_Startup moved to file devsupp.c.
  46.  * 03-Oct-93   fmu   - New buffering options 'S' and 'C'.
  47.  *                   - Fixed bug in cdlock.
  48.  *             - Fixed bug in ACTION_CURRENT_VOLUME.
  49.  * 27-Sep-93   fmu   Added ACTION_SAME_LOCK
  50.  * 25-Sep-93   fmu   - Send 'disk inserted' / 'disk removed' event via
  51.  *                     input.device if disk has been changed.
  52.  *                   - Corrected bug in ACTION_DISK_INFO.
  53.  * 24-Sep-93   fmu   - Added fast memory option 'F'.
  54.  *                   - Added ACTION_IS_FILESYSTEM.
  55.  *                   - Added 'write protected' error for write actions.
  56.  *                   - Added ACTION_CURRENT_VOLUME.
  57.  *                   - Unload handler code after ACTION_DIE.
  58.  *                   - Immediately terminate program if called from CLI.
  59.  *                   - Added library version number.
  60.  *                   - Set volume label to "Unnamed" for disks without name.
  61.  * 16-Sep-93   fmu   Added code to detect whether a lock stems from the
  62.  *                   current volume or from another volume which has
  63.  *                   been removed from the drive.
  64.  */
  65.  
  66. /*
  67.  *  Debugging routines are disabled by simply attempting to open the
  68.  *  file "debugoff", turned on again with "debugon".  No prefix may be
  69.  *  attached to these names (you must be CD'd to TEST:).
  70.  *
  71.  *  See Documentation for a detailed discussion.
  72.  */
  73.  
  74. #include <stdlib.h>
  75. #include <string.h>
  76.  
  77. #if defined(_DCC) && !defined(REGISTERED)
  78. #define abs
  79. #endif
  80.  
  81. #include "device.h"
  82. #include "intui.h"
  83. #include "devsupp.h"
  84. #include "cdcontrol.h"
  85. #include "params.h"
  86. #include "rock.h"
  87.  
  88. /*
  89.  *  Since this code might be called several times in a row without being
  90.  *  unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!!  This also goes
  91.  *  for any global/static assignments that might be changed by running the
  92.  *  code.
  93.  */
  94.  
  95. PROC    *DosProc;     /* Our Process                        */
  96. DEVNODE *DosNode;     /* Our DOS node.. created by DOS for us            */
  97. DEVLIST *DevList;     /* Device List structure for our volume node       */
  98.  
  99. #if !defined(_DCC) || defined(REGISTERED)
  100. EXECLIB *SysBase;     /* EXEC library base                */
  101. #endif
  102. DOSLIB    *DOSBase;     /* DOS library base for debug process        */
  103. LIB    *UtilityBase; /* Utility library for miscellaneous tasks     */
  104.  
  105. CDROM   *g_cd;
  106. VOLUME  *g_volume;
  107. CDROM_OBJ *g_top_level_obj;
  108. char    *g_vol_name;
  109.  
  110. PORT *g_timer_mp;           /*  timer message port        */
  111. struct timerequest *g_timer_io; /*  timer i/o request        */
  112. ULONG g_timer_sigbit;
  113. ULONG g_dos_sigbit;
  114.  
  115. char    g_device[80];        /* SCSI device name */
  116. short    g_unit;            /* SCSI unit */
  117. short    g_use_rock_ridge;    /* Use Rock Ridge flag 'R' */
  118. short    g_maybe_map_to_lowercase;/* Conditional map to lower case flag 'ML' */
  119. short    g_map_to_lowercase;    /* Map to lower case flag 'L' */
  120. int     g_trackdisk;        /* Use trackdisk calls instead of SCSI-direct */
  121.  
  122. int    g_std_buffers;        /* Number of buffers for standard SCSI access */
  123. int    g_file_buffers;        /* Number of buffers for contiguous reads */
  124.  
  125. t_bool    g_show_version_numbers; /* Show version numbers */
  126.  
  127. int    g_scan_interval;    /* Time between successive diskchange checks */
  128.  
  129. t_bool    g_disk_inserted;    /* Is a disk inserted? */
  130.  
  131. char    g_play_cdda_command[80];/* Command invoked if appicon is activated */
  132.  
  133. int    g_inhibited;        /* Number of active INHIBIT(TRUE) packets */
  134.  
  135. struct MsgPort *DosTask;
  136.  
  137. #if !defined(NDEBUG) || defined(DEBUG_SECTORS)
  138.             /*    DEBUGGING            */
  139. PORT *Dbport;        /*    owned by the debug process    */
  140. PORT *Dback;        /*    owned by the DOS device driver    */
  141. short DBDisable;
  142. MSG DummyMsg;        /*    Dummy message that debug proc can use    */
  143. #endif
  144.  
  145. void *dosalloc(ulong);
  146. void dosfree (ulong *);
  147. void btos(LONG, char *);
  148. void *NextNode (NODE *);
  149. void *GetHead (LIST *);
  150. LOCK *cdlock(CDROM_OBJ *, int);
  151. void cdunlock (LOCK *);
  152. CDROM_OBJ *getlockfile (LONG);
  153. char *typetostr (int);
  154. void returnpacket(struct DosPacket *);
  155. int packetsqueued (void);
  156. int Check_For_Volume_Name_Prefix (char *);
  157. void Fill_FileInfoBlock (FIB *, CDROM_INFO *, VOLUME *);
  158. void Mount (void);
  159. void Unmount (int);
  160. int Mount_Check (void);
  161. void Check_Disk (void);
  162. void Send_Timer_Request (void);
  163. void Cleanup_Timer_Device (void);
  164. int Open_Timer_Device (void);
  165. void Send_Event (int);
  166. void Remove_Seglist (void);
  167. BPTR Make_FSSM (void);
  168.  
  169. /*
  170.  *  Don't call the entry point main().  This way, if you make a mistake
  171.  *  with the compile options you'll get a link error.
  172.  */
  173.  
  174. #if defined(LATTICE)
  175. int __saveds handler (void)
  176. #elif defined(_DCC) && !defined(REGISTERED)
  177. int _main (void)
  178. #elif defined(_DCC) && defined(REGISTERED)
  179. __geta4 int handler (void)
  180. #else
  181. int handler (void)
  182. #endif
  183. {
  184.     register PACKET *packet;
  185.     register short   error;
  186.     MSG     *msg;
  187.     ubyte   notdone = 1;
  188.     char    buf[256];
  189.     void    *tmp;
  190.     ULONG   signals;
  191.     ubyte   playing = FALSE;
  192.  
  193.     /*
  194.      *    Initialize all global variables. SysBase MUST be initialized before
  195.      *  we can make Exec calls. The DOS library is opened for the debug
  196.      *  process only.
  197.      */
  198.  
  199. #if !defined(_DCC) || defined(REGISTERED)
  200.     SysBase = *(EXECLIB **) 4L;
  201. #endif
  202.     DosProc = (PROC *) FindTask(NULL);
  203.     if (DosProc->pr_CLI)
  204.       return RETURN_FAIL;
  205.     DOSBase = (DOSLIB *) OpenLibrary ((UBYTE *) "dos.library",37);
  206.     UtilityBase = (LIB *) OpenLibrary ((UBYTE *) "utility.library",37);
  207.  
  208.     BUG2(DBDisable = 0;)                /*  Init. globals    */
  209.     BUG2(Dbport = Dback = NULL;)
  210.     DevList = NULL;
  211.     {
  212.     WaitPort(&DosProc->pr_MsgPort);     /*  Get Startup Packet    */
  213.     msg = GetMsg(&DosProc->pr_MsgPort);
  214.     packet = (PACKET *)msg->mn_Node.ln_Name;
  215.  
  216.     /*
  217.      *  Loading DosNode->dn_Task causes DOS *NOT* to startup a new
  218.      *  instance of the device driver for every reference.    E.G. if
  219.      *  you were writing a CON device you would want this field to
  220.      *  be NULL.
  221.      */
  222.  
  223.         DosNode = BTOC(packet->dp_Arg3);
  224.  
  225.     Init_Intui ();
  226.  
  227.     if (UtilityBase && DOSBase && Get_Startup (packet->dp_Arg2)) {
  228.         packet->dp_Res1 = DOS_TRUE;
  229.          packet->dp_Res2 = 0;
  230.  
  231.       /*
  232.        *  Set dn_Task field which tells DOS not to startup a new
  233.        *  process on every reference.
  234.        */
  235.  
  236.             DosNode->dn_Task = &DosProc->pr_MsgPort;
  237.         DosTask = DosNode->dn_Task;
  238.  
  239.       /*
  240.        *  Load dn_Startup field with a BPTR to a FileSysStartupMsg.
  241.        *  (According to the official documentation, this is not
  242.        *  required. Some debugging tools, however, depend on it.)
  243.        */
  244.         
  245.         DosNode->dn_Startup = Make_FSSM ();
  246.  
  247.     } else {                /*    couldn't open dos.library   */
  248.         packet->dp_Res1 = DOS_FALSE;
  249.         returnpacket(packet);
  250.         if (DOSBase) {
  251.           Close_Intui ();
  252.               if (UtilityBase)
  253.                 CloseLibrary (UtilityBase);
  254.           CloseLibrary ((struct Library *) DOSBase);
  255.         }
  256.         return 0;                /*    exit process            */
  257.     }
  258.     returnpacket(packet);
  259.     }
  260.  
  261.     g_inhibited = 0;
  262.  
  263.     g_disk_inserted = (Test_Unit_Ready (g_cd) ||
  264.                    Test_Unit_Ready (g_cd));
  265.  
  266.     /*
  267.      *    Initialize debugging code
  268.      */
  269.  
  270.     BUG2(dbinit();)
  271.  
  272.     BUG(dbprintf("%d std buffers, %d file buffers\n",
  273.              g_std_buffers, g_file_buffers);)
  274.  
  275.     g_timer_sigbit = 0;
  276.     if (g_scan_interval > 0) {
  277.       /* Initialize timer: */
  278.       if (Open_Timer_Device ())
  279.         Send_Timer_Request ();
  280.     }
  281.  
  282.     g_vol_name = dosalloc (128);
  283.  
  284.     /* Mount volume (if any disk is inserted): */
  285.     Mount ();
  286.  
  287.     g_dos_sigbit = 1L << DosProc->pr_MsgPort.mp_SigBit;
  288.  
  289.     /*
  290.      *    Here begins the endless loop, waiting for requests over our
  291.      *    message port and executing them.  Since requests are sent over
  292.      *    our message port, this precludes being able to call DOS functions
  293.      *    ourselves (that is why the debugging routines are a separate process)
  294.      */
  295.  
  296. top:
  297.     for (; notdone;) {
  298.     signals = Wait(g_dos_sigbit | g_timer_sigbit | g_app_sigbit);
  299.     if (signals & g_timer_sigbit) {
  300.       GetMsg (g_timer_mp);
  301.       if (!g_inhibited)
  302.         Check_Disk ();
  303.       Send_Timer_Request ();
  304.           if (g_retry_show_cdda_icon)
  305.         Show_CDDA_Icon ();
  306.     }
  307.     if (signals & g_app_sigbit) {
  308.       struct Message *msg;
  309.       while (msg = GetMsg (g_app_port)) {
  310.         ReplyMsg (msg);
  311.         if (g_play_cdda_command[0])
  312.           SystemTags ((UBYTE *) g_play_cdda_command,
  313.                   SYS_Input, Open ((UBYTE *) "NIL:", MODE_OLDFILE),
  314.                   SYS_Output, Open ((UBYTE *) "NIL:", MODE_NEWFILE),
  315.                   SYS_Asynch, TRUE,
  316.               TAG_END);
  317.         else {
  318.           int res;
  319.           if (playing)
  320.             res = Stop_Play_Audio (g_cd);
  321.           else
  322.             res = Start_Play_Audio (g_cd);
  323.               if (!res)
  324.             Display_Error ("Cannot perform play audio command!");
  325.           else
  326.             playing = !playing;
  327.         }
  328.       }
  329.     }
  330.     if (!(signals & g_dos_sigbit))
  331.       continue;
  332.     while (msg = GetMsg(&DosProc->pr_MsgPort)) {
  333.         packet = (PACKET *)msg->mn_Node.ln_Name;
  334.         packet->dp_Res1 = DOS_TRUE;
  335.         packet->dp_Res2 = 0;
  336.         error = 0;
  337. #ifndef NDEBUG
  338.         dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ",
  339.         packet->dp_Type,
  340.         packet->dp_Arg1, packet->dp_Arg2,
  341.         packet->dp_Arg3,
  342.         typetostr(packet->dp_Type)
  343.         );
  344. #endif
  345.  
  346.             if (g_inhibited) {
  347.           switch (packet->dp_Type) {
  348.           /* packets we will handle even if the handler is inhibited: */
  349.           case ACTION_DIE:
  350.           case ACTION_INHIBIT:
  351.           case ACTION_MORE_CACHE:
  352.           case ACTION_DISK_INFO:
  353.             break;
  354.           /* packets we cannot handle because the handler is inhibited: */
  355.           default:
  356.             packet->dp_Res1 = DOS_FALSE;
  357.         packet->dp_Res2 = ERROR_NOT_A_DOS_DISK;
  358.             BUG(dbprintf("ERR=%ld\n", (long) packet->dp_Res2);)
  359.             returnpacket(packet);
  360.                 continue;            
  361.           }
  362.         } else if (DevList == NULL) {
  363.           switch (packet->dp_Type) {
  364.           /* packets we will handle even if no disk is inserted: */
  365.           case ACTION_DIE:
  366.           case ACTION_USER:
  367.           case ACTION_IS_FILESYSTEM:
  368.           case ACTION_INHIBIT:
  369.           case ACTION_MORE_CACHE:
  370.           case ACTION_FLUSH:
  371.         break;
  372.           /* packets we cannot handle because no disk is inserted: */
  373.           default:
  374.             packet->dp_Res1 = DOS_FALSE;
  375.             packet->dp_Res2 = (g_disk_inserted ? ERROR_NOT_A_DOS_DISK :
  376.                    ERROR_NO_DISK);
  377.             BUG(dbprintf("ERR=%ld\n", (long) packet->dp_Res2);)
  378.             returnpacket(packet);
  379.                 continue;            
  380.           }
  381.         }
  382.  
  383.         switch(packet->dp_Type) {
  384.         case ACTION_DIE:        /*    attempt to die?             */
  385.         notdone = 0;        /*    try to die                */
  386.         break;
  387.         case ACTION_USER:        /*  Mode,Par1,Par2            Bool    */
  388.             error = Handle_Control_Packet (packet->dp_Arg1,
  389.                            packet->dp_Arg2,
  390.                            packet->dp_Arg3);
  391.             break;
  392.         case ACTION_FINDINPUT:  /*  FileHandle,Lock,Name        Bool    */
  393.         {
  394.             if (Mount_Check ()) {
  395.               CDROM_OBJ *obj;
  396.               CDROM_OBJ *parentdir = getlockfile(packet->dp_Arg2);
  397.               int       offs;
  398.  
  399.               if (parentdir->volume != g_volume) {
  400.                   /* old lock from another disk: */
  401.             error = ERROR_DEVICE_NOT_MOUNTED;
  402.             goto openbreak;
  403.               }
  404.               
  405.               btos(packet->dp_Arg3,buf);
  406.               BUG(dbprintf("'%s' ", buf);)
  407.               offs = Check_For_Volume_Name_Prefix (buf);
  408.               if (obj = Open_Object (parentdir, buf + offs)) {
  409.                 if (obj->symlink_f) {
  410.                 error = ERROR_IS_SOFT_LINK;
  411.                 goto openbreak;
  412.             }
  413.             if (obj->directory_f) {
  414.                 error = ERROR_OBJECT_WRONG_TYPE;
  415.                 goto openbreak;
  416.             }
  417.               } else {
  418.             if (iso_errno == ISOERR_ILLEGAL_NAME) {
  419.                 error = ERROR_INVALID_COMPONENT_NAME;
  420.                 goto openbreak;
  421.             } else if (iso_errno == ISOERR_NOT_FOUND)
  422.                   error = ERROR_OBJECT_NOT_FOUND;
  423.             else if (iso_errno == ISOERR_NO_MEMORY) {
  424.               error = ERROR_NO_FREE_STORE;
  425.               goto openbreak;
  426.             } else if (iso_errno == ISOERR_IS_SYMLINK) {
  427.               error = ERROR_IS_SOFT_LINK;
  428.               goto openbreak;
  429.             } else {
  430.               error = 333;
  431.               goto openbreak;
  432.             }
  433.               }
  434.               if (!error) {
  435.                 g_volume->file_handles++;
  436.               ((FH *)BTOC(packet->dp_Arg1))->fh_Arg1 = (long) obj;
  437.             Register_File_Handle (obj);
  438.               }
  439.             } else
  440.               error = ERROR_NO_DISK;
  441.         }
  442.           openbreak:
  443.         break;
  444.         case ACTION_READ:        /*     FHArg1,CPTRBuffer,Length   ActLength  */
  445.         {
  446.             CDROM_OBJ *obj = (CDROM_OBJ *) packet->dp_Arg1;
  447.             char      *ptr = (char *) packet->dp_Arg2;
  448.             long    length = packet->dp_Arg3;
  449.             int     actual;
  450.  
  451.             if (obj->volume != g_volume) {
  452.               /* old lock from another disk: */
  453.               error = ERROR_DEVICE_NOT_MOUNTED;
  454.               break;
  455.             }
  456.             actual = Read_From_File (obj, ptr, length);
  457.             packet->dp_Res1 = actual;
  458.         }
  459.         break;
  460.         case ACTION_END:        /*     FHArg1             Bool:TRUE  */
  461.         {
  462.             CDROM_OBJ *obj = (CDROM_OBJ *) packet->dp_Arg1;
  463.  
  464.             if (obj->volume != g_volume) {
  465.               /* old lock from another disk: */
  466.               error = ERROR_DEVICE_NOT_MOUNTED;
  467.               break;
  468.             }
  469.             Unregister_File_Handle (obj);
  470.             Close_Object (obj);
  471.             g_volume->file_handles--;
  472.         }
  473.         break;
  474.         case ACTION_SEEK:        /*     FHArg1,Position,Mode        OldPosition*/
  475.             {
  476.             CDROM_OBJ *obj = (CDROM_OBJ *) packet->dp_Arg1;
  477.             long offset = packet->dp_Arg2;
  478.             int mode = packet->dp_Arg3;
  479.             
  480.             if (obj->volume != g_volume) {
  481.               /* old lock from another disk: */
  482.               error = ERROR_DEVICE_NOT_MOUNTED;
  483.               break;
  484.             }
  485.             packet->dp_Res1 = obj->pos;
  486.             if (!Seek_Position (obj, offset, mode)) {
  487.               error = ERROR_SEEK_ERROR;
  488.               packet->dp_Res1 = -1;
  489.             }
  490.         }
  491.         break;
  492.         case ACTION_EXAMINE_NEXT: /*   Lock,Fib              Bool     */
  493.         {
  494.             FIB       *fib = BTOC (packet->dp_Arg2);
  495.             CDROM_OBJ *dir = getlockfile (packet->dp_Arg1);
  496.             CDROM_INFO info;
  497.  
  498.             if (dir->volume != g_volume) {
  499.               /* old lock from another disk: */
  500.               error = ERROR_DEVICE_NOT_MOUNTED;
  501.               break;
  502.             }
  503.             if (!dir->directory_f) {
  504.             error = ERROR_OBJECT_WRONG_TYPE;
  505.             break;
  506.             }
  507.             if (Examine_Next (dir, &info,
  508.                           (unsigned long *) &fib->fib_DiskKey)) {
  509.               error = 0;
  510.               Fill_FileInfoBlock (fib, &info, dir->volume);
  511.             } else {
  512.               error = ERROR_NO_MORE_ENTRIES;
  513.             }
  514.             break;
  515.         }
  516.         case ACTION_EXAMINE_OBJECT: /*   Lock,Fib            Bool       */
  517.         {
  518.             FIB *fib = BTOC (packet->dp_Arg2);
  519.                     CDROM_OBJ *obj = getlockfile (packet->dp_Arg1);
  520.             CDROM_INFO info;
  521.  
  522.             if (obj->volume != g_volume) {
  523.               /* old lock from another disk: */
  524.               error = ERROR_DEVICE_NOT_MOUNTED;
  525.               break;
  526.             }
  527.             fib->fib_DiskKey = 0;
  528.             error = 0;
  529.             if (!CDROM_Info (obj, &info))
  530.               error = -1;
  531.             else
  532.               Fill_FileInfoBlock (fib, &info, obj->volume);
  533.         }
  534.         break;
  535.         case ACTION_INFO:        /*    Lock, InfoData      Bool:TRUE    */
  536.         tmp = BTOC(packet->dp_Arg2);
  537.         error = -1;
  538.         /*  fall through    */
  539.         case ACTION_DISK_INFO:  /*    InfoData      Bool:TRUE    */
  540.         {
  541.           if (Mount_Check ()) {
  542.             register INFODATA *id;
  543.  
  544.             (error) ? (id = tmp) : (id = BTOC (packet->dp_Arg1));
  545.             error = 0;
  546.             memset (id, 0, sizeof(*id));
  547.             id->id_UnitNumber = g_unit;
  548.             id->id_VolumeNode = (long) CTOB (DevList);
  549.             id->id_InUse = 0;
  550.                 id->id_DiskState = ID_WRITE_PROTECTED;
  551.             if (g_inhibited) {
  552.               id->id_DiskType = 0x42555359 /* "BUSY" */;
  553.               id->id_NumBlocks     = 0;
  554.               id->id_BytesPerBlock = 0;
  555.             } else {
  556.               id->id_DiskType = ID_DOS_DISK;
  557.               id->id_NumBlocks     = Volume_Size (g_volume);
  558.               id->id_BytesPerBlock = Block_Size (g_volume);
  559.             }
  560.             id->id_NumBlocksUsed = id->id_NumBlocks;
  561.           }
  562.         }
  563.         break;
  564.         case ACTION_IS_FILESYSTEM:   /*  -                      Bool */
  565.           packet->dp_Res1 = DOSTRUE;
  566.           break;
  567.         case ACTION_PARENT:     /*     Lock                ParentLock */
  568.             {
  569.           if (Mount_Check ()) {
  570.             if (packet->dp_Arg1) {
  571.               CDROM_OBJ *obj = getlockfile (packet->dp_Arg1);
  572.               CDROM_OBJ *parent;
  573.           
  574.               if (obj->volume != g_volume) {
  575.                 /* old lock from another disk: */
  576.                 error = ERROR_DEVICE_NOT_MOUNTED;
  577.                 break;
  578.               }
  579.               if (Is_Top_Level_Object (obj)) {
  580.                 packet->dp_Res1 = packet->dp_Res2 = 0;
  581.               } else {
  582.                 parent = Find_Parent (obj);
  583.                 if (!parent) {
  584.                   if (iso_errno == ISOERR_NO_MEMORY)
  585.                     error = ERROR_NO_FREE_STORE;
  586.                   else
  587.                     error = ERROR_OBJECT_NOT_FOUND;
  588.                 } else {
  589.                   packet->dp_Res1 = (long)
  590.                 CTOB (cdlock (parent, ACCESS_READ));
  591.                 }
  592.               }
  593.             } else
  594.               error = ERROR_OBJECT_NOT_FOUND;
  595.           } else
  596.             error = ERROR_NO_DISK;
  597.         }
  598.         break;
  599.         case ACTION_LOCATE_OBJECT:    /*   Lock,Name,Mode        Lock       */
  600.         {
  601.           if (Mount_Check ()) {
  602.             CDROM_OBJ *parentdir = getlockfile (packet->dp_Arg1);
  603.                     CDROM_OBJ *obj;
  604.             int offs;
  605.  
  606.             if (parentdir->volume != g_volume) {
  607.               /* old lock from another disk: */
  608.               error = ERROR_DEVICE_NOT_MOUNTED;
  609.               break;
  610.             }
  611.             btos (packet->dp_Arg2, buf);
  612. #ifndef NDEBUG
  613.             dbprintf ("'%s' %ld ", buf, packet->dp_Arg3);
  614.             if (strcmp(buf,"debugoff") == 0)
  615.             DBDisable = 1;
  616.             if (strcmp(buf,"debugon") == 0)
  617.             DBDisable = 0;
  618. #endif
  619.  
  620.             offs = Check_For_Volume_Name_Prefix (buf);
  621.             if (buf[offs]==0) {
  622.               if (parentdir)
  623.                 obj = Clone_Object (parentdir);
  624.               else
  625.                 obj = Open_Top_Level_Directory (g_volume);
  626.             } else
  627.               obj = Open_Object (parentdir, buf + offs);
  628.             
  629.             if (obj) {
  630.               if (obj->symlink_f)
  631.                 error = ERROR_IS_SOFT_LINK;
  632.               else
  633.                 packet->dp_Res1 = (long) CTOB (cdlock (obj, packet->dp_Arg3));
  634.             } else {
  635.               if (iso_errno == ISOERR_SCSI_ERROR) {
  636.                 error = ERROR_OBJECT_NOT_FOUND;
  637.             Unmount (FALSE);
  638.               } else if (iso_errno == ISOERR_ILLEGAL_NAME)
  639.             error = ERROR_INVALID_COMPONENT_NAME;
  640.               else if (iso_errno == ISOERR_NOT_FOUND)
  641.             error = ERROR_OBJECT_NOT_FOUND;
  642.               else if (iso_errno == ISOERR_NO_MEMORY)
  643.                 error = ERROR_NO_FREE_STORE;
  644.               else if (iso_errno == ISOERR_IS_SYMLINK)
  645.                 error = ERROR_IS_SOFT_LINK;
  646.               else
  647.                 error = 333;
  648.             }
  649.           } else
  650.             error = ERROR_NO_DISK;
  651.         }
  652.         break;
  653.         case ACTION_COPY_DIR:   /*     Lock,                Lock       */
  654.             {
  655.           if (packet->dp_Arg1) {
  656.             CDROM_OBJ *obj = getlockfile (packet->dp_Arg1);
  657.             CDROM_OBJ *new;
  658.             
  659.             if (obj->volume != g_volume) {
  660.               /* old lock from another disk: */
  661.               error = ERROR_DEVICE_NOT_MOUNTED;
  662.               break;
  663.             }
  664.             new = Clone_Object (obj);
  665.             if (!new)
  666.               error = ERROR_NO_FREE_STORE;
  667.             else
  668.               packet->dp_Res1 = (long) CTOB (cdlock (new, ACCESS_READ));
  669.           } else
  670.             packet->dp_Res1 = 0;
  671.         }
  672.         break;
  673.         case ACTION_FREE_LOCK:  /*     Lock,                Bool       */
  674.         if (packet->dp_Arg1);
  675.             cdunlock (BTOC(packet->dp_Arg1));
  676.         break;
  677.         case ACTION_CURRENT_VOLUME: /* FHArg1                   DevList    */
  678.             {
  679.           CDROM_OBJ *obj = (CDROM_OBJ*) packet->dp_Arg1;
  680.               if (obj)
  681.             packet->dp_Res1 = (long) CTOB (Find_Dev_List (obj));
  682.           else
  683.                 packet->dp_Res1 = (long) CTOB (DevList);
  684.               break;
  685.         }
  686.         case ACTION_INHIBIT:    /*     Bool                Bool       */
  687.                 if (packet->dp_Arg1 != DOS_FALSE) {
  688.                   /* true meens forbid access */
  689.           g_inhibited++;
  690.                   if (DevList)
  691.                     Unmount(FALSE);
  692.                   Hide_CDDA_Icon ();
  693.                   g_cd->t_changeint2 = -2;
  694.                 } else {
  695.                   /* false meens access allowed */
  696.           if (g_inhibited)
  697.             g_inhibited--;
  698.           if (g_inhibited == 0) {
  699.             g_disk_inserted = FALSE;
  700.                     Check_Disk();
  701.           }
  702.                 }
  703.         break;
  704.         /*
  705.          *  FINDINPUT and FINDOUTPUT normally should return the
  706.          *  'write protected' error. If the field 'Name', however,
  707.          *  designates the root (e.g. CD0:), then the 'wrong type'
  708.          *  error should be returned. Otherwise, AmigaDOS would do
  709.          *  some funny things (such as saying 'Volume CD0: is write-
  710.          *  protected') if you try to mount the handler with the
  711.          *  field 'Mount' set to 1.
  712.          */
  713.         case ACTION_FINDOUTPUT: /*   Handle  Lock  Name         Bool       */
  714.         case ACTION_FINDUPDATE: /*   Handle  Lock  Name         Bool       */
  715.         {
  716.           int pos;
  717.           
  718.           btos(packet->dp_Arg3,buf);
  719.           BUG(dbprintf("'%s' ", buf);)
  720.           if ((pos = Check_For_Volume_Name_Prefix (buf)) &&
  721.               buf[pos] == 0)
  722.         error = ERROR_OBJECT_WRONG_TYPE;
  723.           else
  724.         error = ERROR_DISK_WRITE_PROTECTED;
  725.           break;
  726.         }
  727.         case ACTION_SAME_LOCK: /*    Lock  Lock                 Bool       */
  728.         {
  729.           CDROM_OBJ *obj1 = getlockfile(packet->dp_Arg1),
  730.                     *obj2 = getlockfile(packet->dp_Arg2);
  731.  
  732.               if (Same_Objects (obj1, obj2))
  733.                 packet->dp_Res1 = DOSTRUE;
  734.           else
  735.                 packet->dp_Res1 = DOSFALSE;
  736.  
  737.           break;
  738.         }
  739.         case ACTION_READ_LINK: /*  Lock Name Buf Length         NumChar    */
  740.         {
  741.           CDROM_OBJ *obj;
  742.           CDROM_OBJ *parentdir = getlockfile (packet->dp_Arg1);
  743.           char *outbuf = (char *) packet->dp_Arg3;
  744.           t_ulong maxlength = packet->dp_Arg4;
  745.           int offs;
  746.           char buf[256];
  747.           int res;
  748.  
  749.           if (parentdir->volume != g_volume) {
  750.             /* old lock from another disk: */
  751.             error = ERROR_DEVICE_NOT_MOUNTED;
  752.             break;
  753.           }
  754.               BUG(dbprintf ("'%s' len=%lu ", packet->dp_Arg2, maxlength);)
  755.           offs = Check_For_Volume_Name_Prefix ((char *) packet->dp_Arg2);
  756.           obj = Open_Object (parentdir, (char *) packet->dp_Arg2 + offs);
  757.           if (obj) {
  758.             res = Get_Link_Name (obj, buf, sizeof (buf));
  759.         if (res == 0 ||
  760.             strlen (buf) + strlen (g_vol_name+1) + 1 >= maxlength)
  761.           strncpy (outbuf, "illegal_link", maxlength - 1);
  762.         else {
  763.           if (buf[0] == ':')
  764.             strcpy (outbuf, g_vol_name+1);
  765.           else
  766.             outbuf[0] = 0;
  767.           strcat (outbuf, buf);
  768.         }
  769.         outbuf[maxlength - 1] = 0;
  770.         Close_Object (obj);
  771.         packet->dp_Res1 = strlen (outbuf);
  772.           } else {
  773.         if (iso_errno == ISOERR_ILLEGAL_NAME)
  774.           error = ERROR_INVALID_COMPONENT_NAME;
  775.         else if (iso_errno == ISOERR_NOT_FOUND)
  776.           error = ERROR_OBJECT_NOT_FOUND;
  777.         else if (iso_errno == ISOERR_NO_MEMORY)
  778.           error = ERROR_NO_FREE_STORE;
  779.         else if (iso_errno == ISOERR_IS_SYMLINK)
  780.           error = ERROR_IS_SOFT_LINK;
  781.         else
  782.           error = 333;
  783.           }
  784.           break;
  785.         }
  786.         case ACTION_RENAME_DISK:
  787.         case ACTION_WRITE:
  788.         case ACTION_SET_PROTECT:
  789.         case ACTION_DELETE_OBJECT:
  790.         case ACTION_RENAME_OBJECT:
  791.         case ACTION_CREATE_DIR:
  792.         case ACTION_SET_COMMENT:
  793.         case ACTION_SET_DATE:
  794.         case ACTION_SET_FILE_SIZE:
  795.               error = ERROR_DISK_WRITE_PROTECTED;
  796.               break;
  797.         case ACTION_FLUSH:        /*     writeout bufs, disk motor off           */
  798.           break;
  799.         /*
  800.          *    A few other packet types which we do not support
  801.          */
  802.         case ACTION_MORE_CACHE: /*     #BufsToAdd            Bool       */
  803.         case ACTION_WAIT_CHAR:  /*     Timeout, ticks         Bool       */
  804.         case ACTION_SCREEN_MODE:/*     Bool(-1:RAW 0:CON)        OldState   */
  805.         default:
  806.         error = ERROR_ACTION_NOT_KNOWN;
  807.         break;
  808.         }
  809.         if (packet) {
  810.         if (error) {
  811.             BUG(dbprintf("ERR=%ld\n", error);)
  812.             packet->dp_Res1 = DOS_FALSE;
  813.             packet->dp_Res2 = error;
  814.         } else {
  815.             BUG(dbprintf("RES=%06lx\n", packet->dp_Res1));
  816.         }
  817.         returnpacket(packet);
  818.         }
  819.     }
  820.     }
  821.     BUG(dbprintf("Can we remove ourselves? ");)
  822.     Delay(100);        /*    I wanna even see the debug message! */
  823.     Forbid();
  824.     if (packetsqueued() ||
  825.         (g_volume && (g_volume->locks || g_volume->file_handles))) {
  826.     Permit();
  827.     BUG(dbprintf(" ..  not yet!\n");)
  828.     notdone = 1;
  829.     goto top;        /*  sorry... can't exit     */
  830.     } else
  831.     BUG(dbprintf(" ..  yes!\n");)
  832.  
  833.     /* remove timer device and any pending timer requests: */
  834.     if (g_timer_sigbit)
  835.       Cleanup_Timer_Device ();
  836.  
  837.     /* this is getting dangerous. We will unload our very own
  838.      * code via UnLoadSeg() and need to keep the system in
  839.      * Forbid() state in order to avoid getting the free memory
  840.      * reclaimed by other tasks. This means: *NO* Wait() after
  841.      * Unmount(TRUE) ist called!
  842.      */
  843.  
  844.     Unmount (TRUE);
  845.     
  846.     dosfree ((ulong *) g_vol_name);
  847.  
  848.     Cleanup_CDROM (g_cd);
  849.  
  850.     Close_Intui ();
  851.  
  852.     /*
  853.      *    Remove debug process, closedown, fall of the end of the world
  854.      *    (which is how you kill yourself if a PROCESS.  A TASK would have
  855.      *    had to RemTask(NULL) itself).
  856.      */
  857.  
  858.     BUG2(Delay (50);)
  859.     BUG2(dbuninit();)
  860.     if (UtilityBase)
  861.       CloseLibrary (UtilityBase);
  862.     if (DOSBase)
  863.       CloseLibrary ((struct Library *) DOSBase);
  864.     
  865.     return 0;
  866. }
  867.  
  868.  
  869. /*
  870.  *  PACKET ROUTINES.    Dos Packets are in a rather strange format as you
  871.  *  can see by this and how the PACKET structure is extracted in the
  872.  *  GetMsg() of the main routine.
  873.  */
  874.  
  875. void returnpacket(struct DosPacket *packet)
  876. {
  877.     register struct Message *mess;
  878.     register struct MsgPort *replyport;
  879.  
  880.     replyport             = packet->dp_Port;
  881.     mess             = packet->dp_Link;
  882.     packet->dp_Port         = &DosProc->pr_MsgPort;
  883.     mess->mn_Node.ln_Name    = (char *)packet;
  884.     mess->mn_Node.ln_Succ    = NULL;
  885.     mess->mn_Node.ln_Pred    = NULL;
  886.     PutMsg(replyport, mess);
  887. }
  888.  
  889. /*
  890.  *  Are there any packets queued to our device?
  891.  */
  892.  
  893. int packetsqueued (void)
  894. {
  895.     return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
  896.         (void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
  897. }
  898.  
  899. /*
  900.  *  DOS MEMORY ROUTINES
  901.  *
  902.  *  DOS makes certain assumptions about LOCKS.    A lock must minimally be
  903.  *  a FileLock structure, with additional private information after the
  904.  *  FileLock structure.  The longword before the beginning of the structure
  905.  *  must contain the length of structure + 4.
  906.  *
  907.  *  NOTE!!!!! The workbench does not follow the rules and assumes it can
  908.  *  copy lock structures.  This means that if you want to be workbench
  909.  *  compatible, your lock structures must be EXACTLY sizeof(struct FileLock).
  910.  */
  911.  
  912. void *dosalloc(ulong bytes)
  913. {
  914.     register ulong *ptr;
  915.  
  916.     bytes += 4;
  917.     ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
  918.     *ptr = bytes;
  919.     return(ptr+1);
  920. }
  921.  
  922. void dosfree (ulong *ptr)
  923. {
  924.     --ptr;
  925.     FreeMem(ptr, *ptr);
  926. }
  927.  
  928. /*
  929.  *  Convert a BSTR into a normal string.. copying the string into buf.
  930.  *  I use normal strings for internal storage, and convert back and forth
  931.  *  when required.
  932.  */
  933.  
  934. void btos(LONG bstr, char *buf)
  935. {
  936.     unsigned char *str = BTOC(bstr);
  937.     bmov((char *) str+1, buf, *str);
  938.     buf[*str] = 0;
  939. }
  940.  
  941. /*
  942.  *  Some EXEC list handling routines not found in the EXEC library.
  943.  */
  944.  
  945. void *NextNode (NODE *node)
  946. {
  947.     node = node->mln_Succ;
  948.     if (node->mln_Succ == NULL)
  949.     return(NULL);
  950.     return(node);
  951. }
  952.  
  953. void *GetHead (LIST *list)
  954. {
  955.     if ((void *)list->mlh_Head != (void *)&list->mlh_Tail)
  956.     return(list->mlh_Head);
  957.     return(NULL);
  958. }
  959.  
  960. /*
  961.  *  The lock function.    The file has already been checked to see if it
  962.  *  is lockable given the mode.
  963.  */
  964.  
  965. LOCK *cdlock(CDROM_OBJ *cdfile, int mode)
  966. {
  967.   LOCK *lock = dosalloc (sizeof(LOCK));
  968.  
  969.   cdfile->volume->locks++;
  970.   lock->fl_Key = (long) cdfile;
  971.   lock->fl_Access = ACCESS_READ;
  972.   lock->fl_Task = &DosProc->pr_MsgPort;
  973.   lock->fl_Volume = (BPTR) CTOB (DevList);
  974.   Register_Lock (lock);
  975.   return(lock);
  976. }
  977.  
  978. void cdunlock (LOCK *lock)
  979. {
  980.   CDROM_OBJ *obj = (CDROM_OBJ *) lock->fl_Key;
  981.  
  982.   Unregister_Lock (lock);
  983.   --obj->volume->locks;
  984.   Close_Object (obj);
  985.   dosfree ((ulong *) lock);                /* free lock        */
  986. }
  987.  
  988. /*
  989.  *  GETLOCKFILE(bptrlock)
  990.  *
  991.  *  Return the CDROM_OBJ (file or directory) associated with the
  992.  *  given lock, which is passed as a BPTR.
  993.  *
  994.  *  According to the DOS spec, the only way a NULL lock will ever be
  995.  *  passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
  996.  *  In anycase, If a NULL lock is passed to me I simply assume it means
  997.  *  the root directory of the CDROM.
  998.  */
  999.  
  1000. CDROM_OBJ *getlockfile (LONG lock)
  1001. {
  1002.   LOCK *rl = BTOC (lock);
  1003.  
  1004.   if (rl)
  1005.     return (CDROM_OBJ *) rl->fl_Key;
  1006.   return g_top_level_obj;
  1007. }
  1008.  
  1009. /*
  1010.  * If p_pathname contains a ':' character, return the position of the first
  1011.  * character after ':'
  1012.  * Otherwise, return 0.
  1013.  */
  1014.  
  1015. int Check_For_Volume_Name_Prefix (char *p_pathname)
  1016. {
  1017.   char *pos = strchr (p_pathname, ':');
  1018.   
  1019.   return pos ? (pos - p_pathname) + 1 : 0;
  1020. }
  1021.  
  1022. /*
  1023.  * Fills a FileInfoBlock with the information contained in the
  1024.  * directory record of a CD-ROM directory or file.
  1025.  */
  1026.  
  1027. void Fill_FileInfoBlock (FIB *p_fib, CDROM_INFO *p_info, VOLUME *p_volume)
  1028. {
  1029.   char *src = p_info->name;
  1030.   char *dest = p_fib->fib_FileName+1;
  1031.   int len = p_info->name_length;
  1032.  
  1033.   if (p_info->symlink_f)
  1034.     p_fib->fib_DirEntryType = ST_SOFTLINK;
  1035.   else
  1036.     p_fib->fib_DirEntryType = p_info->directory_f ? ST_USERDIR : ST_FILE;
  1037.   
  1038.   /* I don't know exactly why I have to set fib_EntryType, but other
  1039.    * handlers (e.g. DiskHandler by J Toebes et.al.) also do this.
  1040.    */
  1041.    
  1042.   p_fib->fib_EntryType = p_fib->fib_DirEntryType;
  1043.   
  1044.   if (len == 1 && *src == ':') {
  1045.     /* root of file system: */
  1046.     p_fib->fib_DirEntryType = ST_USERDIR;
  1047.     /* file name == volume name: */
  1048.     memcpy (p_fib->fib_FileName, g_vol_name, (int)(g_vol_name[0])+1);
  1049.   } else {
  1050.     /* copy file name: */
  1051.     if (g_show_version_numbers) {
  1052.       p_fib->fib_FileName[0] = len;
  1053.       for (; len; len--)
  1054.         *dest++ = *src++;
  1055.     } else {
  1056.       short i, real_len=len;
  1057.       for (i=0; i<len; i++) {
  1058.         if (*src == ';')
  1059.       real_len = i;
  1060.         *dest++ = *src++;
  1061.       }
  1062.       p_fib->fib_FileName[0] = real_len;
  1063.     }
  1064.  
  1065.     if ((g_map_to_lowercase && p_volume->protocol == PRO_ISO) ||
  1066.         (g_maybe_map_to_lowercase && !p_volume->mixed_char_filenames)) {
  1067.       /* convert ISO filename to lowercase: */
  1068.       int i, len = p_fib->fib_FileName[0];
  1069.       char *cp = p_fib->fib_FileName + 1;
  1070.     
  1071.       for (i=0; i<len; i++, cp++)
  1072.         *cp = ToLower (*cp);
  1073.     }
  1074.   }
  1075.   p_fib->fib_Protection = 0;
  1076.   p_fib->fib_Size = p_info->file_length;
  1077.   p_fib->fib_NumBlocks = p_info->file_length >> 11;
  1078.   if (p_info->symlink_f)
  1079.     strcpy (p_fib->fib_Comment, "\x0DSymbolic link");
  1080.   else
  1081.     p_fib->fib_Comment[0] = 0;
  1082.  
  1083.   p_fib->fib_Date.ds_Days   = p_info->date / (24 * 60 * 60);
  1084.   p_fib->fib_Date.ds_Minute = (p_info->date % (24 * 60 * 60)) / 60;
  1085.   p_fib->fib_Date.ds_Tick   = (p_info->date % 60) * TICKS_PER_SECOND;
  1086. }
  1087.  
  1088. /*
  1089.  * Create Volume node and add to the device list. This will
  1090.  * cause the WORKBENCH to recognize us as a disk. If we don't
  1091.  * create a Volume node, Wb will not recognize us.
  1092.  */
  1093.  
  1094. void Create_Volume_Node (LONG p_disk_type, ULONG p_volume_date)
  1095. {
  1096.   DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
  1097.   DEVLIST *dl;
  1098.   char *name;
  1099.  
  1100.   if (dl = Find_Volume_Node (g_vol_name + 1)) {
  1101.     BUG(dbprintf("[Reusing old volume node]");)
  1102.     Forbid ();
  1103.     DevList = dl;
  1104.     dl->dl_Task = &DosProc->pr_MsgPort;
  1105.     Permit ();
  1106.   } else {
  1107.     Forbid ();
  1108.     DevList = dl = dosalloc(sizeof(DEVLIST));
  1109.     dl->dl_Type = DLT_VOLUME;
  1110.     dl->dl_Task = &DosProc->pr_MsgPort;
  1111.     dl->dl_DiskType = p_disk_type;
  1112.     name = dosalloc (g_vol_name[0]+2);
  1113.     if (name) {
  1114.       dl->dl_Name = (BSTR) CTOB (name);
  1115.       memcpy (name, g_vol_name, g_vol_name[0]+2);
  1116.     }
  1117.     dl->dl_VolumeDate.ds_Days = p_volume_date / (24 * 60 * 60);
  1118.     dl->dl_VolumeDate.ds_Minute = (p_volume_date % (24 * 60 * 60)) / 60;
  1119.     dl->dl_VolumeDate.ds_Tick = (p_volume_date % 60) * TICKS_PER_SECOND;
  1120.     dl->dl_Next = di->di_DevInfo;
  1121.     di->di_DevInfo = (long)CTOB(dl);
  1122.     Permit ();
  1123.     Register_Volume_Node (dl);
  1124.   }
  1125. }
  1126.  
  1127. /*
  1128.  * Mount a volume.
  1129.  */
  1130.  
  1131. void Mount (void)
  1132. {
  1133.   char buf[33];
  1134.  
  1135.   if (Has_Audio_Tracks (g_cd))
  1136.     Show_CDDA_Icon ();
  1137.  
  1138.   g_volume = Open_Volume (g_cd, g_use_rock_ridge);
  1139.   if (!g_volume) {
  1140.     BUG(dbprintf ("!!! cannot open VOLUME (iso_errno=%d) !!!\n", iso_errno);)
  1141.     return;
  1142.   } else {
  1143.     g_disk_inserted = TRUE;
  1144.     g_cd->t_changeint2 = g_cd->t_changeint;
  1145.     g_top_level_obj = Open_Top_Level_Directory (g_volume);
  1146.     if (!g_top_level_obj) {
  1147.       BUG(dbprintf ("!!! cannot open top level directory !!!\n");)
  1148.       return;
  1149.     }
  1150.   }
  1151.   
  1152.   BUG(dbprintf ("***mounting*** ");)
  1153.  
  1154.   Volume_ID (g_volume, buf, sizeof (buf)-1);  
  1155.   g_vol_name[0] = strlen (buf);
  1156.   memcpy (g_vol_name+1, buf, strlen (buf));
  1157.  
  1158.   if (!(g_vol_name[0]))
  1159.     memcpy (g_vol_name, "\7Unnamed", 8);
  1160.  
  1161.   /* AmigaDOS expects the BCPL string g_vol_name to be null-terminated: */
  1162.   g_vol_name[(int)(g_vol_name[0])+1] = 0;
  1163.  
  1164.   /* convert ISO volume name to lowercase: */
  1165.   if ((g_map_to_lowercase && g_volume->protocol == PRO_ISO) ||
  1166.       (g_maybe_map_to_lowercase && !g_volume->mixed_char_filenames)) {
  1167.     int i;
  1168.     for (i=0; i<g_vol_name[0]; i++)
  1169.       g_vol_name[i+1] = ToLower (g_vol_name[i+1]);
  1170.   }
  1171.  
  1172.   g_volume->locks = Reinstall_Locks ();
  1173.   g_volume->file_handles = Reinstall_File_Handles ();
  1174.  
  1175.   Create_Volume_Node (ID_DOS_DISK, Volume_Creation_Date (g_volume));
  1176.   g_cd->t_changeint2 = g_cd->t_changeint;
  1177.   Send_Event (TRUE);
  1178. }
  1179.  
  1180. void Remove_Seglist (void)
  1181. {
  1182.   DOSINFO *di;
  1183.   void *dlp;
  1184.   DEVNODE *dn;
  1185.  
  1186.   di = BTOC (((ROOTNODE *) DOSBase->dl_Root)->rn_Info);
  1187.   dlp = &di->di_DevInfo;
  1188.   dn = BTOC (di->di_DevInfo);
  1189.  
  1190.   while (dn) {
  1191.     if (dn->dn_Task == DosTask) {
  1192.       BUG(dbprintf("got it!\n");)
  1193.       if (TypeOfMem (BADDR (dn->dn_SegList))) {
  1194.     UnLoadSeg (dn->dn_SegList);
  1195.     dn->dn_SegList = 0;
  1196.       } else
  1197.     BUG(dbprintf("not a valid seglist\n");)
  1198.  
  1199.       dn->dn_Task = NULL;
  1200.  
  1201.       *(BPTR *) dlp = dn->dn_Next;
  1202.       dosfree ((ulong *) dn);
  1203.  
  1204.       break;
  1205.     } else {
  1206.       dlp = &dn->dn_Next;
  1207.       dn = BTOC (dn->dn_Next);
  1208.     }
  1209.   }
  1210. }
  1211.  
  1212. /*  Remove a volume node from the DOS list.
  1213.  *  (Must be nested between Forbid() and Permit()!)
  1214.  *  Since DOS uses singly linked lists, we must (ugg) search it manually to find
  1215.  *  the link before our Volume entry.
  1216.  */
  1217.  
  1218. void Remove_Volume_Node (DEVLIST* p_volume)
  1219. {
  1220.   DOSINFO *di;
  1221.   DEVLIST *dl;
  1222.   void *dlp;
  1223.  
  1224.   Unregister_Volume_Node (p_volume);
  1225.  
  1226.   di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
  1227.  
  1228.   dlp = &di->di_DevInfo;
  1229.   for (dl = BTOC(di->di_DevInfo); dl && dl != p_volume; dl = BTOC(dl->dl_Next))
  1230.     dlp = &dl->dl_Next;
  1231.   if (dl == p_volume) {
  1232.     *(BPTR *)dlp = dl->dl_Next;
  1233.     dosfree((ulong *) BTOC (dl->dl_Name));
  1234.     dosfree((ulong *) dl);
  1235.   } else {
  1236.     BUG(dbprintf("****PANIC: Unable to find volume node\n");)
  1237.   }
  1238. }
  1239.  
  1240. /*  Unmount a volume, and optionally unload the handler code.
  1241.  */
  1242.  
  1243. void Unmount (int p_remove_seglist)
  1244. {
  1245.   g_cd->t_changeint2 = g_cd->t_changeint;
  1246.   Hide_CDDA_Icon ();
  1247.  
  1248.   Forbid ();
  1249.  
  1250.   if (DevList) {
  1251.  
  1252.     BUG(dbprintf("***unmounting***");)
  1253.  
  1254.     Close_Object (g_top_level_obj);
  1255.     if (g_volume->locks == 0 && g_volume->file_handles == 0) {
  1256.       Remove_Volume_Node (DevList);
  1257.       Close_Volume (g_volume);
  1258.     } else {
  1259.       BUG(dbprintf("[there are still %d locks on this volume]",
  1260.                  g_volume->locks);)
  1261.       BUG(dbprintf("[there are still %d file handles on this volume]",
  1262.                  g_volume->file_handles);)
  1263.       DevList->dl_Task = NULL;
  1264.     }
  1265.  
  1266.     DevList = NULL;
  1267.   }
  1268.  
  1269.   Send_Event (FALSE);
  1270.  
  1271.   g_volume = 0;
  1272.  
  1273.   /* when the handler code exits the corresponding device
  1274.    * node (e.g. "CD0") will be modified. The handler code
  1275.    * will be unloaded and the task entry will be set to
  1276.    * zero, so the next device access will reload and
  1277.    * restart the handler code.
  1278.    */
  1279.  
  1280.   if (p_remove_seglist) {
  1281.     Remove_Seglist ();
  1282.   } else
  1283.     Permit ();
  1284. }
  1285.  
  1286. /*
  1287.  * Mount_Check returns 1 if a valid disk is inserted in the drive. A check is
  1288.  * only performed if previously the drive was empty.
  1289.  */
  1290.  
  1291. int Mount_Check (void)
  1292. {
  1293.   if (!g_disk_inserted) {
  1294.    /*
  1295.     * No disk was inserted up to now: we will check whether
  1296.     * a disk has been inserted by sending the test unit ready
  1297.     * command. We have to send the command twice because
  1298.     * the first SCSI command after inserting a new disk is
  1299.     * always rejected.
  1300.     */
  1301.     if (Test_Unit_Ready (g_cd) ||
  1302.         Test_Unit_Ready (g_cd)) {
  1303.       g_disk_inserted = TRUE;
  1304.       Mount ();
  1305.     } else {
  1306.       return 0;
  1307.     }
  1308.     if (DevList)
  1309.       return 1;
  1310.     else {
  1311.       /* Mount() did not succeed: */
  1312.       return 0;
  1313.     }
  1314.   }
  1315.   return 1;
  1316. }
  1317.  
  1318. /*
  1319.  *  Open timer device structures:
  1320.  */
  1321.  
  1322. int Open_Timer_Device (void)
  1323. {
  1324.   if (!(g_timer_mp = CreateMsgPort ())) {
  1325.     BUG(dbprintf ("cannot create timer message port!\n");)
  1326.     return 0;
  1327.   }
  1328.   if (!(g_timer_io = (struct timerequest *)
  1329.        CreateIORequest (g_timer_mp, sizeof (struct timerequest)))) {
  1330.     BUG(dbprintf ("cannot create timer i/o structure!\n");)
  1331.     DeleteMsgPort (g_timer_mp);
  1332.     return 0;
  1333.   }
  1334.   if (OpenDevice ((UBYTE *) TIMERNAME, UNIT_VBLANK,
  1335.             (struct IORequest *) g_timer_io, 0)) {
  1336.     BUG(dbprintf ("cannot open timer device!\n");)
  1337.     DeleteIORequest ((struct IORequest *) g_timer_io);
  1338.     DeleteMsgPort (g_timer_mp);
  1339.     return 0;
  1340.   }
  1341.   g_timer_sigbit = 1L << g_timer_mp->mp_SigBit;
  1342.   return 1;
  1343. }
  1344.  
  1345. /*
  1346.  *  Remove timer device structures:
  1347.  */
  1348.  
  1349. void Cleanup_Timer_Device (void)
  1350. {
  1351.   /* remove any pending requests: */
  1352.   if (!CheckIO ((struct IORequest *) g_timer_io))
  1353.     AbortIO ((struct IORequest *) g_timer_io);
  1354.   WaitIO ((struct IORequest *) g_timer_io);
  1355.   
  1356.   CloseDevice ((struct IORequest *) g_timer_io);
  1357.   DeleteIORequest ((struct IORequest *) g_timer_io);
  1358.   DeleteMsgPort (g_timer_mp);
  1359. }
  1360.  
  1361. /*
  1362.  *  Send timer request
  1363.  */
  1364.  
  1365. void Send_Timer_Request (void)
  1366. {
  1367.   g_timer_io->tr_node.io_Command = TR_ADDREQUEST;
  1368.   g_timer_io->tr_time.tv_secs = g_scan_interval;
  1369.   g_timer_io->tr_time.tv_micro = 0;
  1370.   SendIO ((struct IORequest *) g_timer_io);
  1371. }
  1372.  
  1373. /*
  1374.  *  Check whether the disk has been removed or inserted.
  1375.  */
  1376.  
  1377. void Check_Disk (void)
  1378. {
  1379.   int i;
  1380.   unsigned long l1, l2;
  1381.  
  1382.   BUG(dbprintf ("Checking Disk... ");)
  1383.   if (g_cd->use_trackdisk) {
  1384.     i = (Test_Unit_Ready (g_cd) ||
  1385.          Test_Unit_Ready (g_cd));
  1386.     l1 = g_cd->t_changeint;
  1387.     l2 = g_cd->t_changeint2;
  1388.     BUG(if (l1==l2 && i) dbprintf ("no disk change (T %ld)", l1);)
  1389.     if (l1!=l2 && i) {
  1390.       g_disk_inserted = TRUE;
  1391.       BUG(dbprintf ("disk has been inserted (T %ld)", l1);)
  1392.       if (DevList)
  1393.         Unmount (FALSE);
  1394.       Delay (50);
  1395.       Clear_Sector_Buffers (g_cd);
  1396.       Mount ();
  1397.     }
  1398.     BUG(if (l1==l2 && !i) dbprintf ("no disk in drive (T %ld)", l1);)
  1399.     if (l1!=l2 && !i) {
  1400.       g_disk_inserted = FALSE;
  1401.       BUG(dbprintf ("disk has been removed (T %ld)", l1);)
  1402.       if (DevList)
  1403.         Unmount (FALSE);
  1404.       g_cd->t_changeint2 = g_cd->t_changeint;
  1405.     }
  1406.   } else {
  1407.     if (g_disk_inserted) {
  1408.       if (Test_Unit_Ready (g_cd)) {
  1409.         BUG(dbprintf ("no disk change"));
  1410.       } else {
  1411.         g_disk_inserted = FALSE;
  1412.         BUG(dbprintf ("disk has been removed");)
  1413.         if (DevList)
  1414.           Unmount (FALSE);
  1415.         Hide_CDDA_Icon ();
  1416.       }
  1417.     }
  1418.     if (!g_disk_inserted) {
  1419.       if (Test_Unit_Ready (g_cd) ||
  1420.           Test_Unit_Ready (g_cd)) {
  1421.         g_disk_inserted = TRUE;
  1422.         BUG(dbprintf ("disk has been inserted");)
  1423.         Clear_Sector_Buffers (g_cd);
  1424.         Mount ();
  1425.       }
  1426.     }
  1427.   }
  1428.   BUG(dbprintf ("\n");)
  1429. }
  1430.  
  1431. /* The following lines will generate a `disk inserted/removed' event, in order
  1432.  * to get Workbench to rescan the DosList and update the list of
  1433.  * volume icons.
  1434.  */
  1435.  
  1436. void Send_Event (int p_inserted)
  1437. {
  1438.   struct IOStdReq *InputRequest;
  1439.   struct MsgPort *InputPort;
  1440.  
  1441.   if (InputPort = (struct MsgPort *) CreateMsgPort ()) {
  1442.     if (InputRequest = (struct IOStdReq *)
  1443.         CreateIORequest (InputPort, sizeof (struct IOStdReq))) {
  1444.       if (!OpenDevice ((UBYTE *) "input.device", 0,
  1445.                      (struct IORequest *) InputRequest, 0)) {
  1446.     static struct InputEvent InputEvent;
  1447.  
  1448.     memset (&InputEvent, 0, sizeof (struct InputEvent));
  1449.  
  1450.     InputEvent.ie_Class = p_inserted ? IECLASS_DISKINSERTED :
  1451.                        IECLASS_DISKREMOVED;
  1452.  
  1453.     InputRequest->io_Command = IND_WRITEEVENT;
  1454.     InputRequest->io_Data = &InputEvent;
  1455.     InputRequest->io_Length = sizeof (struct InputEvent);
  1456.  
  1457.     DoIO ((struct IORequest *) InputRequest);
  1458.  
  1459.     CloseDevice ((struct IORequest *) InputRequest);
  1460.       }
  1461.       DeleteIORequest (InputRequest);
  1462.     }
  1463.     DeleteMsgPort (InputPort);
  1464.   }
  1465. }
  1466.  
  1467. BPTR Make_FSSM (void)
  1468. {
  1469.   struct FileSysStartupMsg* fssm;
  1470.   struct DosEnvec *env;
  1471.   char *dev;
  1472.   
  1473.   fssm = dosalloc (sizeof (*fssm));
  1474.   fssm->fssm_Unit = g_unit;
  1475.   dev = dosalloc (strlen (g_device) + 2);
  1476.   strcpy (dev+1, g_device);
  1477.   dev[0] = strlen (g_device);
  1478.   fssm->fssm_Device = (BPTR) CTOB (dev);
  1479.   env = dosalloc (sizeof (struct DosEnvec));
  1480.   env->de_TableSize = 19;
  1481.   env->de_SizeBlock = 2048;
  1482.   env->de_Surfaces = 1;
  1483.   env->de_SectorPerBlock = 1;
  1484.   env->de_BlocksPerTrack = 1;
  1485.   env->de_DosType = 0x4E444F53; /* NDOS */
  1486.   fssm->fssm_Environ = (BPTR) CTOB (env);
  1487.   fssm->fssm_Flags = 0;
  1488.  
  1489.   return (BPTR) CTOB (fssm);
  1490. }
  1491.