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"
14 #include "cachedio_protos.h"
15 #include "req_protos.h"
16 #include "support_protos.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
);
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");
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
) {
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
94 removecurrentoperation2(blck
, ROOT
);
99 LONG
inittransactions() {
103 #ifdef CHECKCODE_TRANSACTIONS
104 operationsentinel
.magicvalue
=0x4a4f;
107 poolsize
=globals
->bytes_block
*4;
112 if((globals
->compressbuffer
=AllocMem(globals
->bytes_block
+(globals
->bytes_block
>>4),1))!=0) {
113 if((globals
->transactionpool
=CreatePool(0,poolsize
,poolsize
>>1))!=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
);
140 UBYTE
*dest
=ts
->data
;
145 *src
=(UBYTE
*)&(*o
)->oi
;
146 *length
=((*o
)->oi
.length
| 1)+sizeof(struct OperationInformation
)-1;
156 if(copylength
>bytesleft
) {
157 copylength
=bytesleft
;
160 CopyMem(*src
,dest
,copylength
);
166 bytesleft
-=copylength
;
169 if(*length
==0 && *o
==0) {
179 LONG
savetransaction(BLCK
*firsttransactionblock
) {
180 struct CacheBuffer
*cb
;
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
;
197 if((errorcode
=findspace(1, globals
->block_rovingblockptr
, globals
->block_rovingblockptr
, &blckno
))==0) {
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. */
208 _XDEBUG((DEBUG_TRANSACTION
,"savetransaction: Storing operations of transaction in block %ld\n",blckno
));
212 clearcachebuffer(cb
);
214 if(*firsttransactionblock
==0) {
215 *firsttransactionblock
=blckno
;
219 ts
->bheader
.id
=TRANSACTIONSTORAGE_ID
;
220 ts
->bheader
.be_ownblock
=L2BE(blckno
);
222 done
=fillwithoperations(ts
,&o
,&src
,&length
);
225 if(startblock
==globals
->block_rovingblockptr
) {
226 errorcode
=ERROR_DISK_FULL
;
230 if((errorcode
=findspace(1, startblock
, globals
->block_rovingblockptr
, &blckno
))!=0) {
234 ts
->be_next
=L2BE(blckno
);
238 if((errorcode
=writecachebuffer(cb
))!=0) {
242 } while(done
==FALSE
);
245 unlockcachebuffer(cb
);
248 errorcode
=ERROR_NO_FREE_STORE
;
251 _XDEBUG((DEBUG_TRANSACTION
,"savetransaction: Exiting with errorcode = %ld\n",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;
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
);
277 while(bytesleft
!=0) {
279 UWORD copylength
=length
;
281 if(copylength
>bytesleft
) {
282 copylength
=bytesleft
;
285 CopyMem(src
,dest
,copylength
);
290 bytesleft
-=copylength
;
293 UWORD len
=*((UWORD
*)src
);
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) {
325 LONG
checkfortransaction(void) {
326 struct CacheBuffer
*cb
;
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");
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");
370 return(INTERR_CHECKSUM_FAILURE
);
380 static void combineoperations(void) {
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
;
397 UBYTE tnc
=globals
->transactionnestcount
-1;
399 #ifdef CHECKCODE_TRANSACTIONS
400 cop(o
, "combineoperations 1");
416 if(oold
!=0 && onew
!=0) {
417 #ifdef CHECKCODE_TRANSACTIONS
418 cop(oold
, "combineoperations 2");
419 cop(onew
, "combineoperations 3");
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
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
437 if((cb
=findlatestcachebuffer(onew
->oi
.blckno
))!=0) {
438 emptycachebuffer(cb
);
441 if((cb
=findoriginalcachebuffer(onew
->oi
.blckno
))!=0) {
445 removeoperation(onew
);
448 removeoperation(oold
);
458 if(block
!=onext
->oi
.blckno
) {
470 LONG
addoperation2(struct CacheBuffer
*cb_org
, struct CacheBuffer
*cb_new
) {
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) {
484 transactionpoolsize
+=bytes_block
+sizeof(struct Operation
);
487 compress(cb_org
->data
,cb_new
->data
,o
->oi
.data
);
490 compressfromzero(cb_new
->data
,o
->oi
.data
);
493 if((cb_new
->bits
& CB_EMPTY
)!=0) {
497 o
->oi
.blckno
=cb_new
->blckno
;
498 o
->oi
.length
=bytes_block
;
500 o
->new=transactionnestcount
;
507 return(ERROR_NO_FREE_STORE
);
513 LONG
addoperation(BLCK block
,UBYTE
*data
,UWORD length
,UBYTE bits
) {
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
);
528 #ifdef CHECKCODE_TRANSACTIONS
529 o
->magicvalue
=0x4A4F;
531 o
->new=globals
->transactionnestcount
;
533 CopyMem(data
,o
->oi
.data
,length
);
540 return(ERROR_NO_FREE_STORE
);
545 LONG
addfreeoperation(BLCK block
) {
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
);
561 o
->oi
.bits
=OI_DELETE
;
562 #ifdef CHECKCODE_TRANSACTIONS
563 o
->magicvalue
=0x4A4F;
565 o
->new=globals
->transactionnestcount
;
572 return(ERROR_NO_FREE_STORE
);
577 WORD
isthereanoperationfor(BLCK block
) {
578 if(FindNode(block
)==0) {
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"));
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. */
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"));
634 void deletetransaction(void) {
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
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"));
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
) {
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",
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",
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
));
743 LONG
writeoperations(void) {
744 struct CacheBuffer
*cb
;
746 struct Operation
*oprev
;
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) {
767 request(PROGRAMNAME " request","%s\n"\
768 "Debug requester 3 (errorcode = %ld)\n",
769 "Ok",((UBYTE *)BADDR(devnode->dn_Name))+1, errorcode);
774 while((errorcode
=writecachebuffer(cb
))!=0 && flusherror(errorcode
)==1) {
778 request(PROGRAMNAME " request","%s\n"\
779 "Debug requester 4 (errorcode = %ld)\n",
780 "Ok",((UBYTE *)BADDR(devnode->dn_Name))+1, errorcode);
785 emptyoriginalcachebuffer(cb
->blckno
);
787 cb
->bits
|=CB_ORIGINAL
|CB_LATEST
;
791 removeoperation(oprev
);
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
);
816 LONG
removetransactionfailure(void) {
817 struct CacheBuffer
*cb
;
820 while((errorcode
=flushiocache())!=0 && flusherror(errorcode
)==1) { /* This commits any dirty data. */
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);
841 while((errorcode
=writecachebuffer(cb
))!=0 && flusherror(errorcode
)==1) {
845 while((errorcode
=flushiocache())!=0 && flusherror(errorcode
)==1) { /* This commits any dirty data. */
850 errorcode
=ERROR_NO_FREE_STORE
;
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
;
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) {
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"));
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;
908 while((errorcode
=writecachebuffer(cb
))!=0 && flusherror(errorcode
)==1) {
911 _XDEBUG((DEBUG_TRANSACTION
,"flushtransaction: Set TransactionFailure block\n"));
915 while((errorcode
=flushiocache())!=0 && flusherror(errorcode
)==1) { /* This commits any dirty data. */
918 _XDEBUG((DEBUG_TRANSACTION
,"flushtransaction: Disk updated (2)\n"));
921 if((errorcode
=writeoperations())==0) { /* writeoperations() */
923 _XDEBUG((DEBUG_TRANSACTION
,"flushtransaction: Updated all blocks\n"));
925 if((errorcode
=removetransactionfailure())==0) {
933 errorcode
=ERROR_NO_FREE_STORE
;
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
));
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) {
971 if(o3
!=0 && o3
->new > o
->new) {
981 struct Operation
*getlatestoperation(BLCK block
) {
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"));
998 void restorecachebuffer(struct CacheBuffer
*cb
) {
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
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
);
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
);
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
;
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
;
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
);
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
;
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
);
1123 if((errorcode
=readoriginalcachebuffer(&cb
, block
))!=0) {
1127 saveoriginalcachebuffer(cb
);
1129 uncompress(cb
->data
, o
->oi
.data
, o
->oi
.length
);
1136 _XDEBUG((DEBUG_CACHEBUFFER
,"applyoperation: Entry. block = %ld -> returning original!\n",block
));
1138 if((errorcode
=readoriginalcachebuffer(returned_cb
, block
))!=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).
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
1203 lockcachebuffer & unlockcachebuffer may be called at any
1204 time. The only rule is that you must call unlockcachebuffer
1205 for every lockcachebuffer.
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)
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.
1226 All operations of the current transaction nest level are
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
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