2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007 The AROS Development Team
7 * This program is free software; you can redistribute it and/or modify it
8 * under the same terms as AROS itself.
13 #include <exec/types.h>
14 #include <exec/execbase.h>
15 #include <exec/memory.h>
16 #include <dos/dosextens.h>
17 #include <dos/filehandler.h>
19 #include <proto/exec.h>
20 #include <proto/dos.h>
21 #include <proto/utility.h>
26 #include "fat_protos.h"
28 #if defined(DEBUG_FULL) && DEBUG_FULL != 0
33 #include <aros/debug.h>
38 void DumpLocks(struct FSSuper
*sb
) {
39 struct GlobalLock
*gl
;
42 bug("[fat] global locks:\n");
44 ListLength(&sb
->root_lock
.locks
, count
);
45 bug(" root: %ld references\n", count
);
47 ForeachNode(&sb
->locks
, gl
) {
48 ListLength(&gl
->locks
, count
);
49 bug(" (%ld/%ld) %.*s: %ld references\n", gl
->dir_cluster
, gl
->dir_entry
, gl
->name
[0], &(gl
->name
[1]), count
);
54 LONG
TestLock(struct ExtFileLock
*fl
) {
55 if (fl
== 0 && glob
->sb
== NULL
) {
56 if (glob
->disk_inserted
== FALSE
)
59 return ERROR_NOT_A_DOS_DISK
;
62 if (glob
->sb
== NULL
|| glob
->disk_inhibited
|| (fl
&& fl
->fl_Volume
!= MKBADDR(glob
->sb
->doslist
)))
63 return ERROR_DEVICE_NOT_MOUNTED
;
65 if (fl
&& fl
->magic
!= ID_FAT_DISK
)
66 return ERROR_OBJECT_WRONG_TYPE
;
71 LONG
LockFileByName(struct ExtFileLock
*fl
, UBYTE
*name
, LONG namelen
, LONG access
, struct ExtFileLock
**lock
) {
72 LONG err
= ERROR_OBJECT_NOT_FOUND
;
78 /* the base lock must be a directory. if its NULL, then its the root,
79 * otherwise we check its attributes */
80 if (fl
!= NULL
&& !(fl
->gl
->attr
& ATTR_DIRECTORY
))
81 return ERROR_OBJECT_WRONG_TYPE
;
83 /* the . and .. entries are invisible to the user */
84 if (name
[0] == '.' && (namelen
== 1 || (name
[1] == '.' && namelen
== 2))) {
85 D(bug("[fat] not allowing access to '.' or '..' entries\n"));
86 return ERROR_OBJECT_NOT_FOUND
;
89 /* get the first cluster of the directory to look for the file in */
90 dir_cluster
= (fl
!= NULL
) ? fl
->ioh
.first_cluster
: 0;
92 D(bug("[fat] trying to obtain lock on '%.*s' in dir at cluster %ld\n", namelen
, name
, dir_cluster
));
95 InitDirHandle(glob
->sb
, dir_cluster
, &dh
);
97 /* if it starts with a volume specifier (or just a :), remove it and get
98 * us back to the root dir */
99 for (i
= 0; i
< namelen
; i
++)
100 if (name
[i
] == ':') {
101 D(bug("[fat] name has volume specifier, moving to the root dir\n"));
106 InitDirHandle(dh
.ioh
.sb
, 0, &dh
);
111 /* look for the entry */
112 if ((err
= GetDirEntryByPath(&dh
, name
, namelen
, &de
)) != 0) {
113 ReleaseDirHandle(&dh
);
114 D(bug("[fat] couldn't get lock\n"));
118 /* found it, do the locking proper */
119 if (de
.e
.entry
.attr
& ATTR_DIRECTORY
&& FIRST_FILE_CLUSTER(&de
) == 0)
120 err
= LockRoot(access
, lock
);
122 err
= LockFile(dh
.ioh
.first_cluster
, de
.index
, access
, lock
);
124 ReleaseDirHandle(&dh
);
129 LONG
LockFile(ULONG dir_cluster
, ULONG dir_entry
, LONG access
, struct ExtFileLock
**lock
) {
130 struct GlobalLock
*node
, *gl
;
131 struct ExtFileLock
*fl
;
136 D(bug("[fat] locking file (%ld/%ld) (%s)\n", dir_cluster
, dir_entry
, access
== SHARED_LOCK
? "shared" : "exclusive"));
138 /* first see if we already have a global lock for this file */
140 ForeachNode(&glob
->sb
->locks
, node
)
141 if (node
->dir_cluster
== dir_cluster
&& node
->dir_entry
== dir_entry
) {
146 /* if we do and we're trying for an exclusive lock, then bail out */
147 if (gl
!= NULL
&& access
== EXCLUSIVE_LOCK
) {
148 D(bug("[fat] can't obtain exclusive lock on already-locked file\n"));
149 return ERROR_OBJECT_IN_USE
;
152 /* allocate space for the lock. we do this first so that we don't go to
153 * all the effort of setting up the global lock only to have to discard it
154 * if the filelock allocation fails */
155 if ((fl
= AllocVecPooled(glob
->mempool
, sizeof(struct ExtFileLock
))) == NULL
)
156 return ERROR_NO_FREE_STORE
;
158 /* if we don't have a global lock we need to build one */
160 if ((gl
= AllocVecPooled(glob
->mempool
, sizeof(struct GlobalLock
))) == NULL
) {
161 FreeVecPooled(glob
->mempool
, fl
);
162 return ERROR_NO_FREE_STORE
;
165 gl
->dir_cluster
= dir_cluster
;
166 gl
->dir_entry
= dir_entry
;
169 /* gotta fish some stuff out of the dir entry too */
170 InitDirHandle(glob
->sb
, dir_cluster
, &dh
);
171 GetDirEntry(&dh
, dir_entry
, &de
);
173 gl
->first_cluster
= FIRST_FILE_CLUSTER(&de
);
174 if (gl
->first_cluster
== 0) gl
->first_cluster
= 0xffffffff;
176 gl
->attr
= de
.e
.entry
.attr
;
177 gl
->size
= AROS_LE2LONG(de
.e
.entry
.file_size
);
179 GetDirEntryShortName(&de
, &(gl
->name
[1]), &len
); gl
->name
[0] = (UBYTE
) len
;
180 GetDirEntryLongName(&de
, &(gl
->name
[1]), &len
); gl
->name
[0] = (UBYTE
) len
;
182 ReleaseDirHandle(&dh
);
186 ADDTAIL(&glob
->sb
->locks
, gl
);
188 D(bug("[fat] created new global lock\n"));
190 /* look through the notify list. if there's any in there that aren't
191 * currently attached to a global lock, expand them and are for this
192 * file, fill them in */
194 struct NotifyNode
*nn
;
196 ForeachNode(&glob
->sb
->notifies
, nn
)
197 if (nn
->gl
== NULL
) {
198 D(bug("[fat] searching for notify name '%s'\n", nn
->nr
->nr_FullName
));
200 if (InitDirHandle(glob
->sb
, 0, &dh
) != 0)
203 if (GetDirEntryByPath(&dh
, nn
->nr
->nr_FullName
, strlen(nn
->nr
->nr_FullName
), &de
) != 0)
206 if (gl
->dir_cluster
== de
.cluster
&& gl
->dir_entry
== de
.index
) {
207 D(bug("[fat] found and matched to the global lock (%ld/%ld)\n", gl
->dir_cluster
, gl
->dir_entry
));
214 /* now setup the file lock */
217 fl
->fl_Access
= access
;
218 fl
->fl_Task
= glob
->ourport
;
219 fl
->fl_Volume
= MKBADDR(glob
->sb
->doslist
);
221 fl
->magic
= ID_FAT_DISK
;
223 fl
->ioh
.sb
= glob
->sb
;
224 fl
->ioh
.first_cluster
= gl
->first_cluster
;
225 fl
->ioh
.block
= NULL
;
226 RESET_HANDLE(&(fl
->ioh
));
230 fl
->do_notify
= FALSE
;
233 ADDTAIL(&gl
->locks
, &fl
->node
);
235 D(bug("[fat] created file lock 0x%08x\n", fl
));
243 LONG
LockRoot(LONG access
, struct ExtFileLock
**lock
) {
244 struct ExtFileLock
*fl
;
246 D(bug("[fat] locking root\n"));
248 if (access
== EXCLUSIVE_LOCK
) {
249 D(bug("[fat] can't obtain exclusive lock on the fs root\n"));
250 return ERROR_OBJECT_IN_USE
;
253 if ((fl
= AllocVecPooled(glob
->mempool
, sizeof(struct ExtFileLock
))) == NULL
)
254 return ERROR_NO_FREE_STORE
;
258 fl
->fl_Access
= SHARED_LOCK
;
259 fl
->fl_Task
= glob
->ourport
;
260 fl
->fl_Volume
= MKBADDR(glob
->sb
->doslist
);
262 fl
->magic
= ID_FAT_DISK
;
264 fl
->ioh
.sb
= glob
->sb
;
265 fl
->ioh
.first_cluster
= 0;
266 fl
->ioh
.block
= NULL
;
267 RESET_HANDLE(&(fl
->ioh
));
271 fl
->do_notify
= FALSE
;
273 fl
->gl
= &glob
->sb
->root_lock
;
274 ADDTAIL(&glob
->sb
->root_lock
.locks
, &fl
->node
);
276 D(bug("[fat] created root lock 0x%08x\n", fl
));
284 LONG
CopyLock(struct ExtFileLock
*fl
, struct ExtFileLock
**lock
) {
285 D(bug("[fat] copying lock\n"));
287 if (fl
== NULL
|| fl
->gl
== &glob
->sb
->root_lock
)
288 return LockRoot(SHARED_LOCK
, lock
);
290 if (fl
->fl_Access
== EXCLUSIVE_LOCK
) {
291 D(bug("[fat] can't copy exclusive lock\n"));
292 return ERROR_OBJECT_IN_USE
;
295 return LockFile(fl
->gl
->dir_cluster
, fl
->gl
->dir_entry
, SHARED_LOCK
, lock
);
298 LONG
LockParent(struct ExtFileLock
*fl
, LONG access
, struct ExtFileLock
**lock
) {
302 ULONG parent_cluster
;
304 /* the root has no parent */
305 if (fl
== NULL
|| fl
->gl
== &glob
->sb
->root_lock
)
306 return ERROR_OBJECT_NOT_FOUND
;
308 /* if we're in the root directory, then the root is our parent */
309 if (fl
->gl
->dir_cluster
== glob
->sb
->rootdir_cluster
)
310 return LockRoot(access
, lock
);
312 /* get the parent dir */
313 InitDirHandle(glob
->sb
, fl
->gl
->dir_cluster
, &dh
);
314 if ((err
= GetDirEntryByPath(&dh
, "/", 1, &de
)) != 0) {
315 ReleaseDirHandle(&dh
);
319 /* and its cluster */
320 parent_cluster
= FIRST_FILE_CLUSTER(&de
);
322 /* then we go through the parent dir, looking for a link back to us. we do
323 * this so that we have an entry with the proper name for copying by
325 InitDirHandle(glob
->sb
, parent_cluster
, &dh
);
326 while ((err
= GetDirEntry(&dh
, dh
.cur_index
+ 1, &de
)) == 0) {
327 /* don't go past the end */
328 if (de
.e
.entry
.name
[0] == 0x00) {
329 err
= ERROR_OBJECT_NOT_FOUND
;
333 /* we found it if its not empty, and its not the volume id or a long
334 * name, and it is a directory, and it does point to us */
335 if (de
.e
.entry
.name
[0] != 0xe5 &&
336 !(de
.e
.entry
.attr
& ATTR_VOLUME_ID
) &&
337 de
.e
.entry
.attr
& ATTR_DIRECTORY
&&
338 FIRST_FILE_CLUSTER(&de
) == fl
->gl
->dir_cluster
) {
340 err
= LockFile(parent_cluster
, dh
.cur_index
, access
, lock
);
345 ReleaseDirHandle(&dh
);
349 void FreeLock(struct ExtFileLock
*fl
) {
350 struct NotifyNode
*nn
;
355 D(bug("[fat] freeing lock 0x%08x\n", fl
));
358 SendNotifyByLock(fl
->gl
);
362 if (IsListEmpty(&fl
->gl
->locks
) && fl
->gl
!= &glob
->sb
->root_lock
) {
365 ForeachNode(&glob
->sb
->notifies
, nn
)
369 FreeVecPooled(glob
->mempool
, fl
->gl
);
371 D(bug("[fat] freed associated global lock\n"));
374 if (fl
->ioh
.block
!= NULL
)
375 cache_put_block(glob
->sb
->cache
, fl
->ioh
.block
, 0);
377 FreeVecPooled(glob
->mempool
, fl
);
383 LONG
FreeLockSB(struct ExtFileLock
*fl
, struct FSSuper
*sb
) {
387 return ERROR_OBJECT_NOT_FOUND
;
388 if (fl
->magic
!= ID_FAT_DISK
)
389 return ERROR_OBJECT_WRONG_TYPE
;
391 if (fl
== BADDR(sb
->doslist
->dol_misc
.dol_volume
.dol_LockList
)) {
392 sb
->doslist
->dol_misc
.dol_volume
.dol_LockList
= fl
->fl_Link
;
396 struct ExtFileLock
*prev
= NULL
, *ptr
= BADDR(sb
->doslist
->dol_misc
.dol_volume
.dol_LockList
);
398 while (ptr
!= NULL
) {
400 prev
->fl_Link
= fl
->fl_Link
;
405 ptr
= BADDR(ptr
->fl_Link
);
410 D(bug("\tFreeing lock.\n"));
414 if (fl
->ioh
.block
!= NULL
)
415 cache_put_block(sb
->cache
, fl
->ioh
.block
, 0);
417 FreeVecPooled(glob
->mempool
, fl
);
422 return ERROR_OBJECT_NOT_FOUND
;
425 void FreeLock(struct ExtFileLock
*fl
) {
426 struct FSSuper
*ptr
= glob
->sblist
, *prev
=NULL
;
428 if (FreeLockSB(fl
, glob
->sb
) == 0)
431 while (ptr
!= NULL
) {
432 if (FreeLockSB(fl
, ptr
) == 0)
440 if (ptr
->doslist
->dol_misc
.dol_volume
.dol_LockList
== NULL
) { /* check if the device can be removed */
441 D(bug("\tRemoving disk completely\n"));
443 SendVolumePacket(ptr
->doslist
, ACTION_VOLUME_REMOVE
);
449 prev
->next
= ptr
->next
;
451 glob
->sblist
= ptr
->next
;
453 FreeVecPooled(glob
->mempool
, ptr
);