revert between 56095 -> 55830 in arch
[AROS.git] / rom / filesys / SFS / FS / transactions.c
blob46c10e46f8416ecb437c76d8c03e160e62c06c7e
1 #include "asmsupport.h"
3 //#include <clib/alib_protos.h>
4 #include <devices/trackdisk.h>
5 #include <exec/types.h>
6 #include <proto/exec.h>
8 #include "transactions.h"
9 #include "transactions_protos.h"
11 #include "bitmap_protos.h"
12 #include "cachebuffers_protos.h"
13 #include "debug.h"
14 #include "cachedio_protos.h"
15 #include "req_protos.h"
16 #include "support_protos.h"
18 #include "globals.h"
20 extern void outputcachebuffer(struct CacheBuffer *cb);
22 extern void setchecksum(struct CacheBuffer *);
23 extern BOOL checkchecksum(struct CacheBuffer *);
24 extern LONG readcachebuffercheck(struct CacheBuffer **,ULONG,ULONG);
25 extern void starttimeout(void);
26 extern void stoptimeout(void);
28 /* Internal prototypes */
30 void linkoperation(struct Operation *o);
31 LONG removetransactionfailure(void);
32 LONG writeoperations(void);
33 void restorecachebuffer(struct CacheBuffer *cb);
35 /* Operation : A single block modification
36 Transaction : A series of block modifications which together result in a valid disk */
38 /* Definitions for Red Black tree */
40 #define ROOT globals->operationroot /* The name of the root variable */
41 #define RBNODE struct Operation /* The name of the structure containing the left,
42 right, parent, nodecolor & data fields. */
43 #define SENTINEL globals->operationsentinel /* The name of the sentinel node variable (must be a RBNODE) */
44 #define DATA oi.blckno /* The name of the data field (example: data, sub.mydata) */
46 #define CompLT(a,b) (a < b) /* modify these lines to establish data type */
47 #define CompEQ(a,b) (a == b)
49 #ifdef CHECKCODE_TRANSACTIONS
50 void cop(struct Operation *o, UBYTE *where) {
51 if(o->magicvalue!=0x4a4f) {
52 req_unusual("magicvalue of operation is incorrect.\nAddress = 0x%08lx (location: %s)\n", o, where);
55 #endif
57 #include "redblacktree.c"
59 #define linkoperation(x) InsertNode(x)
61 /* linkoperation() and removeoperation() are the functions which add and remove
62 operations to a list or tree of somekind. They can be easily changed to
63 support different forms of lists. */
65 void removeoperation(struct Operation *o) {
66 #ifdef CHECKCODE_TRANSACTIONS
67 cop(o, "removeoperation");
68 #endif
69 DeleteNode(o);
71 globals->transactionpoolsize-=o->oi.length+sizeof(struct Operation);
72 FreePooled(globals->transactionpool,o,o->oi.length+sizeof(struct Operation));
75 void removecurrentoperation2(BLCK blck, struct Operation *o) {
76 if((o=FindNodeFrom(o,blck))!=0) {
77 if(o->new==globals->transactionnestcount) {
78 removeoperation(o);
80 else {
81 removecurrentoperation2(blck, o->left);
82 removecurrentoperation2(blck, o->right);
89 void removecurrentoperation(BLCK blck) {
90 /* This function removes the operation for a specific block for the current
91 transaction. Operations for this block in older transactions won't be
92 affected. */
94 removecurrentoperation2(blck, ROOT);
99 LONG inittransactions() {
100 ULONG poolsize;
102 InitRedBlackTree();
103 #ifdef CHECKCODE_TRANSACTIONS
104 operationsentinel.magicvalue=0x4a4f;
105 #endif
107 poolsize=globals->bytes_block*4;
108 if(poolsize<16384) {
109 poolsize=16384;
112 if((globals->compressbuffer=AllocMem(globals->bytes_block+(globals->bytes_block>>4),1))!=0) {
113 if((globals->transactionpool=CreatePool(0,poolsize,poolsize>>1))!=0) {
114 return(0);
118 cleanuptransactions();
120 return(ERROR_NO_FREE_STORE);
125 void cleanuptransactions() {
126 if(globals->transactionpool!=0) {
127 DeletePool(globals->transactionpool);
130 if(globals->compressbuffer!=0) {
131 FreeMem(globals->compressbuffer,globals->bytes_block+(globals->bytes_block>>4));
137 static LONG fillwithoperations(struct fsTransactionStorage *ts,struct Operation **o,UBYTE **src,LONG *length) {
138 LONG bytesleft=globals->bytes_block-sizeof(struct fsTransactionStorage);
139 LONG copylength;
140 UBYTE *dest=ts->data;
142 while(bytesleft>0) {
143 if(*length==0) {
144 if(*o!=0) {
145 *src=(UBYTE *)&(*o)->oi;
146 *length=((*o)->oi.length | 1)+sizeof(struct OperationInformation)-1;
148 *o=NextNode(*o);
150 else {
151 return(TRUE);
155 copylength=*length;
156 if(copylength>bytesleft) {
157 copylength=bytesleft;
160 CopyMem(*src,dest,copylength);
162 *src+=copylength;
163 *length-=copylength;
165 dest+=copylength;
166 bytesleft-=copylength;
169 if(*length==0 && *o==0) {
170 return(TRUE);
173 return(FALSE);
179 LONG savetransaction(BLCK *firsttransactionblock) {
180 struct CacheBuffer *cb;
181 ULONG blckno;
182 LONG errorcode;
184 _XDEBUG((DEBUG_TRANSACTION,"savetransaction: Entry. Transaction size = %ld\n", globals->transactionpoolsize));
186 *firsttransactionblock=0;
188 if((cb=getcachebuffer())!=0) {
189 struct Operation *o=FirstNode();
190 struct fsTransactionStorage *ts=cb->data;
191 UBYTE *src = NULL;
192 LONG length=0;
193 LONG done;
195 lockcachebuffer(cb);
197 if((errorcode=findspace(1, globals->block_rovingblockptr, globals->block_rovingblockptr, &blckno))==0) {
198 ULONG startblock;
200 _XDEBUG((DEBUG_TRANSACTION,"savetransaction: findspace succesfully completed\n"));
202 /* Warning, this function doesn't mark space, so it assumes that
203 findspace never returns the same space if startblock is set
204 to blckno+1 each time. */
206 do {
208 _XDEBUG((DEBUG_TRANSACTION,"savetransaction: Storing operations of transaction in block %ld\n",blckno));
210 startblock=blckno+1;
212 clearcachebuffer(cb);
214 if(*firsttransactionblock==0) {
215 *firsttransactionblock=blckno;
217 cb->blckno=blckno;
219 ts->bheader.id=TRANSACTIONSTORAGE_ID;
220 ts->bheader.be_ownblock=L2BE(blckno);
222 done=fillwithoperations(ts,&o,&src,&length);
224 if(done==FALSE) {
225 if(startblock==globals->block_rovingblockptr) {
226 errorcode=ERROR_DISK_FULL;
227 break;
230 if((errorcode=findspace(1, startblock, globals->block_rovingblockptr, &blckno))!=0) {
231 break;
234 ts->be_next=L2BE(blckno);
237 setchecksum(cb);
238 if((errorcode=writecachebuffer(cb))!=0) {
239 break;
242 } while(done==FALSE);
245 unlockcachebuffer(cb);
247 else {
248 errorcode=ERROR_NO_FREE_STORE;
251 _XDEBUG((DEBUG_TRANSACTION,"savetransaction: Exiting with errorcode = %ld\n",errorcode));
253 return(errorcode);
258 /* Read TransactionFailure block and process the transaction list it points to. */
260 LONG loadtransaction(BLCK block) {
261 struct CacheBuffer *cb;
262 struct Operation *o=0;
263 UBYTE *src;
264 UBYTE *dest=0;
265 UWORD length=0;
266 UWORD bytesleft;
267 LONG errorcode=0;
269 while(block!=0 && (errorcode=readcachebuffercheck(&cb,block,TRANSACTIONSTORAGE_ID))==0) {
270 struct fsTransactionStorage *ts=cb->data;
272 block=BE2L(ts->be_next);
274 bytesleft=globals->bytes_block-sizeof(struct fsTransactionStorage);
275 src=ts->data;
277 while(bytesleft!=0) {
278 if(length!=0) {
279 UWORD copylength=length;
281 if(copylength>bytesleft) {
282 copylength=bytesleft;
285 CopyMem(src,dest,copylength);
287 src+=copylength;
288 dest+=copylength;
289 length-=copylength;
290 bytesleft-=copylength;
292 else {
293 UWORD len=*((UWORD *)src);
295 if(o!=0) {
296 linkoperation(o);
299 if(len==0) {
300 return(0);
303 length=(len | 1)+sizeof(struct OperationInformation)-1;
305 if((o=AllocPooled(globals->transactionpool,len+sizeof(struct Operation)))==0) {
306 return(ERROR_NO_FREE_STORE);
308 globals->transactionpoolsize+=len+sizeof(struct Operation);
310 dest=(UBYTE *)&o->oi;
315 if(errorcode==0 && o!=0) {
316 linkoperation(o);
319 return(errorcode);
325 LONG checkfortransaction(void) {
326 struct CacheBuffer *cb;
327 LONG errorcode;
329 if((errorcode=readcachebuffer(&cb,globals->block_root+2))==0) {
330 struct fsBlockHeader *bh=cb->data;
332 if(checkchecksum(cb)==DOSTRUE && bh->be_ownblock==L2BE(globals->block_root+2)) {
333 if(bh->id==TRANSACTIONFAILURE_ID) {
334 struct fsTransactionFailure *tf=cb->data;
336 /* A transaction failed to complete.. we'll need to re-do it. */
338 req("Has an unfinished transaction which\nwill be loaded now.", "Ok");
340 if((errorcode=loadtransaction(BE2L(tf->be_firsttransaction)))==0) {
342 /* Succesfully loaded the Transaction buffer of the Transaction which
343 failed to complete before. */
345 req("The transaction loaded succesfully and\nit will now be re-applied to this volume.", "Ok");
347 _DEBUG(("Before writeoperations()\n"));
349 if((errorcode=writeoperations())==0) {
352 request(PROGRAMNAME " request","%s\n"\
353 "Debug requester 1\n",
354 "Ok",((UBYTE *)BADDR(devnode->dn_Name))+1);
356 _DEBUG(("Before removetransactionfailure()\n"));
357 if((errorcode=removetransactionfailure())==0) {
359 req("The transaction has been succesfully\napplied to this volume.\nYou can continue normally now.", "Ok");
364 if(errorcode!=0) {
365 req("The transaction couldn't be re-applied\nto the volume because of error %ld.\n\nPlease reboot your system to try again.", "Ok");
369 else {
370 return(INTERR_CHECKSUM_FAILURE);
374 return(errorcode);
380 static void combineoperations(void) {
381 struct Operation *o;
383 /* Combine operations will combine the operations of the current
384 transaction with those of the previous transaction level. The
385 newer operations are kept, and any old operations operating on
386 the same block are discarded. The newer operations are given
387 a transaction level one lower than their current level.
389 Operations with OI_DELETE will be removed, including a possible
390 existing operation for the same block. */
392 if((o=FirstNode())!=0) {
393 struct Operation *oold=0;
394 struct Operation *onew=0;
395 struct Operation *onext;
396 BLCK block;
397 UBYTE tnc=globals->transactionnestcount-1;
399 #ifdef CHECKCODE_TRANSACTIONS
400 cop(o, "combineoperations 1");
401 #endif
403 for(;;) {
404 onext=NextNode(o);
405 block=o->oi.blckno;
407 if(o->new>=tnc) {
408 if(o->new==tnc) {
409 oold=o;
411 else {
412 onew=o;
413 onew->new=tnc;
416 if(oold!=0 && onew!=0) {
417 #ifdef CHECKCODE_TRANSACTIONS
418 cop(oold, "combineoperations 2");
419 cop(onew, "combineoperations 3");
420 #endif
422 if((onew->oi.bits & OI_DELETE)!=0) {
423 struct CacheBuffer *cb;
425 /* The new operation indicates that the operations on this block
426 can be discarded because the block has been deleted. In this
427 case, we not only delete the old operation, but the new one
428 as well.
430 Also, because there will be no operations for this block
431 anymore a possibly present CB_ORIGINAL cachebuffer might need
432 to be turned into a CB_ORIGIGNAL|CB_LATEST cachebuffer.
434 Also, all CB_LATEST cachebuffers, except original ones, must
435 be terminated. */
437 if((cb=findlatestcachebuffer(onew->oi.blckno))!=0) {
438 emptycachebuffer(cb);
441 if((cb=findoriginalcachebuffer(onew->oi.blckno))!=0) {
442 cb->bits|=CB_LATEST;
445 removeoperation(onew);
448 removeoperation(oold);
449 oold=0;
450 onew=0;
454 if((o=onext)==0) {
455 break;
458 if(block!=onext->oi.blckno) {
459 oold=0;
460 onew=0;
468 #ifdef NOCOMPRESSION
470 LONG addoperation2(struct CacheBuffer *cb_org, struct CacheBuffer *cb_new) {
471 struct Operation *o;
473 // _DEBUG(("addoperation: Adding a new operation for block %ld\n",block));
475 /* First remove all Operations with the same block in this Transaction */
477 removecurrentoperation(cb_new->blckno);
479 /* Add our new Operation to the Transaction */
481 if((o=AllocPooled(transactionpool,bytes_block+sizeof(struct Operation)))!=0) {
482 UBYTE bits=0;
484 transactionpoolsize+=bytes_block+sizeof(struct Operation);
486 if(cb_org!=0) {
487 compress(cb_org->data,cb_new->data,o->oi.data);
489 else {
490 compressfromzero(cb_new->data,o->oi.data);
493 if((cb_new->bits & CB_EMPTY)!=0) {
494 bits=OI_EMPTY;
497 o->oi.blckno=cb_new->blckno;
498 o->oi.length=bytes_block;
499 o->oi.bits=bits;
500 o->new=transactionnestcount;
502 linkoperation(o);
504 return(0);
507 return(ERROR_NO_FREE_STORE);
510 #endif
513 LONG addoperation(BLCK block,UBYTE *data,UWORD length,UBYTE bits) {
514 struct Operation *o;
516 /* First remove all Operations with the same block in this Transaction */
518 removecurrentoperation(block);
520 /* Add our new Operation to the Transaction */
522 if((o=AllocPooled(globals->transactionpool, length+sizeof(struct Operation)))!=0) {
523 globals->transactionpoolsize+=length+sizeof(struct Operation);
525 o->oi.blckno=block;
526 o->oi.length=length;
527 o->oi.bits=bits;
528 #ifdef CHECKCODE_TRANSACTIONS
529 o->magicvalue=0x4A4F;
530 #endif
531 o->new=globals->transactionnestcount;
533 CopyMem(data,o->oi.data,length);
535 linkoperation(o);
537 return(0);
540 return(ERROR_NO_FREE_STORE);
545 LONG addfreeoperation(BLCK block) {
546 struct Operation *o;
548 /* Adds an operation for the indicated block which indicates that
549 this block is no longer used, and thus when this transaction is
550 merged with the previous ones all operations for this block can
551 be removed. Any operations already present in this transaction
552 level which deal with the same block are discarded. */
554 removecurrentoperation(block);
556 if((o=AllocPooled(globals->transactionpool, sizeof(struct Operation)))!=0) {
557 globals->transactionpoolsize+=sizeof(struct Operation);
559 o->oi.blckno=block;
560 o->oi.length=0;
561 o->oi.bits=OI_DELETE;
562 #ifdef CHECKCODE_TRANSACTIONS
563 o->magicvalue=0x4A4F;
564 #endif
565 o->new=globals->transactionnestcount;
567 linkoperation(o);
569 return(0);
572 return(ERROR_NO_FREE_STORE);
577 WORD isthereanoperationfor(BLCK block) {
578 if(FindNode(block)==0) {
579 return(FALSE);
582 return(TRUE);
587 void newtransaction(void) {
588 /* Begins a new transaction. */
590 if(globals->transactionnestcount!=0) {
591 req_unusual("Transaction nest count wasn't zero!");
594 globals->transactionnestcount++;
596 _XDEBUG((DEBUG_TRANSACTION,"--NEW-----> poolsize = %ld\n",globals->transactionpoolsize));
601 void endtransaction(void) {
603 /* Call this to indicate that the entire transaction was succesfull.
605 CB_ORIGINAL -> leave alone.
606 CB_ORIGINAL|CB_LATEST -> leave alone.
607 CB_LATEST -> leave alone. */
609 _XDEBUG((DEBUG_TRANSACTION,"--END----->\n"));
611 combineoperations();
613 if(--globals->transactionnestcount==0) { /* Important! Combineoperations() must be called before lowering transactionnestcount */
615 /* Actions below only need to be done when transactionnestcount is zero */
617 /* We'll have to indicate that a flush is needed
618 and (re)set the timeout. */
620 starttimeout();
622 /* If the transaction buffer becomes a specific size, we might want
623 to enforce a flush. */
625 if(globals->transactionpoolsize>MAX_TRANSACTIONPOOLSIZE) {
626 _TDEBUG(("endtransaction: poolsize larger than 32 kB -> flushed transaction\n"));
627 flushtransaction();
634 void deletetransaction(void) {
635 struct Operation *o;
636 struct Operation *oprev;
638 /* Deletes everything in the transaction buffer which belongs to the
639 current transaction level. All CacheBuffers which are locked will
640 be re-read to ensure they are restored to what they were before
641 newtransaction() was called.
643 All CacheBuffers which had an operation on them in the to be deleted
644 transaction will be restored to what they were at the time of
645 newtransaction().
647 CB_ORIGINAL -> if there is no latest operation for this cb then turn into
648 CB_ORIGINAL|CB_LATEST.
650 CB_ORIGINAL|CB_LATEST -> leave alone.
652 CB_LATEST -> if there is an operation in this transaction for this Cachebuffer,
653 then reload with latest version. It doesn't matter if it is still
654 locked, because it is possible that there is no latest version which
655 means the block must become the original version again:
656 CB_LATEST -> CB_LATEST|CB_ORIGINAL.
658 CB_EMPTY|CB_LATEST -> if there is an operation in this transaction for this
659 Cachebuffer then restore to latest version, if present.
660 Otherwise clear it. */
662 _DEBUG(("-DEL------>\n"));
664 o=FirstNode();
666 while(o!=0) {
667 oprev=o;
668 o=NextNode(o);
670 if(oprev->new==globals->transactionnestcount) {
671 struct CacheBuffer *cb;
672 BLCK block=oprev->oi.blckno;
673 UBYTE bits=oprev->oi.bits;
675 removeoperation(oprev); // Must be placed before restorecachebuffer() call below.
677 if((bits & OI_DELETE)==0) { // Apr 3 1999: Added this if.
678 if((cb=findlatestcachebuffer(block))!=0) {
679 if((cb->bits & CB_ORIGINAL)!=0) {
680 dreq("deletetransaction: Fatal error - there is an operation for a cachebuffer which has not been modified!\nPlease notify the author!");
681 outputcachebuffer(cb);
684 /* Found a CB_LATEST cachebuffer which must be restored. */
686 restorecachebuffer(cb);
688 else if((cb=findoriginalcachebuffer(block))!=0) {
689 if((cb->bits & CB_LATEST)!=0) {
690 dreq("deletetransaction: Fatal error - there is an operation for a cachebuffer which hasn't been modified!\nPlease notify the author!");
691 outputcachebuffer(cb);
694 /* Found a CB_ORIGINAL cachebuffer which must be marked LATEST if there are
695 no operations for this cachebuffer anymore. */
697 if(isthereanoperationfor(cb->blckno)==FALSE) {
698 cb->bits|=CB_LATEST;
705 globals->transactionnestcount--;
710 LONG flusherror(LONG errorcode) {
711 if(errorcode==TDERR_WriteProt) {
712 return(req("Updating the volume failed because it is write protected!\n"\
713 "Please disable the write protection and press 'Retry'.\n"\
714 "Failing to do so may result in loss of data and may\n"\
715 "confuse programs which have just written to this disk.\n",
716 "Retry|Cancel"));
718 else if(errorcode==TDERR_DiskChanged) {
719 return(req("Updating the volume failed because no disk is inserted!\n"\
720 "Please insert the disk and press 'Retry'.\n"\
721 "Failing to do so may result in loss of data and may\n"\
722 "confuse programs which have just written to this disk.\n",
723 "Retry|Cancel"));
725 else {
726 ULONG freeblocks=-1;
728 getfreeblocks(&freeblocks);
730 return(req("Updating the volume failed due to an unknown error.\n"\
731 "The errorcode is %ld (free blocks = %ld).\n"\
732 "Please try and fix the problem and press 'Retry'.\n"\
733 "Failing to do so may result in loss of data and may\n"\
734 "confuse programs which have just written to this disk.\n",
735 "Retry|Cancel", errorcode, freeblocks));
738 return(0);
743 LONG writeoperations(void) {
744 struct CacheBuffer *cb;
745 struct Operation *o;
746 struct Operation *oprev;
747 LONG errorcode=0;
749 o=FirstNode();
751 while(o!=0) {
752 /* Luckily just 'reading' the most recent version of the cachebuffer
753 is enough to get the most recent version of the block we're looking
754 for. Writing it to disk is all we need to do. */
757 _DEBUG(("writeoperations: Writing block %ld\n",o->oi.blckno));
758 request(PROGRAMNAME " request","%s\n"\
759 "Writing block %ld\n",
760 "Ok",((UBYTE *)BADDR(devnode->dn_Name))+1, o->oi.blckno);
763 while((errorcode=readcachebuffer(&cb,o->oi.blckno))!=0 && flusherror(errorcode)==1) {
765 if(errorcode!=0) {
767 request(PROGRAMNAME " request","%s\n"\
768 "Debug requester 3 (errorcode = %ld)\n",
769 "Ok",((UBYTE *)BADDR(devnode->dn_Name))+1, errorcode);
771 break;
774 while((errorcode=writecachebuffer(cb))!=0 && flusherror(errorcode)==1) {
776 if(errorcode!=0) {
778 request(PROGRAMNAME " request","%s\n"\
779 "Debug requester 4 (errorcode = %ld)\n",
780 "Ok",((UBYTE *)BADDR(devnode->dn_Name))+1, errorcode);
782 break;
785 emptyoriginalcachebuffer(cb->blckno);
786 cb->bits&=~CB_EMPTY;
787 cb->bits|=CB_ORIGINAL|CB_LATEST;
789 oprev=o;
790 o=NextNode(o);
791 removeoperation(oprev);
794 #ifdef CHECKCODE
796 struct CacheBuffer *cb;
798 cb=(struct CacheBuffer *)globals->cblrulist.mlh_Head;
800 while(cb->node.mln_Succ!=0) {
801 if((cb->bits & (CB_ORIGINAL|CB_LATEST))==CB_ORIGINAL) {
802 dreq("writeoperations: Fatal error!\nPlease notify the author!\n");
803 outputcachebuffer(cb);
806 cb=(struct CacheBuffer *)(cb->node.mln_Succ);
809 #endif
811 return(errorcode);
816 LONG removetransactionfailure(void) {
817 struct CacheBuffer *cb;
818 LONG errorcode;
820 while((errorcode=flushiocache())!=0 && flusherror(errorcode)==1) { /* This commits any dirty data. */
823 if(errorcode==0) {
824 if((cb=getcachebuffer())!=0) {
825 struct fsBlockHeader *bh=cb->data;
827 clearcachebuffer(cb);
829 bh->id=TRANSACTIONOK_ID;
830 bh->be_ownblock=L2BE(globals->block_root+2);
832 cb->blckno=globals->block_root+2;
835 request(PROGRAMNAME " request","%s\n"\
836 "Debug requester 2\n",
837 "Ok",((UBYTE *)BADDR(devnode->dn_Name))+1);
840 setchecksum(cb);
841 while((errorcode=writecachebuffer(cb))!=0 && flusherror(errorcode)==1) {
844 if(errorcode==0) {
845 while((errorcode=flushiocache())!=0 && flusherror(errorcode)==1) { /* This commits any dirty data. */
849 else {
850 errorcode=ERROR_NO_FREE_STORE;
854 return(errorcode);
859 BOOL hastransaction(void) {
861 /* This function returns TRUE if there is an uncommited transaction. */
863 return((BOOL)(ROOT!=&SENTINEL));
868 LONG flushtransaction(void) {
869 struct CacheBuffer *cb;
870 BLCK firsttransactionblock;
871 LONG errorcode;
873 _XDEBUG((DEBUG_TRANSACTION,"flushtransaction: Entry\n"));
875 while((errorcode=flushiocache())!=0 && flusherror(errorcode)==1) { /* This commits any dirty data. */
878 if(errorcode==0 && hastransaction()) {
880 _DEBUG(("flushtransaction: There is a transaction\n"));
882 while((errorcode=savetransaction(&firsttransactionblock))!=0 && flusherror(errorcode)==1) {
885 if(errorcode==0) {
887 _XDEBUG((DEBUG_TRANSACTION,"flushtransaction: Saved transaction\n"));
889 while((errorcode=flushiocache())!=0 && flusherror(errorcode)==1) { /* This commits any dirty data. */
892 _XDEBUG((DEBUG_TRANSACTION,"flushtransaction: Disk updated\n"));
894 if(errorcode==0) {
895 if((cb=getcachebuffer())!=0) {
896 struct fsTransactionFailure *tf=cb->data;
898 clearcachebuffer(cb);
900 tf->bheader.id=TRANSACTIONFAILURE_ID;
901 tf->bheader.be_ownblock=L2BE(globals->block_root+2);
903 tf->be_firsttransaction=L2BE(firsttransactionblock);
905 cb->blckno=globals->block_root+2;
907 setchecksum(cb);
908 while((errorcode=writecachebuffer(cb))!=0 && flusherror(errorcode)==1) {
911 _XDEBUG((DEBUG_TRANSACTION,"flushtransaction: Set TransactionFailure block\n"));
913 if(errorcode==0) {
915 while((errorcode=flushiocache())!=0 && flusherror(errorcode)==1) { /* This commits any dirty data. */
918 _XDEBUG((DEBUG_TRANSACTION,"flushtransaction: Disk updated (2)\n"));
920 if(errorcode==0) {
921 if((errorcode=writeoperations())==0) { /* writeoperations() */
923 _XDEBUG((DEBUG_TRANSACTION,"flushtransaction: Updated all blocks\n"));
925 if((errorcode=removetransactionfailure())==0) {
926 stoptimeout();
932 else {
933 errorcode=ERROR_NO_FREE_STORE;
939 if(errorcode!=0) {
940 /* Okay, we've warned the user quite a few times -- now it's too late. */
942 req("Unable to flush all buffers correctly (errorcode = %ld).\n"\
943 "A reboot is the recommended course of action to prevent\n"\
944 "any (further) loss of data.\n", "Ok", errorcode);
946 /**** More action needs to be taken here! Maybe even a retry option. */
950 _DEBUG(("flushtransaction: Done. errorcode = %ld\n",errorcode));
952 return(errorcode);
958 struct Operation *getlatestoperation2(BLCK block, struct Operation *o) {
960 if((o=FindNodeFrom(o,block))!=0) {
961 struct Operation *o2;
962 struct Operation *o3;
964 o2=getlatestoperation2(block, o->left);
965 o3=getlatestoperation2(block, o->right);
967 if(o2!=0 && o2->new > o->new) {
968 o=o2;
971 if(o3!=0 && o3->new > o->new) {
972 o=o3;
976 return(o);
981 struct Operation *getlatestoperation(BLCK block) {
982 struct Operation *o;
984 /* Returns the latest operation (not necessarily the current operation)
985 for a block. Zero indicates there were no operations for this block. */
987 _XDEBUG((DEBUG_CACHEBUFFER,"getlatestoperation: Entry for block %ld\n",block));
989 o=getlatestoperation2(block, ROOT);
991 _XDEBUG((DEBUG_CACHEBUFFER,"getlatestoperation: Exit\n"));
993 return(o);
998 void restorecachebuffer(struct CacheBuffer *cb) {
999 struct Operation *o;
1001 /* Restore the CacheBuffer to its latest version if present.
1002 If there is no latest version then clear it if it is of type
1003 CB_EMPTY, or make it the original if not. */
1005 /* Be careful when using this function when you're walking
1006 the list of CacheBuffers. emptycachebuffer() can be called
1007 in this function, which means you must make sure you already
1008 know what the next cachebuffer is going to be before calling
1009 this function. */
1011 #ifdef CHECKCODE
1012 if((cb->bits & (CB_ORIGINAL|CB_LATEST))!=CB_LATEST) {
1013 dreq("restorecachebuffer: Fatal error - cachebuffer is of wrong type!\nPlease notify the author!");
1014 outputcachebuffer(cb);
1016 #endif
1018 _XDEBUG((DEBUG_CACHEBUFFER,"restorecachebuffer: Entry. cb->blckno = %ld, cb->bits = %ld\n",cb->blckno, (ULONG)cb->bits));
1020 if((o=getlatestoperation(cb->blckno))!=0 && ((o->oi.bits & OI_EMPTY)!=0 || (cb->bits & CB_EMPTY)==0)) {
1021 if((o->oi.bits & OI_EMPTY)==0) {
1022 struct CacheBuffer *cb_org;
1024 if((cb_org=findoriginalcachebuffer(cb->blckno))==0) {
1025 dreq("restorecachebuffer: Fatal error - couldn't find original cachebuffer!\nPlease notify the author!");
1026 outputcachebuffer(cb);
1029 CopyMemQuick(cb_org->data, cb->data, globals->bytes_block);
1031 uncompress(cb->data, o->oi.data, o->oi.length);
1033 else if((cb->bits & CB_EMPTY)!=0) {
1034 clearcachebuffer(cb);
1036 else {
1037 struct CacheBuffer *cb_org;
1039 if((cb_org=findoriginalcachebuffer(cb->blckno))==0) {
1040 dreq("restorecachebuffer: Fatal error - can't find original cachebuffer!\nPlease notify the author!");
1041 outputcachebuffer(cb);
1044 CopyMemQuick(cb_org->data, cb->data, globals->bytes_block);
1045 resetcachebuffer(cb_org);
1047 cb->bits|=CB_ORIGINAL|CB_LATEST;
1053 #if 0
1054 void restorecachebuffer(struct CacheBuffer *cb) {
1055 struct Operation *o;
1057 /* cb is converted to the latest version of the cachebuffer or if
1058 there's no latest, to the original version. */
1060 if((o=getlatestoperation(cb->blckno))!=0) {
1061 if((o->oi.bits & OI_EMPTY)==0) {
1062 struct CacheBuffer *cb_org;
1063 LONG errorcode;
1065 if((cb_org=findoriginalcachebuffer(cb->blckno))==0) {
1066 dreq("restorecachebuffer: Fatal error - couldn't find original cachebuffer!\nPlease notify the author!");
1069 CopyMemQuick(cb_org->data,cb->data,bytes_block);
1071 uncompress(cb->data,o->oi.data,o->oi.length);
1073 cb->bits&=~(CB_ORIGINAL);
1074 cb->bits|=CB_LATEST;
1076 else if((cb->bits & CB_EMPTY)!=0) {
1077 clearcachebuffer(cb);
1079 else {
1080 struct CacheBuffer *cb_org;
1082 if((cb_org=findoriginalcachebuffer(cb->blckno))==0) {
1083 dreq("restorecachebuffer: Fatal error - couldn't find original cachebuffer!\nPlease notify the author!");
1086 CopyMemQuick(cb_org->data,cb->data,bytes_block);
1087 emptycachebuffer(cb_org);
1089 cb->bits&=~(CB_LATEST);
1090 cb->bits|=CB_ORIGINAL;
1093 #endif
1098 LONG applyoperation(BLCK block, struct CacheBuffer **returned_cb) {
1099 struct Operation *o;
1101 /* This function either creates the latest version of a block (by
1102 reading the original block if needed) or, if there is no latest
1103 version, returns the original block itself.
1105 If no error occurs, then returned_cb is filled with a CacheBuffer
1106 holding the latest version of a block (undefined otherwise). */
1108 if((o=getlatestoperation(block))!=0) {
1109 struct CacheBuffer *cb;
1111 globals->statistics.cache_operationdecode++;
1113 _XDEBUG((DEBUG_CACHEBUFFER,"applyoperation: Entry. block = %ld -> found an operation!\n",block));
1115 if((o->oi.bits & OI_EMPTY)!=0) {
1116 globals->statistics.cache_emptyoperationdecode++;
1118 cb=createnewcachebuffer(block);
1120 else {
1121 LONG errorcode;
1123 if((errorcode=readoriginalcachebuffer(&cb, block))!=0) {
1124 return(errorcode);
1127 saveoriginalcachebuffer(cb);
1129 uncompress(cb->data, o->oi.data, o->oi.length);
1131 *returned_cb=cb;
1133 else {
1134 LONG errorcode;
1136 _XDEBUG((DEBUG_CACHEBUFFER,"applyoperation: Entry. block = %ld -> returning original!\n",block));
1138 if((errorcode=readoriginalcachebuffer(returned_cb, block))!=0) {
1139 return(errorcode);
1143 return(0);
1148 ULONG transactionspace(void) {
1149 UWORD space=(globals->bytes_block-sizeof(struct fsTransactionStorage));
1151 /* Returns the space needed in blocks which would be needed to
1152 store all transaction information. */
1154 /* (X + 512 + 8 + 496 - 1) / 496 = 2.04 */
1156 // return((transactionpoolsize+bytes_block+(bytes_block>>6)+space-1)/space);
1158 /* (X + 496-1) / 496 = 0 */
1160 return((globals->transactionpoolsize + space-1)/space);
1166 things to do after an endtransaction()
1167 --------------------------------------
1168 CB_ORIGINAL -> leave alone.
1169 CB_ORIGINAL|CB_LATEST -> leave alone.
1170 CB_LATEST -> leave alone.
1171 CB_EMPTY|CB_LATEST -> leave alone.
1172 + clear prepared bit for this transaction level.
1174 things to do after an deletetransaction()
1175 -----------------------------------------
1176 CB_ORIGINAL -> if there is no latest operation for this cb then turn into CB_ORIGINAL|CB_LATEST.
1177 CB_ORIGINAL|CB_LATEST -> leave alone.
1178 CB_LATEST -> if prepared in this transaction then reload with latest version.
1179 CB_EMPTY|CB_LATEST -> if prepared in this transaction then restore to latest
1180 version, if present. Otherwise clear it.
1181 + clear prepared bit for this transaction level.
1184 things to do after an flushtransaction()
1185 ----------------------------------------
1186 CB_ORIGINAL -> empty.
1187 CB_ORIGINAL|CB_LATEST -> leave alone.
1188 CB_LATEST -> turn into CB_ORIGINAL|CB_LATEST.
1189 CB_EMPTY|CB_LATEST -> turn into CB_ORIGINAL|CB_LATEST.
1190 (all prepared bits should be cleared at this point).
1192 Rules
1193 -----
1195 prepare-, store-, dump- and newcachebuffer may only be
1196 called during a transaction.
1198 dumpcachebuffer or storecachebuffer MUST be called for every
1199 time a preparecachebuffer or newcachebuffer was called.
1200 Also you must make sure that all this occurs in the same
1201 transaction level.
1203 lockcachebuffer & unlockcachebuffer may be called at any
1204 time. The only rule is that you must call unlockcachebuffer
1205 for every lockcachebuffer.
1208 Operations
1209 ----------
1211 There are basicly 3 types of operations:
1213 1) Modification of existing blocks (bits = 0).
1214 2) Creation of a new block (bits = OI_EMPTY)
1215 3) Deletion of a existing block (bits = OI_DELETED)
1217 EndTransaction:
1219 Operations of type 1&2 are simply merged with existing
1220 operations in previous transactions. Operations of type 3
1221 indicate that all previous operations on that same block are
1222 no longer required and can be deleted.
1224 DeleteTransaction:
1226 All operations of the current transaction nest level are
1227 simply removed.
1230 CacheBuffer rules
1231 -----------------
1233 There rules are in effect at all times, and when
1234 adding/deleting cachebuffers or operations you must make
1235 sure that these rules don't break.
1237 - There is at maximum one CB_ORIGINAL cachebuffer of any
1238 block.
1240 - There is at maximum one CB_LATEST cachebuffer of any block.
1242 - CB_ORIGINAL and CB_LATEST can be set for the same buffer.
1243 In that case the block was never modified and is exactly as
1244 it is (and will be) on disk. Is it now allowed that there
1245 are operations for such a block.
1247 - If there is a cachebuffer with only CB_LATEST set, then the
1248 respective cachebuffer with CB_ORIGINAL set MUST be present
1249 in the cache as well.
1251 - If there is a cachebuffer with either CB_ORIGINAL or
1252 CB_LATEST set (not both!), then there MUST be an operation
1253 present for that block. The presence of an operation for a
1254 block however does not mean a cachebuffer of that block has
1255 to be present. Note that the presence of a cachebuffer with
1256 only CB_LATEST set means there must be one with CB_ORIGINAL
1257 set as well (see the other rules).
1259 There rules have a very important purpose. For one thing
1260 they ensure that certain key operations can never fail. For
1261 example, when a diff is needed between a original and latest
1262 version of a cachebuffer, then the original MUST be present
1263 in the cache. If we would have to read the original first,
1264 that would be a potential source of errors (read error, out
1265 of memory error) which is undesireable at some critical
1266 points.