alsa.audio: move handling of XRUN when writting to the slave task
[AROS.git] / rom / filesys / SFS / FS / objects.c
blobe2554d68904968834f01c14a9f0f0cd3d14de421
1 #include "asmsupport.h"
3 #include <exec/types.h>
4 #include <proto/dos.h>
5 #include <proto/exec.h>
6 #include <proto/utility.h>
8 #include <utility/tagitem.h>
10 #include "objects.h"
11 #include "objects_protos.h"
13 #include "adminspaces_protos.h"
14 #include "cachebuffers_protos.h"
15 #include "debug.h"
16 #include "locks_protos.h"
17 #include "nodes_protos.h"
18 #include "support_protos.h"
19 #include "transactions_protos.h"
20 #include "req_protos.h"
21 #include "globals.h"
23 #include <string.h>
25 extern UBYTE *fullpath(struct CacheBuffer *cbstart,struct fsObject *o);
26 extern LONG readcachebuffercheck(struct CacheBuffer **,ULONG,ULONG);
28 extern BOOL freeupspace(void);
30 extern LONG deleteextents(ULONG key);
32 extern void checknotifyforobject(struct CacheBuffer *cb,struct fsObject *o,UBYTE notifyparent);
33 extern void checknotifyforpath(UBYTE *path,UBYTE notifyparent);
35 extern LONG deletefileslowly(struct CacheBuffer *cbobject, struct fsObject *o);
37 static LONG dehashobjectquick(NODE objectnode, UBYTE *name, NODE parentobjectnode);
38 LONG simpleremoveobject(struct CacheBuffer *cb,struct fsObject *o);
39 LONG locatelockableparent(struct ExtFileLock *lock,UBYTE *path,struct CacheBuffer **returned_cb,struct fsObject **returned_o);
40 #ifdef DEBUGCODE
41 static void copyobject(struct fsObject *o2,struct fsObject *o);
42 #endif
43 LONG findobjectspace(struct CacheBuffer **io_cb,struct fsObject **io_o,ULONG bytesneeded);
44 static UBYTE *emptyspaceinobjectcontainer(struct fsObjectContainer *oc);
45 WORD objectsize(struct fsObject *o);
46 UBYTE *getcomment(struct fsObject *o);
47 static WORD changeobjectsize(struct CacheBuffer *cb, struct fsObject *o, WORD bytes);
48 static LONG deleteobjectnode(NODE objectnode);
52 LONG hashobject(BLCK hashblock, struct fsObjectNode *on, NODE nodeno, UBYTE *objectname) {
53 struct CacheBuffer *cbhash;
54 LONG errorcode=0;
56 /* This function takes a HashBlock pointer, an ObjectNode and an ObjectName.
57 If there is a hashblock, then this function will correctly link the object
58 into the hashchain. If there isn't a hashblock (=0) then this function
59 does nothing.
61 A transaction must be in progress before calling this function, and the
62 ObjectNode's CacheBuffer must be prepared. */
64 if(hashblock!=0 && (errorcode=readcachebuffercheck(&cbhash, hashblock, HASHTABLE_ID))==0) {
65 struct fsHashTable *ht=cbhash->data;
66 NODE nexthash;
67 UWORD hashvalue, hashchain;
69 hashvalue=hash(objectname, globals->is_casesensitive);
70 hashchain=HASHCHAIN(hashvalue);
71 nexthash=BE2L(ht->be_hashentry[hashchain]);
73 preparecachebuffer(cbhash);
75 checksum_writelong_be(cbhash->data, &ht->be_hashentry[hashchain], nodeno);
77 // ht->hashentry[hashchain]=nodeno;
79 if((errorcode=storecachebuffer_nochecksum(cbhash))==0) {
80 on->be_next=L2BE(nexthash);
81 on->be_hash16=W2BE(hashvalue);
85 return(errorcode);
90 LONG setrecycledinfo(ULONG deletedfiles, ULONG deletedblocks) {
91 struct CacheBuffer *cb;
92 LONG errorcode;
94 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
95 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
97 preparecachebuffer(cb);
99 checksum_writelong_be(cb->data, &ri->be_deletedfiles, deletedfiles);
100 checksum_writelong_be(cb->data, &ri->be_deletedblocks, deletedblocks);
102 // ri->deletedfiles=deletedfiles;
103 // ri->deletedblocks=deletedblocks;
105 errorcode=storecachebuffer_nochecksum(cb);
108 return(errorcode);
113 LONG setrecycledinfodiff(LONG deletedfiles, LONG deletedblocks) {
114 struct CacheBuffer *cb;
115 LONG errorcode;
117 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
118 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
120 preparecachebuffer(cb);
122 checksum_writelong_be(cb->data, &ri->be_deletedfiles, BE2L(ri->be_deletedfiles) + deletedfiles);
123 checksum_writelong_be(cb->data, &ri->be_deletedblocks, BE2L(ri->be_deletedblocks) + deletedblocks);
125 // ri->deletedfiles+=deletedfiles;
126 // ri->deletedblocks+=deletedblocks;
128 errorcode=storecachebuffer_nochecksum(cb);
131 return(errorcode);
136 LONG getrecycledinfo(ULONG *returned_deletedfiles, ULONG *returned_deletedblocks) {
137 struct CacheBuffer *cb;
138 LONG errorcode;
140 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
141 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
143 *returned_deletedfiles=BE2L(ri->be_deletedfiles);
144 *returned_deletedblocks=BE2L(ri->be_deletedblocks);
147 return(errorcode);
152 static LONG safedeleteobjectquick(struct CacheBuffer *cb, struct fsObject *o, WORD sendnotify) {
153 struct CacheBuffer *cbdd;
154 struct fsObject *odd;
155 UBYTE *s=globals->string2;
156 UBYTE *n=o->name;
157 WORD len=0;
158 LONG errorcode;
160 while((*s++=*n++)!=0) {
161 len++;
164 lockcachebuffer(cb);
165 len-=(globals->max_name_length-4);
167 if(len>0) {
168 s-=len;
170 s--;
172 if((errorcode=readobject(RECYCLEDNODE, &cbdd, &odd))==0) {
173 struct CacheBuffer *cb2;
174 struct fsObject *o2;
175 UWORD count=0;
177 lockcachebuffer(cbdd);
179 for(;;) {
180 cb2=cbdd;
181 o2=odd;
183 if((errorcode=locateobject(globals->string2, &cb2, &o2))==ERROR_OBJECT_NOT_FOUND) {
184 ULONG blocks=(BE2L(o->object.file.be_size)+globals->bytes_block-1)>>globals->shifts_block;
186 globals->internalrename=TRUE;
188 if((errorcode=renameobject2(cb, o, cbdd, odd, globals->string2, sendnotify))==0) {
189 errorcode=setrecycledinfodiff(1, blocks);
192 globals->internalrename=FALSE;
193 break;
195 else {
196 UBYTE *s2=s;
198 *s2++='$';
199 *s2++=((count & 0xF00)>>8)+65;
200 *s2++=((count & 0x0F0)>>4)+65;
201 *s2++=(count & 0x00F)+65;
202 *s2=0;
204 if(count++>0xFFF) {
205 errorcode=INTERR_SAFE_DELETE;
206 break;
211 unlockcachebuffer(cbdd);
214 unlockcachebuffer(cb);
216 _XDEBUG((DEBUG_OBJECTS,"safedeleteobjectquick: exiting with errorcode %ld\n",errorcode));
218 return(errorcode);
224 BOOL cleanupdeletedfiles(void) {
225 /* This function returns TRUE if atleast one or more files were succesfully
226 deleted. FALSE is returned if no space at all could be freed.
228 This function returns FALSE if there's no recycled directory. */
230 _DEBUG(("cleanupdeletedfiles: entry\n"));
232 if(globals->has_recycled!=FALSE) {
233 struct CacheBuffer *cbdd;
234 struct fsObject *odd;
235 ULONG kbdeleted=0;
236 ULONG filesdeleted=0;
237 UBYTE filedeleted;
239 if(readobject(RECYCLEDNODE, &cbdd, &odd)==0 && odd->object.dir.be_firstdirblock!=0) {
241 lockcachebuffer(cbdd);
243 while(kbdeleted<1024 && filesdeleted<10) {
244 struct CacheBuffer *cb=0;
245 struct fsObjectContainer *oc;
246 struct fsObject *o;
247 BLCK nextblock=BE2L(odd->object.dir.be_firstdirblock);
249 filedeleted=FALSE;
251 while(nextblock!=0 && readcachebuffercheck(&cb, nextblock, OBJECTCONTAINER_ID)==0) {
252 oc=cb->data;
253 nextblock=BE2L(oc->be_next);
256 if(nextblock!=0) {
257 break;
260 do {
261 oc=cb->data;
262 o=oc->object;
264 do {
265 if(lockable(BE2L(o->be_objectnode),EXCLUSIVE_LOCK)!=DOSFALSE) {
266 ULONG size=BE2L(o->object.file.be_size)>>10;
268 _DEBUG(("cleanupdeletedfiles: deleting %s, objectnode = %ld\n",o->name,BE2L(o->be_objectnode)));
270 if(deletefileslowly(cb, o)==0) {
271 kbdeleted+=size;
272 filesdeleted++;
273 filedeleted=TRUE;
274 break;
278 o=nextobject(o);
279 } while(isobject(o,oc)!=FALSE);
281 } while(filedeleted==FALSE && oc->be_previous!=0 && readcachebuffercheck(&cb, BE2L(oc->be_previous), OBJECTCONTAINER_ID)==0);
283 if(oc->be_previous==0) {
284 break;
288 unlockcachebuffer(cbdd);
291 _DEBUG(("cleanupdeletedfiles: Deleted %ld files for %ld kB worth of space\n",filesdeleted,kbdeleted));
293 if(filesdeleted!=0) {
294 return(TRUE);
298 return(FALSE);
303 static LONG deleteobjectquick(struct CacheBuffer *cb, struct fsObject *o, WORD sendnotify) {
304 UWORD bits=o->bits;
305 BLCK hashblckno=BE2L(o->object.dir.be_hashtable);
306 BLCK extentbnode=BE2L(o->object.file.be_data);
307 UBYTE *notifypath=0;
308 LONG errorcode;
310 /* cb & o refer to the object to be deleted.
312 Note: This function deletes an object without first checking if
313 this is allowed. Use deleteobject() instead. */
315 if(sendnotify!=FALSE) {
316 notifypath=fullpath(cb,o);
319 if((errorcode=removeobject(cb, o))==0) {
321 /* What we need to do now depends on the type of object we are
322 deleting. For a directory we need to kill its HashTable block,
323 and for a File we need to remove all of its Extents and the
324 used space associated with them. */
326 if((bits & OTYPE_LINK)!=0) {
327 _XDEBUG((DEBUG_OBJECTS,"deleteobject: Object is soft link!\n"));
329 errorcode=freeadminspace(extentbnode);
331 else if((bits & OTYPE_DIR)!=0) {
332 _XDEBUG((DEBUG_OBJECTS,"deleteobject: Object is a directory!\n"));
334 errorcode=freeadminspace(hashblckno);
336 else {
337 _XDEBUG((DEBUG_OBJECTS,"deleteobject: Object is a file\n"));
339 errorcode=deleteextents(extentbnode);
342 if(errorcode==0 && sendnotify!=FALSE) {
343 checknotifyforpath(notifypath,TRUE);
347 return(errorcode);
352 LONG deleteobject(struct ExtFileLock *lock, UBYTE *path, WORD sendnotify) {
353 struct CacheBuffer *cb;
354 struct fsObject *o;
355 NODE objectnode;
356 LONG errorcode;
358 /* This function deletes the specified object. It will only delete directories
359 if they are empty. All space associated with a file will be marked free. */
361 _XDEBUG((DEBUG_OBJECTS,"deleteobject: Entry -- deleting object %s\n",path));
363 if(lock==0) {
364 objectnode=ROOTNODE;
366 else {
367 objectnode=lock->objectnode;
370 if((errorcode=readobject(objectnode,&cb,&o))==0) {
371 errorcode=locateobject2(&path,&cb,&o);
373 while(*path!=0) {
374 if(*path=='/') {
375 break;
377 path++;
380 if(errorcode==0 || (errorcode==ERROR_IS_SOFT_LINK && *path==0)) {
381 if((o->be_protection & L2BE(FIBF_DELETE))!=0) {
382 if(lockable(BE2L(o->be_objectnode),EXCLUSIVE_LOCK)!=DOSFALSE && (o->bits & OTYPE_UNDELETABLE)==0) {
383 /* Object to be deleted was read correctly by the above locateobject call */
385 if((o->bits & OTYPE_DIR)==0 || o->object.dir.be_firstdirblock==0) {
386 struct fsObjectContainer *oc=cb->data;
388 /* At this point we can be sure that the object is allowed
389 to be deleted. */
391 if((o->bits & OTYPE_DIR)!=0 || (o->bits & OTYPE_LINK)!=0 || globals->has_recycled==FALSE || oc->be_parent==L2BE(RECYCLEDNODE)) {
392 errorcode=deleteobjectquick(cb, o, sendnotify);
394 else {
395 errorcode=safedeleteobjectquick(cb, o, sendnotify);
398 else {
399 errorcode=ERROR_DIRECTORY_NOT_EMPTY;
402 else {
403 errorcode=ERROR_OBJECT_IN_USE;
406 else {
407 errorcode=ERROR_DELETE_PROTECTED;
412 _XDEBUG((DEBUG_OBJECTS,"deleteobject: Exiting with errorcode %ld\n",errorcode));
414 return(errorcode);
419 LONG removeobjectcontainer(struct CacheBuffer *cb) {
420 struct fsObjectContainer *oc=cb->data;
421 LONG errorcode;
423 /* Removes an ObjectContainer from a directory chain. Make
424 sure it is empty before removing it! */
426 _XDEBUG((DEBUG_OBJECTS,"removeobjectcontainer: entry\n"));
428 lockcachebuffer(cb);
430 if(oc->be_next!=0 && oc->be_next!=oc->bheader.be_ownblock) { // BE-BE compare!
431 struct CacheBuffer *next_cb;
432 struct fsObjectContainer *next_oc;
434 if((errorcode=readcachebuffercheck(&next_cb,BE2L(oc->be_next),OBJECTCONTAINER_ID))!=0) {
435 return(errorcode);
438 preparecachebuffer(next_cb);
440 next_oc=next_cb->data;
442 next_oc->be_previous=oc->be_previous; // BE-BE copy!
443 if((errorcode=storecachebuffer(next_cb))!=0) {
444 return(errorcode);
448 if(oc->be_previous!=0 && oc->be_previous!=oc->bheader.be_ownblock) { // BE-BE compare!
449 struct CacheBuffer *previous_cb;
450 struct fsObjectContainer *previous_oc;
452 if((errorcode=readcachebuffercheck(&previous_cb,BE2L(oc->be_previous),OBJECTCONTAINER_ID))!=0) {
453 return(errorcode);
456 preparecachebuffer(previous_cb);
458 previous_oc=previous_cb->data;
460 previous_oc->be_next=oc->be_next; // BE-BE copy
461 if((errorcode=storecachebuffer(previous_cb))!=0) {
462 return(errorcode);
465 else {
466 struct CacheBuffer *parent_cb;
467 struct fsObject *parent_o;
469 if((errorcode=readobject(BE2L(oc->be_parent),&parent_cb,&parent_o))!=0) {
470 return(errorcode);
473 preparecachebuffer(parent_cb);
475 if((parent_o->bits & OTYPE_RINGLIST)!=0) {
476 parent_o->object.dir.be_firstdirblock=0;
478 else {
479 parent_o->object.dir.be_firstdirblock=oc->be_next; // BE-BE copy
482 if((errorcode=storecachebuffer(parent_cb))!=0) {
483 return(errorcode);
487 unlockcachebuffer(cb);
489 if((errorcode=freeadminspace(cb->blckno))!=0) {
490 return(errorcode);
493 // emptycachebuffer(cb);
495 return(0);
500 LONG bumpobject(struct CacheBuffer *cb, struct fsObject *o) {
501 ULONG newdate=getdate();
502 ULONG newprotection=BE2L(o->be_protection) & ~FIBF_ARCHIVE;
504 /* Updates the date of the object and clears the archived bit. */
506 if(newdate!=BE2L(o->be_datemodified) || BE2L(o->be_protection)!=newprotection) {
507 preparecachebuffer(cb);
509 checksum_writelong_be(cb->data, &o->be_datemodified, newdate);
510 checksum_writelong_be(cb->data, &o->be_protection, newprotection);
512 // o->datemodified=newdate;
513 // o->protection=newprotection;
515 return(storecachebuffer_nochecksum(cb));
518 return(0);
522 LONG simpleremoveobject(struct CacheBuffer *cb, struct fsObject *o) {
523 struct fsObjectContainer *oc=cb->data;
524 BLCK parent=BE2L(oc->be_parent);
525 LONG errorcode;
527 /* This function removes the fsObject structure passed in from the passed
528 in CacheBuffer (a fsObjectContainer). If the ObjectContainer becomes
529 completely empty it will be delinked from the ObjectContainer chain and
530 marked free for reuse. newtransaction() must have been called before
531 calling this function.
533 This function doesn't delink the object from the hashchain! */
535 _XDEBUG((DEBUG_OBJECTS,"simpleremoveobject: Entry\n"));
537 if(BE2L(oc->be_parent)==RECYCLEDNODE) {
539 /* This object is removed from the Recycled directory. */
541 if((errorcode=setrecycledinfodiff(-1, -((BE2L(o->object.file.be_size)+globals->bytes_block-1)>>globals->shifts_block)))!=0) {
542 return(errorcode);
546 if(isobject(nextobject(oc->object),oc)==FALSE) {
547 errorcode=removeobjectcontainer(cb);
549 else {
550 struct fsObject *nexto;
551 WORD words;
552 UWORD *src;
553 UWORD *dst;
555 preparecachebuffer(cb);
557 nexto=nextobject(o);
558 src=(UWORD *)nexto;
559 dst=(UWORD *)o;
560 words=(globals->bytes_block-((UBYTE *)nexto-(UBYTE *)oc))>>1;
562 while(words-->0) {
563 *dst++=*src++;
566 words=((UBYTE *)nexto-(UBYTE *)o)>>1;
568 while(words-->0) {
569 *dst++=0;
572 errorcode=storecachebuffer(cb);
575 if(errorcode==0 && (errorcode=readobject(parent,&cb,&o))==0) { // reading parent block
576 errorcode=bumpobject(cb, o); /* An object was removed from this directory -- update date. */
579 return(errorcode);
584 static LONG dehashobjectquick(NODE objectnode, UBYTE *name, NODE parentobjectnode) {
585 struct CacheBuffer *cb;
586 struct fsObject *o;
587 LONG errorcode;
589 _XDEBUG((DEBUG_OBJECTS,"dehashobject: Delinking object %ld (=ObjectNode) from hashchain. Parentnode = %ld\n",objectnode,parentobjectnode));
591 /* This function delinks the passed in ObjectNode from its hash-chain. Handy when deleting
592 the object, or when renaming/moving it. newtransaction() must have been called before
593 calling this function. */
595 if((errorcode=readobject(parentobjectnode,&cb,&o))==0 && o->object.dir.be_hashtable!=0) {
596 if((errorcode=readcachebuffercheck(&cb,BE2L(o->object.dir.be_hashtable),HASHTABLE_ID))==0) {
597 struct CacheBuffer *cbnode;
598 struct fsObjectNode *on;
599 struct fsHashTable *ht=cb->data;
600 NODE nexthash;
602 lockcachebuffer(cb);
604 if((errorcode=findnode(globals->block_objectnoderoot, sizeof(struct fsObjectNode), objectnode, &cbnode, (struct fsNode **)&on))==0) {
605 UWORD hashchain;
607 _XDEBUG((DEBUG_OBJECTS,"dehashobject: Read HashTable block of parent object of object to be delinked\n"));
609 lockcachebuffer(cbnode);
611 hashchain=HASHCHAIN(hash(name, globals->is_casesensitive));
612 nexthash=BE2L(ht->be_hashentry[hashchain]);
614 if(nexthash==objectnode) {
615 /* The hashtable directly points to the fsObject to be delinked. We simply
616 modify the Hashtable to point to the new nexthash entry. */
618 _XDEBUG((DEBUG_OBJECTS,"dehashobject: The hashtable points directly to the to be delinked object\n"));
620 preparecachebuffer(cb);
622 checksum_writelong_be(cb->data, &ht->be_hashentry[hashchain], BE2L(on->be_next));
624 // ht->hashentry[hashchain]=on->next;
626 errorcode=storecachebuffer_nochecksum(cb);
628 else {
629 struct CacheBuffer *cb=0;
630 struct fsObjectNode *onsearch=0;
632 _XDEBUG((DEBUG_OBJECTS,"dehashobject: Walking through hashchain\n"));
634 while(nexthash!=0 && nexthash!=objectnode) {
635 if((errorcode=findnode(globals->block_objectnoderoot, sizeof(struct fsObjectNode), nexthash, &cb, (struct fsNode **)&onsearch))!=0) {
636 break;
639 nexthash=BE2L(onsearch->be_next);
642 if(errorcode==0) {
643 if(nexthash!=0) {
644 /* Previous fsObjectNode found in hash chain. Modify the fsObjectNode to 'skip' the
645 ObjectNode which is being delinked from the hash chain. */
647 preparecachebuffer(cb);
649 checksum_writelong_be(cb->data, &onsearch->be_next, BE2L(on->be_next));
651 // onsearch->next=on->next;
653 errorcode=storecachebuffer_nochecksum(cb);
655 else {
656 req("Hashchain of object %ld is\ncorrupt or incorrectly linked.", "Ok", objectnode);
658 /*** This is strange. We have been looking for the fsObjectNode which is located before the
659 passed in fsObjectNode in the hash-chain. However, we never found the
660 fsObjectNode reffered to in the hash-chain! Has to be somekind
661 of internal error... */
663 errorcode=ERROR_OBJECT_NOT_FOUND;
668 unlockcachebuffer(cbnode);
671 unlockcachebuffer(cb);
675 return(errorcode);
680 static LONG createobjecttagitem(struct CacheBuffer **io_cb, struct fsObject **io_o, UBYTE *objectname, struct TagItem *tags) {
681 struct CacheBuffer *cb=*io_cb;
682 struct fsObject *o=*io_o;
683 LONG errorcode;
685 /* io_cb & io_o refer to the direct parent of the new object. Objectname is the
686 name of the new object (name only). Use the Tags to pass a comment (important
687 since you can't change the size of the Object afterwards to accomodate one).
689 io_cb & io_o will not be locked, so make sure you lock the parent if you intend
690 to use it after this function.
692 This function updates the parent's date and Archive bit. It also check if the
693 object already exists to avoid duplicates.
695 If this function returns no error it will return in io_cb & io_o the new object
696 (prepared). */
698 _XDEBUG((DEBUG_OBJECTS,"createobjecttags: Creating object '%s' in dir '%s'.\n",objectname,(*io_o)->name));
700 if(BE2L((*io_o)->be_objectnode)!=RECYCLEDNODE || globals->internalrename!=FALSE) {
701 struct TagItem *tag;
703 lockcachebuffer(*io_cb);
705 /* Either duplicates are allowed or the object doesn't exist yet: */
707 if(((tag=FindTagItem(CO_ALLOWDUPLICATES, tags))!=0 && tag->ti_Data!=FALSE) || (errorcode=locateobject(objectname, &cb, &o))==ERROR_OBJECT_NOT_FOUND) {
709 unlockcachebuffer(*io_cb);
711 _XDEBUG((DEBUG_OBJECTS,"createobjecttags: Object didn't exist, so we can safely create it.\n"));
713 errorcode=0;
715 if((tag=FindTagItem(CO_UPDATEPARENT, tags))!=0 && tag->ti_Data!=FALSE) {
716 errorcode=bumpobject(*io_cb, *io_o); /* Update the date and the ARCHIVE bit of the parent directory. */
719 if(errorcode==0) {
720 ULONG objectsize;
721 ULONG hashblock=BE2L((*io_o)->object.dir.be_hashtable);
723 objectsize=sizeof(struct fsObject)+strlen(objectname)+2;
725 if((tag=FindTagItem(CO_COMMENT, tags))!=0) {
726 objectsize+=strlen((UBYTE *)tag->ti_Data);
729 if((errorcode=findobjectspace(io_cb, io_o, objectsize))==0) {
730 struct TagItem *tstate=tags;
731 struct fsObject *o=*io_o;
732 UBYTE *name=o->name;
733 UBYTE *objname=objectname;
735 o->bits=0;
737 if((tag=FindTagItem(CO_BITS, tags))!=0) {
738 o->bits=tag->ti_Data;
741 /* Setting defaults in case tags aren't specified: */
743 o->be_owneruid=0;
744 o->be_ownergid=0;
745 o->be_protection=L2BE(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
746 o->be_datemodified=L2BE(getdate());
748 /* Copying name */
750 while(*objname!=0) {
751 *name++=*objname++;
753 *name++=0;
754 *name=0; /* zero byte for comment */
756 while((tag=NextTagItem(&tstate))!=0) {
757 switch(tag->ti_Tag) {
758 case CO_OWNER:
759 o->be_owneruid=W2BE(tag->ti_Data>>16);
760 o->be_ownergid=W2BE(tag->ti_Data & 0xFFFF);
761 break;
762 case CO_DATEMODIFIED:
763 o->be_datemodified=L2BE(tag->ti_Data);
764 break;
765 case CO_PROTECTION:
766 o->be_protection=L2BE(tag->ti_Data);
767 break;
768 case CO_COMMENT:
770 UBYTE *comment=(UBYTE *)tag->ti_Data;
772 while(*comment!=0) {
773 *name++=*comment++;
775 *name=0;
777 break;
778 case CO_SIZE:
779 if((o->bits & OTYPE_DIR)==0) {
780 o->object.file.be_size=L2BE(tag->ti_Data);
782 break;
783 case CO_DATA:
784 if((o->bits & OTYPE_DIR)==0) {
785 o->object.file.be_data=L2BE(tag->ti_Data);
787 break;
788 case CO_FIRSTDIRBLOCK:
789 if((o->bits & OTYPE_DIR)!=0) {
790 o->object.dir.be_firstdirblock=L2BE(tag->ti_Data);
792 break;
796 if(errorcode==0) { // ObjectNode reuse or creation:
797 struct CacheBuffer *cbnode;
798 struct fsObjectNode *on;
800 if((tag=FindTagItem(CO_OBJECTNODE, tags))!=0) {
802 o->be_objectnode=L2BE(tag->ti_Data);
804 if((errorcode=findnode(globals->block_objectnoderoot, sizeof(struct fsObjectNode), BE2L(o->be_objectnode), &cbnode, (struct fsNode **)&on))==0) {
805 preparecachebuffer(cbnode);
808 else {
809 NODE nodeno;
811 if((errorcode=createnode(globals->block_objectnoderoot, sizeof(struct fsObjectNode), &cbnode, (struct fsNode **)&on, &nodeno))==0) {
812 on->be_hash16=W2BE(hash(o->name, globals->is_casesensitive));
813 o->be_objectnode=L2BE(nodeno);
817 if(errorcode==0) {
818 /* CacheBuffer cbnode is prepared. */
820 on->node.be_data=L2BE((*io_cb)->blckno);
822 if((tag=FindTagItem(CO_HASHOBJECT, tags))!=0 && tag->ti_Data!=FALSE) {
823 errorcode=hashobject(hashblock, on, BE2L(o->be_objectnode), objectname);
826 if(errorcode==0) {
827 errorcode=storecachebuffer(cbnode);
829 else {
830 dumpcachebuffer(cbnode);
835 if(errorcode==0) { // HashBlock reuse or creation:
836 if((o->bits & OTYPE_DIR)!=0) {
837 struct CacheBuffer *hashcb;
839 if((tag=FindTagItem(CO_HASHBLOCK, tags))!=0) {
840 o->object.dir.be_hashtable=L2BE(tag->ti_Data);
842 else if((errorcode=allocadminspace(&hashcb))==0) { // Create a new HashTable block.
843 struct fsHashTable *ht=hashcb->data;
845 o->object.dir.be_hashtable=L2BE(hashcb->blckno);
847 ht->bheader.id=HASHTABLE_ID;
848 ht->bheader.be_ownblock=L2BE(hashcb->blckno);
849 ht->be_parent=o->be_objectnode; // BE-BE copy!
851 errorcode=storecachebuffer(hashcb);
856 if(errorcode==0) { // SoftLink creation:
857 if((o->bits & (OTYPE_LINK|OTYPE_HARDLINK))==OTYPE_LINK) {
858 if((tag=FindTagItem(CO_SOFTLINK, tags))!=0) {
859 struct CacheBuffer *cb;
861 if((errorcode=allocadminspace(&cb))==0) {
862 struct fsSoftLink *sl=cb->data;
863 UBYTE *dest=sl->string;
864 UBYTE *softlink=(UBYTE *)tag->ti_Data;
866 o->object.file.be_data=L2BE(cb->blckno);
868 sl->bheader.id=SOFTLINK_ID;
869 sl->bheader.be_ownblock=L2BE(cb->blckno);
870 sl->be_parent=o->be_objectnode; // BE-BE copy
871 sl->be_next=0;
872 sl->be_previous=0;
874 while((*dest++=*softlink++)!=0) {
877 errorcode=storecachebuffer(cb);
883 if(errorcode!=0) {
884 dumpcachebuffer(*io_cb);
889 else {
890 if(errorcode==0) {
891 errorcode=ERROR_OBJECT_EXISTS;
894 unlockcachebuffer(*io_cb);
897 else {
898 errorcode=ERROR_OBJECT_IN_USE; /* Occurs when someone tries to create something in the recycled directory. */
901 return(errorcode);
905 #ifdef DEBUGCODE
906 static void copyobject(struct fsObject *o2,struct fsObject *o) {
907 o->be_objectnode=o2->be_objectnode; // BE-BE copy
908 o->be_owneruid=o2->be_owneruid; // BE-BE copy
909 o->be_ownergid=o2->be_ownergid; // BE-BE copy
910 o->be_protection=o2->be_protection; // BE-BE copy
911 o->be_datemodified=o2->be_datemodified; // BE-BE copy
912 o->bits=o2->bits;
914 if((o->bits & OTYPE_DIR)!=0) {
915 o->object.dir.be_firstdirblock=o2->object.dir.be_firstdirblock; // BE-BE copy
916 o->object.dir.be_hashtable=o2->object.dir.be_hashtable; // BE-BE copy
918 else {
919 o->object.file.be_data=o2->object.file.be_data; // BE-BE copy
920 o->object.file.be_size=o2->object.file.be_size; // BE-BE copy
923 #endif
926 LONG setcomment2(struct CacheBuffer *cb, struct fsObject *o, UBYTE *comment) {
927 UWORD commentlength=strlen(comment);
928 LONG errorcode=0;
930 if(commentlength<80) {
931 UBYTE *oldcomment=getcomment(o);
932 UWORD oldcommentlength=strlen(oldcomment);
934 if(oldcommentlength!=0 || commentlength!=0) {
935 if(changeobjectsize(cb, o, commentlength - oldcommentlength)==TRUE) {
936 while((*oldcomment++=*comment++)!=0) {
939 if((errorcode=storecachebuffer(cb))==0) {
940 checknotifyforobject(cb,o,TRUE);
943 else {
944 struct fsObject *oldo;
945 UBYTE object[sizeof(struct fsObject)+110+80];
946 NODE parent=BE2L(((struct fsObjectContainer *)cb->data)->be_parent);
948 oldo=(struct fsObject *)object;
950 CopyMem(o,object,(UBYTE *)nextobject(o)-(UBYTE *)o);
952 if((errorcode=simpleremoveobject(cb,o))==0) {
953 if((errorcode=readobject(parent, &cb, &o))==0) { // out: cb & o = Parent of Object to get new comment.
954 ULONG tag1,tag2;
955 ULONG val1,val2;
957 if((oldo->bits & OTYPE_DIR)!=0) {
958 tag1=CO_HASHBLOCK;
959 tag2=CO_FIRSTDIRBLOCK;
961 val1=BE2L(oldo->object.dir.be_hashtable);
962 val2=BE2L(oldo->object.dir.be_firstdirblock);
964 else {
965 tag1=CO_DATA;
966 tag2=CO_SIZE;
968 val1=BE2L(oldo->object.file.be_data);
969 val2=BE2L(oldo->object.file.be_size);
972 /* We've removed the object, but didn't remove it from the hashchain.
973 This means functions like locateobject could stumble upon the
974 removed object's ObjectNode, and follow it to a non-existing
975 object. That's why the CO_ALLOWDUPLICATES tag is passed. */
977 /* In goes the Parent cb & o, out comes the New object's cb & o :-) */
979 struct TagItem tags[] = {
980 { CO_ALLOWDUPLICATES, TRUE },
981 { CO_COMMENT, (IPTR)comment },
982 { CO_OBJECTNODE, BE2L(oldo->be_objectnode) },
983 { CO_PROTECTION, BE2L(oldo->be_protection) },
984 { CO_DATEMODIFIED, BE2L(oldo->be_datemodified) },
985 { CO_OWNER, (BE2W(oldo->be_owneruid)<<16) + BE2W(oldo->be_ownergid) },
986 { CO_BITS, oldo->bits },
987 { tag1, val1 },
988 { tag2, val2 },
989 { TAG_DONE, 0 }
992 if((errorcode=createobjecttagitem(&cb, &o, oldo->name, tags))==0) {
994 if((errorcode=storecachebuffer(cb))==0) {
995 checknotifyforobject(cb,o,TRUE);
1003 else {
1004 errorcode=ERROR_COMMENT_TOO_BIG;
1007 return(errorcode);
1013 LONG setcomment(struct ExtFileLock *lock, UBYTE *path, UBYTE *comment) {
1014 struct CacheBuffer *cb;
1015 struct fsObject *o;
1016 LONG errorcode;
1018 if((errorcode=locatelockableobject(lock, path, &cb, &o))==0) { // out: cb & o = Object to get new comment.
1019 errorcode=setcomment2(cb, o, comment);
1022 return(errorcode);
1027 LONG locatelockableparent(struct ExtFileLock *lock,UBYTE *path,struct CacheBuffer **returned_cb,struct fsObject **returned_o) {
1028 LONG errorcode;
1030 if(*path=='\0') {
1031 errorcode=locatelockableobject(lock,"/",returned_cb,returned_o);
1033 else {
1034 UBYTE *objectname=FilePart(path);
1035 UBYTE c=*objectname;
1037 *objectname=0;
1039 errorcode=locatelockableobject(lock,path,returned_cb,returned_o);
1041 *objectname=c;
1044 return(errorcode);
1049 LONG locateparent(struct ExtFileLock *lock, UBYTE *path, struct CacheBuffer **returned_cb, struct fsObject **returned_o) {
1050 LONG errorcode;
1052 if(*path=='\0') {
1053 errorcode=locateobjectfromlock(lock,"/",returned_cb,returned_o);
1055 else {
1056 UBYTE *objectname=FilePart(path);
1057 UBYTE c=*objectname;
1059 *objectname=0;
1061 errorcode=locateobjectfromlock(lock,path,returned_cb,returned_o);
1063 *objectname=c;
1066 return(errorcode);
1071 LONG lockfile(struct ExtFileLock *lock, UBYTE *path, LONG accessmode,struct ExtFileLock **returned_lock) {
1072 LONG errorcode;
1074 /* Locks a file -- ERROR_OBJECT_WRONG_TYPE is returned if a dir was attempted to be locked. */
1076 if((errorcode=lockobject(lock, path, accessmode, returned_lock))==0) {
1077 if(((*returned_lock)->bits & EFL_FILE)==0) { // You're not allowed to open directories or softlinks as files.
1078 freelock(*returned_lock);
1079 errorcode=ERROR_OBJECT_WRONG_TYPE;
1083 return(errorcode);
1088 LONG findcreate(struct ExtFileLock **returned_lock, UBYTE *path, LONG packettype, UBYTE *softlink) {
1089 struct ExtFileLock *lock;
1090 UBYTE create,delete;
1091 LONG accessmode;
1092 LONG errorcode;
1094 /* Used to open files, create files and create directories.
1096 First we check if the file mentioned exists, so we try to locate it.
1097 If the file does exist then depending on the packettype we need to
1098 open it or delete it. If we had to delete the file/directory then
1099 we create a new one in its place. */
1101 /* There seems to be some confusion as to what ACTION_FINDUPDATE is supposed
1102 to do. The 3 modes defined for the Amiga all work slightly different:
1104 - SHARED_LOCK or EXCLUSIVE_LOCK.
1105 - Creates a new file if one didn't exist.
1106 - Deletes existing file if one existed.
1108 ACTION_FINDINPUT: SHARED_LOCK, NO CREATE, NO DELETE
1109 ACTION_FINDUPDATE (1.3): EXCLUSIVE_LOCK, NO CREATE, NO DELETE
1110 ACTION_FINDUPDATE (2.0): SHARED_LOCK, CREATE, NO DELETE
1111 ACTION_FINDOUTPUT: EXCLUSIVE_LOCK, CREATE, DELETE
1112 ACTION_DELETE_OBJECT: EXCLUSIVE_LOCK, NO CREATE, DELETE :-) */
1114 path=validatepath(path);
1116 UBYTE *s=FilePart(path);
1118 if(*s==0) {
1119 return(ERROR_INVALID_COMPONENT_NAME);
1123 if(packettype==ACTION_FINDINPUT) {
1124 accessmode=SHARED_LOCK;
1125 create=0;
1126 delete=0;
1128 _XDEBUG((DEBUG_OBJECTS,"ACTION_FINDINPUT: %s\n",path));
1130 else if(packettype==ACTION_FINDOUTPUT || packettype==ACTION_MAKE_LINK) {
1131 accessmode=EXCLUSIVE_LOCK;
1132 create=1;
1133 delete=1;
1135 _XDEBUG((DEBUG_OBJECTS,"ACTION_FINDOUTPUT/ACTION_MAKE_LINK: %s\n",path));
1137 else if(packettype==ACTION_CREATE_DIR) {
1138 accessmode=EXCLUSIVE_LOCK;
1139 create=1;
1140 delete=0;
1142 _XDEBUG((DEBUG_OBJECTS,"ACTION_CREATE_DIR: %s\n",path));
1144 else { // if(packettype==ACTION_FINDUPDATE)
1145 accessmode=SHARED_LOCK;
1146 create=1;
1147 delete=0;
1149 _XDEBUG((DEBUG_OBJECTS,"ACTION_FINDUPDATE: %s\n",path));
1150 } /*
1151 else if(packettype==ACTION_DELETE_OBJECT) {
1152 accessmode= ?? ;
1153 create=0;
1154 delete=1;
1155 } just an example... :-) */
1160 lock=*returned_lock;
1162 if(delete==0 && create==0) {
1163 errorcode=lockfile(lock,path,accessmode,returned_lock);
1165 else {
1166 struct CacheBuffer *cb;
1167 struct fsObject *o;
1169 errorcode=locateobjectfromlock(lock,path,&cb,&o);
1171 /* 0, NO DELETE, CREATE
1172 0, DELETE, NO CREATE
1173 0, DELETE, CREATE
1174 OBJECT_NOT_FOUND, NO DELETE, CREATE
1175 OBJECT_NOT_FOUND, DELETE, NO CREATE
1176 OBJECT_NOT_FOUND, DELETE, CREATE */
1178 if(errorcode==0 && delete==0) {
1179 if(packettype==ACTION_CREATE_DIR) {
1180 errorcode=ERROR_OBJECT_EXISTS;
1182 else {
1183 errorcode=lockfile(lock,path,accessmode,returned_lock);
1186 else if(errorcode==0 || errorcode==ERROR_OBJECT_NOT_FOUND) {
1187 if(delete==1 && create==0) { // unused
1188 if(errorcode==0) {
1189 newtransaction();
1190 if((errorcode=deleteobject(lock, path, TRUE))==0) {
1191 endtransaction();
1193 else {
1194 deletetransaction();
1198 else {
1199 LONG lasterrorcode=errorcode;
1201 do {
1203 /* When we get here, we ALWAYS need to create a new object (possible
1204 after having deleted any existing objects) */
1206 newtransaction();
1208 if(lasterrorcode==0 && delete==1) {
1209 if((o->bits & OTYPE_DIR)!=0) {
1211 /* We are not allowed to overwrite directories (even if they're empty) */
1213 errorcode=ERROR_OBJECT_EXISTS;
1215 else {
1216 errorcode=deleteobject(lock, path, FALSE); /* Implicit deletes should not send a notify! */
1219 else {
1220 errorcode=0;
1223 _XDEBUG((DEBUG_OBJECTS,"findcreate: locating lockable parent\n"));
1225 if(errorcode==0 && (errorcode=locateparent(lock,path,&cb,&o))==0) { // was locatelockableparent()
1226 UBYTE bits=0;
1228 _XDEBUG((DEBUG_OBJECTS,"findcreate: creating object\n"));
1230 if(packettype==ACTION_CREATE_DIR) {
1231 bits|=OTYPE_DIR;
1233 else if(packettype==ACTION_MAKE_LINK) {
1234 bits|=OTYPE_LINK;
1237 struct TagItem tags[] = {
1238 { CO_BITS, bits },
1239 { CO_HASHOBJECT, TRUE },
1240 { CO_SOFTLINK, (IPTR)softlink },
1241 { CO_UPDATEPARENT, TRUE },
1242 { TAG_DONE, 0 }
1245 if((errorcode=createobjecttagitem(&cb, &o, FilePart(path), tags))==0) {
1247 _XDEBUG((DEBUG_OBJECTS,"findcreate: New object is now complete\n"));
1249 if((errorcode=storecachebuffer(cb))==0) {
1251 lockcachebuffer(cb);
1253 if(packettype==ACTION_MAKE_LINK) {
1254 checknotifyforobject(cb,o,TRUE);
1256 else if((errorcode=lockobject(lock, path, accessmode, returned_lock))==0) {
1257 if(packettype==ACTION_CREATE_DIR) {
1258 checknotifyforobject(cb,o,TRUE);
1260 else {
1261 (*returned_lock)->bits|=EFL_MODIFIED;
1265 unlockcachebuffer(cb);
1270 if(errorcode!=0) {
1271 deletetransaction();
1273 else {
1274 endtransaction();
1276 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1281 return(errorcode);
1286 LONG findobjectspace(struct CacheBuffer **io_cb, struct fsObject **io_o, ULONG bytesneeded) {
1287 struct CacheBuffer *cbparent=*io_cb;
1288 struct fsObject *oparent=*io_o;
1289 struct CacheBuffer *cb;
1290 ULONG nextblock=BE2L(oparent->object.dir.be_firstdirblock);
1291 LONG errorcode=0;
1293 _XDEBUG((DEBUG_OBJECTS,"findobjectspace: Looking for %ld bytes in directory with ObjectNode number %ld (in block %ld)\n",bytesneeded,BE2L((*io_o)->be_objectnode),(*io_cb)->blckno));
1295 /* This function will look in the directory indicated by io_o
1296 for an ObjectContainer block which contains bytesneeded free
1297 bytes. If none is found then this function simply creates a
1298 new ObjectContainer and adds that to the indicated directory.
1300 If OTYPE_QUICKDIR is set then only the first dir block is
1301 searched for enough free space, and if none is found a new
1302 block is added immediately.
1304 As this function may need to modify the disk, an operation
1305 should be in progress before calling this function. */
1307 lockcachebuffer(cbparent);
1309 while(nextblock!=0 && (errorcode=readcachebuffercheck(&cb,nextblock,OBJECTCONTAINER_ID))==0) {
1310 struct fsObjectContainer *oc=cb->data;
1311 UBYTE *emptyspace;
1313 /* We need to find out how much free space this ObjectContainer has */
1315 emptyspace=emptyspaceinobjectcontainer(oc);
1317 if((UBYTE *)oc+globals->bytes_block-emptyspace >= bytesneeded) {
1318 /* We found enough space in one of the ObjectContainer blocks!!
1319 We return a struct fsObject *. */
1321 preparecachebuffer(cb);
1323 *io_cb=cb;
1324 *io_o=(struct fsObject *)emptyspace;
1326 break;
1329 if((oparent->bits & OTYPE_QUICKDIR)!=0 || ((oparent->bits & OTYPE_RINGLIST)!=0 && oc->be_next==oparent->object.dir.be_firstdirblock)) { // BE-BE compare
1330 nextblock=0;
1332 else {
1333 nextblock=BE2L(oc->be_next);
1337 if(nextblock==0) {
1338 struct CacheBuffer *cb;
1340 /* If we get here, we traversed the *entire* directory (ough!) and found no empty
1341 space large enough for our entry. We allocate new space and add it to this
1342 directory. */
1344 if((errorcode=allocadminspace(&cb))==0) {
1345 struct fsObjectContainer *oc=cb->data;
1346 UBYTE ringlist=oparent->bits & OTYPE_RINGLIST;
1348 _XDEBUG((DEBUG_OBJECTS,"findobjectspace: No room was found, allocated new block at %ld\n",cb->blckno));
1350 /* Allocated new block. We will now link it to the START of the directory chain
1351 so the new free space can be found quickly when more entries need to be added. */
1353 oc->bheader.id=OBJECTCONTAINER_ID;
1354 oc->bheader.be_ownblock=L2BE(cb->blckno);
1355 oc->be_parent=oparent->be_objectnode; // BE-BE copy
1356 oc->be_next=oparent->object.dir.be_firstdirblock; // BE-BE copy
1357 oc->be_previous=0;
1359 preparecachebuffer(cbparent);
1361 oparent->object.dir.be_firstdirblock=L2BE(cb->blckno);
1363 if((errorcode=storecachebuffer(cbparent))==0) {
1364 struct CacheBuffer *cbnext;
1366 if(oc->be_next!=0 && (errorcode=readcachebuffercheck(&cbnext,BE2L(oc->be_next),OBJECTCONTAINER_ID))==0) {
1367 struct fsObjectContainer *ocnext=cbnext->data;
1368 BLCK lastblock=BE2L(ocnext->be_previous);
1370 preparecachebuffer(cbnext);
1372 ocnext->be_previous=L2BE(cb->blckno);
1374 if((errorcode=storecachebuffer(cbnext))==0 && ringlist!=0) {
1376 oc->be_previous=L2BE(lastblock);
1378 if((errorcode=readcachebuffercheck(&cbnext,lastblock,OBJECTCONTAINER_ID))==0) {
1379 ocnext=cbnext->data;
1381 preparecachebuffer(cbnext);
1383 checksum_writelong_be(cbnext->data, &ocnext->be_next, cb->blckno);
1385 // ocnext->next=cb->blckno;
1387 errorcode=storecachebuffer_nochecksum(cbnext);
1391 else if(ringlist!=0) {
1392 oc->be_previous=L2BE(cb->blckno);
1393 oc->be_next=L2BE(cb->blckno);
1396 *io_cb=cb;
1397 *io_o=oc->object;
1400 if(errorcode!=0) {
1401 dumpcachebuffer(cb);
1406 unlockcachebuffer(cbparent);
1408 return(errorcode);
1413 static LONG deleteobjectnode(NODE objectnode) {
1414 struct CacheBuffer *cb;
1415 struct fsObjectNode *on;
1416 LONG errorcode;
1418 _XDEBUG((DEBUG_NODES,"deleteobjectnode: Deleting Node %ld",objectnode));
1420 if((errorcode=findnode(globals->block_objectnoderoot, sizeof(struct fsObjectNode), objectnode, &cb, (struct fsNode **)&on))==0) {
1421 errorcode=deletenode(globals->block_objectnoderoot, cb, (struct fsNode *)on, sizeof(struct fsObjectNode));
1424 return(errorcode);
1429 struct fsObject *findobjectbyname(struct fsObjectContainer *oc, UBYTE *name) {
1430 struct fsObject *o=oc->object;
1432 while(isobject(o, oc)!=FALSE) {
1433 UBYTE *name1=o->name;
1434 UBYTE *name2=name;
1436 if(globals->is_casesensitive!=FALSE) {
1437 while(*name1!=0) {
1438 if(*name1!=*name2) {
1439 break;
1441 name1++;
1442 name2++;
1445 else {
1446 while(*name1!=0) {
1447 if(upperchar(*name1)!=upperchar(*name2)) {
1448 break;
1450 name1++;
1451 name2++;
1455 if(*name1==0 && (*name2==0 || *name2=='/')) {
1456 return(o);
1459 o=nextobject(o);
1462 return(0);
1467 LONG scandir(struct CacheBuffer **io_cb, struct fsObject **io_o, UBYTE *name) {
1468 struct CacheBuffer *cb;
1469 BLCK hashblock=BE2L((*io_o)->object.dir.be_hashtable);
1470 LONG errorcode=0;
1472 /* Scans a directory for the object with the name /name/. The directory
1473 to be scanned is given in *io_cb & *io_o. The resulting object (if any)
1474 is returned in *io_cb and *io_o.
1476 This function handles directories with no hashblock correctly. */
1478 if(hashblock!=0) {
1479 if((errorcode=readcachebuffercheck(&cb, hashblock, HASHTABLE_ID))==0) {
1480 struct fsHashTable *ht=cb->data;
1481 struct fsObjectNode *on;
1482 UWORD hash16;
1483 NODE nextobjectnode,objectnode;
1485 /* cb is a HashTable block. name is used to calculate the correct
1486 hash-value and the correct hash-chain is traversed until either
1487 the hash chain ends, or the name is located. */
1489 hash16=hash(name, globals->is_casesensitive);
1490 objectnode=BE2L(ht->be_hashentry[HASHCHAIN(hash16)]);
1492 while(objectnode!=0) {
1493 if((errorcode=findnode(globals->block_objectnoderoot, sizeof(struct fsObjectNode), objectnode, &cb, (struct fsNode **)&on))!=0) {
1494 return(errorcode);
1497 nextobjectnode=BE2L(on->be_next);
1499 if(BE2W(on->be_hash16)==hash16) {
1500 if((errorcode=readcachebuffercheck(&cb, BE2L(on->node.be_data), OBJECTCONTAINER_ID))==0) {
1501 struct fsObject *o;
1503 if((o=findobjectbyname((struct fsObjectContainer *)cb->data, name))!=0) {
1504 *io_cb=cb;
1505 *io_o=o;
1507 if((o->bits & OTYPE_LINK)!=0) {
1508 return(ERROR_IS_SOFT_LINK);
1511 return(0);
1515 /* Code below doesn't handle Soft Links correctly. There is a slight chance
1516 that it returns ERROR_IS_SOFT_LINK before knowing that the names really
1517 match.
1519 if((errorcode=readobjectquick(on->node.data,objectnode,&cb,&o))!=0) {
1520 if(errorcode==ERROR_IS_SOFT_LINK) {
1521 *io_cb=cb;
1522 *io_o=o;
1525 return(errorcode);
1528 if(compareobjectnames(o->name, name)!=FALSE) {
1529 *io_cb=cb;
1530 *io_o=o;
1532 return(0);
1539 objectnode=nextobjectnode;
1542 return(ERROR_OBJECT_NOT_FOUND);
1545 else {
1546 struct fsObjectContainer *oc;
1547 struct fsObject *o;
1548 BLCK nextdirblock=BE2L((*io_o)->object.dir.be_firstdirblock);
1550 while(nextdirblock!=0 && (errorcode=readcachebuffercheck(&cb, nextdirblock, OBJECTCONTAINER_ID))==0) {
1551 oc=cb->data;
1553 if((o=findobjectbyname(oc, name))!=0) {
1554 *io_cb=cb;
1555 *io_o=o;
1557 if((o->bits & OTYPE_LINK)!=0) {
1558 return(ERROR_IS_SOFT_LINK);
1561 return(0);
1564 nextdirblock=BE2L(oc->be_next);
1567 if(nextdirblock==0) {
1568 errorcode=ERROR_OBJECT_NOT_FOUND;
1572 return(errorcode);
1577 LONG readobject(NODE objectnode,struct CacheBuffer **returned_cb,struct fsObject **returned_object) {
1578 struct CacheBuffer *cb;
1579 struct fsObjectNode *on;
1580 LONG errorcode;
1582 if((errorcode=findnode(globals->block_objectnoderoot, sizeof(struct fsObjectNode), objectnode, &cb, (struct fsNode **)&on))==0) {
1583 errorcode=readobjectquick(BE2L(on->node.be_data),objectnode,returned_cb,returned_object);
1586 return(errorcode);
1591 LONG readobjectquick(BLCK objectcontainer,NODE objectnode,struct CacheBuffer **returned_cb,struct fsObject **returned_object) {
1592 LONG errorcode;
1594 if((errorcode=readcachebuffercheck(returned_cb,objectcontainer,OBJECTCONTAINER_ID))==0) {
1595 struct fsObjectContainer *oc=(*returned_cb)->data;
1597 if((*returned_object=findobject(oc,objectnode))==0) {
1598 /**** if the system gets here then there was an ObjectNode which didn't point
1599 to a block in which the object exists. This means there MUST be something wrong
1600 with the filesystem or the disk. */
1602 req("ObjectNode %ld points to a block which\ndoesn't contain the intended Object.", "Ok", objectnode);
1603 errorcode=ERROR_OBJECT_NOT_FOUND;
1605 else if(((*returned_object)->bits & OTYPE_LINK)!=0) {
1606 errorcode=ERROR_IS_SOFT_LINK;
1610 return(errorcode);
1615 struct fsObject *findobject(struct fsObjectContainer *oc,NODE objectnode) {
1616 struct fsObject *o=oc->object;
1617 UBYTE *endadr;
1619 endadr=(UBYTE *)oc+globals->bytes_block-sizeof(struct fsObject)-2;
1621 while((UBYTE *)o<endadr && o->name[0]!=0) {
1622 if(BE2L(o->be_objectnode)==objectnode) {
1623 /* a match, which means the correct object was located. */
1624 return(o);
1626 o=nextobject(o);
1629 return(0);
1634 WORD objectsize(struct fsObject *o) {
1635 UBYTE *p;
1637 /* Returns the exact size of this object in bytes. */
1639 p=(UBYTE *)&o->name[0];
1641 /* skip the filename */
1642 while(*p++!=0) {
1645 /* skip the comment */
1646 while(*p++!=0) {
1649 return((WORD)(p-(UBYTE *)o));
1654 UBYTE *getcomment(struct fsObject *o) {
1655 UBYTE *p;
1657 p=(UBYTE *)&o->name[0];
1659 /* skip the filename */
1660 while(*p++!=0) {
1663 return(p);
1668 struct fsObject *lastobject(struct fsObjectContainer *oc) {
1669 struct fsObject *oprev;
1670 struct fsObject *o=oc->object;
1672 /* returns the last object. */
1674 do {
1675 oprev=o;
1676 o=nextobject(o);
1677 } while(isobject(o, oc)!=FALSE);
1679 return(oprev);
1684 struct fsObject *prevobject(struct fsObject *o, struct fsObjectContainer *oc) {
1685 struct fsObject *oprev=0;
1686 struct fsObject *o2=oc->object;
1688 /* returns the previous object. If there is no previous, zero is returned. */
1690 while(o2!=o) {
1691 oprev=o2;
1692 o2=nextobject(o2);
1693 if(isobject(o2, oc)==FALSE) {
1694 return(0);
1698 return(oprev);
1703 struct fsObject *nextobject(struct fsObject *o) {
1704 UBYTE *p;
1706 /* skips the passed in fsObject and gives a pointer back to the place where
1707 a next fsObject structure could be located */
1709 p=(UBYTE *)&o->name[0];
1711 /* skip the filename */
1712 while(*p++!=0) {
1715 /* skip the comment */
1716 while(*p++!=0) {
1719 /* ensure WORD boundary */
1720 if((((IPTR)p) & 0x01)!=0) {
1721 p++;
1724 return((struct fsObject *)p);
1729 WORD isobject(struct fsObject *o, struct fsObjectContainer *oc) {
1730 UBYTE *endadr;
1732 endadr=(UBYTE *)oc+globals->bytes_block-sizeof(struct fsObject)-2;
1734 if((UBYTE *)o<endadr && o->name[0]!=0) {
1735 return(TRUE);
1737 return(FALSE);
1742 static UBYTE *emptyspaceinobjectcontainer(struct fsObjectContainer *oc) {
1743 struct fsObject *o=oc->object;
1744 UBYTE *endadr;
1746 /* This function returns a pointer to the first unused byte in
1747 an ObjectContainer. */
1749 endadr=(UBYTE *)oc+globals->bytes_block-sizeof(struct fsObject)-2;
1751 while((UBYTE *)o<endadr && o->name[0]!=0) {
1752 o=nextobject(o);
1755 return((UBYTE *)o);
1760 LONG removeobject(struct CacheBuffer *cb, struct fsObject *o) {
1761 struct fsObjectContainer *oc=cb->data;
1762 LONG errorcode;
1764 /* This function removes an object from any directory. It takes care
1765 of delinking the object from the hashchain and also frees the
1766 objectnode number.
1768 This function must be called from within a transaction! */
1770 lockcachebuffer(cb);
1772 if((errorcode=dehashobjectquick(BE2L(o->be_objectnode), o->name, BE2L(oc->be_parent)))==0) {
1773 NODE objectnode=BE2L(o->be_objectnode);
1775 if((errorcode=simpleremoveobject(cb, o))==0) {
1776 errorcode=deleteobjectnode(objectnode);
1780 unlockcachebuffer(cb);
1782 return(errorcode);
1790 LONG renameobject(struct CacheBuffer *cb, struct fsObject *o, struct ExtFileLock *lock, UBYTE *path) {
1791 struct CacheBuffer *cbparent;
1792 struct fsObject *oparent;
1793 LONG errorcode;
1795 /* cb & o is the original object. The lock and path is the new name and location of the object. */
1797 lockcachebuffer(cb);
1799 if((errorcode=locatelockableparent(lock, path, &cbparent, &oparent))==0) {
1800 unlockcachebuffer(cb);
1801 errorcode=renameobject2(cb, o, cbparent, oparent, FilePart(path), TRUE);
1803 else {
1804 unlockcachebuffer(cb);
1807 return(errorcode);
1812 LONG renameobject2(struct CacheBuffer *cb, struct fsObject *o, struct CacheBuffer *cbparent, struct fsObject *oparent, UBYTE *newname, WORD sendnotify) {
1813 struct fsObjectContainer *oc=cb->data;
1814 struct fsObject *oldo;
1815 UBYTE object[sizeof(struct fsObject)+110+80];
1816 UBYTE *comment;
1817 UBYTE *notifypath=0;
1818 NODE objectnode=BE2L(o->be_objectnode);
1819 NODE sourceparentobjectnode=BE2L(oc->be_parent);
1820 LONG errorcode=0;
1822 /* The Object indicated by cb & o, gets renamed to newname and placed
1823 in the directory indicated by cbparent & oparent. */
1825 _XDEBUG((DEBUG_OBJECTS,"renameobject2: Renaming '%s' to '%s' in dir '%s'\n",o->name,newname,oparent->name));
1827 oldo=(struct fsObject *)object;
1829 lockcachebuffer(cb);
1830 lockcachebuffer(cbparent);
1832 if((o->bits & OTYPE_DIR)!=0 && oc->be_parent!=oparent->be_objectnode) {
1833 NODE objectnode=BE2L(o->be_objectnode);
1836 struct CacheBuffer *cb=cbparent;
1837 struct fsObject *o=oparent;
1838 struct fsObjectContainer *oc;
1840 /* We should check if o isn't a parent of oparent so to avoid renaming
1841 a directory into one of its children. Of course, we only need to
1842 do this when o is a directory, and oparent isn't the parent of o. */
1844 do {
1845 oc=cb->data;
1847 if(objectnode==BE2L(o->be_objectnode)) {
1848 errorcode=ERROR_OBJECT_IN_USE;
1849 break;
1852 } while(oc->be_parent!=0 && (errorcode=readobject(BE2L(oc->be_parent), &cb, &o))==0);
1856 if(errorcode==0) {
1857 if(sendnotify!=FALSE) {
1858 notifypath=fullpath(cb,o);
1861 CopyMem(o, object, (UBYTE *)nextobject(o)-(UBYTE *)o);
1863 comment=oldo->name;
1865 /* skip the filename */
1866 while(*comment++!=0) {
1869 if((errorcode=dehashobjectquick(objectnode, o->name ,BE2L(oc->be_parent)))==0) {
1870 ULONG parentobjectnode=BE2L(oparent->be_objectnode);
1872 unlockcachebuffer(cb);
1874 /* If moving an Object from a directory to a subdirectory of that directory and
1875 the Object is in the same ObjectContainer, then it is possible that oparent
1876 won't point to a valid object anymore after calling simpleremoveobject(). */
1878 if((errorcode=simpleremoveobject(cb,o))==0) {
1879 struct CacheBuffer *cb=cbparent;
1880 struct fsObject *o;
1881 ULONG tag1,tag2;
1882 ULONG val1,val2;
1884 o=findobject((struct fsObjectContainer *)cb->data,parentobjectnode);
1886 if((oldo->bits & OTYPE_DIR)!=0) {
1887 tag1=CO_HASHBLOCK;
1888 tag2=CO_FIRSTDIRBLOCK;
1890 val1=BE2L(oldo->object.dir.be_hashtable);
1891 val2=BE2L(oldo->object.dir.be_firstdirblock);
1893 else {
1894 tag1=CO_DATA;
1895 tag2=CO_SIZE;
1897 val1=BE2L(oldo->object.file.be_data);
1898 val2=BE2L(oldo->object.file.be_size);
1901 /* In goes the Parent cb & o, out comes the New object's cb & o :-) */
1903 struct TagItem tags[] = {
1904 { CO_COMMENT, (IPTR)comment },
1905 { CO_OBJECTNODE, BE2L(oldo->be_objectnode) },
1906 { CO_PROTECTION, BE2L(oldo->be_protection) },
1907 { CO_DATEMODIFIED, BE2L(oldo->be_datemodified) },
1908 { CO_OWNER, (BE2W(oldo->be_owneruid)<<16) + BE2W(oldo->be_ownergid) },
1909 { CO_BITS, oldo->bits },
1910 { CO_HASHOBJECT, TRUE },
1911 { CO_UPDATEPARENT, TRUE },
1912 { tag1, val1 },
1913 { tag2, val2 },
1914 { TAG_DONE, 0 }
1917 if((errorcode=createobjecttagitem(&cb, &o, newname, tags))==0) {
1919 if((errorcode=storecachebuffer(cb))==0 && sendnotify!=FALSE) { // Object itself has been completed.
1921 _XDEBUG((DEBUG_OBJECTS,"renameobject2: Succesfully created & stored new object.\n"));
1923 if(parentobjectnode!=sourceparentobjectnode) {
1924 /* Object was moved! */
1925 checknotifyforpath(notifypath,TRUE);
1927 else {
1928 /* Object was renamed within same directory */
1929 checknotifyforpath(notifypath,FALSE);
1931 checknotifyforobject(cb,o,TRUE); /* fullpath points to a global string so put this notify after the 2 others. */
1936 else {
1937 unlockcachebuffer(cb);
1940 else {
1941 unlockcachebuffer(cb);
1944 unlockcachebuffer(cbparent);
1946 return(errorcode);
1951 static WORD changeobjectsize(struct CacheBuffer *cb, struct fsObject *o, WORD bytes) {
1952 struct fsObjectContainer *oc=cb->data;
1953 WORD currentobjectsize;
1954 WORD newobjectsize;
1955 WORD words;
1957 /* This function resizes the passed in Object, if possible.
1958 A negative bytes number reduces the size, a positive number
1959 increases the size. This function will return TRUE on
1960 success. newtransaction() must have been called before calling
1961 this function. */
1963 // currentobjectsize & newobjectsize in bytes:
1965 currentobjectsize=objectsize(o);
1966 newobjectsize=currentobjectsize+bytes;
1968 // currentobjectsize & newobjectsize in words:
1970 currentobjectsize++;
1971 currentobjectsize>>=1;
1973 newobjectsize++;
1974 newobjectsize>>=1;
1976 words=newobjectsize-currentobjectsize;
1978 if(words==0) {
1979 preparecachebuffer(cb);
1981 return(TRUE);
1984 if(words<0) {
1985 UWORD *src;
1986 UWORD *dst;
1987 WORD copywords;
1989 preparecachebuffer(cb);
1991 words=-words;
1993 src=(UWORD *)o + currentobjectsize;
1994 dst=src-words;
1996 copywords=(globals->bytes_block>>1)-(src-(UWORD *)oc);
1998 while(--copywords>=0) {
1999 *dst++=*src++;
2002 while(--words>=0) {
2003 *dst++=0;
2006 else if(emptyspaceinobjectcontainer(oc)-(UBYTE *)oc+(words<<1)<=globals->bytes_block) {
2007 UWORD *src;
2008 UWORD *dst;
2009 WORD copywords;
2011 preparecachebuffer(cb);
2013 dst=(UWORD *)((UBYTE *)oc+globals->bytes_block);
2014 src=dst-words;
2016 copywords=(globals->bytes_block>>1) - (((UWORD *)o + currentobjectsize + words)-(UWORD *)oc);
2018 while(--copywords>=0) {
2019 *--dst=*--src;
2022 *--dst=0;
2024 else {
2025 return(FALSE);
2028 return(TRUE);