Removed write-protected requester. Traditionally, AmigaOS CD filesystems
[AROS.git] / rom / filesys / CDVDFS / src / device.c
blob832eaaace28297c388a2aa011c62b27fd1e7e11f
1 /* device.c:
3 * Handler for ISO-9660 (+ Rock Ridge) + HFS CDROM filing system.
4 * Based on DOSDEV V1.10 (2 Nov 87) by Matthew Dillon.
6 * ----------------------------------------------------------------------
7 * This code is (C) Copyright 1993,1994 by Frank Munkert.
8 * (C) Copyright 2002-2013 The AROS Development Team
9 * All rights reserved.
10 * This software may be freely distributed and redistributed for
11 * non-commercial purposes, provided this notice is included.
12 * ----------------------------------------------------------------------
13 * History:
15 * 08-Nov-13 neil - Only delay 2 seconds upon exiting if debugging is
16 * enabled.
17 * 18-Dec-11 twilen - Added media change interrupt support (SCANINTERVAL=-1).
18 * 11-Aug-10 sonic - Fixed for 64-bit compatibility
19 * 04-Jun-10 neil - No longer removes device node and seglist when
20 * exiting. It isn't our job.
21 * - Delay returning ACTION_DIE packet until handler is
22 * mostly closed down.
23 * 20-Mar-09 sonic - Fixed ID_CDFS_DISK redefinition warning when building with new
24 * MorphOS SDK
25 * 18-Mar-09 sonic - Numerous BSTR handling adjustments, now can be compiled as a
26 * packet-type handler for AROS
27 * 27-Aug-07 sonic - Register_Volume_Node() now takes separate pointer to a volume name.
28 * - Now reports correct DOS type for volumes ('CDFS').
29 * 29-May-07 sonic - Replies FileSysStartupMsg and loads character
30 * set translation table before mounting volume
31 * - Does not unmount volume any more in case of
32 * disk read error
33 * 15-May-07 sonic - Fixed CDDA handling
34 * 06-May-07 sonic - Added support for protection bits and file comments
35 * - Fixed memory trashing when file name length is greater than AmigaOS
36 * can accept
37 * 08-Apr-07 sonic - removed redundant "TRACKDISK" option
38 * 01-Apr-07 sonic - seglist unloading is much more safe
39 * - removed unneeded search in Remove_Volume_Node()
40 * 31-Mar-07 sonic - ACTION_SAME_LOCK now returns 100% correct values
41 * - removed Make_FSSM()
42 * - removed many #ifdef's
43 * - fixed warnings
44 * 09-Apr-06 sonic - fixed binary-compatible BCPL file name string handling
45 * - code style improved, removed many #ifdef's.
46 * - now works if compiled for binary-compatible flavour
47 * 07-Jul-02 sheutlin various changes/fixes when porting to AROS
48 * - all global variables are now in the structure global
49 * - changed types in lowercase to uppercase (ulong, ...)
50 * - replaced BTOC() by dos/dos.h/BADDR()
51 * - replaced CTOB() by dos/dos.h/MKBADDR()
52 * - fixed various warnings
53 * - replaced (unsigned) long by (U)LONG
54 * - replaced short by WORD
55 * - removed GetHead(), NextNode() (was unused)
56 * - changed Create_Volume_Node() to use
57 * MakeDosEntry()/AddDosEntry()
58 * - changed Remove_Volume_Node() to use
59 * RemDosEntry()/FreeDosEntry() now
60 * - removed dosalloc()/dosfree() and replaced these calls
61 * by AllocVec()/FreeVec()
62 * - put packet handling into own function to split
63 * code into init-main-deinit
64 * - replaced memcpy by CopyMem
65 * - changed SAME_LOCK returning LOCK_SAME/LOCK_DIFFERENT
66 * - replaced DOS_[TRUE|FALSE] by dos/dos.h/DOS[TRUE|FALSE]
67 * - optimisations here and there
68 * - removed static definitions
69 * 03-Nov-94 fmu Truncate file names to 30 characters.
70 * 12-Oct-94 fmu Return startup packet even if the scsi device cannot
71 * be opened immediately.
72 * 01-Sep-94 fmu - Added ACTION_EXAMINE_FH.
73 * - Added ACTION_PARENT_FH.
74 * - Added ACTION_MAKE_LINK (-> error message).
75 * - Automatically remove volume if all locks on the
76 * the volume have been freed.
77 * 17-May-94 fmu New option MAYBELOWERCASE (=ML).
78 * 20-Apr-94 fmu Improved implementation of ACTION_INHIBIT.
79 * 17-Apr-94 fmu Fixed bug concerning TRACKDISK disk change recognition.
80 * 12-Apr-94 fmu Adapted ACTION_CURRENT_VOLUME to new filehandle
81 * management.
82 * 09-Apr-94 fmu Volume management: locks and filehandles will not
83 * be forgotten if a CDROM is removed from the drive.
84 * 06-Feb-94 dmb - Full support for ACTION_INHIBIT
85 * - Change Check_Disk() for trackdisk support
86 * 05-Jan-93 fmu - Retry displaying CD-DA icon if WB is not open.
87 * - id_UnitNumber of InfoData set to SCSI unit number.
88 * - Added Make_FSSM().
89 * 01-Jan-93 fmu Support for symbolic links on RockRidge disks.
90 * 11-Dec-93 fmu - ACTION_FLUSH always returns DOSTRUE.
91 * - ISO volume names are mapped to lowercase if
92 * the option LOWERCASE has been selected.
93 * 26-Nov-93 fmu Some packets are now handled even if no disk
94 * is inserted.
95 * 21-Nov-93 fmu - User programmable diskchange check interval.
96 * - Better support for ACTION_INHIBIT.
97 * - Handles filenames with ';'.
98 * 15-Nov-93 fmu Missing return value for 'handler' inserted.
99 * 14-Nov-93 fmu Added ACTION_USER packet for 'cdcontrol' program.
100 * 15-Oct-93 fmu Adapted to new VOLUME structure.
101 * 10-Oct-93 fmu - Creates volume node for 'no DOS' disks.
102 * - Volume node now contains the correct volume
103 * creation date.
104 * 09-Oct-93 fmu - New format for mountlist startup field.
105 * - SAS/C support.
106 * - Debug process assembly tag adapted to small
107 * memory model.
108 * - Get_Startup moved to file devsupp.c.
109 * 03-Oct-93 fmu - New buffering options 'S' and 'C'.
110 * - Fixed bug in cdlock.
111 * - Fixed bug in ACTION_CURRENT_VOLUME.
112 * 27-Sep-93 fmu Added ACTION_SAME_LOCK
113 * 25-Sep-93 fmu - Send 'disk inserted' / 'disk removed' event via
114 * input.device if disk has been changed.
115 * - Corrected bug in ACTION_DISK_INFO.
116 * 24-Sep-93 fmu - Added fast memory option 'F'.
117 * - Added ACTION_IS_FILESYSTEM.
118 * - Added 'write protected' error for write actions.
119 * - Added ACTION_CURRENT_VOLUME.
120 * - Unload handler code after ACTION_DIE.
121 * - Immediately terminate program if called from CLI.
122 * - Added library version number.
123 * - Set volume label to "Unnamed" for disks without name.
124 * 16-Sep-93 fmu Added code to detect whether a lock stems from the
125 * current volume or from another volume which has
126 * been removed from the drive.
130 * Debugging routines are disabled by simply attempting to open the
131 * file "debugoff", turned on again with "debugon". No prefix may be
132 * attached to these names (you must be CD'd to TEST:).
134 * See Documentation for a detailed discussion.
137 #include <proto/dos.h>
138 #include <proto/exec.h>
139 #include <proto/utility.h>
140 #include <stdlib.h>
141 #include <string.h>
143 #include "debug.h"
144 #include "device.h"
145 #include "intui.h"
146 #include "devsupp.h"
147 #include "cdcontrol.h"
148 #include "params.h"
149 #include "rock.h"
150 #include "globals.h"
151 #include "volumes.h"
152 #include "charset.h"
153 #include "prefs.h"
154 #include "aros_stuff.h"
156 #ifndef ID_CDFS_DISK
157 #define ID_CDFS_DISK 0x43444653
158 #endif
160 LONG handler(struct ExecBase *);
161 static LOCK *cdlock(CDROM_OBJ *, int);
162 static void cdunlock (LOCK *);
163 static CDROM_OBJ *getlockfile(struct CDVDBase *global, IPTR);
164 static void returnpacket(struct CDVDBase *global, struct DosPacket *);
165 static int packetsqueued (struct CDVDBase *global);
166 static int Check_For_Volume_Name_Prefix (struct CDVDBase *global, char *);
167 static void Fill_FileInfoBlock (struct CDVDBase *global, FIB *, CDROM_INFO *, VOLUME *);
168 static void Mount (struct CDVDBase *global);
169 static void Unmount (struct CDVDBase *global);
170 static int Mount_Check (struct CDVDBase *global);
171 static void Check_Disk (struct CDVDBase *global);
172 static void Send_Timer_Request (struct CDVDBase *global);
173 static void Cleanup_Timer_Device (struct CDVDBase *global);
174 static int Open_Timer_Device (struct CDVDBase *global);
175 static void Send_Event (struct CDVDBase *global, int);
176 /* BPTR Make_FSSM (void); */
177 static void Remove_Volume_Node (struct CDVDBase *global, struct DeviceList *);
178 static LONG handlemessage(struct CDVDBase *global, ULONG);
180 #ifdef USE_FAST_BSTR
181 #define MAX_NAME_LEN 107
182 #define MAX_COMMENT_LEN 79
183 #define btos(bstr, buf) strcpy(buf, bstr)
184 #else
185 #define MAX_NAME_LEN 106
186 #define MAX_COMMENT_LEN 78
188 static void btos(BSTR, char *);
189 #endif
191 #ifdef __MORPHOS__
192 ULONG __abox__ = 1;
193 #endif
195 #ifndef __AROS__
196 char __version__[] = "\0$VER: CDVDFS 1.8 (8.11.2013)";
198 LONG SAVEDS Main(void)
200 return handler(*(struct ExecBase **)4L);
202 #endif
204 static struct CDVDBase *AllocCDVDBase(struct ExecBase *SysBase)
206 struct CDVDBase *cdvd = NULL;
208 cdvd = AllocMem(sizeof(*cdvd), MEMF_CLEAR | MEMF_PUBLIC);
209 if (!cdvd)
210 return NULL;
212 cdvd->g_iconname = "CD-DA";
214 return cdvd;
217 static void FreeCDVDBase(struct CDVDBase *cdvd)
219 FreeMem(cdvd, sizeof(*cdvd));
222 LONG handler(struct ExecBase *SysBase)
224 register PACKET *packet;
225 MSG *msg;
226 ULONG signals;
228 struct CDVDBase *global = AllocCDVDBase(SysBase);
230 D(bug("[CDVDFS] In handler, cdvd=%p\n", global));
231 global->playing = FALSE;
234 * Initialize all global variables. SysBase MUST be initialized before
235 * we can make Exec calls. The DOS library is opened for the debug
236 * process only.
238 global->DosProc = (PROC *) FindTask(NULL);
239 DOSBase = (APTR)OpenLibrary("dos.library",37);
240 UtilityBase = (APTR)OpenLibrary("utility.library",37);
241 CodesetsBase = NULL;
243 global->Dback = CreateMsgPort();
245 #if !defined(NDEBUG) || defined(DEBUG_SECTORS)
246 D(bug("[CDVDFS] Initializing debugging code\n"));
247 global->DBDisable = 0; /* Init. globals */
248 global->Dbport = NULL;
251 * Initialize debugging code
254 if (DOSBase && global->Dback)
255 dbinit(global);
256 #endif
257 global->DevList = NULL;
258 global->PrefsProc = NULL;
259 global->uniCodeset = NULL;
261 WaitPort(&global->DosProc->pr_MsgPort); /* Get Startup Packet */
262 msg = GetMsg(&global->DosProc->pr_MsgPort);
263 packet = (PACKET *)msg->mn_Node.ln_Name;
266 * Loading DosNode->dn_Task causes DOS *NOT* to startup a new
267 * instance of the device driver for every reference. E.G. if
268 * you were writing a CON device you would want this field to
269 * be NULL.
272 global->DosNode = (DEVNODE *)BADDR(packet->dp_Arg3);
274 D(bug("[CDVDFS] Fixing up DosNode %p\n", global->DosNode));
276 * Set dn_Task field which tells DOS not to startup a new
277 * process on every reference.
279 global->DosNode->dn_Task = &global->DosProc->pr_MsgPort;
281 Init_Intui (global);
283 if (UtilityBase && DOSBase && global->Dback &&
284 Get_Startup (global, (struct FileSysStartupMsg *)(BADDR(packet->dp_Arg2))))
286 D(bug("[CDVDFS] On track, globals at %p\n", global));
287 packet->dp_Res1 = DOSTRUE;
288 packet->dp_Res2 = 0;
291 * Load dn_Startup field with a BPTR to a FileSysStartupMsg.
292 * (According to the official documentation, this is not
293 * required. Some debugging tools, however, depend on it.)
296 /* global->DosNode->dn_Startup = Make_FSSM (global); */
298 } else { /* couldn't open dos.library */
299 D(bug("[CDVDFS] Off track, globals at %p\n", global));
300 D(Alert(0));
301 packet->dp_Res1 = DOSFALSE;
302 packet->dp_Res2 = 333; /* any error code */
303 global->DosNode->dn_Task = BNULL;
304 returnpacket(global, packet);
305 Forbid ();
306 if (DOSBase) {
307 BUG2(dbuninit(global);)
308 Close_Intui (global);
309 if (global->Dback)
310 DeleteMsgPort(global->Dback);
311 if (UtilityBase)
312 CloseLibrary((struct Library *)UtilityBase);
313 CloseLibrary ((struct Library *)DOSBase);
315 return 0; /* exit process */
318 global->g_inhibited = 0;
320 BUG2(dbprintf (global, "g_cd = %08lx\n", global->g_cd);)
321 BUG(dbprintf(global, "%d std buffers, %d file buffers\n",
322 global->g_std_buffers, global->g_file_buffers);)
324 global->g_changeint_signumber = AllocSignal(-1);
325 global->g_changeint_sigbit = 1L << global->g_changeint_signumber;
326 /* Initialize timer: */
327 global->g_timer_sigbit = 0;
328 if (Open_Timer_Device (global)) {
329 Send_Timer_Request (global);
330 global->g_scan_time = global->g_scan_interval;
331 global->g_time = 0;
334 global->g_vol_name = AllocVec(128, MEMF_PUBLIC | MEMF_CLEAR);
335 global->g_dos_sigbit = 1L << global->DosProc->pr_MsgPort.mp_SigBit;
336 returnpacket(global, packet);
337 Prefs_Init(global);
338 global->g_disk_inserted = 0;
340 if (global->g_cd) {
341 /* Mount volume (if any disk is inserted): */
342 Mount_Check (global);
346 * Here begins the endless loop, waiting for requests over our
347 * message port and executing them. Since requests are sent over
348 * our message port, this precludes being able to call DOS functions
349 * ourselves (that is why the debugging routines are a separate process)
353 signals = Wait(global->g_dos_sigbit | global->g_timer_sigbit | global->g_app_sigbit | global->g_changeint_sigbit);
354 } while (handlemessage(global, signals));
355 BUG(dbprintf(global, "Terminating the handler\n");)
356 /* remove timer device and any pending timer requests: */
357 if (global->g_timer_sigbit)
358 Cleanup_Timer_Device (global);
360 if (global->g_cd)
361 Unmount (global);
363 FreeVec(global->g_vol_name);
365 if (global->g_cd)
366 Cleanup_CDROM (global->g_cd);
368 if (global->g_changeint_signumber != -1)
369 FreeSignal(global->g_changeint_signumber);
371 Close_Intui (global);
374 * Delay returning a successful ACTION_DIE packet until now, to avoid
375 * inconveniences such as having the trackdisk device disappear before
376 * we close it (e.g. when using a USB drive).
378 returnpacket(global, global->g_death_packet);
381 * Remove processes, closedown, fall off the end of the world
382 * (which is how you kill yourself if a PROCESS. A TASK would have
383 * had to RemTask(NULL) itself).
385 Prefs_Uninit(global);
386 BUG2(Delay (50);)
387 BUG2(dbuninit(global);)
388 DeleteMsgPort(global->Dback);
390 if (CodesetsBase)
391 CloseLibrary(CodesetsBase);
392 CloseLibrary((struct Library *)UtilityBase);
393 CloseLibrary((struct Library *)DOSBase);
395 FreeCDVDBase(global);
396 return 0;
399 LONG handlemessage(struct CDVDBase *global, ULONG signals) {
400 register PACKET *packet;
401 MSG *msg;
402 void *tmp;
403 char buf[256];
404 register WORD error;
405 UBYTE notdone = 1;
407 if (signals & global->g_changeint_sigbit)
409 if (global->g_cd && !global->g_inhibited)
410 Check_Disk (global);
412 if (signals & global->g_timer_sigbit)
414 GetMsg (global->g_timer_mp);
415 global->g_time++;
416 /* retry opening the SCSI device (every 2 seconds): */
417 if (!global->g_cd && (global->g_time & 1))
419 BUG(dbprintf(global, "Device is closed, attempting to open\n");)
420 Get_Startup(global, (struct FileSysStartupMsg *)-1);
422 if (global->g_cd)
424 /* icon retry (every second): */
425 if (global->g_retry_show_cdda_icon)
427 BUG(dbprintf(global, "Showing CDDA icon\n");)
428 Show_CDDA_Icon (global);
430 /* diskchange check: */
431 if (global->g_scan_interval > 0)
433 if (global->g_scan_time == 1)
435 if (!global->g_inhibited)
436 Check_Disk (global);
437 global->g_scan_time = global->g_scan_interval;
439 else
440 global->g_scan_time--;
443 Send_Timer_Request (global);
445 if (signals & global->g_app_sigbit)
447 struct Message *msg;
448 BUG(dbprintf(global, "CDDA icon double-clicked\n");)
449 while ((msg = GetMsg (global->g_app_port)))
451 struct TagItem PlayTags[] = {
452 {SYS_Input , 0 },
453 {SYS_Output, 0 },
454 {SYS_Asynch, TRUE},
455 {TAG_END , 0 }
458 ReplyMsg (msg);
459 if (global->g_play_cdda_command[0]) {
460 PlayTags[0].ti_Data = (IPTR)Open ("NIL:", MODE_OLDFILE);
461 System(global->g_play_cdda_command, PlayTags);
463 else
465 int res;
466 if (global->playing)
467 res = Stop_Play_Audio (global->g_cd);
468 else
469 res = Start_Play_Audio (global->g_cd);
470 if (!res)
471 Display_Error ("Cannot perform play audio command!");
472 else
473 global->playing = !global->playing;
477 if (!(signals & global->g_dos_sigbit))
478 return TRUE;
479 while ((msg = GetMsg(&global->DosProc->pr_MsgPort)))
481 packet = (PACKET *)msg->mn_Node.ln_Name;
482 packet->dp_Res1 = DOSTRUE;
483 packet->dp_Res2 = 0;
484 error = 0;
485 tmp = NULL;
486 BUG(dbprintf(global,
487 "[CDVDFS]\tPacket: %3ld %08lx %08lx %08lx %10s ",
488 packet->dp_Type,
489 packet->dp_Arg1,
490 packet->dp_Arg2,
491 packet->dp_Arg3,
492 typetostr(packet->dp_Type)
494 if (!global->g_cd)
496 switch (packet->dp_Type)
498 /* packets we will handle even if the SCSI device is not yet open: */
499 case ACTION_DIE:
500 break;
501 /* packets we cannot handle because the SCSI device is not yet open: */
502 default:
503 packet->dp_Res1 = DOSFALSE;
505 some return codes cause the WB to pop a requester; we don't
506 want this, though. Therefore we use this "exotic" return code:
508 packet->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
509 BUG(dbprintf(global, "ERR=%ld\n", (LONG) packet->dp_Res2);)
510 returnpacket(global, packet);
511 return TRUE;
514 else if (global->g_inhibited)
516 switch (packet->dp_Type)
518 /* packets we will handle even if the handler is inhibited: */
519 case ACTION_DIE:
520 case ACTION_INHIBIT:
521 case ACTION_MORE_CACHE:
522 case ACTION_DISK_INFO:
523 break;
524 /* packets we cannot handle because the handler is inhibited: */
525 default:
526 packet->dp_Res1 = DOSFALSE;
527 packet->dp_Res2 = ERROR_NOT_A_DOS_DISK;
528 BUG(dbprintf(global, "ERR=%ld\n", (LONG) packet->dp_Res2);)
529 returnpacket(global, packet);
530 return TRUE;
533 else if (global->DevList == NULL)
535 switch (packet->dp_Type)
537 /* packets we will handle even if no disk is inserted: */
538 case ACTION_DIE:
539 case ACTION_USER:
540 case ACTION_IS_FILESYSTEM:
541 case ACTION_INHIBIT:
542 case ACTION_MORE_CACHE:
543 case ACTION_DISK_INFO:
544 case ACTION_FLUSH:
545 case ACTION_FREE_LOCK:
546 break;
547 /* packets we cannot handle because no disk is inserted: */
548 default:
549 packet->dp_Res1 = DOSFALSE;
550 packet->dp_Res2 =
551 (global->g_disk_inserted ?
552 ERROR_NOT_A_DOS_DISK :
553 ERROR_NO_DISK
555 BUG(dbprintf(global, "ERR=%ld\n", (LONG) packet->dp_Res2);)
556 returnpacket(global, packet);
557 return TRUE;
561 switch(packet->dp_Type)
563 case ACTION_DIE: /* attempt to die? */
564 notdone = 0; /* try to die */
565 break;
566 case ACTION_USER: /* Mode,Par1,Par2 Bool */
567 error = Handle_Control_Packet
569 global,
570 packet->dp_Arg1,
571 packet->dp_Arg2,
572 packet->dp_Arg3
574 break;
575 case ACTION_FINDINPUT: /* FileHandle,Lock,Name Bool */
577 if (!(error = Mount_Check (global)))
579 CDROM_OBJ *obj;
580 CDROM_OBJ *parentdir = getlockfile(global, packet->dp_Arg2);
581 int offs;
583 if (parentdir->volume != global->g_volume)
585 /* old lock from another disk: */
586 error = ERROR_DEVICE_NOT_MOUNTED;
587 goto openbreak;
589 btos((BSTR)packet->dp_Arg3,buf);
590 BUG(dbprintf(global, "'%s' ", buf);)
591 offs = Check_For_Volume_Name_Prefix (global, buf);
592 if ((obj = Open_Object (parentdir, buf + offs)))
594 if (obj->symlink_f)
596 error = ERROR_IS_SOFT_LINK;
597 goto openbreak;
599 if (obj->directory_f)
601 error = ERROR_OBJECT_WRONG_TYPE;
602 goto openbreak;
605 else
607 if (global->iso_errno == ISOERR_ILLEGAL_NAME)
609 error = ERROR_INVALID_COMPONENT_NAME;
610 goto openbreak;
612 else if (global->iso_errno == ISOERR_NOT_FOUND)
613 error = ERROR_OBJECT_NOT_FOUND;
614 else if (global->iso_errno == ISOERR_NO_MEMORY)
616 error = ERROR_NO_FREE_STORE;
617 goto openbreak;
619 else if (global->iso_errno == ISOERR_IS_SYMLINK)
621 error = ERROR_IS_SOFT_LINK;
622 goto openbreak;
624 else
626 error = 333;
627 goto openbreak;
630 if (!error)
632 global->g_volume->file_handles++;
633 ((FH *)BADDR(packet->dp_Arg1))->fh_Arg1 = (IPTR)obj;
634 Register_File_Handle (obj);
638 openbreak:
639 break;
640 case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */
642 CDROM_OBJ *obj = (CDROM_OBJ *) packet->dp_Arg1;
643 char *ptr = (char *) packet->dp_Arg2;
644 LONG length = packet->dp_Arg3;
645 int actual;
647 if (obj->volume != global->g_volume)
649 /* old lock from another disk: */
650 error = ERROR_DEVICE_NOT_MOUNTED;
651 break;
653 actual = Read_From_File (obj, ptr, length);
654 packet->dp_Res1 = actual;
656 break;
657 case ACTION_END: /* FHArg1 Bool:TRUE */
659 CDROM_OBJ *obj = (CDROM_OBJ *) packet->dp_Arg1;
661 if (obj->volume != global->g_volume)
663 /* old lock from another disk: */
664 error = ERROR_DEVICE_NOT_MOUNTED;
665 break;
667 Unregister_File_Handle (obj);
668 Close_Object (obj);
669 global->g_volume->file_handles--;
671 break;
672 case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition */
674 CDROM_OBJ *obj = (CDROM_OBJ *) packet->dp_Arg1;
675 LONG offset = packet->dp_Arg2;
676 int mode = packet->dp_Arg3;
678 if (obj->volume != global->g_volume)
680 /* old lock from another disk: */
681 error = ERROR_DEVICE_NOT_MOUNTED;
682 break;
684 packet->dp_Res1 = obj->pos;
685 if (!Seek_Position (obj, offset, mode))
687 error = ERROR_SEEK_ERROR;
688 packet->dp_Res1 = -1;
691 break;
692 case ACTION_EXAMINE_NEXT: /* Lock,Fib Bool */
694 FIB *fib = (FIB *)BADDR (packet->dp_Arg2);
695 CDROM_OBJ *dir = getlockfile (global, packet->dp_Arg1);
696 CDROM_INFO info;
698 if (dir->volume != global->g_volume)
700 /* old lock from another disk: */
701 error = ERROR_DEVICE_NOT_MOUNTED;
702 break;
704 if (!dir->directory_f)
706 error = ERROR_OBJECT_WRONG_TYPE;
707 break;
709 if (Examine_Next (dir, &info, (ULONG *) &fib->fib_DiskKey))
711 error = 0;
712 Fill_FileInfoBlock (global, fib, &info, dir->volume);
714 else
716 error = ERROR_NO_MORE_ENTRIES;
718 break;
720 case ACTION_EXAMINE_FH: /* FHArg1,Fib Bool */
721 case ACTION_EXAMINE_OBJECT: /* Lock,Fib Bool */
723 FIB *fib = (FIB *)BADDR(packet->dp_Arg2);
724 CDROM_INFO info;
725 CDROM_OBJ *obj;
727 if (packet->dp_Type == ACTION_EXAMINE_FH)
728 obj = (CDROM_OBJ*) packet->dp_Arg1;
729 else
730 obj = getlockfile (global, packet->dp_Arg1);
731 if (obj->volume != global->g_volume)
733 /* old lock from another disk: */
734 error = ERROR_DEVICE_NOT_MOUNTED;
735 break;
737 fib->fib_DiskKey = 0;
738 error = 0;
739 if (!CDROM_Info (obj, &info))
740 error = -1;
741 else
742 Fill_FileInfoBlock (global, fib, &info, obj->volume);
744 break;
745 case ACTION_INFO: /* Lock, InfoData Bool:TRUE */
746 tmp = (void *)BADDR(packet->dp_Arg2);
747 error = -1;
748 /* fall through */
749 case ACTION_DISK_INFO: /* InfoData Bool:TRUE */
752 register INFODATA *id;
754 id = (INFODATA *)(error ? tmp : (void *)BADDR (packet->dp_Arg1));
755 error = 0;
756 memset (id, 0, sizeof(*id));
757 id->id_UnitNumber = global->g_unit;
758 if (global->g_inhibited) {
759 id->id_DiskType = 0x42555359 /* "BUSY" */;
760 } else {
761 int errcode = Mount_Check (global);
762 if (errcode == ERROR_NO_DISK) {
763 id->id_DiskType = ID_NO_DISK_PRESENT;
764 } else if (!errcode) {
765 id->id_DiskType = ID_DOS_DISK;
766 id->id_NumBlocks= Volume_Size (global->g_volume);
767 id->id_BytesPerBlock = Block_Size (global->g_volume);
768 id->id_VolumeNode = MKBADDR(global->DevList);
769 } else {
770 /* For example empty CD-R */
771 id->id_DiskType = ID_UNREADABLE_DISK;
772 id->id_NumBlocks = 0;
773 id->id_BytesPerBlock = 2048;
775 id->id_DiskState = ID_WRITE_PROTECTED;
777 id->id_NumBlocksUsed = id->id_NumBlocks;
780 break;
781 case ACTION_IS_FILESYSTEM: /* - Bool */
782 packet->dp_Res1 = DOSTRUE;
783 break;
784 case ACTION_PARENT_FH: /* FHArg1 ParentLock */
785 case ACTION_PARENT: /* Lock ParentLock */
787 if (!(error = Mount_Check (global)))
789 if (packet->dp_Arg1)
791 CDROM_OBJ *obj;
792 CDROM_OBJ *parent;
794 if (packet->dp_Type == ACTION_PARENT_FH)
795 obj = (CDROM_OBJ*) packet->dp_Arg1;
796 else
797 obj = getlockfile (global, packet->dp_Arg1);
798 if (obj->volume != global->g_volume)
800 /* old lock from another disk: */
801 error = ERROR_DEVICE_NOT_MOUNTED;
802 break;
804 if (Is_Top_Level_Object (obj))
806 packet->dp_Res1 = packet->dp_Res2 = 0;
808 else
810 parent = Find_Parent (obj);
811 if (!parent)
813 if (global->iso_errno == ISOERR_NO_MEMORY)
814 error = ERROR_NO_FREE_STORE;
815 else
816 error = ERROR_OBJECT_NOT_FOUND;
818 else
820 packet->dp_Res1 = (IPTR)MKBADDR(cdlock (parent, ACCESS_READ));
824 else
825 error = ERROR_OBJECT_NOT_FOUND;
828 break;
829 case ACTION_LOCATE_OBJECT: /* Lock,Name,Mode Lock */
831 if (!(error = Mount_Check (global)))
833 CDROM_OBJ *parentdir = getlockfile (global, packet->dp_Arg1);
834 CDROM_OBJ *obj;
835 int offs;
837 if (parentdir->volume != global->g_volume)
839 /* old lock from another disk: */
840 error = ERROR_DEVICE_NOT_MOUNTED;
841 break;
843 btos ((BSTR)packet->dp_Arg2, buf);
844 #ifndef NDEBUG
845 dbprintf (global, "'%s' %ld ", buf, packet->dp_Arg3);
846 if (strcmp(buf,"debugoff") == 0)
847 global->DBDisable = 1;
848 if (strcmp(buf,"debugon") == 0)
849 global->DBDisable = 0;
850 #endif
851 offs = Check_For_Volume_Name_Prefix (global, buf);
852 if (buf[offs]==0)
854 if (parentdir)
855 obj = Clone_Object (parentdir);
856 else
857 obj = Open_Top_Level_Directory (global->g_volume);
859 else
860 obj = Open_Object (parentdir, buf + offs);
861 if (obj)
863 if (obj->symlink_f)
864 error = ERROR_IS_SOFT_LINK;
865 else
866 packet->dp_Res1 = (IPTR)MKBADDR(cdlock (obj, packet->dp_Arg3));
868 else
870 if (global->iso_errno == ISOERR_SCSI_ERROR)
871 error = ERROR_SEEK_ERROR;
872 else if (global->iso_errno == ISOERR_ILLEGAL_NAME)
873 error = ERROR_INVALID_COMPONENT_NAME;
874 else if (global->iso_errno == ISOERR_NOT_FOUND)
875 error = ERROR_OBJECT_NOT_FOUND;
876 else if (global->iso_errno == ISOERR_NO_MEMORY)
877 error = ERROR_NO_FREE_STORE;
878 else if (global->iso_errno == ISOERR_IS_SYMLINK)
879 error = ERROR_IS_SOFT_LINK;
880 else
881 error = 333;
885 break;
886 case ACTION_COPY_DIR: /* Lock, Lock */
888 if (packet->dp_Arg1)
890 CDROM_OBJ *obj = getlockfile (global, packet->dp_Arg1);
891 CDROM_OBJ *new;
893 if (obj->volume != global->g_volume)
895 /* old lock from another disk: */
896 error = ERROR_DEVICE_NOT_MOUNTED;
897 break;
899 new = Clone_Object (obj);
900 if (!new)
901 error = ERROR_NO_FREE_STORE;
902 else
903 packet->dp_Res1 = (IPTR)MKBADDR(cdlock (new, ACCESS_READ));
905 else
906 packet->dp_Res1 = 0;
908 break;
909 case ACTION_FREE_LOCK: /* Lock Bool */
910 if (packet->dp_Arg1);
911 cdunlock((LOCK *)BADDR(packet->dp_Arg1));
912 break;
913 case ACTION_CURRENT_VOLUME: /* FHArg1 DevList */
915 CDROM_OBJ *obj = (CDROM_OBJ*) packet->dp_Arg1;
916 if (obj)
917 packet->dp_Res1 = (IPTR)MKBADDR(Find_Dev_List (obj));
918 else
919 packet->dp_Res1 = (IPTR)MKBADDR(global->DevList);
920 break;
922 case ACTION_INHIBIT: /* Bool Bool */
923 if (packet->dp_Arg1 != DOSFALSE)
925 /* true means forbid access */
926 global->g_inhibited++;
927 if (global->DevList)
928 Unmount(global);
929 Hide_CDDA_Icon (global);
930 global->g_cd->t_changeint2 = -2;
932 else
934 /* false means access allowed */
935 if (global->g_inhibited)
936 global->g_inhibited--;
937 if (global->g_inhibited == 0)
939 global->g_disk_inserted = FALSE;
940 Check_Disk(global);
943 break;
945 * FINDINPUT and FINDOUTPUT normally should return the
946 * 'write protected' error. If the field 'Name', however,
947 * designates the root (e.g. CD0:), then the 'wrong type'
948 * error should be returned. Otherwise, AmigaDOS would do
949 * some funny things (such as saying 'Volume CD0: is write-
950 * protected') if you try to mount the handler with the
951 * field 'Mount' set to 1.
953 case ACTION_FINDOUTPUT: /* Handle Lock Name Bool */
954 case ACTION_FINDUPDATE: /* Handle Lock Name Bool */
956 int pos;
958 btos((BSTR)packet->dp_Arg3,buf);
959 BUG(dbprintf(global, "'%s' ", buf);)
960 if ((pos = Check_For_Volume_Name_Prefix (global, buf)) && buf[pos] == 0)
961 error = ERROR_OBJECT_WRONG_TYPE;
962 else
963 error = ERROR_DISK_WRITE_PROTECTED;
964 break;
966 case ACTION_SAME_LOCK: /* Lock Lock Bool */
968 CDROM_OBJ *obj1 = getlockfile(global, packet->dp_Arg1),
969 *obj2 = getlockfile(global, packet->dp_Arg2);
970 if (Same_Objects (obj1, obj2))
971 packet->dp_Res1 = DOSTRUE;
972 else
973 packet->dp_Res1 = DOSFALSE;
974 break;
976 case ACTION_READ_LINK: /* Lock Name Buf Length NumChar */
978 CDROM_OBJ *obj;
979 CDROM_OBJ *parentdir = getlockfile (global, packet->dp_Arg1);
980 char *outbuf = (char *) packet->dp_Arg3;
981 t_ulong maxlength = packet->dp_Arg4;
982 int offs;
983 char buf[256];
984 int res;
986 if (parentdir->volume != global->g_volume)
988 /* old lock from another disk: */
989 error = ERROR_DEVICE_NOT_MOUNTED;
990 break;
992 BUG(dbprintf (global, "'%s' len=%lu ", packet->dp_Arg2, maxlength);)
993 offs = Check_For_Volume_Name_Prefix (global, (char *) packet->dp_Arg2);
994 obj = Open_Object (parentdir, (char *) packet->dp_Arg2 + offs);
995 if (obj)
997 res = Get_Link_Name (obj, buf, sizeof (buf));
998 if (
999 res == 0 ||
1000 strlen (buf) +
1001 strlen (global->g_vol_name+1) + 1
1003 maxlength
1005 strncpy (outbuf, "illegal_link", maxlength - 1);
1006 else
1008 if (buf[0] == ':')
1009 strcpy (outbuf, global->g_vol_name+1);
1010 else
1011 outbuf[0] = 0;
1012 strcat (outbuf, buf);
1014 outbuf[maxlength - 1] = 0;
1015 Close_Object (obj);
1016 packet->dp_Res1 = strlen (outbuf);
1018 else
1020 if (global->iso_errno == ISOERR_ILLEGAL_NAME)
1021 error = ERROR_INVALID_COMPONENT_NAME;
1022 else if (global->iso_errno == ISOERR_NOT_FOUND)
1023 error = ERROR_OBJECT_NOT_FOUND;
1024 else if (global->iso_errno == ISOERR_NO_MEMORY)
1025 error = ERROR_NO_FREE_STORE;
1026 else if (global->iso_errno == ISOERR_IS_SYMLINK)
1027 error = ERROR_IS_SOFT_LINK;
1028 else
1029 error = 333;
1031 break;
1033 case ACTION_MAKE_LINK:
1034 case ACTION_RENAME_DISK:
1035 case ACTION_WRITE:
1036 case ACTION_SET_PROTECT:
1037 case ACTION_DELETE_OBJECT:
1038 case ACTION_RENAME_OBJECT:
1039 case ACTION_CREATE_DIR:
1040 case ACTION_SET_COMMENT:
1041 case ACTION_SET_DATE:
1042 case ACTION_SET_FILE_SIZE:
1043 error = ERROR_DISK_WRITE_PROTECTED;
1044 break;
1045 case ACTION_FLUSH: /* writeout bufs, disk motor off */
1046 break;
1048 * A few other packet types which we do not support
1050 case ACTION_MORE_CACHE: /* #BufsToAdd Bool */
1051 case ACTION_WAIT_CHAR: /* Timeout, ticks Bool */
1052 case ACTION_SCREEN_MODE:/* Bool(-1:RAW 0:CON) OldState */
1053 default:
1054 error = ERROR_ACTION_NOT_KNOWN;
1055 break;
1057 if (packet)
1059 if (error)
1061 BUG(dbprintf(global, "ERR=%ld\n", error);)
1062 packet->dp_Res1 = DOSFALSE;
1063 packet->dp_Res2 = error;
1065 else
1067 BUG(dbprintf(global, "RES=%06lx\n", packet->dp_Res1));
1069 if (packet->dp_Type != ACTION_DIE)
1070 returnpacket(global, packet);
1071 else
1072 global->g_death_packet = packet;
1075 if (notdone)
1076 return notdone;
1077 BUG(dbprintf(global, "Can we remove ourselves? ");)
1078 BUG(Delay(100);) /* I wanna even see the debug message! */
1079 Forbid();
1080 if (
1081 global->g_cd &&
1083 packetsqueued(global) ||
1085 global->g_volume &&
1087 global->g_volume->locks || global->g_volume->file_handles
1093 Permit();
1094 BUG(dbprintf(global, " .. not yet!\n");)
1095 return TRUE; /* sorry... can't exit */
1097 else
1099 BUG(dbprintf(global, " .. yes!\n");)
1101 return FALSE;
1105 * PACKET ROUTINES. Dos Packets are in a rather strange format as you
1106 * can see by this and how the PACKET structure is extracted in the
1107 * GetMsg() of the main routine.
1110 static void returnpacket(struct CDVDBase *global, struct DosPacket *packet)
1112 register struct Message *mess;
1113 register struct MsgPort *replyport;
1115 replyport = packet->dp_Port;
1116 mess = packet->dp_Link;
1117 packet->dp_Port = &global->DosProc->pr_MsgPort;
1118 mess->mn_Node.ln_Name = (char *)packet;
1119 mess->mn_Node.ln_Succ = NULL;
1120 mess->mn_Node.ln_Pred = NULL;
1121 PutMsg(replyport, mess);
1125 * Are there any packets queued to our device?
1128 int packetsqueued (struct CDVDBase *global)
1130 return ((void *)global->DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
1131 (void *)&global->DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
1134 #ifndef USE_FAST_BSTR
1136 * Convert a BSTR into a normal string.. copying the string into buf.
1137 * I use normal strings for internal storage, and convert back and forth
1138 * when required.
1140 static void btos(BSTR bstr, char *buf)
1142 UBYTE *src = BADDR(bstr);
1143 UBYTE len = *src++;
1145 CopyMem(src, buf, len);
1146 buf[len] = 0;
1148 #endif
1151 * The lock function. The file has already been checked to see if it
1152 * is lockable given the mode.
1155 static LOCK *cdlock(CDROM_OBJ *cdfile, int mode)
1157 struct CDVDBase *global = cdfile->global;
1158 LOCK *lock = AllocVec(sizeof(LOCK), MEMF_PUBLIC | MEMF_CLEAR);
1160 cdfile->volume->locks++;
1161 lock->fl_Key = (LONG) Location (cdfile);
1162 lock->fl_Link = (BPTR) cdfile;
1163 lock->fl_Access = ACCESS_READ;
1164 lock->fl_Task = &global->DosProc->pr_MsgPort;
1165 lock->fl_Volume = (BPTR)MKBADDR(global->DevList);
1166 Register_Lock (lock);
1167 return(lock);
1170 static void cdunlock (LOCK *lock)
1172 CDROM_OBJ *obj = (CDROM_OBJ *) lock->fl_Link;
1173 struct CDVDBase *global = obj->global;
1175 Unregister_Lock (lock);
1176 --obj->volume->locks;
1178 /* if all locks and filehandles have been removed, and if the volume
1179 * is not the current volume, then the volume node may be removed:
1181 if (obj->volume != global->g_volume &&
1182 obj->volume->locks == 0 && obj->volume->file_handles == 0) {
1183 VOLUME *vol = obj->volume;
1185 Forbid ();
1186 Remove_Volume_Node (global, vol->devlist);
1187 Permit ();
1188 Close_Object (obj);
1189 Close_Volume (vol);
1190 Send_Event (global, FALSE);
1191 } else
1192 Close_Object (obj);
1194 FreeVec(lock); /* free lock */
1198 * GETLOCKFILE(bptrlock)
1200 * Return the CDROM_OBJ (file or directory) associated with the
1201 * given lock, which is passed as a BPTR.
1203 * According to the DOS spec, the only way a NULL lock will ever be
1204 * passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
1205 * In anycase, If a NULL lock is passed to me I simply assume it means
1206 * the root directory of the CDROM.
1209 CDROM_OBJ *getlockfile (struct CDVDBase *global, IPTR lock)
1211 LOCK *rl = (LOCK *)BADDR (lock);
1213 if (rl)
1214 return (CDROM_OBJ *) rl->fl_Link;
1215 return global->g_top_level_obj;
1219 * If p_pathname contains a ':' character, return the position of the first
1220 * character after ':'
1221 * Otherwise, return 0.
1224 int Check_For_Volume_Name_Prefix (struct CDVDBase *global, char *p_pathname)
1226 char *pos = strchr (p_pathname, ':');
1228 return pos ? (pos - p_pathname) + 1 : 0;
1232 * Fills a FileInfoBlock with the information contained in the
1233 * directory record of a CD-ROM directory or file.
1236 static void Fill_FileInfoBlock (struct CDVDBase *global, FIB *p_fib, CDROM_INFO *p_info, VOLUME *p_volume)
1238 char *src = p_info->name;
1239 char *dest = p_fib->fib_FileName;
1240 int len = p_info->name_length;
1242 if (len > MAX_NAME_LEN)
1243 len = MAX_NAME_LEN;
1244 if (p_info->symlink_f)
1245 p_fib->fib_DirEntryType = ST_SOFTLINK;
1246 else
1247 p_fib->fib_DirEntryType = p_info->directory_f ? ST_USERDIR : ST_FILE;
1249 if (len == 1 && *src == ':')
1251 /* root of file system: */
1252 p_fib->fib_DirEntryType = ST_ROOT;
1253 /* file name == volume name: */
1254 len = global->g_vol_name[0];
1255 *dest++ = len;
1256 strncpy(dest, global->g_vol_name+1, len);
1258 else
1260 /* copy file name: */
1261 if (!global->g_show_version_numbers)
1263 WORD i;
1264 for (i=0; i<len; i++)
1266 if (src[i] == ';') {
1267 len = i;
1268 break;
1272 *dest++ = len;
1273 strncpy(dest, src, len);
1275 if ((global->g_map_to_lowercase && p_volume->protocol == PRO_ISO) ||
1276 (global->g_maybe_map_to_lowercase && !p_volume->mixed_char_filenames))
1278 /* convert ISO filename to lowercase: */
1279 int i;
1280 char *cp = dest;
1281 for (i=0; i<len; i++, cp++)
1282 *cp = ToLower (*cp);
1285 /* terminate string */
1286 dest[len] = 0;
1287 /* I don't know exactly why I have to set fib_EntryType, but other
1288 * handlers (e.g. DiskHandler by J Toebes et.al.) also do this.
1291 p_fib->fib_EntryType = p_fib->fib_DirEntryType;
1293 p_fib->fib_Protection = p_info->protection;
1294 p_fib->fib_Size = p_info->file_length;
1295 p_fib->fib_NumBlocks = p_info->file_length >> 11;
1296 p_fib->fib_Date.ds_Days = p_info->date / (24 * 60 * 60);
1297 p_fib->fib_Date.ds_Minute = (p_info->date % (24 * 60 * 60)) / 60;
1298 p_fib->fib_Date.ds_Tick = (p_info->date % 60) * TICKS_PER_SECOND;
1300 dest = p_fib->fib_Comment;
1301 len = p_info->comment_length;
1302 if (len > MAX_COMMENT_LEN)
1303 len = MAX_COMMENT_LEN;
1304 *dest++ = len;
1305 strncpy(dest, p_info->comment, len);
1306 dest[len] = 0;
1310 * Create Volume node and add to the device list. This will
1311 * cause the WORKBENCH to recognize us as a disk. If we don't
1312 * create a Volume node, Wb will not recognize us.
1315 static void Create_Volume_Node (struct CDVDBase *global, LONG p_disk_type, ULONG p_volume_date) {
1316 struct DeviceList *dl;
1318 BUG(dbprintf(global, "Inserted volume name: %s\n", global->g_vol_name + 1));
1319 dl = Find_Volume_Node (global, global->g_vol_name + 1);
1320 if (dl)
1322 BUG(dbprintf(global, "[Reusing old volume node]");)
1323 Forbid ();
1324 global->DevList = dl;
1325 dl->dl_Task = &global->DosProc->pr_MsgPort;
1326 Permit ();
1328 else
1330 global->DevList = dl = (struct DeviceList *)MakeDosEntry(global->g_vol_name+1, DLT_VOLUME);
1331 dl->dl_Task = &global->DosProc->pr_MsgPort;
1332 dl->dl_DiskType = p_disk_type;
1333 dl->dl_VolumeDate.ds_Days = p_volume_date / (24 * 60 * 60);
1334 dl->dl_VolumeDate.ds_Minute = (p_volume_date % (24 * 60 * 60)) / 60;
1335 dl->dl_VolumeDate.ds_Tick = (p_volume_date % 60) * TICKS_PER_SECOND;
1336 BUG(dbprintf(global, "Name to add: <0x%08lX> %b\n", dl->dl_Name, dl->dl_Name));
1337 AddDosEntry((struct DosList *)dl);
1338 BUG(dbprintf(global, "Added name: <0x%08lX> %b\n", dl->dl_Name, dl->dl_Name));
1339 /* Under MorphOS AddDosEntry() can modify volume name in case if such a volume
1340 is already present in the DosList. In this case it reallocates the name string,
1341 appends "_%d" to it and modifies dl_Name. In this case we can lose our
1342 volume node if a user removes a disc while some locks are pending and then
1343 puts it back, because we wouldn't be able to find it by name in our list.
1344 A typical case is using CDVDFS under MorphOS along with built-in MorphOS
1345 handler, */
1346 Register_Volume_Node (global, dl, global->g_vol_name + 1);
1351 * Mount a volume.
1354 static void Mount (struct CDVDBase *global)
1356 char buf[33];
1358 if (Has_Audio_Tracks (global->g_cd)) {
1359 Show_CDDA_Icon (global);
1360 global->g_cd->t_changeint2 = global->g_cd->t_changeint;
1363 global->g_volume = Open_Volume (global->g_cd, global->g_use_rock_ridge, global->g_use_joliet);
1364 if (!global->g_volume) {
1365 BUG(dbprintf (global, "!!! cannot open VOLUME (iso_errno=%d) !!!\n", global->iso_errno);)
1366 return;
1367 } else {
1368 global->g_disk_inserted = TRUE;
1369 global->g_cd->t_changeint2 = global->g_cd->t_changeint;
1370 global->g_top_level_obj = Open_Top_Level_Directory (global->g_volume);
1371 if (!global->g_top_level_obj) {
1372 BUG(dbprintf (global, "!!! cannot open top level directory !!!\n");)
1373 return;
1377 BUG(dbprintf (global, "***mounting*** ");)
1378 Volume_ID (global->g_volume, buf, sizeof (buf)-1);
1379 global->g_vol_name[0] = strlen (buf);
1380 CopyMem(buf, global->g_vol_name+1, strlen (buf));
1382 if (!(global->g_vol_name[0]))
1383 CopyMem("\7Unnamed", global->g_vol_name, 8);
1385 /* AmigaDOS expects the BCPL string g_vol_name to be null-terminated: */
1386 global->g_vol_name[(int)(global->g_vol_name[0])+1] = 0;
1388 /* convert ISO volume name to lowercase: */
1389 if ((global->g_map_to_lowercase && global->g_volume->protocol == PRO_ISO) ||
1390 (global->g_maybe_map_to_lowercase && !global->g_volume->mixed_char_filenames)) {
1391 int i;
1392 for (i=0; i<global->g_vol_name[0]; i++)
1393 global->g_vol_name[i+1] = ToLower (global->g_vol_name[i+1]);
1396 global->g_volume->locks = Reinstall_Locks (global);
1397 global->g_volume->file_handles = Reinstall_File_Handles (global);
1399 Create_Volume_Node (global, ID_CDFS_DISK, Volume_Creation_Date (global->g_volume));
1400 global->g_volume->devlist = global->DevList;
1401 global->g_cd->t_changeint2 = global->g_cd->t_changeint;
1402 Send_Event (global, TRUE);
1405 /* Remove a volume node from the DOS list.
1406 * (Must be nested between Forbid() and Permit()!)
1407 * Since DOS uses singly linked lists, we must (ugg) search it manually to find
1408 * the link before our Volume entry.
1411 static void Remove_Volume_Node (struct CDVDBase *global, struct DeviceList* p_volume)
1413 struct DosList *dl;
1415 Unregister_Volume_Node (global, p_volume);
1417 dl = LockDosList(LDF_WRITE | LDF_VOLUMES);
1418 if (dl)
1420 RemDosEntry((struct DosList *)p_volume);
1421 FreeDosEntry((struct DosList *)p_volume);
1422 UnLockDosList(LDF_WRITE | LDF_VOLUMES);
1426 /* Unmount a volume, and optionally unload the handler code.
1429 static void Unmount (struct CDVDBase *global)
1431 global->g_cd->t_changeint2 = global->g_cd->t_changeint;
1432 Hide_CDDA_Icon (global);
1434 if (global->DevList) {
1436 BUG(dbprintf(global, "***unmounting*** ");)
1438 Close_Object (global->g_top_level_obj);
1440 if (global->g_volume->locks == 0 && global->g_volume->file_handles == 0) {
1441 Remove_Volume_Node (global, global->DevList);
1442 Close_Volume (global->g_volume);
1443 } else {
1444 BUG(dbprintf(global, "[there are still %d locks on this volume]",
1445 global->g_volume->locks);)
1446 BUG(dbprintf(global, "[there are still %d file handles on this volume]",
1447 global->g_volume->file_handles);)
1448 global->DevList->dl_Task = NULL;
1451 global->DevList = NULL;
1454 Send_Event (global, FALSE);
1456 global->g_volume = 0;
1458 /* when the handler code exits the corresponding device
1459 * node (e.g. "CD0") will be modified. The handler code
1460 * will be unloaded and the task entry will be set to
1461 * zero, so the next device access will reload and
1462 * restart the handler code.
1467 * Mount_Check returns 0 if a valid disk is inserted in the drive.
1468 * Error code is returned if drive is empty or disk is unknown.
1469 * A check is only performed if previously the drive was empty.
1472 int Mount_Check (struct CDVDBase *global)
1474 D(bug("[CDVDFS]\tMount_Check\n"));
1475 if (!global->g_disk_inserted) {
1477 * No disk was inserted up to now: we will check whether
1478 * a disk has been inserted by sending the test unit ready
1479 * command. We have to send the command twice because
1480 * the first SCSI command after inserting a new disk is
1481 * always rejected.
1483 if (0 == Test_Unit_Ready (global->g_cd))
1485 D(bug("[CDVDFS]\tDrive not ready, not mounting\n"));
1486 return ERROR_NO_DISK;
1489 D(bug("[CDVDFS]\tDrive ready, mounting disc\n"));
1490 global->g_disk_inserted = TRUE;
1491 Mount (global);
1492 if (!global->DevList)
1493 return ERROR_NO_DISK;
1494 } else {
1495 D(bug("[CDVDFS]\tDisc already mounted.\n"));
1497 if (global->g_volume == NULL || global->g_volume->handler == NULL)
1498 return ERROR_NOT_A_DOS_DISK;
1499 return 0;
1503 * Open timer device structures:
1506 int Open_Timer_Device (struct CDVDBase *global) {
1508 global->g_timer_mp = CreateMsgPort();
1509 if (global->g_timer_mp)
1511 global->g_timer_io = (struct timerequest *)CreateIORequest
1513 global->g_timer_mp,
1514 sizeof (struct timerequest)
1516 if (global->g_timer_io)
1518 if (
1519 OpenDevice
1521 (UBYTE *)TIMERNAME,
1522 UNIT_VBLANK,
1523 (struct IORequest *)global->g_timer_io,
1525 )==0
1528 global->g_timer_sigbit = 1L << global->g_timer_mp->mp_SigBit;
1529 return 1;
1531 else
1533 BUG(dbprintf (global, "cannot open timer device!\n");)
1535 DeleteIORequest ((struct IORequest *) global->g_timer_io);
1537 else
1539 BUG(dbprintf (global, "cannot create timer i/o structure!\n");)
1541 DeleteMsgPort (global->g_timer_mp);
1543 else
1545 BUG(dbprintf (global, "cannot create timer message port!\n");)
1547 return 0;
1551 * Remove timer device structures:
1554 static void Cleanup_Timer_Device (struct CDVDBase *global)
1556 /* remove any pending requests: */
1557 if (!CheckIO ((struct IORequest *) global->g_timer_io))
1558 AbortIO ((struct IORequest *) global->g_timer_io);
1559 WaitIO ((struct IORequest *) global->g_timer_io);
1561 CloseDevice ((struct IORequest *) global->g_timer_io);
1562 DeleteIORequest ((struct IORequest *) global->g_timer_io);
1563 DeleteMsgPort (global->g_timer_mp);
1567 * Send timer request
1570 static void Send_Timer_Request (struct CDVDBase *global)
1572 global->g_timer_io->tr_node.io_Command = TR_ADDREQUEST;
1573 global->g_timer_io->tr_time.tv_secs = 1;
1574 global->g_timer_io->tr_time.tv_micro = 0;
1575 SendIO ((struct IORequest *) global->g_timer_io);
1579 * Check whether the disk has been removed or inserted.
1582 static void Check_Disk (struct CDVDBase *global)
1584 int i;
1585 ULONG l1, l2;
1587 BUG(dbprintf (global, "[CDVDFS]\tChecking Disk... ");)
1588 i = Test_Unit_Ready (global->g_cd);
1590 l1 = global->g_cd->t_changeint;
1591 l2 = global->g_cd->t_changeint2;
1592 BUG(if (l1==l2 && i) dbprintf (global, "no disk change (T %ld)", l1);)
1593 if (l1!=l2 && i)
1595 global->g_disk_inserted = TRUE;
1596 BUG(dbprintf (global, "disk has been inserted (T %ld)", l1);)
1597 if (global->DevList)
1598 Unmount (global);
1599 Delay (50);
1600 Clear_Sector_Buffers (global->g_cd);
1601 Mount (global);
1603 BUG(if (l1==l2 && !i) dbprintf (global, "no disk in drive (T %ld)", l1);)
1604 if (l1!=l2 && !i)
1606 global->g_disk_inserted = FALSE;
1607 BUG(dbprintf (global, "disk has been removed (T %ld)", l1);)
1608 global->playing = FALSE;
1609 Unmount (global);
1610 global->g_cd->t_changeint2 = global->g_cd->t_changeint;
1612 BUG(dbprintf (global, "\n");)
1615 /* The following lines will generate a `disk inserted/removed' event, in order
1616 * to get Workbench to rescan the DosList and update the list of
1617 * volume icons.
1620 static void Send_Event (struct CDVDBase *global, int p_inserted)
1622 struct IOStdReq *InputRequest;
1623 struct MsgPort *InputPort;
1624 struct InputEvent InputEvent;
1627 InputPort = (struct MsgPort *) CreateMsgPort ();
1628 if (InputPort)
1630 InputRequest = (struct IOStdReq *)
1631 CreateIORequest (InputPort, sizeof (struct IOStdReq));
1632 if (InputRequest)
1634 if (!OpenDevice ((UBYTE *) "input.device", 0,
1635 (struct IORequest *) InputRequest, 0))
1637 memset (&InputEvent, 0, sizeof (struct InputEvent));
1639 InputEvent.ie_Class = p_inserted ? IECLASS_DISKINSERTED :
1640 IECLASS_DISKREMOVED;
1642 InputRequest->io_Command = IND_WRITEEVENT;
1643 InputRequest->io_Data = &InputEvent;
1644 InputRequest->io_Length = sizeof (struct InputEvent);
1646 DoIO ((struct IORequest *) InputRequest);
1648 CloseDevice ((struct IORequest *) InputRequest);
1650 DeleteIORequest ((struct IORequest *)InputRequest);
1652 DeleteMsgPort (InputPort);