dos.library: Compare seg against BNULL, not NULL
[AROS.git] / rom / dos / cliinit.c
blob5f36c7b9e9e98966569e7592782cf49f97fedc55
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: English
7 */
9 #include <aros/debug.h>
10 #include <exec/resident.h>
11 #include <proto/exec.h>
12 #include <proto/arossupport.h>
13 #include <libraries/expansion.h>
14 #include <libraries/expansionbase.h>
15 #include <resources/filesysres.h>
17 #include <ctype.h>
19 #include "dos_intern.h"
20 #include "../expansion/expansion_intern.h"
22 #ifdef DEBUG_DOSTYPE
24 static void PRINT_DOSTYPE(ULONG dt)
26 unsigned int i;
28 bug("Dos/CliInit: DosType is ");
30 for (i = 0; i < 4; i++)
32 unsigned char c = dt >> (24 - i * 8);
34 if (isprint(c))
35 RawPutChar(c);
36 else
37 bug("\\%02X", c);
40 RawPutChar('\n');
43 #else
45 #define PRINT_DOSTYPE(dt)
47 #endif
49 static LONG internalBootCliHandler(void);
51 /*****************************************************************************
53 NAME */
54 #include <dos/dosextens.h>
55 #include <proto/dos.h>
57 AROS_LH1(IPTR, CliInit,
59 /* SYNOPSIS */
60 AROS_LHA(struct DosPacket *, dp, A0),
62 /* LOCATION */
63 struct DosLibrary *, DOSBase, 154, Dos)
65 /* FUNCTION
66 Set up the first shell process.
68 Currently, no DOS Packet arguments are used by this
69 routine.
71 A new Boot Cli process is created, and 'dp' is
72 sent to it. If the boot shell succeeds, then 'dp'
73 is returned with dp_Res1 = DOSTRUE.
74 has started.
76 INPUTS
77 dp - startup arguments specified as a packet
79 RESULT
80 RETURN_OK on success, ERROR_* (from dp_Res2) on failure.
82 NOTES
83 This function is internal to AROS, and should never be
84 called by user space.
86 EXAMPLE
88 BUGS
90 SEE ALSO
92 INTERNALS
94 *****************************************************************************/
96 AROS_LIBFUNC_INIT
98 struct MsgPort *mp, *reply_mp;
99 struct DosPacket *my_dp = NULL;
100 SIPTR Res1, Res2;
101 BPTR seg;
103 /* Create a DOS Process to handle this, since
104 * we're probably being called from a Task context
106 if (dp == NULL) {
107 /* This call does *not* require that
108 * we be a DOS Process. Luckily.
110 my_dp = AllocDosObject(DOS_STDPKT, NULL);
111 dp = my_dp;
114 reply_mp = CreateMsgPort();
115 seg = CreateSegList(internalBootCliHandler);
116 if (dp == NULL || reply_mp == NULL || seg == BNULL) {
117 if (my_dp)
118 FreeDosObject(DOS_STDPKT, my_dp);
119 DeleteMsgPort(reply_mp);
120 UnLoadSeg(seg);
121 return ERROR_NO_FREE_STORE;
124 mp = CreateProc("Boot Mount", 0, seg, AROS_STACKSIZE);
125 if (mp == NULL) {
126 DeleteMsgPort(reply_mp);
127 if (my_dp)
128 FreeDosObject(DOS_STDPKT, my_dp);
129 /* A best guess... */
130 UnLoadSeg(seg);
131 return ERROR_NO_FREE_STORE;
134 /* Preload the reply with failure */
135 dp->dp_Res1 = DOSFALSE;
136 dp->dp_Res2 = ERROR_NOT_IMPLEMENTED;
138 /* Again, doesn't require this Task to be a Process */
139 SendPkt(dp, mp, reply_mp);
141 /* Wait for the message from the Boot Cli */
142 WaitPort(reply_mp);
143 GetMsg(reply_mp);
145 /* We know that if we've received a reply packet,
146 * that we've been able to execute the handler,
147 * therefore we can dispense with the 'CreateSegList()'
148 * stub.
150 UnLoadSeg(seg);
152 Res1 = dp->dp_Res1;
153 Res2 = dp->dp_Res2;
155 if (my_dp)
156 FreeDosObject(DOS_STDPKT, my_dp);
158 DeleteMsgPort(reply_mp);
160 D(bug("Dos/CliInit: Process returned Res1=%ld, Res2=%ld\n", Res1, Res2));
162 /* Did we succeed? */
163 if (Res1 == DOSTRUE)
164 return RETURN_OK;
165 /* Make sure we return non-zero error code, 0 == RETURN_OK */
166 if (Res2 == 0)
167 Res2 = ERROR_UNKNOWN;
169 return Res2;
171 AROS_LIBFUNC_EXIT
172 } /* CliInit */
174 /* Find the most recent version of the matching filesystem
175 * We are assured from the calling function that either
176 * DosType != 0, or Handler != BNULL.
177 * We attempt to match first on DosType, then on Handler.
179 static struct FileSysEntry *internalMatchFileSystemResourceHandler(struct FileSysResource *fsr, ULONG DosType, BPTR Handler)
181 struct FileSysEntry *fse, *best_fse = NULL;
183 ForeachNode(&fsr->fsr_FileSysEntries, fse)
185 BOOL try_patch = FALSE;
187 if (DosType == 0)
189 if (fse->fse_Handler != BNULL &&
190 (fse->fse_PatchFlags & FSEF_HANDLER))
192 try_patch = (strcmp(AROS_BSTR_ADDR(Handler),
193 AROS_BSTR_ADDR(fse->fse_Handler)) == 0)
194 ? TRUE : FALSE;
197 else if (fse->fse_DosType == DosType)
199 try_patch = TRUE;
202 if (try_patch) {
203 if (fse->fse_PatchFlags & (FSEF_HANDLER | FSEF_SEGLIST | FSEF_TASK))
205 if (best_fse == NULL || fse->fse_Version > best_fse->fse_Version)
207 best_fse = fse;
213 return best_fse;
216 /* See if the BootNode's DeviceNode needs to be patched by
217 * an entry in FileSysResource
219 * If de->de_DosType == 0, and no dn_SegList nor dn_Handler,
220 * then the node uses the 'defseg' handler.
222 static void internalPatchBootNode(struct FileSysResource *fsr, struct DeviceNode *dn, BPTR defseg)
224 struct FileSysStartupMsg *fssm;
225 struct DosEnvec *de;
226 struct FileSysEntry *fse;
228 /* If we already have a task installed,
229 * then we're done.
231 if (dn->dn_Task != NULL)
232 return;
234 /* If we already have a handler installed,
235 * then we're done.
237 if (dn->dn_SegList != BNULL)
238 return;
240 fssm = BADDR(dn->dn_Startup);
241 if (fssm == NULL)
242 return;
244 de = BADDR(fssm->fssm_Environ);
245 if (de == NULL)
246 return;
248 /* If the DosType is 0 and dn_Handler == BNULL, use the default handler */
249 if (de->de_DosType == 0 && dn->dn_Handler == BNULL)
251 D(bug("Dos/CliInit: Neither DosType nor Handler specified, using default filesystem\n"));
252 dn->dn_SegList = defseg;
253 dn->dn_GlobalVec = (BPTR)-1;
254 return;
257 /* If no FileSysResource, nothing to do */
258 if (fsr == NULL) {
259 D(bug("Dos/CliInit: No FileSystem.resource, not patching DeviceNode %p\n", dn));
260 return;
263 D(bug("Dos/CliInit: Looking for patches for DeviceNode %p\n", dn));
266 * internalMatchFileSystemResourceHandler looks up the filesystem
268 fse = internalMatchFileSystemResourceHandler(fsr, de->de_DosType, dn->dn_Handler);
269 if (fse != NULL)
271 D(bug("Dos/CliInit: found 0x%p in FileSystem.resource\n", fse));
272 PRINT_DOSTYPE(fse->fse_DosType);
274 dn->dn_SegList = fse->fse_SegList;
275 /* other fse_PatchFlags bits are quite pointless */
276 if (fse->fse_PatchFlags & FSEF_TASK)
277 dn->dn_Task = (APTR)fse->fse_Task;
278 if (fse->fse_PatchFlags & FSEF_LOCK)
279 dn->dn_Lock = fse->fse_Lock;
281 /* Adjust the stack size for 64-bits if needed.
283 if (fse->fse_PatchFlags & FSEF_STACKSIZE)
284 dn->dn_StackSize = (fse->fse_StackSize/sizeof(ULONG))*sizeof(IPTR);
285 if (fse->fse_PatchFlags & FSEF_PRIORITY)
286 dn->dn_Priority = fse->fse_Priority;
287 if (fse->fse_PatchFlags & FSEF_GLOBALVEC)
288 dn->dn_GlobalVec = fse->fse_GlobalVec;
292 static struct MsgPort *mountBootNode(struct DeviceNode *dn, struct FileSysResource *fsr, struct DosLibrary *DOSBase)
294 struct DosList *dl;
296 if ((dn == NULL) || (dn->dn_Name == BNULL))
297 return NULL;
299 D(bug("Dos/CliInit: Mounting 0x%p (%b)...\n", dn, dn->dn_Name));
301 /* Check if the device is already in DOS list */
302 dl = LockDosList(LDF_DEVICES | LDF_READ);
304 while ((dl = NextDosEntry(dl, LDF_DEVICES)))
306 if (dl == (struct DosList *)dn)
307 break;
310 UnLockDosList(LDF_DEVICES | LDF_READ);
312 /* Found in DOS list? Do nothing. */
313 if (dl)
315 D(bug("Dos/CliInit: Found in DOS list, nothing to do\n"));
316 return dl->dol_Task;
319 /* Patch it up, if needed */
320 internalPatchBootNode(fsr, dn, DOSBase->dl_Root->rn_FileHandlerSegment);
322 if (!dn->dn_Handler && !dn->dn_SegList)
324 /* Don't know how to mount? Error... */
325 D(bug("Dos/CliInit: Don't know how to mount\n"));
326 return NULL;
329 if (AddDosEntry((struct DosList *)dn) != DOSFALSE)
332 * Do not check for ADNF_STARTPROC because:
333 * a) On the Amiga ADNF_STARTPROC was not present in KS 1.3 and earlier, there was no deferred mount.
334 * b) In fact if we have something in ExpansionBase, we for sure want it to be mounted.
336 D(bug("Dos/CliInit: Added to DOS list, starting up handler...\n"));
338 if (RunHandler(dn, NULL, DOSBase))
340 D(bug("dn->dn_Task = 0x%p\n", dn->dn_Task));
341 return dn->dn_Task;
344 D(bug("Failed\n"));
345 RemDosEntry((struct DosList *)dn);
347 D(else bug("Dos/CliInit: AddDosEntry() failed\n"));
350 * TODO: AddDosEntry() can fail in case of duplicate name. In this case it would be useful
351 * to append some suffix. AmigaOS IIRC did the same.
352 * This appears to be needed if you have DH0:. DH1:, etc, on your hard drive, and want to
353 * connect a friend's hard drive which also has partitions with these names.
356 return NULL;
359 static BPTR internalBootLock(struct DosLibrary *DOSBase, struct ExpansionBase *ExpansionBase, struct FileSysResource *fsr)
361 struct BootNode *bn;
362 struct DeviceNode *dn;
363 struct MsgPort *mp;
364 BPTR lock = BNULL;
365 STRPTR name;
366 int name_len;
368 /* The first BootNode off of the MountList.
369 * The dosboot.resource strap routine will have
370 * made the desired boot node the first in this list.
372 * If this fails to mount, we will fail, which will
373 * cause dos.library to fail to initialize, and
374 * then dosboot.resource will handle checking the
375 * next device in the list.
377 ObtainSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
378 bn = (struct BootNode *)GetHead(&ExpansionBase->MountList);
379 D(bug("Dos/CliInit: MountList head: 0x%p\n", bn));
381 if (bn == NULL)
383 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
384 return BNULL;
387 dn = bn->bn_DeviceNode;
388 mp = mountBootNode(dn, fsr, DOSBase);
389 if (!mp)
391 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
392 return BNULL;
395 D(bug("Dos/CliInit: %b (%d) appears usable\n", dn->dn_Name, bn->bn_Node.ln_Pri));
397 /* Try to find a Lock for 'name:' */
398 name_len = AROS_BSTR_strlen(dn->dn_Name);
399 name = AllocVec(name_len + 2, MEMF_ANY);
400 if (name != NULL)
402 SIPTR err = 0;
404 /* Make the volume name a volume: name */
405 CopyMem(AROS_BSTR_ADDR(dn->dn_Name), name, name_len);
406 name[name_len+0] = ':';
407 name[name_len+1] = 0;
408 D(bug("Dos/CliInit: Attempt to Lock(\"%s\")... ", name));
410 lock = Lock(name, SHARED_LOCK);
411 D(bug("=> 0x%p\n", BADDR(lock)));
413 if (lock != BNULL)
415 /* If we have a lock, check the per-platform conditional boot code. */
416 if (!__dos_IsBootable(DOSBase, lock))
418 UnLock(lock);
419 lock = BNULL;
420 err = ERROR_OBJECT_WRONG_TYPE; /* Something to more or less reflect "This disk is not bootable" */
422 else
423 /* Let everyone know a boot volume has been chosen */
424 ExpansionBase->Flags |= EBF_DOSFLAG;
426 else
428 err = IoErr();
431 if (!lock)
433 SIPTR dead;
435 /* Darn. Not bootable. Try to unmount it. */
436 D(bug("Dos/CliInit: Does not have a bootable filesystem, unmounting...\n"));
438 /* It's acceptable if this fails */
439 dead = DoPkt(mp, ACTION_DIE, 0, 0, 0, 0, 0);
440 D(bug("Dos/CliInit: ACTION_DIE returned %ld\n", dead));
442 if (dead)
445 * Handlers usually won't remove their DeviceNodes themselves.
446 * And even if they do (ACTION_DIE is poorly documented), RemDosEntry()
447 * on an already removed entry is safe due to nature of DOS list.
448 * What is really prohibited, is unloading own seglist. Well, resident
449 * handlers will never do it, they know...
451 RemDosEntry((struct DosList *)dn);
452 dn->dn_Task = NULL;
454 /* DoPkt() clobbered IoErr() */
455 SetIoErr(err);
458 FreeVec(name);
460 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
462 return lock;
465 static void AddBootAssign(CONST_STRPTR path, CONST_STRPTR assign, APTR DOSBase)
467 BPTR lock;
468 if (!(lock = Lock(path, SHARED_LOCK)))
469 lock = Lock("SYS:", SHARED_LOCK);
470 if (lock)
471 AssignLock(assign, lock);
476 * This is what actually gets the Lock() for SYS:,
477 * sets up the boot assigns, and starts the
478 * startup sequence.
480 static LONG internalBootCliHandler(void)
482 struct ExpansionBase *ExpansionBase;
483 struct DosLibrary *DOSBase;
484 struct MsgPort *mp = &((struct Process *)FindTask(NULL))->pr_MsgPort;
485 BPTR lock;
486 struct DosPacket *dp;
487 ULONG BootFlags;
488 UBYTE Flags;
489 struct BootNode *bn, *tmpbn;
490 struct FileSysResource *fsr;
491 LONG err = 0;
493 /* Ah. A DOS Process context. At last! */
494 WaitPort(mp);
495 dp = (struct DosPacket *)(GetMsg(mp)->mn_Node.ln_Name);
497 DOSBase = (APTR)OpenLibrary("dos.library", 0);
498 if (DOSBase == NULL) {
499 D(bug("Dos/CliInit: Impossible! Where did dos.library go?\n"));
500 Alert(AT_DeadEnd | AG_OpenLib | AO_DOSLib);
503 ExpansionBase = (APTR)OpenLibrary("expansion.library", 0);
504 if (!ExpansionBase)
505 err = ERROR_INVALID_RESIDENT_LIBRARY;
507 if (err == 0)
509 /* It's perfectly fine if this fails. */
510 fsr = OpenResource("FileSystem.resource");
512 /* Find and Lock the proposed boot device */
513 lock = internalBootLock(DOSBase, ExpansionBase, fsr);
514 D(bug("Dos/CliInit: Proposed SYS: lock is: %p\n", BADDR(lock)));
515 if (lock == BNULL)
516 err = IoErr();
519 if (err != 0)
522 * We've failed. Inform our parent and exit.
523 * Immediately after we reply the packet, the parent (Boot Task) can expunge DOSBase.
524 * This is why we first cleanup, then use internal_ReplyPkt (DOSBase is considered
525 * invalid).
526 * Alternatively we could Forbid() before ReplyPkt(), but... Forbid() is so unpolite...
529 if (ExpansionBase != NULL)
530 CloseLibrary(&ExpansionBase->LibNode);
531 CloseLibrary(&DOSBase->dl_lib);
533 /* Immediately after ReplyPkt() DOSBase can be freed. */
534 internal_ReplyPkt(dp, mp, DOSFALSE, err);
535 return err;
538 /* Ok, at this point we've succeeded. Inform our parent. */
539 ReplyPkt(dp, DOSTRUE, 0);
541 /* We're now at the point of no return. */
542 DOSBase->dl_Root->rn_BootProc = ((struct FileLock*)BADDR(lock))->fl_Task;
543 SetFileSysTask(DOSBase->dl_Root->rn_BootProc);
545 AssignLock("SYS", lock);
546 lock = Lock("SYS:", SHARED_LOCK);
547 if (lock == BNULL)
549 D(bug("DOS/CliInit: Impossible! The SYS: assign failed!\n"));
550 Alert(AT_DeadEnd | AG_BadParm | AN_DOSLib);
553 AddBootAssign("SYS:C", "C", DOSBase);
554 AddBootAssign("SYS:Libs", "LIBS", DOSBase);
555 AddBootAssign("SYS:Devs", "DEVS", DOSBase);
556 AddBootAssign("SYS:L", "L", DOSBase);
557 AddBootAssign("SYS:S", "S", DOSBase);
558 AddBootAssign("SYS:Fonts", "FONTS", DOSBase);
560 #if !(mc68000)
561 /* Let hidds in DRIVERS: directory be found by OpenLibrary */
562 if ((lock = Lock("DEVS:Drivers", SHARED_LOCK)) != BNULL) {
563 AssignLock("DRIVERS", lock);
564 AssignAdd("LIBS", lock);
567 * This early assignment prevents Poseidon from asking for ENV:
568 * when popup GUI process is initialized and opens muimaster.library.
569 * On m68k this harms, Workbench 1.x disks fail to boot correctly with
570 * "Can't cancel ENV:" warning.
571 * FIXME: Fix muimaster.library at last, and forget this hack.
573 AssignLate("ENV", "SYS:Prefs/Env-Archive");
574 #endif
575 AssignLate("ENVARC", "SYS:Prefs/Env-Archive");
578 * At this point we have only SYS:, nothing more. Mount the rest.
579 * We do it after assigning SYS: because in some cases we can have
580 * BootNodes with handler name but no seglist (Poseidon could add them for example).
581 * This means the handler needs to be loaded from disk (fat-handler for example).
582 * Here we can already do it.
584 D(bug("Dos/CliInit: Assigns done, mount remaining handlers...\n"));
586 BootFlags = IntExpBase(ExpansionBase)->BootFlags;
587 Flags = ExpansionBase->Flags;
588 D(bug("Dos/CliInit: BootFlags 0x%lx Flags 0x%x\n", BootFlags, Flags));
590 ForeachNodeSafe(&ExpansionBase->MountList, bn, tmpbn)
593 * Don't check for return code. Failed is failed.
594 * One of failure reasons can be missing handler specification for some DOSType.
595 * In this case the DeviceNode will not be mounted, but it will stay in
596 * ExpansionBase->MountList. It can be picked up by disk-based program which would
597 * read mappings for disk-based handlers from file.
598 * This way we could automount e.g. FAT, NTFS, EXT3/2, whatever else, partitions.
600 mountBootNode(bn->bn_DeviceNode, fsr, DOSBase);
603 CloseLibrary((APTR)ExpansionBase);
605 /* Init all the RTF_AFTERDOS code, since we now have SYS:, the dos devices, and all the other assigns */
606 D(bug("Dos/CliInit: Calling InitCode(RTF_AFTERDOS, 0)\n"));
607 InitCode(RTF_AFTERDOS, 0);
609 /* Call the platform-overridable portions */
610 D(bug("Dos/CliInit: Calling __dos_Boot(%p, 0x%lx, 0x%x)\n", DOSBase, BootFlags, Flags));
611 __dos_Boot(DOSBase, BootFlags, Flags);
613 D(bug("Dos/CliInit: Boot sequence exited\n"));
614 CloseLibrary((APTR)DOSBase);
616 /* And exit... */
617 return RETURN_OK;