2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
5 Desc: GetDeviceProc() - Find the filesystem for a path.
8 #include "dos_intern.h"
9 #include <proto/exec.h>
10 #include <proto/utility.h>
13 #include <aros/debug.h>
15 /*****************************************************************************
18 #include <proto/dos.h>
20 AROS_LH2(struct DevProc
*, GetDeviceProc
,
23 AROS_LHA(CONST_STRPTR
, name
, D1
),
24 AROS_LHA(struct DevProc
*, dp
, D2
),
27 struct DosLibrary
*, DOSBase
, 107, Dos
)
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
40 name - Name of the object to find.
41 dp - Previous result of GetDeviceProc() or NULL.
44 A pointer to a DevProc structure containing the information
45 required to send a command to a filesystem.
52 Currently doesn't return dvp_DevNode for locks which are
53 relative to "PROGDIR:", ":", or the current directory.
60 *****************************************************************************/
64 struct Process
*pr
= (struct Process
*)FindTask(NULL
);
65 struct DosList
*dl
= NULL
;
69 BPTR cur
= NULL
, lock
= NULL
;
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 */
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
))) {
82 if (dp
->dvp_Flags
& DVPF_UNLOCK
)
85 FreeMem(dp
, sizeof(struct DevProc
));
89 /* it's fine, we'll start from here */
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 */
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;
105 else if (Stricmp(name
, "OUT:") == 0 || Stricmp(name
, "STDOUT:") == 0) {
106 cur
= pr
->pr_COS
!= NULL
? pr
->pr_COS
: (BPTR
) -1;
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;
116 /* handle doesn't exist */
117 if (cur
== (BPTR
) -1) {
118 SetIoErr(ERROR_DEVICE_NOT_MOUNTED
);
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
);
128 /* we need a lock for the devproc */
129 if ((lock
= DupLockFromFH(cur
)) == NULL
) {
130 FreeMem(dp
, sizeof(struct DevProc
));
134 /* build the devproc for return */
135 dp
->dvp_Port
= (struct MsgPort
*) ((struct FileHandle
*) BADDR(lock
))->fh_Device
;
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
);
148 && ((struct MsgPort
*)dl
->dol_Ext
.dol_AROS
.dol_Device
152 UnLockDosList(LDF_READ
| LDF_ALL
);
156 FreeMem(dp
, sizeof(struct DevProc
));
157 SetIoErr(ERROR_DEVICE_NOT_MOUNTED
);
162 dp
->dvp_DevNode
= dl
;
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 */;
177 cur
= pr
->pr_CurrentDir
;
179 /* if we got NULL, then it's relative to the system root lock */
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 */
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 */
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 */
204 if (NameFromLock(cur
, buf
, 255) != DOSTRUE
)
207 len
= SplitName(buf
, ':', vol
, 0, sizeof(vol
)-1);
209 /* if there isn't one, something is horribly wrong */
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
);
220 /* allocate structure for return */
221 if ((dp
= AllocMem(sizeof(struct DevProc
), MEMF_ANY
| MEMF_CLEAR
)) == NULL
) {
222 SetIoErr(ERROR_NO_FREE_STORE
);
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 */
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
));
242 /* relative to the current dir, then we have enough to get out of here */
244 dp
->dvp_Port
= (struct MsgPort
*) ((struct FileHandle
*) BADDR(cur
))->fh_Device
;
247 dp
->dvp_DevNode
= dl
;
249 UnLockDosList(LDF_ALL
| LDF_READ
);
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
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 */
275 FreeMem(dp
, sizeof(struct DevProc
));
279 /* try to assign it */
280 if (AssignLock(vol
, lock
) == DOSFALSE
) {
282 FreeMem(dp
, sizeof(struct DevProc
));
286 /* we made the assign! now we have to go back over the list and find
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 */
293 UnLockDosList(LDF_ALL
| LDF_READ
);
294 FreeMem(dp
, sizeof(struct DevProc
));
295 SetIoErr(ERROR_DEVICE_NOT_MOUNTED
);
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
;
304 dp
->dvp_DevNode
= dl
;
306 UnLockDosList(LDF_ALL
| LDF_READ
);
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
;
318 dp
->dvp_Flags
= DVPF_UNLOCK
; /* remember to unlock in FreeDeviceNode() */
319 dp
->dvp_DevNode
= dl
;
321 UnLockDosList(LDF_ALL
| LDF_READ
);
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
);
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
;
342 dp
->dvp_DevNode
= dl
;
344 UnLockDosList(LDF_ALL
| LDF_READ
);
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
);
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
);
375 /* finally the tricky bit - multidirectory assigns */
377 /* if we're pointing at the "primary" lock, then we just take the first
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 */
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
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
);
399 /* fill out the lock from the new entry */
400 dp
->dvp_Lock
= al
->al_Lock
;
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
);
415 } /* GetDeviceProc */
417 /* Attempt to start a handler for the DeviceNode */
418 BOOL
RunHandler(struct DeviceNode
*deviceNode
, struct DosLibrary
*DOSBase
)
421 struct IOFileSys
*iofs
;
424 mp
= CreateMsgPort();
428 iofs
= (struct IOFileSys
*)CreateIORequest(mp
, sizeof(struct IOFileSys
));
433 struct FileSysStartupMsg
*fssm
;
436 if (deviceNode
->dn_Handler
== NULL
)
438 handler
= "afs.handler";
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
);
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
;
471 DeleteIORequest(&iofs
->IOFS
);