2 * Copyright (C) 2013, The AROS Development Team
4 * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
6 * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1
9 #include <aros/debug.h>
11 #include <proto/exec.h>
12 #include <proto/dos.h>
18 static LONG
CDFS_DeviceMount(struct CDFS
*cdfs
, struct CDFSDevice
*dev
)
20 struct CDFSVolume
*vol
;
21 LONG err
= ERROR_NO_FREE_STORE
;
22 const struct CDFSOps
*op
= &ISO9660_Ops
;
24 vol
= AllocVec(sizeof(*vol
), MEMF_PUBLIC
| MEMF_CLEAR
);
27 vol
->cv_CDFSBase
= cdfs
;
28 vol
->cv_DosVolume
.dl_Type
= DLT_VOLUME
;
29 vol
->cv_DosVolume
.dl_DiskType
= AROS_MAKE_ID('C','D','F','S');
30 vol
->cv_DosVolume
.dl_Task
= &((struct Process
*)FindTask(NULL
))->pr_MsgPort
;
31 NEWLIST(&vol
->cv_FileLocks
);
33 err
= op
->op_Mount(vol
);
34 if (err
== RETURN_OK
) {
35 struct Library
*DOSBase
;
36 DOSBase
= OpenLibrary("dos.library",0);
41 /* Look for an identical volume */
43 dl
= LockDosList(LDF_VOLUMES
| LDF_READ
);
45 dl
= FindDosEntry(dl
, AROS_BSTR_ADDR(vol
->cv_DosVolume
.dl_Name
), LDF_VOLUMES
| LDF_READ
);
46 if (dl
&& dl
->dol_misc
.dol_volume
.dol_DiskType
== vol
->cv_DosVolume
.dl_DiskType
47 && memcmp(&vol
->cv_DosVolume
.dl_VolumeDate
, &dl
->dol_misc
.dol_volume
.dol_VolumeDate
, sizeof(struct DateStamp
)) == 0
48 && dl
->dol_misc
.dol_volume
.dol_LockList
!= BNULL
// Dismounted
50 /* Identical match found */
55 /* Dispose of the test volume */
56 vol
->cv_Ops
->op_Unmount(vol
);
59 /* Use the old volume */
60 vol
= dev
->cd_Volume
= B_VOLUME(MKBADDR(dl
));
61 D(bug("%s: Remounting '%b'\n", __func__
, dl
->dol_Name
));
63 /* Mark volume as online */
64 vol
->cv_DosVolume
.dl_Task
= &(((struct Process
*)FindTask(NULL
))->pr_MsgPort
);
65 vol
->cv_DosVolume
.dl_LockList
= BNULL
;
67 /* Insert the new volume */
68 AddDosEntry((struct DosList
*)&vol
->cv_DosVolume
);
70 UnLockDosList(LDF_VOLUMES
| LDF_READ
);
71 CloseLibrary(DOSBase
);
79 dev
->cd_Volume
= NULL
;
84 static VOID
CDFS_DeviceUnmount(struct CDFS
*cdfs
, struct CDFSDevice
*dev
)
86 struct CDFSVolume
*vol
= dev
->cd_Volume
;
91 /* Detach from the device */
92 dev
->cd_Volume
= NULL
;
93 /* Still has locks? */
94 if (!IsListEmpty(&vol
->cv_FileLocks
)) {
98 /* Mark the volume as dismounted */
99 vol
->cv_DosVolume
.dl_Task
= NULL
;
101 /* Make the dismounted filelock list */
102 flp
= &vol
->cv_DosVolume
.dl_LockList
;
103 ForeachNode(&vol
->cv_FileLocks
, cl
) {
104 *flp
= MKBADDR(&cl
->cl_FileLock
);
105 flp
= &cl
->cl_FileLock
.fl_Link
;
109 /* Put the volume in the CDFS volume list */
110 ObtainSemaphore(&cdfs
->cb_Semaphore
);
111 AddHead(&cdfs
->cb_Volumes
, (struct Node
*)&vol
->cv_Node
);
112 ReleaseSemaphore(&cdfs
->cb_Semaphore
);
114 struct Library
*DOSBase
= OpenLibrary("dos.library",0);
116 LockDosList(LDF_VOLUMES
| LDF_WRITE
| LDF_DELETE
);
117 RemDosEntry((struct DosList
*)&vol
->cv_DosVolume
);
118 UnLockDosList(LDF_VOLUMES
| LDF_WRITE
| LDF_DELETE
);
120 CloseLibrary(DOSBase
);
122 /* Dispose of the volume */
123 vol
->cv_Ops
->op_Unmount(vol
);
128 static LONG
CDFS_DeviceOpen(struct CDFS
*cdfs
, struct FileSysStartupMsg
*fssm
, struct CDFSDevice
**devp
)
130 struct CDFSDevice
*dev
;
133 dev
= AllocVec(sizeof(*dev
), MEMF_PUBLIC
| MEMF_CLEAR
);
135 err
= BCache_Create(SysBase
, fssm
, &dev
->cd_BCache
);
136 if (err
== RETURN_OK
) {
137 D(bug("%s: BCache created\n", __func__
));
138 ObtainSemaphore(&cdfs
->cb_Semaphore
);
139 AddTail(&cdfs
->cb_Devices
, (struct Node
*)&dev
->cd_Node
);
140 ReleaseSemaphore(&cdfs
->cb_Semaphore
);
141 D(bug("%s: Device opened\n", __func__
));
147 err
= ERROR_NO_FREE_STORE
;
153 static VOID
CDFS_DeviceClose(struct CDFS
*cdfs
, struct CDFSDevice
*dev
)
155 if (dev
->cd_Volume
) {
156 CDFS_DeviceUnmount(cdfs
, dev
);
158 BCache_Delete(dev
->cd_BCache
);
159 ObtainSemaphore(&cdfs
->cb_Semaphore
);
160 Remove((struct Node
*)dev
);
161 ReleaseSemaphore(&cdfs
->cb_Semaphore
);
165 static struct CDFSVolume
*CDFS_DevicePresent(struct CDFS
*cdfs
, struct CDFSDevice
*dev
, struct CDFSLock
**fl
, SIPTR
*res
, SIPTR
*res2
)
167 *res2
= BCache_Present(dev
->cd_BCache
);
168 if (*res2
!= RETURN_OK
) {
169 D(bug("%s: BCache error %d\n", *res2
));
174 if (*res
!= DOSTRUE
&& dev
->cd_Volume
) {
175 D(bug("%s: Disk change detected, unmounting volume\n", __func__
));
176 CDFS_DeviceUnmount(cdfs
, dev
);
177 if (*res2
== RETURN_WARN
) {
178 BCache_Invalidate(dev
->cd_BCache
);
179 *res2
= CDFS_DeviceMount(cdfs
, dev
);
180 D(bug("%s: New disk present, %svolume present\n", __func__
, (*res2
== RETURN_OK
) ? "" : "no "));
181 if (*res2
== RETURN_OK
)
186 /* Mount a volume if possible */
187 if (*res
== DOSTRUE
&& dev
->cd_Volume
== NULL
) {
188 BCache_Invalidate(dev
->cd_BCache
);
189 *res2
= CDFS_DeviceMount(cdfs
, dev
);
190 D(bug("%s: Disk present, %svolume present\n", __func__
, (*res2
== RETURN_OK
) ? "" : "no "));
191 if (*res2
== RETURN_OK
)
195 /* Fix up *fl if it is NULL to the Root directory link */
196 if (*res
== DOSTRUE
&& *fl
== NULL
) {
197 if (dev
->cd_Volume
) {
198 *fl
= B_LOCK(dev
->cd_Volume
->cv_DosVolume
.dl_Lock
);
200 *res2
= ERROR_NOT_A_DOS_DISK
;
205 /* Verify that *fl points to the currently mounted volume */
206 if (*res
== DOSTRUE
&& *fl
) {
207 struct CDFSVolume
*lv
= B_VOLUME((*fl
)->cl_FileLock
.fl_Volume
);
208 if (lv
!= dev
->cd_Volume
) {
209 D(bug("%s: Attempt access lock from volume %p, but mounted is %p\n", __func__
, lv
, dev
->cd_Volume
));
210 *res2
= ERROR_INVALID_LOCK
;
215 return (*res
== DOSTRUE
) ? dev
->cd_Volume
: NULL
;
220 static struct CDFS
*CDFS_Init(struct ExecBase
*SysBase
)
222 struct CDFS
*dispose
, *cdfs
;
224 dispose
= AllocVec(sizeof(*cdfs
), MEMF_ANY
| MEMF_CLEAR
);
229 cdfs
= (struct CDFS
*)FindSemaphore("CDFS");
231 if ((dispose
->cb_UtilityBase
= OpenLibrary("utility.library",0))) {
234 InitSemaphore(&cdfs
->cb_Semaphore
);
235 cdfs
->cb_Semaphore
.ss_Link
.ln_Name
= "CDFS";
236 /* Set up SysBase link */
237 cdfs
->cb_SysBase
= SysBase
;
238 NEWLIST(&cdfs
->cb_Devices
);
239 AddSemaphore(&cdfs
->cb_Semaphore
);
250 #define ACTION_(x) ACTION_##x: D(bug("%s: ACTION_" #x "\n", __func__)); goto case_##x; case_##x
252 #define ACTION_(x) ACTION_##x
255 LONG
CDFS_Handler(struct ExecBase
*SysBase
)
258 struct DosPacket
*dp
;
261 struct CDFSDevice
*dev
;
266 void reply(struct DosPacket
*dp
, SIPTR res
, SIPTR res2
)
271 D(bug("Reply %d => %p (%d)\n", dp
->dp_Type
, res
, res2
));
274 mn
->mn_Node
.ln_Name
= (char*)dp
;
275 dp
->dp_Port
= &((struct Process
*)FindTask(NULL
))->pr_MsgPort
;
281 D(bug("%s: start\n", __func__
));
283 mp
= &((struct Process
*)FindTask(NULL
))->pr_MsgPort
;
285 dp
= (struct DosPacket
*)GetMsg(mp
)->mn_Node
.ln_Name
;
287 D(bug("%s: mp=%p, path='%b'\n", __func__
, mp
, dp
->dp_Arg1
));
289 cdfs
= CDFS_Init(SysBase
);
291 retval
= ERROR_NO_FREE_STORE
;
292 D(bug("%s: %b - error %d\n", __func__
, dp
->dp_Arg1
, retval
));
293 reply(dp
, DOSFALSE
, retval
);
297 /* Open the device */
298 retval
= CDFS_DeviceOpen(cdfs
, (struct FileSysStartupMsg
*)BADDR(dp
->dp_Arg2
), &dev
);
299 if (retval
!= RETURN_OK
) {
300 D(bug("%s: Open %b - error %d\n", __func__
, dp
->dp_Arg1
, retval
));
301 reply(dp
, DOSFALSE
, retval
);
305 /* Mark this as a persistent handler */
306 ((struct DeviceNode
*)BADDR(dp
->dp_Arg3
))->dn_Task
= mp
;
308 D(bug("%s: Opened %b\n", __func__
, dp
->dp_Arg1
));
309 reply(dp
, DOSTRUE
, 0);
311 sigpacket
= 1 << mp
->mp_SigBit
;
315 sigmask
= Wait(sigpacket
);
317 D(bug("%s: Signal 0x%08x\n", __func__
, sigmask
));
319 if (!(sigmask
& sigpacket
))
322 while ((mn
= GetMsg(mp
)) != NULL
) {
324 SIPTR res
= DOSFALSE
;
326 dp
= (struct DosPacket
*)mn
->mn_Node
.ln_Name
;
327 D(bug("%s: Packet %d %p %p %p\n", __func__
, dp
->dp_Type
, dp
->dp_Arg1
, dp
->dp_Arg2
, dp
->dp_Arg3
));
329 switch (dp
->dp_Type
) {
331 CDFS_DeviceUnmount(cdfs
, dev
);
336 /* We don't permit actions that would write to the CDROM */
337 case ACTION_(FORMAT
):
338 case ACTION_(FINDOUTPUT
):
339 case ACTION_(FINDUPDATE
):
341 case ACTION_(SET_FILE_SIZE
):
342 case ACTION_(RENAME_OBJECT
):
343 case ACTION_(RENAME_DISK
):
344 case ACTION_(CREATE_DIR
):
345 case ACTION_(DELETE_OBJECT
):
346 case ACTION_(SET_COMMENT
):
347 case ACTION_(SET_PROTECT
):
348 case ACTION_(SET_DATE
):
349 case ACTION_(INHIBIT
):
350 case ACTION_(SERIALIZE_DISK
):
351 case ACTION_(WRITE_PROTECT
):
353 res2
= ERROR_WRITE_PROTECTED
;
356 case ACTION_(COPY_DIR_FH
):
357 if ((BPTR
)dp
->dp_Arg1
== BNULL
) {
358 res2
= ERROR_OBJECT_NOT_FOUND
;
363 case ACTION_(COPY_DIR
):
365 struct CDFSLock
*fl
, *nl
;
366 struct CDFSVolume
*vol
;
368 fl
= B_LOCK(dp
->dp_Arg1
);
370 vol
= CDFS_DevicePresent(cdfs
, dev
, &fl
, &res
, &res2
);
373 res2
= vol
->cv_Ops
->op_Locate(vol
, fl
, "", MODE_OLDFILE
, &nl
);
374 if (res2
!= RETURN_OK
) {
379 nl
->cl_FileLock
.fl_Link
= BNULL
;
380 nl
->cl_FileLock
.fl_Access
= ACCESS_READ
;
381 nl
->cl_FileLock
.fl_Task
= mp
;
382 nl
->cl_FileLock
.fl_Volume
= MKB_VOLUME(vol
);
383 AddHead(&vol
->cv_FileLocks
, (struct Node
*)&nl
->cl_Node
);
384 res
= (SIPTR
)MKB_LOCK(nl
);
388 case ACTION_(DISK_INFO
):
389 CopyMem(&dev
->cd_Volume
->cv_InfoData
, BADDR(dp
->dp_Arg1
), sizeof(struct InfoData
));
392 case ACTION_(FREE_LOCK
):
395 struct CDFSLock
*fl
= B_LOCK(dp
->dp_Arg1
);
398 struct CDFSVolume
*vol
= B_VOLUME(fl
->cl_FileLock
.fl_Volume
);
399 Remove((struct Node
*)&fl
->cl_Node
);
400 vol
->cv_Ops
->op_Close(vol
, fl
);
405 case ACTION_(EXAMINE_FH
):
407 if ((BPTR
)dp
->dp_Arg1
== BNULL
) {
408 res2
= ERROR_OBJECT_NOT_FOUND
;
414 case ACTION_(EXAMINE_OBJECT
):
416 struct FileInfoBlock
*fib
= BADDR(dp
->dp_Arg2
);
417 struct CDFSLock
*fl
= B_LOCK(dp
->dp_Arg1
);
418 struct CDFSVolume
*vol
;
420 vol
= CDFS_DevicePresent(cdfs
, dev
, &fl
, &res
, &res2
);
429 D(bug("%s: Examine %p.%s\n", __func__
, fl
, &fl
->cl_FileInfoBlock
.fib_FileName
[1]));
430 CopyMem(&fl
->cl_FileInfoBlock
, fib
, sizeof(struct FileInfoBlock
));
431 fib
->fib_DiskKey
= 0;
437 case ACTION_(EXAMINE_NEXT
):
439 struct CDFSLock
*fl
= B_LOCK(dp
->dp_Arg1
);
440 struct FileInfoBlock
*fib
= BADDR(dp
->dp_Arg2
);
441 struct CDFSVolume
*vol
;
443 vol
= CDFS_DevicePresent(cdfs
, dev
, &fl
, &res
, &res2
);
446 res2
= vol
->cv_Ops
->op_ExamineNext(vol
, fl
, fib
);
447 res
= res2
? DOSFALSE
: DOSTRUE
;
450 case ACTION_(FH_FROM_LOCK
):
452 struct FileHandle
*fh
= BADDR(dp
->dp_Arg1
);
453 fh
->fh_Arg1
= dp
->dp_Arg2
;
456 case ACTION_(FINDINPUT
):
458 struct FileHandle
*fh
= BADDR(dp
->dp_Arg1
);
459 struct CDFSLock
*dl
= B_LOCK(dp
->dp_Arg2
);
460 CONST_STRPTR fn
= AROS_BSTR_ADDR(dp
->dp_Arg3
);
462 struct CDFSVolume
*vol
;
464 vol
= CDFS_DevicePresent(cdfs
, dev
, &dl
, &res
, &res2
);
467 res2
= vol
->cv_Ops
->op_Locate(vol
, dl
, fn
, MODE_OLDFILE
, &fl
);
469 if (res2
== RETURN_OK
) {
470 LONG type
= fl
->cl_FileInfoBlock
.fib_DirEntryType
;
471 if (type
== ST_USERDIR
|| type
== ST_ROOT
) {
472 res2
= ERROR_OBJECT_WRONG_TYPE
;
475 fl
->cl_FileLock
.fl_Link
= BNULL
;
476 fl
->cl_FileLock
.fl_Access
= ACCESS_READ
;
477 fl
->cl_FileLock
.fl_Task
= mp
;
478 fl
->cl_FileLock
.fl_Volume
= MKB_VOLUME(vol
);
479 AddHead(&vol
->cv_FileLocks
, (struct Node
*)&fl
->cl_Node
);
480 fh
->fh_Arg1
= (SIPTR
)MKB_LOCK(fl
);
490 struct CDFSLock
*fl
= B_LOCK(dp
->dp_Arg1
);
491 if (CDFS_DevicePresent(cdfs
, dev
, &fl
, &res
, &res2
) == NULL
)
494 if (fl
&& fl
->cl_FileInfoBlock
.fib_DirEntryType
== 0) {
496 res2
= ERROR_OBJECT_NOT_FOUND
;
500 CopyMem(&dev
->cd_Volume
->cv_InfoData
, BADDR(dp
->dp_Arg2
), sizeof(struct InfoData
));
505 case ACTION_(IS_FILESYSTEM
):
508 case ACTION_(LOCATE_OBJECT
):
510 struct CDFSLock
*dl
= B_LOCK(dp
->dp_Arg1
);
512 struct CDFSVolume
*vol
;
513 CONST_STRPTR fn
= AROS_BSTR_ADDR(dp
->dp_Arg2
);
516 vol
= CDFS_DevicePresent(cdfs
, dev
, &dl
, &res
, &res2
);
520 mode
= (dp
->dp_Arg3
== ACCESS_READ
) ? MODE_OLDFILE
: MODE_READWRITE
;
522 res2
= vol
->cv_Ops
->op_Locate(vol
, dl
, fn
, mode
, &fl
);
528 fl
->cl_FileLock
.fl_Link
= BNULL
;
529 fl
->cl_FileLock
.fl_Access
= dp
->dp_Arg3
;
530 fl
->cl_FileLock
.fl_Task
= mp
;
531 fl
->cl_FileLock
.fl_Volume
= MKB_VOLUME(vol
);
532 AddHead(&vol
->cv_FileLocks
, (struct Node
*)&fl
->cl_Node
);
533 res
= (SIPTR
)MKB_LOCK(fl
);
537 case ACTION_(PARENT_FH
):
538 if ((BPTR
)dp
->dp_Arg1
== BNULL
) {
539 res2
= ERROR_OBJECT_NOT_FOUND
;
544 case ACTION_(PARENT
):
546 struct CDFSLock
*ol
= B_LOCK(dp
->dp_Arg1
);
548 struct CDFSVolume
*vol
;
550 vol
= CDFS_DevicePresent(cdfs
, dev
, &ol
, &res
, &res2
);
554 if (ol
->cl_FileInfoBlock
.fib_DirEntryType
== ST_ROOT
) {
560 res2
= vol
->cv_Ops
->op_Locate(vol
, ol
, "/", MODE_OLDFILE
, &fl
);
566 fl
->cl_FileLock
.fl_Link
= BNULL
;
567 fl
->cl_FileLock
.fl_Access
= ACCESS_READ
;
568 fl
->cl_FileLock
.fl_Task
= mp
;
569 fl
->cl_FileLock
.fl_Volume
= MKB_VOLUME(vol
);
570 AddHead(&vol
->cv_FileLocks
, (struct Node
*)&fl
->cl_Node
);
571 res
= (SIPTR
)MKB_LOCK(fl
);
577 struct CDFSLock
*fl
= B_LOCK(dp
->dp_Arg1
);
578 struct CDFSVolume
*vol
;
580 vol
= CDFS_DevicePresent(cdfs
, dev
, &fl
, &res
, &res2
);
583 res2
= vol
->cv_Ops
->op_Read(vol
, fl
, (APTR
)dp
->dp_Arg2
, dp
->dp_Arg3
, &res
);
586 /* ACTION_SAME_LOCK: Not needed, since fl_Key is unique per file */
589 struct CDFSLock
*fl
= B_LOCK(dp
->dp_Arg1
);
590 struct CDFSVolume
*vol
;
592 vol
= CDFS_DevicePresent(cdfs
, dev
, &fl
, &res
, &res2
);
595 res2
= vol
->cv_Ops
->op_Seek(vol
, fl
, dp
->dp_Arg2
, dp
->dp_Arg3
, &res
);
600 res2
= ERROR_ACTION_NOT_KNOWN
;
601 D(bug("%s: Action %d not implemented\n", __func__
, dp
->dp_Type
));
605 reply(dp
, res
, res2
);
609 CDFS_DeviceClose(cdfs
, dev
);
611 /* Remove the 'CDFS' semaphore if we this is the last
612 * handler, and there are no volumes nor devices left.
614 ObtainSemaphore(&cdfs
->cb_Semaphore
);
615 if (IsListEmpty(&cdfs
->cb_Devices
) && IsListEmpty(&cdfs
->cb_Volumes
)) {
616 RemSemaphore(&cdfs
->cb_Semaphore
);
617 ReleaseSemaphore(&cdfs
->cb_Semaphore
);
618 CloseLibrary(cdfs
->cb_UtilityBase
);
621 ReleaseSemaphore(&cdfs
->cb_Semaphore
);