add remtask before addtask
[AROS.git] / rom / filesys / fat / lock.c
blob7db266689a8e89f013c652710bdc4c8e366fdb97
1 /*
2 * fat-handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2013 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 #define AROS_ALMOST_COMPATIBLE
15 #include <aros/macros.h>
16 #include <exec/types.h>
17 #include <exec/execbase.h>
18 #include <exec/memory.h>
19 #include <dos/dosextens.h>
20 #include <dos/filehandler.h>
22 #include <proto/exec.h>
23 #include <proto/dos.h>
24 #include <proto/utility.h>
26 #include <string.h>
28 #include "fat_fs.h"
29 #include "fat_protos.h"
31 #define DEBUG DEBUG_LOCK
32 #include "debug.h"
34 #if DEBUG == 0
35 #define DumpLocks(sb)
36 #else
37 void DumpLocks(struct FSSuper *sb) {
38 struct GlobalLock *gl;
39 ULONG count;
41 bug("[fat] global locks:\n");
43 ListLength(&sb->info->root_lock.locks, count);
44 bug(" root: %ld references\n", count);
46 ForeachNode(&sb->info->locks, gl) {
47 ListLength(&gl->locks, count);
48 bug(" (%ld/%ld) ", gl->dir_cluster, gl->dir_entry); RawPutChars(&(gl->name[1]), gl->name[0]);
49 bug(": %ld references\n", count);
52 #endif
54 LONG TestLock(struct ExtFileLock *fl, struct Globals *glob) {
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, struct Globals *glob) {
72 LONG err = ERROR_OBJECT_NOT_FOUND;
73 struct DirHandle dh;
74 struct DirEntry de;
75 ULONG dir_cluster;
77 /* if the name is empty, just duplicate the base lock */
78 if (namelen == 0)
79 return CopyLock(fl, lock, glob);
81 /* if the base lock is a file, the name must either be empty (handled
82 * above) or start with '/' (handled here) */
83 if (fl != NULL && !(fl->gl->attr & ATTR_DIRECTORY)) {
84 if (name[0] == '/') {
85 if (namelen == 1)
86 return OpLockParent(fl, lock, glob);
87 else {
88 name++;
89 namelen--;
92 else
93 return ERROR_OBJECT_WRONG_TYPE;
96 /* the . and .. entries are invisible to the user */
97 if (name[0] == '.' && (namelen == 1 || (name[1] == '.' && namelen == 2))) {
98 D(bug("[fat] not allowing access to '.' or '..' entries\n"));
99 return ERROR_OBJECT_NOT_FOUND;
102 /* get the first cluster of the directory to look for the file in */
103 if (fl == NULL)
104 dir_cluster = 0;
105 else if (fl->gl->attr & ATTR_DIRECTORY)
106 dir_cluster = fl->ioh.first_cluster;
107 else
108 dir_cluster = fl->gl->dir_cluster;
110 D(bug("[fat] trying to obtain lock on '"); RawPutChars(name, namelen);
111 bug("' in dir at cluster %ld\n", dir_cluster));
113 /* open the dir */
114 InitDirHandle(glob->sb, dir_cluster, &dh, FALSE);
116 /* look for the entry */
117 if ((err = GetDirEntryByPath(&dh, name, namelen, &de)) != 0) {
118 ReleaseDirHandle(&dh);
119 D(bug("[fat] couldn't get lock\n"));
120 return err;
123 /* found it, do the locking proper */
124 if (de.e.entry.attr & ATTR_DIRECTORY && FIRST_FILE_CLUSTER(&de)
125 <= glob->sb->rootdir_cluster)
126 err = LockRoot(access, lock, glob);
127 else
128 err = LockFile(dh.ioh.first_cluster, de.index, access, lock, glob);
130 ReleaseDirHandle(&dh);
132 return err;
135 LONG LockFile(ULONG dir_cluster, ULONG dir_entry, LONG access, struct ExtFileLock **lock, struct Globals *glob) {
136 struct GlobalLock *node, *gl;
137 struct ExtFileLock *fl;
138 struct DirHandle dh;
139 struct DirEntry de;
140 ULONG len;
142 D(bug("[fat] locking file (%ld/%ld) (%s)\n", dir_cluster, dir_entry, access == SHARED_LOCK ? "shared" : "exclusive"));
144 /* first see if we already have a global lock for this file */
145 gl = NULL;
146 ForeachNode(&glob->sb->info->locks, node)
147 if (node->dir_cluster == dir_cluster && node->dir_entry == dir_entry) {
148 gl = node;
149 break;
152 /* if we do and we're trying for an exclusive lock, then bail out */
153 if (gl != NULL && access == EXCLUSIVE_LOCK) {
154 D(bug("[fat] can't obtain exclusive lock on already-locked file\n"));
155 return ERROR_OBJECT_IN_USE;
158 /* allocate space for the lock. we do this first so that we don't go to
159 * all the effort of setting up the global lock only to have to discard it
160 * if the filelock allocation fails */
161 if ((fl = AllocVecPooled(glob->sb->info->mem_pool,
162 sizeof(struct ExtFileLock))) == NULL)
163 return ERROR_NO_FREE_STORE;
165 /* if we don't have a global lock we need to build one */
166 if (gl == NULL) {
167 if ((gl = AllocVecPooled(glob->sb->info->mem_pool,
168 sizeof(struct GlobalLock))) == NULL) {
169 FreeVecPooled(glob->sb->info->mem_pool, fl);
170 return ERROR_NO_FREE_STORE;
173 gl->dir_cluster = dir_cluster;
174 gl->dir_entry = dir_entry;
175 gl->access = access;
177 /* gotta fish some stuff out of the dir entry too */
178 InitDirHandle(glob->sb, dir_cluster, &dh, FALSE);
179 GetDirEntry(&dh, dir_entry, &de);
181 gl->first_cluster = FIRST_FILE_CLUSTER(&de);
182 if (gl->first_cluster == 0) gl->first_cluster = 0xffffffff;
184 gl->attr = de.e.entry.attr;
185 gl->size = AROS_LE2LONG(de.e.entry.file_size);
187 GetDirEntryShortName(&de, &(gl->name[1]), &len); gl->name[0] = (UBYTE) len;
188 GetDirEntryLongName(&de, &(gl->name[1]), &len); gl->name[0] = (UBYTE) len;
189 #if DEBUG_NAMES
190 GetDirEntryShortName(&de, &(gl->shortname[1]), &len); gl->shortname[0] = (UBYTE) len;
191 #endif
193 ReleaseDirHandle(&dh);
195 NEWLIST(&gl->locks);
197 ADDTAIL(&glob->sb->info->locks, gl);
199 D(bug("[fat] created new global lock\n"));
201 /* look through the notify list. if there's any in there that aren't
202 * currently attached to a global lock, expand them and if they are
203 * for this file, fill them in */
205 struct NotifyNode *nn;
207 ForeachNode(&glob->sb->info->notifies, nn)
208 if (nn->gl == NULL) {
209 D(bug("[fat] searching for notify name '%s'\n", nn->nr->nr_FullName));
211 if (InitDirHandle(glob->sb, 0, &dh, TRUE) != 0)
212 continue;
214 if (GetDirEntryByPath(&dh, nn->nr->nr_FullName, strlen(nn->nr->nr_FullName), &de) != 0)
215 continue;
217 if (gl->dir_cluster == de.cluster && gl->dir_entry == de.index) {
218 D(bug("[fat] found and matched to the global lock (%ld/%ld)\n", gl->dir_cluster, gl->dir_entry));
219 nn->gl = gl;
225 /* now setup the file lock */
226 fl->fl_Link = BNULL;
227 fl->fl_Key = 0;
228 fl->fl_Access = access;
229 fl->fl_Task = glob->ourport;
230 fl->fl_Volume = MKBADDR(glob->sb->doslist);
232 fl->magic = ID_FAT_DISK;
234 fl->ioh.sb = glob->sb;
235 fl->ioh.first_cluster = gl->first_cluster;
236 fl->ioh.block = NULL;
237 RESET_HANDLE(&(fl->ioh));
239 fl->pos = 0;
241 fl->do_notify = FALSE;
243 fl->gl = gl;
244 fl->sb = glob->sb;
245 ADDTAIL(&gl->locks, &fl->node);
247 D(bug("[fat] created file lock 0x%08x\n", fl));
249 DumpLocks(glob->sb);
251 *lock = fl;
252 return 0;
255 LONG LockRoot(LONG access, struct ExtFileLock **lock, struct Globals *glob) {
256 struct ExtFileLock *fl;
258 D(bug("[fat] locking root\n"));
260 if (access == EXCLUSIVE_LOCK) {
261 D(bug("[fat] can't obtain exclusive lock on the fs root\n"));
262 return ERROR_OBJECT_IN_USE;
265 if ((fl = AllocVecPooled(glob->sb->info->mem_pool,
266 sizeof(struct ExtFileLock))) == NULL)
267 return ERROR_NO_FREE_STORE;
269 fl->fl_Link = BNULL;
270 fl->fl_Key = 0;
271 fl->fl_Access = SHARED_LOCK;
272 fl->fl_Task = glob->ourport;
273 fl->fl_Volume = MKBADDR(glob->sb->doslist);
275 fl->magic = ID_FAT_DISK;
277 fl->ioh.sb = glob->sb;
278 fl->ioh.first_cluster = 0;
279 fl->ioh.block = NULL;
280 RESET_HANDLE(&(fl->ioh));
282 fl->pos = 0;
284 fl->do_notify = FALSE;
286 if (IsListEmpty(&glob->sb->info->root_lock.locks))
287 ADDTAIL(&glob->sb->info->locks, &glob->sb->info->root_lock);
288 fl->gl = &glob->sb->info->root_lock;
289 fl->sb = glob->sb;
290 ADDTAIL(&glob->sb->info->root_lock.locks, &fl->node);
292 D(bug("[fat] created root lock 0x%08x\n", fl));
294 DumpLocks(glob->sb);
296 *lock = fl;
297 return 0;
300 LONG CopyLock(struct ExtFileLock *fl, struct ExtFileLock **lock, struct Globals *glob) {
301 D(bug("[fat] copying lock\n"));
303 if (fl == NULL || fl->gl == &glob->sb->info->root_lock)
304 return LockRoot(SHARED_LOCK, lock, glob);
306 if (fl->fl_Access == EXCLUSIVE_LOCK) {
307 D(bug("[fat] can't copy exclusive lock\n"));
308 return ERROR_OBJECT_IN_USE;
311 return LockFile(fl->gl->dir_cluster, fl->gl->dir_entry, SHARED_LOCK, lock, glob);
314 void FreeLock(struct ExtFileLock *fl, struct Globals *glob) {
315 struct NotifyNode *nn;
317 if (fl == NULL)
318 return;
320 D(bug("[fat] freeing lock 0x%08x\n", fl));
322 if (fl->do_notify)
323 SendNotifyByLock(fl->ioh.sb, fl->gl);
325 REMOVE(&fl->node);
327 if (IsListEmpty((struct List *)&fl->gl->locks)) {
328 REMOVE(fl->gl);
330 ForeachNode(&fl->sb->info->notifies, nn)
331 if(nn->gl == fl->gl)
332 nn->gl = NULL;
334 if (fl->gl != &fl->sb->info->root_lock)
335 FreeVecPooled(glob->sb->info->mem_pool, fl->gl);
337 D(bug("[fat] freed associated global lock\n"));
340 DumpLocks(fl->sb);
341 if (fl->ioh.block != NULL)
342 Cache_FreeBlock(fl->sb->cache, fl->ioh.block);
344 if (fl->sb != glob->sb)
345 AttemptDestroyVolume(fl->sb);
347 FreeVecPooled(glob->sb->info->mem_pool, fl);