emul-handler: fh_Arg1 should be the only element of struct FileHandle touched during...
[AROS.git] / rom / dos / getdeviceproc.c
blob0af1cb4a90f160eafd1bfa621d2f5efc2df67e1b
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: GetDeviceProc() - Find the filesystem for a path.
6 Lang: english
7 */
9 #include <aros/debug.h>
11 #include <proto/exec.h>
12 #include <proto/utility.h>
14 #include "dos_intern.h"
16 extern struct Process *r(struct DeviceNode *dn, struct DosLibrary *DOSBase);
17 static struct DevProc *deviceproc_internal(struct DosLibrary *DOSBase, CONST_STRPTR name, struct DevProc *dp);
19 /*****************************************************************************
21 NAME */
22 #include <proto/dos.h>
24 AROS_LH2(struct DevProc *, GetDeviceProc,
26 /* SYNOPSIS */
27 AROS_LHA(CONST_STRPTR, name, D1),
28 AROS_LHA(struct DevProc *, dp, D2),
30 /* LOCATION */
31 struct DosLibrary *, DOSBase, 107, Dos)
33 /* FUNCTION
34 GetDeviceProc() will search for the filesystem handler which
35 you should send a command to for a specific path.
37 By calling GetDeviceProc() multiple times, the caller will
38 be able to handle multi-assign paths.
40 The first call to GetDeviceProc() should have the |dp| parameter
41 as NULL.
43 INPUTS
44 name - Name of the object to find.
45 dp - Previous result of GetDeviceProc() or NULL.
47 RESULT
48 A pointer to a DevProc structure containing the information
49 required to send a command to a filesystem.
51 NOTES
53 EXAMPLE
55 BUGS
56 Currently doesn't return dvp_DevNode for locks which are
57 relative to "PROGDIR:", ":", or the current directory.
59 SEE ALSO
60 FreeDeviceProc()
62 INTERNALS
64 *****************************************************************************/
66 AROS_LIBFUNC_INIT
68 struct DevProc *dp2;
70 D(bug("[GetDeviceProc] '%s':0x%p\n", name, dp));
71 dp2 = deviceproc_internal(DOSBase, name, dp);
73 #if DEBUG
74 bug("[GetDeviceProc] = 0x%p", dp2);
75 if (dp2)
76 bug(", port=0x%p lock=0x%p dv=0x%p\n", dp2->dvp_Port, dp2->dvp_Lock, dp2->dvp_DevNode);
77 RawPutChar('\n');
78 #endif
80 return dp2;
82 AROS_LIBFUNC_EXIT
84 } /* GetDeviceProc */
86 static struct DevProc *deviceproc_internal(struct DosLibrary *DOSBase, CONST_STRPTR name, struct DevProc *dp)
88 struct Process *pr = (struct Process *)FindTask(NULL);
89 struct DosList *dl = NULL;
90 char vol[32];
91 LONG len;
92 BPTR lock = BNULL;
93 BOOL res;
94 CONST_STRPTR origname = name;
95 struct FileLock *fl;
97 ASSERT_VALID_PROCESS(pr);
99 /* if they passed us the result of a previous call, then they're wanted to
100 * loop over the targets of a multidirectory assign */
101 if (dp != NULL) {
103 /* if what they passed us is not a multidirectory assign, then there's
104 * nothing for us to do */
105 if (dp->dvp_DevNode != NULL &&
106 (dp->dvp_DevNode->dol_Type != DLT_DIRECTORY || !(dp->dvp_Flags & DVPF_ASSIGN))) {
108 /* cleanup */
109 if (dp->dvp_Flags & DVPF_UNLOCK)
110 UnLock(dp->dvp_Lock);
112 FreeMem(dp, sizeof(struct DevProc));
113 SetIoErr(ERROR_NO_MORE_ENTRIES);
114 return NULL;
117 /* it's fine, we'll start from here */
118 dl = dp->dvp_DevNode;
120 /* lock the dos list here, to match the result of the next block */
121 LockDosList(LDF_ALL | LDF_READ);
124 /* otherwise we need to find a place to start in the doslist based on the
125 * name they passed in */
126 else {
127 if (name == NULL || Strnicmp(name, "NIL:", 4) == 0) {
128 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
129 return NULL;
132 /* allocate structure for return */
133 if ((dp = AllocMem(sizeof(struct DevProc), MEMF_ANY | MEMF_CLEAR)) == NULL) {
134 SetIoErr(ERROR_NO_FREE_STORE);
135 return NULL;
138 /* something real, work out what it's relative to */
139 if (Strnicmp(name, "PROGDIR:", 8) == 0) {
140 lock = pr->pr_HomeDir;
141 /* I am not sure if these are correct but AOS does return
142 * non-NULL PROGDIR: handle even if pr_HomeDir is cleared */
143 if (!lock)
144 lock = pr->pr_CurrentDir;
145 if (lock) {
146 fl = BADDR(lock);
147 dp->dvp_Port = fl->fl_Task;
148 dp->dvp_Lock = lock;
149 dp->dvp_DevNode = BADDR(fl->fl_Volume);
150 } else {
151 dp->dvp_Port = DOSBase->dl_Root->rn_BootProc;
152 dp->dvp_Lock = BNULL;
153 dp->dvp_DevNode = NULL;
155 dp->dvp_Flags = 0;
156 return dp;
159 /* extract the volume name */
160 len = SplitName(name, ':', vol, 0, sizeof(vol) - 1);
162 /* if there wasn't one (or we found a lone ':') -> current dir */
163 if (len <= 1) {
164 lock = pr->pr_CurrentDir;
165 /* if we got NULL, then it's relative to the system root lock */
166 if (lock != BNULL) {
167 fl = BADDR(lock);
168 dp->dvp_Port = fl->fl_Task;
169 dp->dvp_Lock = lock;
170 dp->dvp_DevNode = BADDR(fl->fl_Volume);
171 } else {
172 dp->dvp_Port = DOSBase->dl_Root->rn_BootProc;
173 dp->dvp_Lock = BNULL;
174 dp->dvp_DevNode = NULL;
177 /* Jump to the root directory if name[0] == ':' */
178 if (name[0] == ':')
179 dp->dvp_Lock = (dp->dvp_DevNode) ? dp->dvp_DevNode->dol_Lock : BNULL;
181 dp->dvp_Flags = 0;
182 return dp;
185 do {
186 /* now find the doslist entry for the named volume */
187 dl = LockDosList(LDF_ALL | LDF_READ);
188 dl = FindDosEntry(dl, vol, LDF_ALL);
190 /* not found, bail out */
191 if (dl == NULL) {
192 UnLockDosList(LDF_ALL | LDF_READ);
194 if (ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_INSERT, (IPTR)vol, NULL) == DOSTRUE) {
195 FreeMem(dp, sizeof(struct DevProc));
196 return NULL;
199 } while(dl == NULL);
202 /* at this point, we have an allocated devproc in dp, the doslist is
203 * locked for read, and we have the entry for the named "volume"
204 * (device, assign, etc) in dl and a filename relative to that in name */
206 /* late assign. we resolve the target and then promote the doslist entry
207 * to full assign */
208 if (dl->dol_Type == DLT_LATE) {
209 /* obtain a lock on the target */
210 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
212 /* didn't find the target */
213 if (lock == BNULL) {
214 UnLockDosList(LDF_ALL | LDF_READ);
215 FreeMem(dp, sizeof(struct DevProc));
216 return NULL;
219 /* Directly change assign type without calling AssignXXX(),
220 * mimics AOS behavior, AOS programs assume it is safe to
221 * keep LDF_WRITE lock while calling Lock("late assign:");
223 dl->dol_Type = DLT_DIRECTORY;
224 dl->dol_Lock = lock;
225 dl->dol_Task = ((struct FileLock*)BADDR(lock))->fl_Task;
226 FreeVec(dl->dol_misc.dol_assign.dol_AssignName);
227 dl->dol_misc.dol_assign.dol_AssignName = NULL;
229 /* the added entry will be a DLT_DIRECTORY, so we can just copy the
230 * details in and get out of here */
231 dp->dvp_Port = dl->dol_Task;
232 dp->dvp_Lock = dl->dol_Lock;
233 dp->dvp_Flags = 0;
234 dp->dvp_DevNode = dl;
236 UnLockDosList(LDF_ALL | LDF_READ);
238 return dp;
241 /* nonbinding assign. like a late assign, but with no doslist promotion */
242 if (dl->dol_Type == DLT_NONBINDING) {
243 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
245 /* just fill out the dp and return */
246 dp->dvp_Port = ((struct FileLock *) BADDR(lock))->fl_Task;
247 dp->dvp_Lock = lock;
248 dp->dvp_Flags = DVPF_UNLOCK; /* remember to unlock in FreeDeviceNode() */
249 dp->dvp_DevNode = dl;
251 UnLockDosList(LDF_ALL | LDF_READ);
253 return dp;
256 /* devices and volumes are easy */
257 if (dl->dol_Type == DLT_DEVICE || dl->dol_Type == DLT_VOLUME)
259 struct MsgPort *newhandler = NULL;
261 if (dl->dol_Type == DLT_DEVICE)
263 /* Check if the handler is not started */
264 newhandler = dl->dol_Task;
265 if (!newhandler)
267 D(bug("[GetDeviceProc] Accessing device '%b', path='%s'\n", dl->dol_Name, origname));
270 * Unlock before starting handler, handler may internally
271 * require dos list locks, for example to add volume node.
273 UnLockDosList(LDF_ALL | LDF_READ);
275 newhandler = RunHandler((struct DeviceNode *)dl, origname, DOSBase);
276 if (!newhandler)
278 FreeMem(dp, sizeof(struct DevProc));
279 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
280 return NULL;
283 LockDosList(LDF_ALL | LDF_READ);
286 else
288 res = TRUE;
290 while (res && !dl->dol_Task)
292 D(bug("[GetDeviceProc] Accessing offline volume '%b'\n", dl->dol_Name));
293 res = !ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_VOLUME, (IPTR)dl, NULL);
296 if (!res)
298 UnLockDosList(LDF_ALL | LDF_READ);
299 FreeMem(dp, sizeof(struct DevProc));
300 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
301 return NULL;
306 * A handler theoretically may choose to use custom MsgPort for communications.
307 * Pick up its preference if specified.
309 dp->dvp_Port = dl->dol_Task ? dl->dol_Task : newhandler;
310 dp->dvp_Lock = BNULL;
311 dp->dvp_Flags = 0;
312 dp->dvp_DevNode = dl;
314 UnLockDosList(LDF_ALL | LDF_READ);
316 return dp;
319 /* sanity check */
320 if (dl->dol_Type != DLT_DIRECTORY) {
321 UnLockDosList(LDF_ALL | LDF_READ);
322 FreeMem(dp, sizeof(struct DevProc));
323 kprintf("%s:%d: DosList entry 0x%p has unknown type %d. Probably a bug, report it!\n"
324 " GetDeviceProc() called for '%s'\n",
325 __FILE__, __LINE__, dl, dl->dol_Type, name);
326 SetIoErr(ERROR_BAD_NUMBER);
327 return NULL;
330 /* real assigns. first, see if it's just pointing to a single dir */
331 if (dp->dvp_Flags != DVPF_ASSIGN) {
332 /* just a plain assign, easy */
333 dp->dvp_Port = dl->dol_Task;
334 dp->dvp_Lock = dl->dol_Lock;
335 dp->dvp_DevNode = dl;
337 /* note multidirectory assigns so the caller knows to loop */
338 dp->dvp_Flags = dl->dol_misc.dol_assign.dol_List != NULL ? DVPF_ASSIGN : 0;
340 UnLockDosList(LDF_ALL | LDF_READ);
341 return dp;
344 /* finally the tricky bit - multidirectory assigns */
346 /* if we're pointing at the "primary" lock, then we just take the first
347 * one in the list */
348 if (dp->dvp_Lock == dl->dol_Lock)
349 dp->dvp_Lock = dl->dol_misc.dol_assign.dol_List->al_Lock;
351 /* otherwise we're finding the next */
352 else {
353 struct AssignList *al = dl->dol_misc.dol_assign.dol_List;
355 /* find our current lock (ie the one we returned last time) */
356 for (; al != NULL && al->al_Lock != dp->dvp_Lock; al = al->al_Next);
358 /* if we didn't find it, or we didn't but there's none after it, then
359 * we've run out */
360 if (al == NULL || (al = al->al_Next) == NULL) {
361 UnLockDosList(LDF_ALL | LDF_READ);
362 FreeMem(dp, sizeof(struct DevProc));
364 SetIoErr(ERROR_NO_MORE_ENTRIES);
365 return NULL;
368 /* fill out the lock from the new entry */
369 dp->dvp_Lock = al->al_Lock;
372 /* final pieces */
373 dp->dvp_Port = ((struct FileLock *) BADDR(dp->dvp_Lock))->fl_Task;
374 dp->dvp_Flags = DVPF_ASSIGN;
375 dp->dvp_DevNode = dl;
377 UnLockDosList(LDF_READ|LDF_ALL);
378 /* phew */
379 SetIoErr(0);
380 return dp;