Detabbed
[AROS.git] / rom / dos / getdeviceproc.c
blob9b59f30da8358064350b4733c159dd0711c02b78
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 /* allocate structure for return */
128 if ((dp = AllocMem(sizeof(struct DevProc), MEMF_ANY | MEMF_CLEAR)) == NULL) {
129 SetIoErr(ERROR_NO_FREE_STORE);
130 return NULL;
133 /* something real, work out what it's relative to */
134 if (Strnicmp(name, "PROGDIR:", 8) == 0) {
135 lock = pr->pr_HomeDir;
136 /* I am not sure if these are correct but AOS does return
137 * non-NULL PROGDIR: handle even if pr_HomeDir is cleared */
138 if (!lock)
139 lock = pr->pr_CurrentDir;
140 if (lock) {
141 fl = BADDR(lock);
142 dp->dvp_Port = fl->fl_Task;
143 dp->dvp_Lock = lock;
144 dp->dvp_DevNode = BADDR(fl->fl_Volume);
145 } else {
146 dp->dvp_Port = DOSBase->dl_Root->rn_BootProc;
147 dp->dvp_Lock = BNULL;
148 dp->dvp_DevNode = NULL;
150 dp->dvp_Flags = 0;
151 return dp;
154 /* extract the volume name */
155 len = SplitName(name, ':', vol, 0, sizeof(vol) - 1);
157 /* if there wasn't one (or we found a lone ':') -> current dir */
158 if (len <= 1) {
159 lock = pr->pr_CurrentDir;
160 /* if we got NULL, then it's relative to the system root lock */
161 if (lock != BNULL) {
162 fl = BADDR(lock);
163 dp->dvp_Port = fl->fl_Task;
164 dp->dvp_Lock = lock;
165 dp->dvp_DevNode = BADDR(fl->fl_Volume);
166 } else {
167 dp->dvp_Port = DOSBase->dl_Root->rn_BootProc;
168 dp->dvp_Lock = BNULL;
169 dp->dvp_DevNode = NULL;
172 /* Jump to the root directory if name[0] == ':' */
173 if (name[0] == ':')
174 dp->dvp_Lock = (dp->dvp_DevNode) ? dp->dvp_DevNode->dol_Lock : BNULL;
176 dp->dvp_Flags = 0;
177 return dp;
180 do {
181 /* now find the doslist entry for the named volume */
182 dl = LockDosList(LDF_ALL | LDF_READ);
183 dl = FindDosEntry(dl, vol, LDF_ALL);
185 /* not found, bail out */
186 if (dl == NULL) {
187 UnLockDosList(LDF_ALL | LDF_READ);
189 if (ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_INSERT, (IPTR)vol, NULL) == DOSTRUE) {
190 FreeMem(dp, sizeof(struct DevProc));
191 return NULL;
194 } while(dl == NULL);
197 /* at this point, we have an allocated devproc in dp, the doslist is
198 * locked for read, and we have the entry for the named "volume"
199 * (device, assign, etc) in dl and a filename relative to that in name */
201 /* late assign. we resolve the target and then promote the doslist entry
202 * to full assign */
203 if (dl->dol_Type == DLT_LATE) {
204 /* obtain a lock on the target */
205 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
207 /* didn't find the target */
208 if (lock == BNULL) {
209 UnLockDosList(LDF_ALL | LDF_READ);
210 FreeMem(dp, sizeof(struct DevProc));
211 return NULL;
214 /* Directly change assign type without calling AssignXXX(),
215 * mimics AOS behavior, AOS programs assume it is safe to
216 * keep LDF_WRITE lock while calling Lock("late assign:");
218 dl->dol_Type = DLT_DIRECTORY;
219 dl->dol_Lock = lock;
220 dl->dol_Task = ((struct FileLock*)BADDR(lock))->fl_Task;
221 FreeVec(dl->dol_misc.dol_assign.dol_AssignName);
222 dl->dol_misc.dol_assign.dol_AssignName = NULL;
224 /* the added entry will be a DLT_DIRECTORY, so we can just copy the
225 * details in and get out of here */
226 dp->dvp_Port = dl->dol_Task;
227 dp->dvp_Lock = dl->dol_Lock;
228 dp->dvp_Flags = 0;
229 dp->dvp_DevNode = dl;
231 UnLockDosList(LDF_ALL | LDF_READ);
233 return dp;
236 /* nonbinding assign. like a late assign, but with no doslist promotion */
237 if (dl->dol_Type == DLT_NONBINDING) {
238 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
240 /* just fill out the dp and return */
241 dp->dvp_Port = ((struct FileLock *) BADDR(lock))->fl_Task;
242 dp->dvp_Lock = lock;
243 dp->dvp_Flags = DVPF_UNLOCK; /* remember to unlock in FreeDeviceNode() */
244 dp->dvp_DevNode = dl;
246 UnLockDosList(LDF_ALL | LDF_READ);
248 return dp;
251 /* devices and volumes are easy */
252 if (dl->dol_Type == DLT_DEVICE || dl->dol_Type == DLT_VOLUME)
254 struct MsgPort *newhandler = NULL;
256 if (dl->dol_Type == DLT_DEVICE)
258 /* Check if the handler is not started */
259 newhandler = dl->dol_Task;
260 if (!newhandler)
262 D(bug("[GetDeviceProc] Accessing device '%b', path='%s'\n", dl->dol_Name, origname));
265 * Unlock before starting handler, handler may internally
266 * require dos list locks, for example to add volume node.
268 UnLockDosList(LDF_ALL | LDF_READ);
270 newhandler = RunHandler((struct DeviceNode *)dl, origname, DOSBase);
271 if (!newhandler)
273 FreeMem(dp, sizeof(struct DevProc));
274 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
275 return NULL;
278 LockDosList(LDF_ALL | LDF_READ);
281 else
283 res = TRUE;
285 while (res && !dl->dol_Task)
287 D(bug("[GetDeviceProc] Accessing offline volume '%b'\n", dl->dol_Name));
288 res = !ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_VOLUME, (IPTR)dl, NULL);
291 if (!res)
293 UnLockDosList(LDF_ALL | LDF_READ);
294 FreeMem(dp, sizeof(struct DevProc));
295 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
296 return NULL;
301 * A handler theoretically may choose to use custom MsgPort for communications.
302 * Pick up its preference if specified.
304 dp->dvp_Port = dl->dol_Task ? dl->dol_Task : newhandler;
305 dp->dvp_Lock = BNULL;
306 dp->dvp_Flags = 0;
307 dp->dvp_DevNode = dl;
309 UnLockDosList(LDF_ALL | LDF_READ);
311 return dp;
314 /* sanity check */
315 if (dl->dol_Type != DLT_DIRECTORY) {
316 UnLockDosList(LDF_ALL | LDF_READ);
317 FreeMem(dp, sizeof(struct DevProc));
318 kprintf("%s:%d: DosList entry 0x%p has unknown type %d. Probably a bug, report it!\n"
319 " GetDeviceProc() called for '%s'\n",
320 __FILE__, __LINE__, dl, dl->dol_Type, name);
321 SetIoErr(ERROR_BAD_NUMBER);
322 return NULL;
325 /* real assigns. first, see if it's just pointing to a single dir */
326 if (dp->dvp_Flags != DVPF_ASSIGN) {
327 /* just a plain assign, easy */
328 dp->dvp_Port = dl->dol_Task;
329 dp->dvp_Lock = dl->dol_Lock;
330 dp->dvp_DevNode = dl;
332 /* note multidirectory assigns so the caller knows to loop */
333 dp->dvp_Flags = dl->dol_misc.dol_assign.dol_List != NULL ? DVPF_ASSIGN : 0;
335 UnLockDosList(LDF_ALL | LDF_READ);
336 return dp;
339 /* finally the tricky bit - multidirectory assigns */
341 /* if we're pointing at the "primary" lock, then we just take the first
342 * one in the list */
343 if (dp->dvp_Lock == dl->dol_Lock)
344 dp->dvp_Lock = dl->dol_misc.dol_assign.dol_List->al_Lock;
346 /* otherwise we're finding the next */
347 else {
348 struct AssignList *al = dl->dol_misc.dol_assign.dol_List;
350 /* find our current lock (ie the one we returned last time) */
351 for (; al != NULL && al->al_Lock != dp->dvp_Lock; al = al->al_Next);
353 /* if we didn't find it, or we didn't but there's none after it, then
354 * we've run out */
355 if (al == NULL || (al = al->al_Next) == NULL) {
356 UnLockDosList(LDF_ALL | LDF_READ);
357 FreeMem(dp, sizeof(struct DevProc));
359 SetIoErr(ERROR_NO_MORE_ENTRIES);
360 return NULL;
363 /* fill out the lock from the new entry */
364 dp->dvp_Lock = al->al_Lock;
367 /* final pieces */
368 dp->dvp_Port = ((struct FileLock *) BADDR(dp->dvp_Lock))->fl_Task;
369 dp->dvp_Flags = DVPF_ASSIGN;
370 dp->dvp_DevNode = dl;
372 UnLockDosList(LDF_READ|LDF_ALL);
373 /* phew */
374 SetIoErr(0);
375 return dp;