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