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 static BOOL
VolumeIsOffline(struct DosList
*dl
);
17 /*****************************************************************************
20 #include <proto/dos.h>
22 AROS_LH2(struct DevProc
*, GetDeviceProc
,
25 AROS_LHA(CONST_STRPTR
, name
, D1
),
26 AROS_LHA(struct DevProc
*, dp
, D2
),
29 struct DosLibrary
*, DOSBase
, 107, Dos
)
32 GetDeviceProc() will search for the filesystem handler which
33 you should send a command to for a specific path.
35 By calling GetDeviceProc() multiple times, the caller will
36 be able to handle multi-assign paths.
38 The first call to GetDeviceProc() should have the |dp| parameter
42 name - Name of the object to find.
43 dp - Previous result of GetDeviceProc() or NULL.
46 A pointer to a DevProc structure containing the information
47 required to send a command to a filesystem.
54 Currently doesn't return dvp_DevNode for locks which are
55 relative to "PROGDIR:", ":", or the current directory.
62 *****************************************************************************/
66 struct Process
*pr
= (struct Process
*)FindTask(NULL
);
67 struct DosList
*dl
= NULL
;
71 BPTR cur
= NULL
, lock
= NULL
;
75 /* if they passed us the result of a previous call, then they're wanted to
76 * loop over the targets of a multidirectory assign */
79 /* if what they passed us is not a multidirectory assign, then there's
80 * nothing for us to do */
81 if (dp
->dvp_DevNode
!= NULL
&&
82 (dp
->dvp_DevNode
->dol_Type
!= DLT_DIRECTORY
|| !(dp
->dvp_Flags
& DVPF_ASSIGN
))) {
85 if (dp
->dvp_Flags
& DVPF_UNLOCK
)
88 FreeMem(dp
, sizeof(struct DevProc
));
92 /* it's fine, we'll start from here */
95 /* lock the dos list here, to match the result of the next block */
96 LockDosList(LDF_ALL
| LDF_READ
);
99 /* otherwise we need to find a place to start in the doslist based on the
100 * name they passed in */
103 /* handle standard I/O streams as "virtual" devices */
104 if (Stricmp(name
, "IN:") == 0 || Stricmp(name
, "STDIN:") == 0) {
105 cur
= pr
->pr_CIS
!= NULL
? pr
->pr_CIS
: (BPTR
) -1;
108 else if (Stricmp(name
, "OUT:") == 0 || Stricmp(name
, "STDOUT:") == 0) {
109 cur
= pr
->pr_COS
!= NULL
? pr
->pr_COS
: (BPTR
) -1;
112 else if (Stricmp(name
, "ERR:") == 0 || Stricmp(name
, "STDERR:") == 0) {
113 cur
= pr
->pr_CES
!= NULL
? pr
->pr_CES
:
114 pr
->pr_COS
!= NULL
? pr
->pr_COS
: (BPTR
) -1;
119 /* handle doesn't exist */
120 if (cur
== (BPTR
) -1) {
121 SetIoErr(ERROR_DEVICE_NOT_MOUNTED
);
125 /* got it, make a fake devproc */
126 if ((dp
= AllocMem(sizeof(struct DevProc
), MEMF_ANY
| MEMF_CLEAR
)) == NULL
) {
127 SetIoErr(ERROR_NO_FREE_STORE
);
131 /* we need a lock for the devproc */
132 if ((lock
= DupLockFromFH(cur
)) == NULL
) {
133 FreeMem(dp
, sizeof(struct DevProc
));
137 /* build the devproc for return */
138 dp
->dvp_Port
= (struct MsgPort
*) ((struct FileHandle
*) BADDR(lock
))->fh_Device
;
140 dp
->dvp_Flags
= DVPF_UNLOCK
; /* remember to unlock in FreeDeviceNode() */
142 /* finding the device node
143 * XXX this is naive - if the device appears on the doslist twice
144 * we have no way to tell which one is ours. we can't even use
145 * NameFromLock() to get the volume name and then find the doslist
146 * entry from that as console handlers probably don't even
147 * implement names. bring on packets I say */
149 dl
= LockDosList(LDF_ALL
| LDF_READ
);
151 && ((struct MsgPort
*)dl
->dol_Ext
.dol_AROS
.dol_Device
155 UnLockDosList(LDF_READ
| LDF_ALL
);
159 FreeMem(dp
, sizeof(struct DevProc
));
160 SetIoErr(ERROR_DEVICE_NOT_MOUNTED
);
165 dp
->dvp_DevNode
= dl
;
170 /* something real, work out what it's relative to */
171 if (Strnicmp(name
, "PROGDIR:", 8) == 0) {
172 cur
= pr
->pr_HomeDir
;
174 /* move past the "volume" name, so that we end up in the "no
175 * volume name" case below */;
180 cur
= pr
->pr_CurrentDir
;
182 /* if we got NULL, then it's relative to the system root lock */
184 cur
= DOSBase
->dl_SYSLock
;
186 /* extract the volume name */
187 len
= SplitName(name
, ':', vol
, 0, sizeof(vol
)-1);
189 /* move the name past it, it's now relative to the volume */
192 /* if there wasn't one (or we found a lone ':'), then we need to
193 * extract it from the name of the current dir */
195 /* XXX this block sucks. NameFromLock () calls DupLock(), which calls
196 * Lock(), which would end up back here if it wasn't for the
197 * special-case in Lock(). once we have real FileLock locks, then this
198 * code will go and we can just look at cur->fl_Volume to get the
199 * doslist entry. see the morphos version for details */
202 /* if we didn't find a ':' at all, then we'll need to return the
203 * lock that it's relative to, so make a note */
207 if (NameFromLock(cur
, buf
, 255) != DOSTRUE
)
210 len
= SplitName(buf
, ':', vol
, 0, sizeof(vol
)-1);
212 /* if there isn't one, something is horribly wrong */
214 kprintf("%s:%d: NameFromLock() returned a path without a volume. Probably a bug, report it!\n"
215 " GetDeviceProc() called for '%s'\n"
216 " NameFromLock() called on 0x%08x, returned '%s'\n",
217 __FILE__
, __LINE__
, name
, cur
, buf
);
218 SetIoErr(ERROR_INVALID_COMPONENT_NAME
);
223 /* allocate structure for return */
224 if ((dp
= AllocMem(sizeof(struct DevProc
), MEMF_ANY
| MEMF_CLEAR
)) == NULL
) {
225 SetIoErr(ERROR_NO_FREE_STORE
);
230 /* now find the doslist entry for the named volume */
231 dl
= LockDosList(LDF_ALL
| LDF_READ
);
232 dl
= FindDosEntry(dl
, vol
, LDF_ALL
);
234 /* not found, bail out */
236 UnLockDosList(LDF_ALL
| LDF_READ
);
238 if (ErrorReport(ERROR_DEVICE_NOT_MOUNTED
, REPORT_INSERT
, (ULONG
) vol
, NULL
) == DOSTRUE
) {
239 FreeMem(dp
, sizeof(struct DevProc
));
245 /* relative to the current dir, then we have enough to get out of here */
247 dp
->dvp_Port
= (struct MsgPort
*) ((struct FileHandle
*) BADDR(cur
))->fh_Device
;
250 dp
->dvp_DevNode
= dl
;
252 UnLockDosList(LDF_ALL
| LDF_READ
);
258 /* at this point, we have an allocated devproc in dp, the doslist is
259 * locked for read, and we have the entry for the named "volume"
260 * (device, assign, etc) in dl and a filename relative to that in name */
262 /* late assign. we resolve the target and then promote the doslist entry
264 if (dl
->dol_Type
== DLT_LATE
) {
265 /* obtain a lock on the target */
266 lock
= Lock(dl
->dol_misc
.dol_assign
.dol_AssignName
, SHARED_LOCK
);
268 /* unlock the list, either we have a lock and need to assign it, or we
269 * don't and need to bail out */
270 UnLockDosList(LDF_ALL
| LDF_READ
);
272 /* XXX there's a race here. with the doslist unlocked, it's possible
273 * that some other process will add or remove this assign, blowing
274 * everything up. need more tuits before attempting a fix */
276 /* didn't find the target */
278 FreeMem(dp
, sizeof(struct DevProc
));
282 /* try to assign it */
283 if (AssignLock(vol
, lock
) == DOSFALSE
) {
285 FreeMem(dp
, sizeof(struct DevProc
));
289 /* we made the assign! now we have to go back over the list and find
291 dl
= LockDosList(LDF_ALL
| LDF_READ
);
292 dl
= FindDosEntry(dl
, vol
, LDF_ALL
);
294 /* not found. XXX this will only happen if we hit that race above */
296 UnLockDosList(LDF_ALL
| LDF_READ
);
297 FreeMem(dp
, sizeof(struct DevProc
));
298 SetIoErr(ERROR_DEVICE_NOT_MOUNTED
);
302 /* the added entry will be a DLT_DIRECTORY, so we can just copy the
303 * details in and get out of here */
304 dp
->dvp_Port
= (struct MsgPort
*) dl
->dol_Ext
.dol_AROS
.dol_Device
;
305 dp
->dvp_Lock
= dl
->dol_Lock
;
307 dp
->dvp_DevNode
= dl
;
309 UnLockDosList(LDF_ALL
| LDF_READ
);
314 /* nonbinding assign. like a late assign, but with no doslist promotion */
315 if (dl
->dol_Type
== DLT_NONBINDING
) {
316 lock
= Lock(dl
->dol_misc
.dol_assign
.dol_AssignName
, SHARED_LOCK
);
318 /* just fill out the dp and return */
319 dp
->dvp_Port
= (struct MsgPort
*) ((struct FileHandle
*) BADDR(lock
))->fh_Device
;
321 dp
->dvp_Flags
= DVPF_UNLOCK
; /* remember to unlock in FreeDeviceNode() */
322 dp
->dvp_DevNode
= dl
;
324 UnLockDosList(LDF_ALL
| LDF_READ
);
329 /* devices and volumes are easy */
330 if (dl
->dol_Type
== DLT_DEVICE
|| dl
->dol_Type
== DLT_VOLUME
) {
332 if (dl
->dol_Type
== DLT_DEVICE
) {
333 if (!dl
->dol_Ext
.dol_AROS
.dol_Device
) {
334 D(bug("Accessing offline device %s\n", dl
->dol_Ext
.dol_AROS
.dol_DevName
));
335 res
= RunHandler((struct DeviceNode
*)dl
, DOSBase
);
338 while (res
&& VolumeIsOffline(dl
)) {
339 D(bug("Accessing offline volume %s\n", dl
->dol_Ext
.dol_AROS
.dol_DevName
));
340 res
= !ErrorReport(ERROR_DEVICE_NOT_MOUNTED
, REPORT_VOLUME
, (ULONG
)dl
, NULL
);
344 UnLockDosList(LDF_ALL
| LDF_READ
);
345 FreeMem(dp
, sizeof(struct DevProc
));
346 SetIoErr(ERROR_DEVICE_NOT_MOUNTED
);
349 dp
->dvp_Port
= (struct MsgPort
*) dl
->dol_Ext
.dol_AROS
.dol_Device
;
352 dp
->dvp_DevNode
= dl
;
354 UnLockDosList(LDF_ALL
| LDF_READ
);
360 if (dl
->dol_Type
!= DLT_DIRECTORY
) {
361 UnLockDosList(LDF_ALL
| LDF_READ
);
362 FreeMem(dp
, sizeof(struct DevProc
));
363 kprintf("%s:%d: DosList entry 0x%08x has unknown type %d. Probably a bug, report it!\n"
364 " GetDeviceProc() called for '%s'\n",
365 __FILE__
, __LINE__
, dl
, dl
->dol_Type
, name
);
366 SetIoErr(ERROR_BAD_NUMBER
);
370 /* real assigns. first, see if it's just pointing to a single dir */
371 if (dp
->dvp_Flags
!= DVPF_ASSIGN
) {
372 /* just a plain assign, easy */
373 dp
->dvp_Port
= (struct MsgPort
*) dl
->dol_Ext
.dol_AROS
.dol_Device
;
374 dp
->dvp_Lock
= dl
->dol_Lock
;
375 dp
->dvp_DevNode
= dl
;
377 /* note multidirectory assigns so the caller knows to loop */
378 dp
->dvp_Flags
= dl
->dol_misc
.dol_assign
.dol_List
!= NULL
? DVPF_ASSIGN
: 0;
380 UnLockDosList(LDF_ALL
| LDF_READ
);
385 /* finally the tricky bit - multidirectory assigns */
387 /* if we're pointing at the "primary" lock, then we just take the first
389 if (dp
->dvp_Lock
== dl
->dol_Lock
)
390 dp
->dvp_Lock
= dl
->dol_misc
.dol_assign
.dol_List
->al_Lock
;
392 /* otherwise we're finding the next */
394 struct AssignList
*al
= dl
->dol_misc
.dol_assign
.dol_List
;
396 /* find our current lock (ie the one we returned last time) */
397 for (; al
!= NULL
&& al
->al_Lock
!= dp
->dvp_Lock
; al
= al
->al_Next
);
399 /* if we didn't find it, or we didn't but there's none after it, then
401 if (al
== NULL
|| (al
= al
->al_Next
) == NULL
) {
402 UnLockDosList(LDF_ALL
| LDF_READ
);
403 FreeMem(dp
, sizeof(struct DevProc
));
405 SetIoErr(ERROR_NO_MORE_ENTRIES
);
409 /* fill out the lock from the new entry */
410 dp
->dvp_Lock
= al
->al_Lock
;
414 dp
->dvp_Port
= (struct MsgPort
*) ((struct FileHandle
*) BADDR(dp
->dvp_Lock
))->fh_Device
;
415 dp
->dvp_Flags
= DVPF_ASSIGN
;
416 dp
->dvp_DevNode
= dl
;
418 UnLockDosList(LDF_READ
|LDF_ALL
);
425 } /* GetDeviceProc */
427 /* Attempt to start a handler for the DeviceNode */
428 BOOL
RunHandler(struct DeviceNode
*deviceNode
, struct DosLibrary
*DOSBase
)
431 struct IOFileSys
*iofs
;
434 mp
= CreateMsgPort();
438 iofs
= (struct IOFileSys
*)CreateIORequest(mp
, sizeof(struct IOFileSys
));
443 struct FileSysStartupMsg
*fssm
;
446 if (deviceNode
->dn_Handler
== NULL
)
448 handler
= "afs.handler";
452 handler
= AROS_BSTR_ADDR(deviceNode
->dn_Handler
);
455 /* FIXME: this assumes that dol_Startup points to struct FileSysStartupMsg.
456 This is not true for plain handlers, dol_Startup is a BSTR in this case.
457 In order to make use of this we should implement direct support for
458 packet-style handlers in dos.library */
459 fssm
= (struct FileSysStartupMsg
*)BADDR(deviceNode
->dn_Startup
);
462 iofs
->io_Union
.io_OpenDevice
.io_DeviceName
= AROS_BSTR_ADDR(fssm
->fssm_Device
);
463 iofs
->io_Union
.io_OpenDevice
.io_Unit
= fssm
->fssm_Unit
;
464 iofs
->io_Union
.io_OpenDevice
.io_Environ
= (IPTR
*)BADDR(fssm
->fssm_Environ
);
465 fssmFlags
= fssm
->fssm_Flags
;
467 iofs
->io_Union
.io_OpenDevice
.io_DosName
= deviceNode
->dn_Ext
.dn_AROS
.dn_DevName
;
468 iofs
->io_Union
.io_OpenDevice
.io_DeviceNode
= deviceNode
;
470 D(bug("Starting up %s\n", handler
));
471 if (!OpenDevice(handler
, 0, &iofs
->IOFS
, fssmFlags
) ||
472 !OpenDevice("packet.handler", 0, &iofs
->IOFS
, fssmFlags
))
474 /* Ok, this means that the handler was able to open. */
475 D(bug("Handler started\n"));
476 deviceNode
->dn_Ext
.dn_AROS
.dn_Device
= iofs
->IOFS
.io_Device
;
477 deviceNode
->dn_Ext
.dn_AROS
.dn_Unit
= iofs
->IOFS
.io_Unit
;
481 DeleteIORequest(&iofs
->IOFS
);
489 static BOOL
VolumeIsOffline(struct DosList
*dl
)
491 if (strcmp(dl
->dol_Ext
.dol_AROS
.dol_Device
->dd_Library
.lib_Node
.ln_Name
,
493 return !dl
->dol_Ext
.dol_AROS
.dol_Unit
;
495 return !dl
->dol_Task
;