Indentation fix, cleanup.
[AROS.git] / rom / filesys / cdfs / cdfs.c
blobe7e9cab4305e177c883652b3182f2cdfc644c6b5
1 /*
2 * Copyright (C) 2013, The AROS Development Team
3 * All right reserved.
4 * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
6 * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1
7 */
9 #include <aros/debug.h>
11 #include <proto/exec.h>
12 #include <proto/dos.h>
14 #include "cdfs.h"
15 #include "iso9660.h"
16 #include "bcache.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);
25 if (vol) {
26 vol->cv_Device = dev;
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);
38 vol->cv_Ops = op;
40 if (DOSBase) {
41 /* Look for an identical volume */
42 struct DosList *dl;
43 dl = LockDosList(LDF_VOLUMES | LDF_READ);
44 while (dl != BNULL) {
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
49 ) {
50 /* Identical match found */
51 break;
54 if (dl) {
55 /* Dispose of the test volume */
56 vol->cv_Ops->op_Unmount(vol);
57 FreeVec(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;
66 } else {
67 /* Insert the new volume */
68 AddDosEntry((struct DosList *)&vol->cv_DosVolume);
70 UnLockDosList(LDF_VOLUMES | LDF_READ);
71 CloseLibrary(DOSBase);
73 dev->cd_Volume = vol;
74 return RETURN_OK;
76 FreeVec(vol);
79 dev->cd_Volume = NULL;
80 return err;
84 static VOID CDFS_DeviceUnmount(struct CDFS *cdfs, struct CDFSDevice *dev)
86 struct CDFSVolume *vol = dev->cd_Volume;
88 if (vol == NULL)
89 return;
91 /* Detach from the device */
92 dev->cd_Volume = NULL;
93 /* Still has locks? */
94 if (!IsListEmpty(&vol->cv_FileLocks)) {
95 struct CDFSLock *cl;
96 BPTR *flp;
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;
107 *flp = BNULL;
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);
113 } else {
114 struct Library *DOSBase = OpenLibrary("dos.library",0);
115 if (DOSBase) {
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);
124 FreeVec(vol);
128 static LONG CDFS_DeviceOpen(struct CDFS *cdfs, struct FileSysStartupMsg *fssm, struct CDFSDevice **devp)
130 struct CDFSDevice *dev;
131 LONG err;
133 dev = AllocVec(sizeof(*dev), MEMF_PUBLIC | MEMF_CLEAR);
134 if (dev) {
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__));
142 *devp = dev;
143 return RETURN_OK;
145 FreeVec(dev);
146 } else {
147 err = ERROR_NO_FREE_STORE;
149 *devp = NULL;
150 return err;
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);
162 FreeVec(dev);
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));
170 *res = DOSFALSE;
171 } else
172 *res = DOSTRUE;
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)
182 *res = DOSTRUE;
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)
192 *res = DOSTRUE;
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);
199 } else {
200 *res2 = ERROR_NOT_A_DOS_DISK;
201 *res = DOSFALSE;
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;
211 *res = DOSFALSE;
215 return (*res == DOSTRUE) ? dev->cd_Volume : NULL;
218 #undef SysBase
220 static struct CDFS *CDFS_Init(struct ExecBase *SysBase)
222 struct CDFS *dispose, *cdfs;
224 dispose = AllocVec(sizeof(*cdfs), MEMF_ANY | MEMF_CLEAR);
225 if (dispose == NULL)
226 return NULL;
228 Forbid();
229 cdfs = (struct CDFS *)FindSemaphore("CDFS");
230 if (cdfs == NULL) {
231 if ((dispose->cb_UtilityBase = OpenLibrary("utility.library",0))) {
232 cdfs = dispose;
233 dispose = NULL;
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);
242 Permit();
244 FreeVec(dispose);
246 return cdfs;
249 #if DEBUG
250 #define ACTION_(x) ACTION_##x: D(bug("%s: ACTION_" #x "\n", __func__)); goto case_##x; case_##x
251 #else
252 #define ACTION_(x) ACTION_##x
253 #endif
255 LONG CDFS_Handler(struct ExecBase *SysBase)
257 struct MsgPort *mp;
258 struct DosPacket *dp;
259 struct Message *mn;
260 struct CDFS *cdfs;
261 struct CDFSDevice *dev;
262 LONG retval;
263 BOOL die = FALSE;
264 ULONG sigpacket;
266 void reply(struct DosPacket *dp, SIPTR res, SIPTR res2)
268 struct MsgPort *mp;
269 struct Message *mn;
271 D(bug("Reply %d => %p (%d)\n", dp->dp_Type, res, res2));
272 mp = dp->dp_Port;
273 mn = dp->dp_Link;
274 mn->mn_Node.ln_Name = (char*)dp;
275 dp->dp_Port = &((struct Process*)FindTask(NULL))->pr_MsgPort;
276 dp->dp_Res1 = res;
277 dp->dp_Res2 = res2;
278 PutMsg(mp, mn);
281 D(bug("%s: start\n", __func__));
283 mp = &((struct Process *)FindTask(NULL))->pr_MsgPort;
284 WaitPort(mp);
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);
290 if (cdfs == NULL) {
291 retval = ERROR_NO_FREE_STORE;
292 D(bug("%s: %b - error %d\n", __func__, dp->dp_Arg1, retval));
293 reply(dp, DOSFALSE, retval);
294 return RETURN_FAIL;
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);
302 return RETURN_FAIL;
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;
312 while (!die) {
313 ULONG sigmask;
315 sigmask = Wait(sigpacket);
317 D(bug("%s: Signal 0x%08x\n", __func__, sigmask));
319 if (!(sigmask & sigpacket))
320 continue;
322 while ((mn = GetMsg(mp)) != NULL) {
323 SIPTR res2 = 0;
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) {
330 case ACTION_(DIE):
331 CDFS_DeviceUnmount(cdfs, dev);
332 res2 = 0;
333 res = DOSTRUE;
334 break;
336 /* We don't permit actions that would write to the CDROM */
337 case ACTION_(FORMAT):
338 case ACTION_(FINDOUTPUT):
339 case ACTION_(FINDUPDATE):
340 case ACTION_(WRITE):
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):
352 res = DOSFALSE;
353 res2 = ERROR_WRITE_PROTECTED;
354 break;
356 case ACTION_(COPY_DIR_FH):
357 if ((BPTR)dp->dp_Arg1 == BNULL) {
358 res2 = ERROR_OBJECT_NOT_FOUND;
359 res = DOSFALSE;
360 break;
362 /* FALLTHROUGH */
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);
371 if (!vol) break;
373 res2 = vol->cv_Ops->op_Locate(vol, fl, "", MODE_OLDFILE, &nl);
374 if (res2 != RETURN_OK) {
375 res = DOSFALSE;
376 break;
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);
385 res2 = 0;
387 break;
388 case ACTION_(DISK_INFO):
389 CopyMem(&dev->cd_Volume->cv_InfoData, BADDR(dp->dp_Arg1), sizeof(struct InfoData));
390 res = DOSTRUE;
391 break;
392 case ACTION_(FREE_LOCK):
393 case ACTION_(END):
395 struct CDFSLock *fl = B_LOCK(dp->dp_Arg1);
397 if (fl != NULL) {
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);
402 res = DOSTRUE;
404 break;
405 case ACTION_(EXAMINE_FH):
406 #if 0
407 if ((BPTR)dp->dp_Arg1 == BNULL) {
408 res2 = ERROR_OBJECT_NOT_FOUND;
409 res = DOSFALSE;
410 break;
412 #endif
413 /* FALLTHROUGH */
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);
421 if (!vol) break;
423 if (fib == NULL) {
424 res = DOSTRUE;
425 res2 = 0;
426 break;
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;
433 res = DOSTRUE;
434 res2 = 0;
436 break;
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);
444 if (!vol) break;
446 res2 = vol->cv_Ops->op_ExamineNext(vol, fl, fib);
447 res = res2 ? DOSFALSE : DOSTRUE;
449 break;
450 case ACTION_(FH_FROM_LOCK):
452 struct FileHandle *fh = BADDR(dp->dp_Arg1);
453 fh->fh_Arg1 = dp->dp_Arg2;
455 break;
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);
461 struct CDFSLock *fl;
462 struct CDFSVolume *vol;
464 vol = CDFS_DevicePresent(cdfs, dev, &dl, &res, &res2);
465 if (!vol) break;
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;
473 res = DOSFALSE;
474 } else {
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);
481 res = DOSTRUE;
483 } else {
484 res = DOSFALSE;
487 break;
488 case ACTION_(INFO):
490 struct CDFSLock *fl = B_LOCK(dp->dp_Arg1);
491 if (CDFS_DevicePresent(cdfs, dev, &fl, &res, &res2) == NULL)
492 break;
494 if (fl && fl->cl_FileInfoBlock.fib_DirEntryType == 0) {
495 res = DOSFALSE;
496 res2 = ERROR_OBJECT_NOT_FOUND;
497 break;
500 CopyMem(&dev->cd_Volume->cv_InfoData, BADDR(dp->dp_Arg2), sizeof(struct InfoData));
501 res = DOSTRUE;
502 res2 = 0;
504 break;
505 case ACTION_(IS_FILESYSTEM):
506 res = DOSTRUE;
507 break;
508 case ACTION_(LOCATE_OBJECT):
510 struct CDFSLock *dl = B_LOCK(dp->dp_Arg1);
511 struct CDFSLock *fl;
512 struct CDFSVolume *vol;
513 CONST_STRPTR fn = AROS_BSTR_ADDR(dp->dp_Arg2);
514 ULONG mode;
516 vol = CDFS_DevicePresent(cdfs, dev, &dl, &res, &res2);
517 if (!vol)
518 break;
520 mode = (dp->dp_Arg3 == ACCESS_READ) ? MODE_OLDFILE : MODE_READWRITE;
522 res2 = vol->cv_Ops->op_Locate(vol, dl, fn, mode, &fl);
523 if (res2 != 0) {
524 res = DOSFALSE;
525 break;
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);
534 res2 = 0;
536 break;
537 case ACTION_(PARENT_FH):
538 if ((BPTR)dp->dp_Arg1 == BNULL) {
539 res2 = ERROR_OBJECT_NOT_FOUND;
540 res = DOSFALSE;
541 break;
543 /* FALLTHROUGH */
544 case ACTION_(PARENT):
546 struct CDFSLock *ol = B_LOCK(dp->dp_Arg1);
547 struct CDFSLock *fl;
548 struct CDFSVolume *vol;
550 vol = CDFS_DevicePresent(cdfs, dev, &ol, &res, &res2);
551 if (!vol)
552 break;
554 if (ol->cl_FileInfoBlock.fib_DirEntryType == ST_ROOT) {
555 res2 = 0;
556 res = 0;
557 break;
560 res2 = vol->cv_Ops->op_Locate(vol, ol, "/", MODE_OLDFILE, &fl);
561 if (res2) {
562 res = DOSFALSE;
563 break;
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);
572 res2 = 0;
574 break;
575 case ACTION_(READ):
577 struct CDFSLock *fl = B_LOCK(dp->dp_Arg1);
578 struct CDFSVolume *vol;
580 vol = CDFS_DevicePresent(cdfs, dev, &fl, &res, &res2);
581 if (!vol) break;
583 res2 = vol->cv_Ops->op_Read(vol, fl, (APTR)dp->dp_Arg2, dp->dp_Arg3, &res);
585 break;
586 /* ACTION_SAME_LOCK: Not needed, since fl_Key is unique per file */
587 case ACTION_(SEEK):
589 struct CDFSLock *fl = B_LOCK(dp->dp_Arg1);
590 struct CDFSVolume *vol;
592 vol = CDFS_DevicePresent(cdfs, dev, &fl, &res, &res2);
593 if (!vol) break;
595 res2 = vol->cv_Ops->op_Seek(vol, fl, dp->dp_Arg2, dp->dp_Arg3, &res);
597 break;
598 default:
599 res = DOSFALSE;
600 res2 = ERROR_ACTION_NOT_KNOWN;
601 D(bug("%s: Action %d not implemented\n", __func__, dp->dp_Type));
602 break;
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);
619 FreeVec(cdfs);
620 } else {
621 ReleaseSemaphore(&cdfs->cb_Semaphore);
624 return RETURN_OK;