Added a test for MUIA_Listview_SelectChange.
[AROS.git] / rom / filesys / pfs3 / fs / lock.c
blob992862e182cf4b6055a732adfc58199499a39c3d
1 /* $Id$ */
2 /* $Log: lock.c $
3 * Revision 11.4 1999/02/22 16:33:43 Michiel
4 * Changes for increasing deldir capacity
6 * Revision 11.3 1995/12/21 12:05:55 Michiel
7 * bugfix: anodechain wasn't freed after closing file
9 * Revision 11.2 1995/12/07 15:25:38 Michiel
10 * rollover support and bugfixes
12 * Revision 11.1 1995/10/03 11:19:27 Michiel
13 * merged develop: anodechain
15 * Revision 10.15 1995/08/04 11:51:19 Michiel
16 * Some memory-allocation fail conditions tests added
18 * Revision 10.14 1995/07/21 06:54:46 Michiel
19 * MakeListEntry() adapted for DELDIR
21 * Revision 10.13 1995/07/18 06:56:13 Michiel
22 * changed multiuser access functions
24 * Revision 10.12 1995/06/15 18:56:53 Michiel
25 * pooled mem
27 * Revision 10.11 1995/06/07 19:17:19 Michiel
28 * added multiuser routines
30 * Revision 10.10 1995/06/04 06:04:31 Michiel
31 * minor
33 * Revision 10.9 1995/03/24 16:31:07 Michiel
34 * MakeListEntry adapted for soflinks & bugfixed
36 * Revision 10.8 1995/02/28 18:26:58 Michiel
37 * MakeListEntry resolves links now
39 * Revision 10.7 1995/02/15 16:43:39 Michiel
40 * Release version
41 * Using new headers (struct.h & blocks.h)
43 * Revision 10.6 1995/01/18 04:29:34 Michiel
44 * Bugfixes. Now ready for beta release.
46 * Revision 10.5 1994/11/15 17:52:30 Michiel
47 * GURU book update
49 * Revision 10.4 1994/10/29 08:55:03 Michiel
50 * changed process references to msgport references
52 * Revision 10.3 1994/10/28 06:06:40 Michiel
53 * use new listentry field 'anodenr'
54 * this was essential for AccessConflict()
56 * Revision 10.1 1994/10/24 11:16:28 Michiel
57 * first RCS revision
58 * */
60 //#define DEBUG 1
61 #define __USE_SYSBASE
63 #include "debug.h"
65 // own includes
66 #include "blocks.h"
67 #include "struct.h"
68 #include "lock_protos.h"
69 #include "directory_protos.h"
70 #include "volume_protos.h"
71 #include "anodes_protos.h"
72 #include "disk_protos.h"
74 /**********************************************************************/
75 /* DEBUG */
76 /**********************************************************************/
78 #ifdef DEBUG
79 extern BOOL debug;
80 static UBYTE debugbuf[120];
81 #define DebugOn debug++
82 #define DebugOff debug=(debug?debug-1:0)
83 #define DebugMsg(msg) if(debug) {NormalErrorMsg(msg, NULL); debug=0;}
84 #define DebugMsgNum(msg, num) sprintf(debugbuf, "%s 0x%08lx.", msg, num); \
85 if(debug) {NormalErrorMsg(debugbuf, NULL); debug=0;}
86 #define DebugMsgName(msg, name) sprintf(debugbuf, "%s >%s<.", msg, name); \
87 if(debug) {NormalErrorMsg(debugbuf, NULL); debug=0;}
88 #else
89 #define DebugOn
90 #define DebugOff
91 #define DebugMsg(m)
92 #define DebugMsgNum(msg,num)
93 #define DebugMsgName(msg, name)
94 #endif
96 /**********************************************************************/
97 /* MAKE/REMOVE/FREE */
98 /* MAKE/REMOVE/FREE */
99 /* MAKE/REMOVE/FREE */
100 /**********************************************************************/
102 /* MakeListEntry
104 ** Allocated filentry structure and fill it with data from objectinfo and
105 ** listtype. The result should be freed with FreeListEntry.
107 ** input : - info: objectinfo of object
108 ** - type: desired type (readlock, writelock, readfe, writefe)
110 ** result: the fileentry, or NULL if failure
112 struct listentry *MakeListEntry (union objectinfo *info, listtype type, SIPTR *error, globaldata *g)
114 listentry_t *listentry;
115 union objectinfo newinfo;
116 ULONG size;
117 struct extrafields extrafields;
118 #if DELDIR
119 struct deldirentry *dde = 0;
120 #endif
122 ENTER("MakeListEntry");
124 // alloceren fileentry
125 switch (type.flags.type)
127 case ETF_FILEENTRY: size = sizeof(fileentry_t); break;
128 case ETF_VOLUME:
129 case ETF_LOCK: size = sizeof(lockentry_t); break;
130 default: listentry = NULL; return listentry;
133 DB(Trace(1,"MakeListEntry","size = %lx\n",size));
135 #if DELDIR
136 if (IsDelDir(*info) || IsVolume(*info) || IsDir(*info))
137 #else
138 if (IsVolume(*info) || IsDir(*info))
139 #endif
140 type.flags.dir = 1;
142 /* softlinks cannot directly be opened */
143 #if DELDIR
144 if (info->deldir.special > SPECIAL_DELFILE && info->file.direntry->type == ST_SOFTLINK)
145 #else
146 if (!IsVolume(*info) && info->file.direntry->type == ST_SOFTLINK)
147 #endif
149 *error = ERROR_IS_SOFT_LINK;
150 return NULL;
153 if (!(listentry = AllocMemP (size, g)))
155 *error = ERROR_NO_FREE_STORE;
156 return NULL;
159 /* go after link and fetch the fileinfo of the real object
160 * (stored in 'newinfo'
162 #if DELDIR
163 if (info->deldir.special > SPECIAL_DELFILE && (
164 #else
165 if (!IsVolume(*info) && (
166 #endif
167 (info->file.direntry->type == ST_LINKFILE) ||
168 (info->file.direntry->type == ST_LINKDIR)))
170 struct canode linknode;
172 /* The clustersize of the linknode (direntry.anode)
173 * actually is the anodenr of the directory the linked to
174 * object is in. The object can be found by searching for
175 * 'anode == objectid'. This objectid can be found in
176 * the extrafields
178 GetExtraFields(info->file.direntry, &extrafields);
179 GetAnode(&linknode, info->file.direntry->anode, g);
180 if (!FetchObject(linknode.clustersize, extrafields.link, &newinfo, g))
182 *error = ERROR_OBJECT_NOT_FOUND;
183 return NULL;
186 else
188 newinfo = *info;
191 // general
192 listentry->type = type;
193 #if DELDIR
194 switch (newinfo.delfile.special)
196 case 0:
197 listentry->anodenr = ANODE_ROOTDIR; break;
199 case SPECIAL_DELDIR:
200 listentry->anodenr = 0; break;
202 case SPECIAL_DELFILE:
203 dde = GetDeldirEntryQuick(newinfo.delfile.slotnr, g);
204 listentry->anodenr = dde->anodenr;
205 break;
207 default:
208 listentry->anodenr = newinfo.file.direntry->anode; break;
210 #else
211 listentry->anodenr = (newinfo.file.direntry) ? (newinfo.file.direntry->anode) : ANODE_ROOTDIR;
212 #endif
214 listentry->info = newinfo;
215 listentry->lock.fl_Access = (type.flags.access & 2) ? EXCLUSIVE_LOCK : SHARED_LOCK;
216 listentry->lock.fl_Task = g->msgport;
217 listentry->lock.fl_Volume = MKBADDR(g->currentvolume->devlist);
218 listentry->volume = g->currentvolume;
220 // type specific
221 switch (type.flags.type)
223 case ETF_VOLUME:
224 listentry->lock.fl_Key = 0;
225 // listentry->lock.fl_Volume = MKBADDR(newinfo.volume.volume->devlist);
226 // listentry->volume = newinfo.volume.volume;
227 break;
229 case ETF_LOCK:
230 /* every dirlock MUST have a different fl_Key (DOPUS!) */
231 listentry->lock.fl_Key = listentry->anodenr;
232 // listentry->lock.fl_Volume = MKBADDR(newinfo.file.dirblock->volume->devlist);
233 // listentry->volume = newinfo.file.dirblock->volume;
234 break;
236 case ETF_FILEENTRY:
237 #define fe ((fileentry_t *)listentry)
238 listentry->lock.fl_Key = listentry->anodenr;
239 // listentry->lock.fl_Volume = MKBADDR(MKBADDR(newinfo.file.dirblock->volume->devlist);
240 // listentry->volume = newinfo.file.dirblock->volume;
241 fe->originalsize = IsDelFile(newinfo) ? GetDDFileSize(dde, g) :
242 GetDEFileSize(newinfo.file.direntry, g);
244 /* Get anodechain. If it fails anodechain will become NULL. This has to be
245 * taken into account by functions that use the chain
247 fe->anodechain = GetAnodeChain (listentry->anodenr, g);
248 fe->currnode = &fe->anodechain->head;
250 #if ROLLOVER
251 /* Rollover file: set offset to rollfileoffset */
252 /* check for rollover files */
253 if (IsRollover(newinfo))
255 GetExtraFields(newinfo.file.direntry, &extrafields);
256 SeekInFile(fe,extrafields.rollpointer,OFFSET_BEGINNING,error,g);
258 #endif /* ROLLOVER */
259 #undef fe
260 break;
262 default:
263 listentry = NULL;
264 return listentry;
267 return listentry;
271 /* AddListEntry
273 ** Checks if the listentry causes access conflicts
274 ** Adds the entry to the locklist
276 BOOL _AddListEntry (listentry_t *entry, globaldata *g)
278 struct volumedata *volume;
280 DB(Trace(1,"AddListEntry","fe = %lx\n", entry->volume->fileentries.mlh_Head));
282 if (entry==NULL)
283 return DOSFALSE;
285 if (AccessConflict(entry))
287 DB(Trace(1,"AddListEntry","found accessconflict!"));
288 return DOSFALSE;
291 volume = entry->volume;
293 /* add to head of list; als link locks using BPTRs */
294 if (!IsMinListEmpty(&volume->fileentries))
295 entry->lock.fl_Link = MKBADDR(&(((listentry_t *)HeadOf(&volume->fileentries))->lock));
296 else
297 entry->lock.fl_Link = BNULL;
299 MinAddHead (&volume->fileentries, entry);
301 return DOSTRUE;
304 /* RemoveListEntry
306 ** removes 'entry' from the list and frees entry with FreeFileEntry
308 ** also think about empty lists: kill volume if empty and not present
309 ** also makes lock-links
311 void RemoveListEntry (listentry_t *entry, globaldata *g)
313 struct volumedata *volume;
314 struct MinList *previous;
316 /* get volume */
317 volume = entry->volume;
319 /* remove from list */
320 MinRemove (entry);
322 /* update FileLock link */
323 previous = (struct MinList *)entry->prev;
324 if (!IsHead(entry))
326 if (!IsTail(entry))
327 ((listentry_t *)previous)->lock.fl_Link =
328 MKBADDR(&(((listentry_t *)entry)->next->lock));
329 else
330 ((listentry_t *)previous)->lock.fl_Link = BNULL;
332 entry->lock.fl_Task = NULL;
333 FreeListEntry (entry, g);
335 if (g->currentvolume != volume)
337 /* check if last lock; yes:kill (only if not current disk)
339 if (IsMinListEmpty (&volume->fileentries))
341 DB(Trace(1,"RemoveListEntry", "killing volumedata\n"));
343 // LockDosList (LDF_VOLUMES|LDF_READ);
344 Forbid ();
345 RemDosEntry ((struct DosList *)volume->devlist);
346 FreeDosEntry ((struct DosList *)volume->devlist);
347 FreeVolumeResources (volume, g);
348 // UnLockDosList (LDF_VOLUMES|LDF_READ);
349 Permit ();
351 /* update doslist->dl_LockList if necessary
353 else if (previous == (struct MinList *)&volume->fileentries)
355 // LockDosList (LDF_VOLUMES|LDF_READ);
356 Forbid ();
357 volume->devlist->dl_LockList = MKBADDR(&(((fileentry_t *)HeadOf(&volume->fileentries))->le.lock));
358 // UnLockDosList (LDF_VOLUMES|LDF_READ);
359 Permit ();
364 void FreeListEntry(listentry_t *entry, globaldata *g)
366 #define fe ((fileentry_t *)entry)
367 if (IsFileEntry(entry) && fe->anodechain)
368 DetachAnodeChain(fe->anodechain, g);
369 FreeMemP(entry, g);
370 #undef fe
373 /**********************************************************************/
374 /* CHANGE */
375 /* CHANGE */
376 /* CHANGE */
377 /**********************************************************************/
379 BOOL _ChangeAccessMode(listentry_t *file, LONG mode, SIPTR *error, globaldata *g)
381 UWORD oldmode, newmode;
383 // -I- make dosmode compatible with listtype-mode
384 switch(mode)
386 case MODE_READWRITE:
387 case MODE_NEWFILE:
388 case EXCLUSIVE_LOCK:
390 newmode = ET_EXCLWRITE; break;
392 default:
394 newmode = ET_SHAREDREAD; break;
397 // -II- check if mode is already correct
398 oldmode = file->type.flags.access;
399 if (oldmode == newmode)
400 return TRUE;
402 // -III- operation always ok if oldmode exclusive or newmode shared
403 if (oldmode>1 || newmode<=1)
405 file->type.flags.access = newmode;
406 file->lock.fl_Access = (newmode > 1) ? EXCLUSIVE_LOCK : SHARED_LOCK;
407 return TRUE;
410 // -IV- otherwise: check for accessconflict
411 MinRemove(file); // prevents access conflict on itself
412 file->type.flags.access = newmode;
413 file->lock.fl_Access = (newmode > 1) ? EXCLUSIVE_LOCK : SHARED_LOCK;
414 if (!AccessConflict(file))
416 MinAddHead(&file->volume->fileentries, file);
417 return DOSTRUE;
419 else
421 file->type.flags.access = oldmode;
422 file->lock.fl_Access = (oldmode > 1) ? EXCLUSIVE_LOCK : SHARED_LOCK;
423 MinAddHead(&file->volume->fileentries, file);
424 *error = ERROR_OBJECT_IN_USE;
425 return DOSFALSE;
429 /**********************************************************************/
430 /* CHECK */
431 /* CHECK */
432 /* CHECK */
433 /**********************************************************************/
435 /* AccessConflict
437 ** input : - [entry]: the object to be granted access
438 ** This object should contain valid references
440 ** result: TRUE = accessconflict; FALSE = no accessconflict
441 ** All locks on same ANODE are checked. So a lock on a link can
442 ** be denied if the linked to file is locked exclusively.
444 ** Because UpdateReference always updates all references to a dirblock,
445 ** and the match object is valid, a flushed reference CANNOT point to
446 ** the same dirblock. If it is a link, it CAN reference the same
447 ** object
449 ** Returns FALSE if there is an exclusive lock or if there is
450 ** write access on a shared lock
453 BOOL AccessConflict (listentry_t *entry)
455 ULONG anodenr;
456 listentry_t *fe;
457 struct volumedata *volume;
459 DB(Trace(1,"Accessconflict","entry %lx\n",entry));
461 // -I- get anodenr
462 anodenr = entry->anodenr;
463 volume = entry->volume;
465 // -II- zoek locks naar zelfde object
466 for(fe = HeadOf(&volume->fileentries); fe->next; fe=fe->next)
468 if(fe->type.flags.type == ETF_VOLUME)
470 if(entry->type.flags.type == ETF_VOLUME &&
471 (!SHAREDLOCK(fe->type) || !SHAREDLOCK(entry->type)))
473 DB(Trace(1,"Accessconflict","on volume\n"));
474 return(TRUE);
477 else if(fe->anodenr == anodenr)
479 // on of the two wants or has an exclusive lock?
480 if(!SHAREDLOCK(fe->type) || !SHAREDLOCK(entry->type))
482 DB(Trace(1,"Accessconflict","exclusive lock\n"));
483 return(TRUE);
486 // new & old shared lock, both write?
487 else if(fe->type.flags.access == ET_SHAREDWRITE &&
488 entry->type.flags.access == ET_SHAREDWRITE)
490 DB(Trace(1,"Accessconflict","two write locks\n"));
491 return(TRUE);
496 return FALSE; // no conflicting locks
499 /* ScanLockList
501 * checks <list> for a lock on the file with anode anodenr
502 * ONLY FOR LOCKS TO CURRENTVOLUME
504 BOOL ScanLockList (listentry_t *list, ULONG anodenr)
506 for (;list->next; list=list->next)
508 if (list->anodenr == anodenr)
509 return TRUE;
511 return FALSE;
515 #if MULTIUSER
517 /*****************************************************************************/
518 /* muFS routines */
519 /*****************************************************************************/
522 * Check if access allowed
523 * protection is the LW protection of the object
524 * flags are flags of GetRelationshipA
525 * returns error
528 ULONG muFS_CheckReadAccess (ULONG protection, ULONG flags, globaldata *g)
530 if (flags & (muRelF_UID_MATCH | muRelF_NO_OWNER))
532 if (protection & FIBF_READ)
533 return ERROR_READ_PROTECTED;
535 else if (flags & (muRelF_GID_MATCH))
537 if (!(protection & FIBF_GRP_READ))
538 return ERROR_READ_PROTECTED;
540 else if (!(protection & FIBF_OTR_READ))
541 return ERROR_READ_PROTECTED;
543 return NULL;
546 ULONG muFS_CheckWriteAccess (ULONG protection, ULONG flags, globaldata *g)
548 if (flags & (muRelF_UID_MATCH | muRelF_NO_OWNER))
550 if (protection & FIBF_WRITE)
551 return ERROR_WRITE_PROTECTED;
553 else if (flags & (muRelF_GID_MATCH))
555 if (!(protection & FIBF_GRP_WRITE))
556 return ERROR_WRITE_PROTECTED;
558 else if (!(protection & FIBF_OTR_WRITE))
559 return ERROR_WRITE_PROTECTED;
561 return NULL;
564 ULONG muFS_CheckDeleteAccess (ULONG protection, ULONG flags, globaldata *g)
566 if (flags & (muRelF_UID_MATCH | muRelF_NO_OWNER))
568 if (protection & FIBF_DELETE)
569 return ERROR_DELETE_PROTECTED;
571 else if (flags & (muRelF_GID_MATCH))
573 if (!(protection & FIBF_GRP_DELETE))
574 return ERROR_DELETE_PROTECTED;
576 else if (!(protection & FIBF_OTR_DELETE))
577 return ERROR_DELETE_PROTECTED;
579 return NULL;
582 #endif