Don't dereference fssm when it's NULL.
[tangerine.git] / rom / dos / getdeviceproc.c
blobd361c553d9efc2509065290c6d8e44d51927be6b
1 /*
2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: GetDeviceProc() - Find the filesystem for a path.
6 Lang: english
7 */
8 #include "dos_intern.h"
9 #include <proto/exec.h>
10 #include <proto/utility.h>
12 #define DEBUG 0
13 #include <aros/debug.h>
15 /*****************************************************************************
17 NAME */
18 #include <proto/dos.h>
20 AROS_LH2(struct DevProc *, GetDeviceProc,
22 /* SYNOPSIS */
23 AROS_LHA(CONST_STRPTR, name, D1),
24 AROS_LHA(struct DevProc *, dp, D2),
26 /* LOCATION */
27 struct DosLibrary *, DOSBase, 107, Dos)
29 /* FUNCTION
30 GetDeviceProc() will search for the filesystem handler which
31 you should send a command to for a specific path.
33 By calling GetDeviceProc() multiple times, the caller will
34 be able to handle multi-assign paths.
36 The first call to GetDeviceProc() should have the |dp| parameter
37 as NULL.
39 INPUTS
40 name - Name of the object to find.
41 dp - Previous result of GetDeviceProc() or NULL.
43 RESULT
44 A pointer to a DevProc structure containing the information
45 required to send a command to a filesystem.
47 NOTES
49 EXAMPLE
51 BUGS
52 Currently doesn't return dvp_DevNode for locks which are
53 relative to "PROGDIR:", ":", or the current directory.
55 SEE ALSO
56 FreeDeviceProc()
58 INTERNALS
60 *****************************************************************************/
62 AROS_LIBFUNC_INIT
64 struct Process *pr = (struct Process *)FindTask(NULL);
65 struct DosList *dl = NULL;
66 char vol[32];
67 LONG len;
68 char buf[256];
69 BPTR cur = NULL, lock = NULL;
70 BOOL stdio = FALSE;
72 /* if they passed us the result of a previous call, then they're wanted to
73 * loop over the targets of a multidirectory assign */
74 if (dp != NULL) {
76 /* if what they passed us is not a multidirectory assign, then there's
77 * nothing for us to do */
78 if (dp->dvp_DevNode != NULL &&
79 (dp->dvp_DevNode->dol_Type != DLT_DIRECTORY || !(dp->dvp_Flags & DVPF_ASSIGN))) {
81 /* cleanup */
82 if (dp->dvp_Flags & DVPF_UNLOCK)
83 UnLock(dp->dvp_Lock);
85 FreeMem(dp, sizeof(struct DevProc));
86 return NULL;
89 /* it's fine, we'll start from here */
90 dl = dp->dvp_DevNode;
92 /* lock the dos list here, to match the result of the next block */
93 LockDosList(LDF_ALL | LDF_READ);
96 /* otherwise we need to find a place to start in the doslist based on the
97 * name they passed in */
98 else {
100 /* handle standard I/O streams as "virtual" devices */
101 if (Stricmp(name, "IN:") == 0 || Stricmp(name, "STDIN:") == 0) {
102 cur = pr->pr_CIS != NULL ? pr->pr_CIS : (BPTR) -1;
103 stdio = TRUE;
105 else if (Stricmp(name, "OUT:") == 0 || Stricmp(name, "STDOUT:") == 0) {
106 cur = pr->pr_COS != NULL ? pr->pr_COS : (BPTR) -1;
107 stdio = TRUE;
109 else if (Stricmp(name, "ERR:") == 0 || Stricmp(name, "STDERR:") == 0) {
110 cur = pr->pr_CES != NULL ? pr->pr_CES :
111 pr->pr_COS != NULL ? pr->pr_COS : (BPTR) -1;
112 stdio = TRUE;
115 if (stdio) {
116 /* handle doesn't exist */
117 if (cur == (BPTR) -1) {
118 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
119 return NULL;
122 /* got it, make a fake devproc */
123 if ((dp = AllocMem(sizeof(struct DevProc), MEMF_ANY | MEMF_CLEAR)) == NULL) {
124 SetIoErr(ERROR_NO_FREE_STORE);
125 return NULL;
128 /* we need a lock for the devproc */
129 if ((lock = DupLockFromFH(cur)) == NULL) {
130 FreeMem(dp, sizeof(struct DevProc));
131 return NULL;
134 /* build the devproc for return */
135 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(lock))->fh_Device;
136 dp->dvp_Lock = lock;
137 dp->dvp_Flags = DVPF_UNLOCK; /* remember to unlock in FreeDeviceNode() */
139 /* finding the device node
140 * XXX this is naive - if the device appears on the doslist twice
141 * we have no way to tell which one is ours. we can't even use
142 * NameFromLock() to get the volume name and then find the doslist
143 * entry from that as console handlers probably don't even
144 * implement names. bring on packets I say */
146 dl = LockDosList(LDF_ALL | LDF_READ);
147 while (dl != NULL
148 && ((struct MsgPort *)dl->dol_Ext.dol_AROS.dol_Device
149 != dp->dvp_Port))
150 dl = dl->dol_Next;
152 UnLockDosList(LDF_READ | LDF_ALL);
154 /* not found */
155 if (dl == NULL) {
156 FreeMem(dp, sizeof(struct DevProc));
157 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
158 return NULL;
161 /* take it */
162 dp->dvp_DevNode = dl;
164 return dp;
167 /* something real, work out what it's relative to */
168 if (Strnicmp(name, "PROGDIR:", 8) == 0) {
169 cur = pr->pr_HomeDir;
171 /* move past the "volume" name, so that we end up in the "no
172 * volume name" case below */;
173 name = &name[8];
176 else
177 cur = pr->pr_CurrentDir;
179 /* if we got NULL, then it's relative to the system root lock */
180 if (cur == NULL)
181 cur = DOSBase->dl_SYSLock;
183 /* extract the volume name */
184 len = SplitName(name, ':', vol, 0, sizeof(vol)-1);
186 /* move the name past it, it's now relative to the volume */
187 name += len;
189 /* if there wasn't one (or we found a lone ':'), then we need to
190 * extract it from the name of the current dir */
192 /* XXX this block sucks. NameFromLock () calls DupLock(), which calls
193 * Lock(), which would end up back here if it wasn't for the
194 * special-case in Lock(). once we have real FileLock locks, then this
195 * code will go and we can just look at cur->fl_Volume to get the
196 * doslist entry. see the morphos version for details */
197 if (len <= 1) {
199 /* if we didn't find a ':' at all, then we'll need to return the
200 * lock that it's relative to, so make a note */
201 if (len == -1)
202 lock = cur;
204 if (NameFromLock(cur, buf, 255) != DOSTRUE)
205 return NULL;
207 len = SplitName(buf, ':', vol, 0, sizeof(vol)-1);
209 /* if there isn't one, something is horribly wrong */
210 if (len <= 1) {
211 kprintf("%s:%d: NameFromLock() returned a path without a volume. Probably a bug, report it!\n"
212 " GetDeviceProc() called for '%s'\n"
213 " NameFromLock() called on 0x%08x, returned '%s'\n",
214 __FILE__, __LINE__, name, cur, buf);
215 SetIoErr(ERROR_INVALID_COMPONENT_NAME);
216 return NULL;
220 /* allocate structure for return */
221 if ((dp = AllocMem(sizeof(struct DevProc), MEMF_ANY | MEMF_CLEAR)) == NULL) {
222 SetIoErr(ERROR_NO_FREE_STORE);
223 return NULL;
226 do {
227 /* now find the doslist entry for the named volume */
228 dl = LockDosList(LDF_ALL | LDF_READ);
229 dl = FindDosEntry(dl, vol, LDF_ALL);
231 /* not found, bail out */
232 if (dl == NULL) {
233 UnLockDosList(LDF_ALL | LDF_READ);
235 if (ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_INSERT, (ULONG) vol, NULL) == DOSTRUE) {
236 FreeMem(dp, sizeof(struct DevProc));
237 return NULL;
240 } while(dl == NULL);
242 /* relative to the current dir, then we have enough to get out of here */
243 if (lock != NULL) {
244 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(cur))->fh_Device;
245 dp->dvp_Lock = lock;
246 dp->dvp_Flags = 0;
247 dp->dvp_DevNode = dl;
249 UnLockDosList(LDF_ALL | LDF_READ);
251 return dp;
255 /* at this point, we have an allocated devproc in dp, the doslist is
256 * locked for read, and we have the entry for the named "volume"
257 * (device, assign, etc) in dl and a filename relative to that in name */
259 /* late assign. we resolve the target and then promote the doslist entry
260 * to full assign */
261 if (dl->dol_Type == DLT_LATE) {
262 /* obtain a lock on the target */
263 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
265 /* unlock the list, either we have a lock and need to assign it, or we
266 * don't and need to bail out */
267 UnLockDosList(LDF_ALL | LDF_READ);
269 /* XXX there's a race here. with the doslist unlocked, it's possible
270 * that some other process will add or remove this assign, blowing
271 * everything up. need more tuits before attempting a fix */
273 /* didn't find the target */
274 if (lock == NULL) {
275 FreeMem(dp, sizeof(struct DevProc));
276 return NULL;
279 /* try to assign it */
280 if (AssignLock(vol, lock) == DOSFALSE) {
281 UnLock(lock);
282 FreeMem(dp, sizeof(struct DevProc));
283 return NULL;
286 /* we made the assign! now we have to go back over the list and find
287 * the new entry */
288 dl = LockDosList(LDF_ALL | LDF_READ);
289 dl = FindDosEntry(dl, vol, LDF_ALL);
291 /* not found. XXX this will only happen if we hit that race above */
292 if (dl == NULL) {
293 UnLockDosList(LDF_ALL | LDF_READ);
294 FreeMem(dp, sizeof(struct DevProc));
295 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
296 return NULL;
299 /* the added entry will be a DLT_DIRECTORY, so we can just copy the
300 * details in and get out of here */
301 dp->dvp_Port = (struct MsgPort *) dl->dol_Ext.dol_AROS.dol_Device;
302 dp->dvp_Lock = dl->dol_Lock;
303 dp->dvp_Flags = 0;
304 dp->dvp_DevNode = dl;
306 UnLockDosList(LDF_ALL | LDF_READ);
308 return dp;
311 /* nonbinding assign. like a late assign, but with no doslist promotion */
312 if (dl->dol_Type == DLT_NONBINDING) {
313 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
315 /* just fill out the dp and return */
316 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(lock))->fh_Device;
317 dp->dvp_Lock = lock;
318 dp->dvp_Flags = DVPF_UNLOCK; /* remember to unlock in FreeDeviceNode() */
319 dp->dvp_DevNode = dl;
321 UnLockDosList(LDF_ALL | LDF_READ);
323 return dp;
326 /* devices and volumes are easy */
327 if (dl->dol_Type == DLT_DEVICE || dl->dol_Type == DLT_VOLUME) {
328 if (dl->dol_Type == DLT_DEVICE) {
329 if (!dl->dol_Ext.dol_AROS.dol_Device) {
330 D(bug("Accessing offline device %s\n", dl->dol_Ext.dol_AROS.dol_DevName));
331 RunHandler((struct DeviceNode *)dl, DOSBase);
333 } else {
334 while (!dl->dol_Ext.dol_AROS.dol_Device) {
335 D(bug("Accessing offline volume %s\n", dl->dol_Ext.dol_AROS.dol_DevName));
336 ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_VOLUME, (ULONG)dl, NULL);
339 dp->dvp_Port = (struct MsgPort *) dl->dol_Ext.dol_AROS.dol_Device;
340 dp->dvp_Lock = NULL;
341 dp->dvp_Flags = 0;
342 dp->dvp_DevNode = dl;
344 UnLockDosList(LDF_ALL | LDF_READ);
346 return dp;
349 /* sanity check */
350 if (dl->dol_Type != DLT_DIRECTORY) {
351 UnLockDosList(LDF_ALL | LDF_READ);
352 FreeMem(dp, sizeof(struct DevProc));
353 kprintf("%s:%d: DosList entry 0x%08x has unknown type %d. Probably a bug, report it!\n"
354 " GetDeviceProc() called for '%s'\n",
355 __FILE__, __LINE__, dl, dl->dol_Type, name);
356 SetIoErr(ERROR_BAD_NUMBER);
357 return NULL;
360 /* real assigns. first, see if it's just pointing to a single dir */
361 if (dp->dvp_Flags != DVPF_ASSIGN) {
362 /* just a plain assign, easy */
363 dp->dvp_Port = (struct MsgPort *) dl->dol_Ext.dol_AROS.dol_Device;
364 dp->dvp_Lock = dl->dol_Lock;
365 dp->dvp_DevNode = dl;
367 /* note multidirectory assigns so the caller knows to loop */
368 dp->dvp_Flags = dl->dol_misc.dol_assign.dol_List != NULL ? DVPF_ASSIGN : 0;
370 UnLockDosList(LDF_ALL | LDF_READ);
372 return dp;
375 /* finally the tricky bit - multidirectory assigns */
377 /* if we're pointing at the "primary" lock, then we just take the first
378 * one in the list */
379 if (dp->dvp_Lock == dl->dol_Lock)
380 dp->dvp_Lock = dl->dol_misc.dol_assign.dol_List->al_Lock;
382 /* otherwise we're finding the next */
383 else {
384 struct AssignList *al = dl->dol_misc.dol_assign.dol_List;
386 /* find our current lock (ie the one we returned last time) */
387 for (; al != NULL && al->al_Lock != dp->dvp_Lock; al = al->al_Next);
389 /* if we didn't find it, or we didn't but there's none after it, then
390 * we've run out */
391 if (al == NULL || (al = al->al_Next) == NULL) {
392 UnLockDosList(LDF_ALL | LDF_READ);
393 FreeMem(dp, sizeof(struct DevProc));
395 SetIoErr(ERROR_NO_MORE_ENTRIES);
396 return NULL;
399 /* fill out the lock from the new entry */
400 dp->dvp_Lock = al->al_Lock;
403 /* final pieces */
404 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(dp->dvp_Lock))->fh_Device;
405 dp->dvp_Flags = DVPF_ASSIGN;
406 dp->dvp_DevNode = dl;
408 UnLockDosList(LDF_READ|LDF_ALL);
410 /* phew */
411 SetIoErr(0);
412 return dp;
414 AROS_LIBFUNC_EXIT
415 } /* GetDeviceProc */
417 /* Attempt to start a handler for the DeviceNode */
418 BOOL RunHandler(struct DeviceNode *deviceNode, struct DosLibrary *DOSBase)
420 struct MsgPort *mp;
421 struct IOFileSys *iofs;
422 BOOL ok = FALSE;
424 mp = CreateMsgPort();
426 if (mp != NULL)
428 iofs = (struct IOFileSys *)CreateIORequest(mp, sizeof(struct IOFileSys));
430 if (iofs != NULL)
432 STRPTR handler;
433 struct FileSysStartupMsg *fssm;
434 ULONG fssmFlags = 0;
436 if (deviceNode->dn_Handler == NULL)
438 handler = "afs.handler";
440 else
442 handler = AROS_BSTR_ADDR(deviceNode->dn_Handler);
445 /* FIXME: this assumes that dol_Startup points to struct FileSysStartupMsg.
446 This is not true for plain handlers, dol_Startup is a BSTR in this case.
447 In order to make use of this we should implement direct support for
448 packet-style handlers in dos.library */
449 fssm = (struct FileSysStartupMsg *)BADDR(deviceNode->dn_Startup);
450 if (fssm != NULL)
452 iofs->io_Union.io_OpenDevice.io_DeviceName = AROS_BSTR_ADDR(fssm->fssm_Device);
453 iofs->io_Union.io_OpenDevice.io_Unit = fssm->fssm_Unit;
454 iofs->io_Union.io_OpenDevice.io_Environ = (IPTR *)BADDR(fssm->fssm_Environ);
455 fssmFlags = fssm->fssm_Flags;
457 iofs->io_Union.io_OpenDevice.io_DosName = deviceNode->dn_Ext.dn_AROS.dn_DevName;
458 iofs->io_Union.io_OpenDevice.io_DeviceNode = deviceNode;
460 D(bug("Starting up %s\n", handler));
461 if (!OpenDevice(handler, 0, &iofs->IOFS, fssmFlags) ||
462 !OpenDevice("packet.handler", 0, &iofs->IOFS, fssmFlags))
464 /* Ok, this means that the handler was able to open. */
465 D(bug("Handler started\n"));
466 deviceNode->dn_Ext.dn_AROS.dn_Device = iofs->IOFS.io_Device;
467 deviceNode->dn_Ext.dn_AROS.dn_Unit = iofs->IOFS.io_Unit;
468 ok = TRUE;
471 DeleteIORequest(&iofs->IOFS);
474 DeleteMsgPort(mp);
476 return ok;