mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 05:23:03 +01:00
Mostly comments needed after fix for CORE-5041, also added some checks to be safer with repeated I/O
This commit is contained in:
parent
d5e5d40b15
commit
8d436826c6
@ -252,6 +252,7 @@ namespace Jrd {
|
||||
if (!LCK_convert(tdbb, stateLock, CRYPT_NORMAL,
|
||||
(flags & CRYPT_HDR_NOWAIT) ? LCK_NO_WAIT : LCK_WAIT))
|
||||
{
|
||||
// Failed to take state lock - swith to slow IO mode
|
||||
slowIO = LCK_read_data(tdbb, stateLock);
|
||||
fb_assert(slowIO);
|
||||
}
|
||||
@ -673,42 +674,48 @@ namespace Jrd {
|
||||
// Therefore use old (status vector based) method
|
||||
try
|
||||
{
|
||||
if (slowIO)
|
||||
if (!slowIO)
|
||||
{
|
||||
BarSync::LockGuard lockGuard(tdbb, sync);
|
||||
for (SINT64 previous = slowIO; ; previous = slowIO)
|
||||
// Normal case (almost always get here)
|
||||
// Take shared lock on crypto manager and read data
|
||||
BarSync::IoGuard ioGuard(tdbb, sync);
|
||||
if (!slowIO)
|
||||
return internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace) == SUCCESS_ALL;
|
||||
}
|
||||
|
||||
// Slow IO - we need exclusive lock on crypto manager.
|
||||
// That may happen only when another process changed DB encyption.
|
||||
BarSync::LockGuard lockGuard(tdbb, sync);
|
||||
for (SINT64 previous = slowIO; ; previous = slowIO)
|
||||
{
|
||||
switch (internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace))
|
||||
{
|
||||
switch (internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace))
|
||||
{
|
||||
case SUCCESS_ALL:
|
||||
if (slowIO)
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
return true;
|
||||
case SUCCESS_ALL:
|
||||
if (!slowIO) // if we took a lock last time
|
||||
return true; // nothing else left to do - IO complete
|
||||
|
||||
case FAILED_IO:
|
||||
return false;
|
||||
// An attempt to take a lock, if it fails
|
||||
// we get fresh data from lock needed to validate state of encryption.
|
||||
// Notice - if lock was taken that's also a kind of state
|
||||
// change and first time we must proceed with one more read.
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
if (slowIO == previous) // if crypt state did not change
|
||||
return true; // we successfully completed IO
|
||||
break;
|
||||
|
||||
case FAILED_CRYPT:
|
||||
if (slowIO)
|
||||
{
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
if (slowIO == previous)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
case FAILED_IO:
|
||||
return false; // not related with crypto manager error
|
||||
|
||||
case FAILED_CRYPT:
|
||||
if (!slowIO) // if we took a lock last time
|
||||
return false; // we can't recover from error here
|
||||
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
if (slowIO == previous) // if crypt state did not change
|
||||
return false; // we can't recover from error here
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BarSync::IoGuard ioGuard(tdbb, sync);
|
||||
if (internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace) != SUCCESS_ALL)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
@ -768,48 +775,43 @@ namespace Jrd {
|
||||
// Therefore use old (status vector based) method
|
||||
try
|
||||
{
|
||||
if (slowIO)
|
||||
if (!slowIO)
|
||||
{
|
||||
BarSync::LockGuard lockGuard(tdbb, sync);
|
||||
for (SINT64 previous = slowIO; ; previous = slowIO)
|
||||
{
|
||||
switch (internalWrite(tdbb, sv, file, bdb, page))
|
||||
{
|
||||
case SUCCESS_ALL:
|
||||
if (slowIO)
|
||||
{
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
if (slowIO == previous)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
// Normal case (almost always get here)
|
||||
// Take shared lock on crypto manager and write data
|
||||
BarSync::IoGuard ioGuard(tdbb, sync);
|
||||
if (!slowIO)
|
||||
return internalWrite(tdbb, sv, file, bdb, page) == SUCCESS_ALL;
|
||||
}
|
||||
|
||||
case FAILED_IO:
|
||||
// Have to use slow method - see full comments in read() function
|
||||
BarSync::LockGuard lockGuard(tdbb, sync);
|
||||
for (SINT64 previous = slowIO; ; previous = slowIO)
|
||||
{
|
||||
switch (internalWrite(tdbb, sv, file, bdb, page))
|
||||
{
|
||||
case SUCCESS_ALL:
|
||||
if (!slowIO)
|
||||
return true;
|
||||
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
if (slowIO == previous)
|
||||
return true;
|
||||
break;
|
||||
|
||||
case FAILED_IO:
|
||||
return false;
|
||||
|
||||
case FAILED_CRYPT:
|
||||
if (!slowIO)
|
||||
return false;
|
||||
|
||||
case FAILED_CRYPT:
|
||||
if (slowIO)
|
||||
{
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
if (slowIO == previous)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
if (slowIO == previous)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BarSync::IoGuard ioGuard(tdbb, sync);
|
||||
if (internalWrite(tdbb, sv, file, bdb, page) != SUCCESS_ALL)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
|
@ -59,6 +59,20 @@ class thread_db;
|
||||
class Lock;
|
||||
class PageSpace;
|
||||
|
||||
|
||||
//
|
||||
// This very specific locking class makes it possible to perform traditional read/write locks,
|
||||
// but in addition it can on special request perform some predefined action or (in a case when
|
||||
// >=1 read lock is taken) set barrier for new locks (new locks will not be granted) and
|
||||
// at the moment when last existing lock is released execute that predefined action in context
|
||||
// of a thread, releasing last lock.
|
||||
//
|
||||
// In our case special request is done from AST handler - and therefore called ast.
|
||||
// Read locks are done when performing IO+crypt activity - and called ioBegin/ioEnd.
|
||||
// Write locks are done when some exclusive activity like changing crypt state is
|
||||
// needed - they are full locks and called lockBegin/lockEnd.
|
||||
//
|
||||
|
||||
class BarSync
|
||||
{
|
||||
public:
|
||||
@ -349,6 +363,28 @@ private:
|
||||
Database& dbb;
|
||||
Lock* stateLock;
|
||||
Lock* threadLock;
|
||||
|
||||
// This counter works only in a case when database encryption is changed.
|
||||
// Traditional processing of AST can not be used for crypto manager.
|
||||
// The problem is with taking state lock after AST.
|
||||
// That should be done before next IO operation to guarantee database
|
||||
// consistency, but IO operation may be requested from another AST
|
||||
// (database cache blocking) when 'wait for a lock' operations are
|
||||
// prohibited. I.e. we can't proceed with normal IO without a lock
|
||||
// but at the same time can't take it.
|
||||
|
||||
// The solution is to use crypt versions counter in a lock (incremented
|
||||
// by one each time one issues ALTER DATABASE ENCRYPT/DECRYPT), read
|
||||
// it when lock can't be taken, store in slowIO variable, perform IO
|
||||
// and compare stored value with current lock data. In case when values
|
||||
// differ encryption status of database was changed during IO operation
|
||||
// and such operation should be repeated. When data in lock does not
|
||||
// change during IO that means that crypt rules remained the same even
|
||||
// without state lock taken by us and therefore result of IO operation
|
||||
// is correct. As soon as non-waiting attempt to take state lock succeeds
|
||||
// slowIO mode is off (slowIO counter becomes zero) and we return to
|
||||
// normal operation.
|
||||
|
||||
SINT64 slowIO;
|
||||
bool crypt, process, down;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user