r26407@plastic: rob | 2007-05-15 19:13:02 +1000
[cake.git] / workbench / fs / fat / lock.c
blob28e235f509bdfe39f9f9ada1dc12fbefddabc123
1 /*
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.
10 * $Id$
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>
23 #include <string.h>
25 #include "fat_fs.h"
26 #include "fat_protos.h"
28 #if defined(DEBUG_FULL) && DEBUG_FULL != 0
29 #define DEBUG 1
30 #else
31 #define DEBUG 0
32 #endif
33 #include <aros/debug.h>
35 #if DEBUG == 0
36 #define DumpLocks(sb)
37 #else
38 void DumpLocks(struct FSSuper *sb) {
39 struct GlobalLock *gl;
40 ULONG count;
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);
52 #endif
54 LONG TestLock(struct ExtFileLock *fl) {
55 if (fl == 0 && glob->sb == NULL) {
56 if (glob->disk_inserted == FALSE)
57 return ERROR_NO_DISK;
58 else
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;
68 return 0;
71 LONG LockFileByName(struct ExtFileLock *fl, UBYTE *name, LONG namelen, LONG access, struct ExtFileLock **lock) {
72 LONG err = ERROR_OBJECT_NOT_FOUND;
73 struct DirHandle dh;
74 struct DirEntry de;
75 ULONG dir_cluster;
76 int i;
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));
94 /* open the dir */
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"));
103 namelen -= (i+1);
104 name = &name[i+1];
106 InitDirHandle(dh.ioh.sb, 0, &dh);
108 break;
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"));
115 return err;
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);
121 else
122 err = LockFile(dh.ioh.first_cluster, de.index, access, lock);
124 ReleaseDirHandle(&dh);
126 return err;
129 LONG LockFile(ULONG dir_cluster, ULONG dir_entry, LONG access, struct ExtFileLock **lock) {
130 struct GlobalLock *node, *gl;
131 struct ExtFileLock *fl;
132 struct DirHandle dh;
133 struct DirEntry de;
134 ULONG len;
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 */
139 gl = NULL;
140 ForeachNode(&glob->sb->locks, node)
141 if (node->dir_cluster == dir_cluster && node->dir_entry == dir_entry) {
142 gl = node;
143 break;
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 */
159 if (gl == NULL) {
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;
167 gl->access = access;
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);
184 NEWLIST(&gl->locks);
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)
201 continue;
203 if (GetDirEntryByPath(&dh, nn->nr->nr_FullName, strlen(nn->nr->nr_FullName), &de) != 0)
204 continue;
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));
208 nn->gl = gl;
214 /* now setup the file lock */
215 fl->fl_Link = NULL;
216 fl->fl_Key = 0;
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));
228 fl->pos = 0;
230 fl->do_notify = FALSE;
232 fl->gl = gl;
233 ADDTAIL(&gl->locks, &fl->node);
235 D(bug("[fat] created file lock 0x%08x\n", fl));
237 DumpLocks(glob->sb);
239 *lock = fl;
240 return 0;
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;
256 fl->fl_Link = NULL;
257 fl->fl_Key = 0;
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));
269 fl->pos = 0;
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));
278 DumpLocks(glob->sb);
280 *lock = fl;
281 return 0;
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) {
299 LONG err;
300 struct DirHandle dh;
301 struct DirEntry de;
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);
316 return err;
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
324 * LockFile() */
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;
330 break;
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);
341 break;
345 ReleaseDirHandle(&dh);
346 return err;
349 void FreeLock(struct ExtFileLock *fl) {
350 struct NotifyNode *nn;
352 if (fl == NULL)
353 return;
355 D(bug("[fat] freeing lock 0x%08x\n", fl));
357 if (fl->do_notify)
358 SendNotifyByLock(fl->gl);
360 REMOVE(&fl->node);
362 if (IsListEmpty(&fl->gl->locks) && fl->gl != &glob->sb->root_lock) {
363 REMOVE(fl->gl);
365 ForeachNode(&glob->sb->notifies, nn)
366 if(nn->gl == fl->gl)
367 nn->gl = NULL;
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);
379 DumpLocks(glob->sb);
382 #if 0
383 LONG FreeLockSB(struct ExtFileLock *fl, struct FSSuper *sb) {
384 LONG found = FALSE;
386 if (sb == NULL)
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;
393 found = TRUE;
395 else {
396 struct ExtFileLock *prev = NULL, *ptr = BADDR(sb->doslist->dol_misc.dol_volume.dol_LockList);
398 while (ptr != NULL) {
399 if (ptr == fl) {
400 prev->fl_Link = fl->fl_Link;
401 found = TRUE;
402 break;
404 prev = ptr;
405 ptr = BADDR(ptr->fl_Link);
409 if (found) {
410 D(bug("\tFreeing lock.\n"));
412 fl->fl_Task = NULL;
414 if (fl->ioh.block != NULL)
415 cache_put_block(sb->cache, fl->ioh.block, 0);
417 FreeVecPooled(glob->mempool, fl);
419 return 0;
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)
429 return;
431 while (ptr != NULL) {
432 if (FreeLockSB(fl, ptr) == 0)
433 break;
435 prev = ptr;
436 ptr = ptr->next;
439 if (ptr) {
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);
445 ptr->doslist = NULL;
446 FreeFATSuper(ptr);
448 if (prev)
449 prev->next = ptr->next;
450 else
451 glob->sblist = ptr->next;
453 FreeVecPooled(glob->mempool, ptr);
457 #endif