mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Update the SECURE_DELETE code to track the latest changes in the pager. (CVS 5928)
FossilOrigin-Name: e058f509374e98e48eafeba2f1dadff9633d1190
This commit is contained in:
18
manifest
18
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Fix\ssome\scompiler\swarnings\sthat\sshow\sup\swhen\sbuilding\sthe\samalgamation\sonly.\s(CVS\s5927)
|
C Update\sthe\sSECURE_DELETE\scode\sto\strack\sthe\slatest\schanges\sin\sthe\spager.\s(CVS\s5928)
|
||||||
D 2008-11-19T16:52:44
|
D 2008-11-19T18:30:29
|
||||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||||
F Makefile.in 0aa7bbe3be6acc4045706e3bb3fd0b8f38f4a3b5
|
F Makefile.in 0aa7bbe3be6acc4045706e3bb3fd0b8f38f4a3b5
|
||||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||||
@@ -98,7 +98,7 @@ F src/alter.c 0a00c184052b7fd20271e783f65fec4efec6fe30
|
|||||||
F src/analyze.c 301a1358f4f4d84d96608927d8ddcf471c53ad9a
|
F src/analyze.c 301a1358f4f4d84d96608927d8ddcf471c53ad9a
|
||||||
F src/attach.c 85c6a3d0daf11965b47604190d7cf5597dc88382
|
F src/attach.c 85c6a3d0daf11965b47604190d7cf5597dc88382
|
||||||
F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
|
F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
|
||||||
F src/bitvec.c 9e922b2577b7e46d8f95349bca6a52f7674d7582
|
F src/bitvec.c 4300d311b17fb3c1476623fd895a8feac02a0b08
|
||||||
F src/btmutex.c 63c5cc4ad5715690767ffcb741e185d7bc35ec1a
|
F src/btmutex.c 63c5cc4ad5715690767ffcb741e185d7bc35ec1a
|
||||||
F src/btree.c 76c1b09263b6f028b4f84bd6d811e9d017448216
|
F src/btree.c 76c1b09263b6f028b4f84bd6d811e9d017448216
|
||||||
F src/btree.h 179c3ea813780df78a289a8f5130db18e6d4616e
|
F src/btree.h 179c3ea813780df78a289a8f5130db18e6d4616e
|
||||||
@@ -139,7 +139,7 @@ F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60
|
|||||||
F src/os_os2.c d12285d66df674c42f6f544a6f7c21bf1a954ee1
|
F src/os_os2.c d12285d66df674c42f6f544a6f7c21bf1a954ee1
|
||||||
F src/os_unix.c 03c76b5269361adcd68cf9d6713181922535ac6e
|
F src/os_unix.c 03c76b5269361adcd68cf9d6713181922535ac6e
|
||||||
F src/os_win.c 08d0b059ac01f32e8813bb96fc573486592b83f5
|
F src/os_win.c 08d0b059ac01f32e8813bb96fc573486592b83f5
|
||||||
F src/pager.c ae5da38b6415bdd88e56a2a484f29282c4ea09f5
|
F src/pager.c db12a8333e54e7bbf62dc621ada5507adb3a6493
|
||||||
F src/pager.h a02ef8e6cc7e78b54874166e5ce786c9d4c489bf
|
F src/pager.h a02ef8e6cc7e78b54874166e5ce786c9d4c489bf
|
||||||
F src/parse.y 2c4758b4c5ead6de8cf7112f5a7cce7561d313fe
|
F src/parse.y 2c4758b4c5ead6de8cf7112f5a7cce7561d313fe
|
||||||
F src/pcache.c f3121a531745b20f5b824201eb63949a7e2959ac
|
F src/pcache.c f3121a531745b20f5b824201eb63949a7e2959ac
|
||||||
@@ -233,7 +233,7 @@ F test/bigfile.test 9a6a8346e4042d9c781ed6cb6553ac871ae30618
|
|||||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||||
F test/bind.test 1134441f1ea47abd1c740090435a6ecbe9ceb263
|
F test/bind.test 1134441f1ea47abd1c740090435a6ecbe9ceb263
|
||||||
F test/bindxfer.test d4f573750e06c34ef2309acb95ad57da1d3c983f
|
F test/bindxfer.test d4f573750e06c34ef2309acb95ad57da1d3c983f
|
||||||
F test/bitvec.test 62a512c3f7041d1df12558eb25990e5a19820571
|
F test/bitvec.test ecea9aa315f36991e56e326701279b7775cb2bef
|
||||||
F test/blob.test 2a38d867bdf08f9ce081776acec1ac8d4bca66be
|
F test/blob.test 2a38d867bdf08f9ce081776acec1ac8d4bca66be
|
||||||
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
|
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
|
||||||
F test/cache.test 3ff445c445742a7b6b9ba6e1d62a25263f9424b9
|
F test/cache.test 3ff445c445742a7b6b9ba6e1d62a25263f9424b9
|
||||||
@@ -659,7 +659,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
|||||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||||
P 70b2f6839ca97dfc08f72875283f5c75c8fcf0cc
|
P d1abe8a1c9a990b02c71d6c249436381c9fde443
|
||||||
R a6290aee52476a40993da5bd0918f0b1
|
R 28c8816dfcd8d3fc398d31f18c45452f
|
||||||
U danielk1977
|
U drh
|
||||||
Z dc69f77ff700b57401f380f9e520f0dd
|
Z e8976cc9cb4c6c0e128dc1d970073d42
|
||||||
|
@@ -1 +1 @@
|
|||||||
d1abe8a1c9a990b02c71d6c249436381c9fde443
|
e058f509374e98e48eafeba2f1dadff9633d1190
|
146
src/bitvec.c
146
src/bitvec.c
@@ -34,21 +34,42 @@
|
|||||||
** start of a transaction, and is thus usually less than a few thousand,
|
** start of a transaction, and is thus usually less than a few thousand,
|
||||||
** but can be as large as 2 billion for a really big database.
|
** but can be as large as 2 billion for a really big database.
|
||||||
**
|
**
|
||||||
** @(#) $Id: bitvec.c,v 1.8 2008/11/11 15:48:48 drh Exp $
|
** @(#) $Id: bitvec.c,v 1.9 2008/11/19 18:30:35 shane Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
|
/* Size of the Bitvec structure in bytes. */
|
||||||
#define BITVEC_SZ 512
|
#define BITVEC_SZ 512
|
||||||
|
|
||||||
/* Round the union size down to the nearest pointer boundary, since that's how
|
/* Round the union size down to the nearest pointer boundary, since that's how
|
||||||
** it will be aligned within the Bitvec struct. */
|
** it will be aligned within the Bitvec struct. */
|
||||||
#define BITVEC_USIZE (((BITVEC_SZ-12)/sizeof(Bitvec*))*sizeof(Bitvec*))
|
#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
|
||||||
#define BITVEC_NCHAR BITVEC_USIZE
|
|
||||||
#define BITVEC_NBIT (BITVEC_NCHAR*8)
|
/* Type of the array "element" for the bitmap representation.
|
||||||
#define BITVEC_NINT (BITVEC_USIZE/4)
|
** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
|
||||||
|
** Setting this to the "natural word" size of your CPU may improve
|
||||||
|
** performance. */
|
||||||
|
#define BITVEC_TELEM u8
|
||||||
|
/* Size, in bits, of the bitmap element. */
|
||||||
|
#define BITVEC_SZELEM 8
|
||||||
|
/* Number of elements in a bitmap array. */
|
||||||
|
#define BITVEC_NELEM (BITVEC_USIZE/sizeof(BITVEC_TELEM))
|
||||||
|
/* Number of bits in the bitmap array. */
|
||||||
|
#define BITVEC_NBIT (BITVEC_NELEM*BITVEC_SZELEM)
|
||||||
|
|
||||||
|
/* Number of u32 values in hash table. */
|
||||||
|
#define BITVEC_NINT (BITVEC_USIZE/sizeof(u32))
|
||||||
|
/* Maximum number of entries in hash table before
|
||||||
|
** sub-dividing and re-hashing. */
|
||||||
#define BITVEC_MXHASH (BITVEC_NINT/2)
|
#define BITVEC_MXHASH (BITVEC_NINT/2)
|
||||||
|
/* Hashing function for the aHash representation.
|
||||||
|
** Empirical testing showed that the *37 multiplier
|
||||||
|
** (an arbitrary prime)in the hash function provided
|
||||||
|
** no fewer collisions than the no-op *1. */
|
||||||
|
#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT)
|
||||||
|
|
||||||
#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *))
|
#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *))
|
||||||
|
|
||||||
#define BITVEC_HASH(X) (((X)*37)%BITVEC_NINT)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** A bitmap is an instance of the following structure.
|
** A bitmap is an instance of the following structure.
|
||||||
@@ -72,11 +93,15 @@
|
|||||||
** to hold deal with values between 1 and iDivisor.
|
** to hold deal with values between 1 and iDivisor.
|
||||||
*/
|
*/
|
||||||
struct Bitvec {
|
struct Bitvec {
|
||||||
u32 iSize; /* Maximum bit index */
|
u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */
|
||||||
u32 nSet; /* Number of bits that are set */
|
u32 nSet; /* Number of bits that are set - only valid for aHash element */
|
||||||
u32 iDivisor; /* Number of bits handled by each apSub[] entry */
|
/* Max nSet is BITVEC_NINT. For BITVEC_SZ of 512, this would be 125. */
|
||||||
|
u32 iDivisor; /* Number of bits handled by each apSub[] entry. */
|
||||||
|
/* Should >=0 for apSub element. */
|
||||||
|
/* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */
|
||||||
|
/* For a BITVEC_SZ of 512, this would be 34,359,739. */
|
||||||
union {
|
union {
|
||||||
u8 aBitmap[BITVEC_NCHAR]; /* Bitmap representation */
|
BITVEC_TELEM aBitmap[BITVEC_NELEM]; /* Bitmap representation */
|
||||||
u32 aHash[BITVEC_NINT]; /* Hash table representation */
|
u32 aHash[BITVEC_NINT]; /* Hash table representation */
|
||||||
Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */
|
Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */
|
||||||
} u;
|
} u;
|
||||||
@@ -105,16 +130,19 @@ Bitvec *sqlite3BitvecCreate(u32 iSize){
|
|||||||
int sqlite3BitvecTest(Bitvec *p, u32 i){
|
int sqlite3BitvecTest(Bitvec *p, u32 i){
|
||||||
if( p==0 ) return 0;
|
if( p==0 ) return 0;
|
||||||
if( i>p->iSize || i==0 ) return 0;
|
if( i>p->iSize || i==0 ) return 0;
|
||||||
if( p->iSize<=BITVEC_NBIT ){
|
i--;
|
||||||
i--;
|
while( p->iDivisor ){
|
||||||
return (p->u.aBitmap[i/8] & (1<<(i&7)))!=0;
|
u32 bin = i/p->iDivisor;
|
||||||
|
i = i%p->iDivisor;
|
||||||
|
p = p->u.apSub[bin];
|
||||||
|
if (!p) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if( p->iDivisor>0 ){
|
if( p->iSize<=BITVEC_NBIT ){
|
||||||
u32 bin = (i-1)/p->iDivisor;
|
return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0;
|
||||||
i = (i-1)%p->iDivisor + 1;
|
} else{
|
||||||
return sqlite3BitvecTest(p->u.apSub[bin], i);
|
u32 h = BITVEC_HASH(i++);
|
||||||
}else{
|
|
||||||
u32 h = BITVEC_HASH(i);
|
|
||||||
while( p->u.aHash[h] ){
|
while( p->u.aHash[h] ){
|
||||||
if( p->u.aHash[h]==i ) return 1;
|
if( p->u.aHash[h]==i ) return 1;
|
||||||
h++;
|
h++;
|
||||||
@@ -141,35 +169,50 @@ int sqlite3BitvecSet(Bitvec *p, u32 i){
|
|||||||
assert( p!=0 );
|
assert( p!=0 );
|
||||||
assert( i>0 );
|
assert( i>0 );
|
||||||
assert( i<=p->iSize );
|
assert( i<=p->iSize );
|
||||||
if( p->iSize<=BITVEC_NBIT ){
|
i--;
|
||||||
i--;
|
while((p->iSize > BITVEC_NBIT) && p->iDivisor) {
|
||||||
p->u.aBitmap[i/8] |= 1 << (i&7);
|
u32 bin = i/p->iDivisor;
|
||||||
return SQLITE_OK;
|
i = i%p->iDivisor;
|
||||||
}
|
|
||||||
if( p->iDivisor ){
|
|
||||||
u32 bin = (i-1)/p->iDivisor;
|
|
||||||
i = (i-1)%p->iDivisor + 1;
|
|
||||||
if( p->u.apSub[bin]==0 ){
|
if( p->u.apSub[bin]==0 ){
|
||||||
sqlite3BeginBenignMalloc();
|
sqlite3BeginBenignMalloc();
|
||||||
p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
|
p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
|
||||||
sqlite3EndBenignMalloc();
|
sqlite3EndBenignMalloc();
|
||||||
if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
|
if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
return sqlite3BitvecSet(p->u.apSub[bin], i);
|
p = p->u.apSub[bin];
|
||||||
}
|
}
|
||||||
h = BITVEC_HASH(i);
|
if( p->iSize<=BITVEC_NBIT ){
|
||||||
while( p->u.aHash[h] ){
|
p->u.aBitmap[i/BITVEC_SZELEM] |= 1 << (i&(BITVEC_SZELEM-1));
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
h = BITVEC_HASH(i++);
|
||||||
|
/* if there wasn't a hash collision, and this doesn't */
|
||||||
|
/* completely fill the hash, then just add it without */
|
||||||
|
/* worring about sub-dividing and re-hashing. */
|
||||||
|
if( !p->u.aHash[h] ){
|
||||||
|
if (p->nSet<(BITVEC_NINT-1)) {
|
||||||
|
goto bitvec_set_end;
|
||||||
|
} else {
|
||||||
|
goto bitvec_set_rehash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* there was a collision, check to see if it's already */
|
||||||
|
/* in hash, if not, try to find a spot for it */
|
||||||
|
do {
|
||||||
if( p->u.aHash[h]==i ) return SQLITE_OK;
|
if( p->u.aHash[h]==i ) return SQLITE_OK;
|
||||||
h++;
|
h++;
|
||||||
if( h==BITVEC_NINT ) h = 0;
|
if( h>=BITVEC_NINT ) h = 0;
|
||||||
}
|
} while( p->u.aHash[h] );
|
||||||
p->nSet++;
|
/* we didn't find it in the hash. h points to the first */
|
||||||
|
/* available free spot. check to see if this is going to */
|
||||||
|
/* make our hash too "full". */
|
||||||
|
bitvec_set_rehash:
|
||||||
if( p->nSet>=BITVEC_MXHASH ){
|
if( p->nSet>=BITVEC_MXHASH ){
|
||||||
unsigned int j;
|
unsigned int j;
|
||||||
int rc;
|
int rc;
|
||||||
u32 aiValues[BITVEC_NINT];
|
u32 aiValues[BITVEC_NINT];
|
||||||
memcpy(aiValues, p->u.aHash, sizeof(aiValues));
|
memcpy(aiValues, p->u.aHash, sizeof(aiValues));
|
||||||
memset(p->u.apSub, 0, sizeof(p->u.apSub[0])*BITVEC_NPTR);
|
memset(p->u.apSub, 0, sizeof(aiValues));
|
||||||
p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR;
|
p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR;
|
||||||
rc = sqlite3BitvecSet(p, i);
|
rc = sqlite3BitvecSet(p, i);
|
||||||
for(j=0; j<BITVEC_NINT; j++){
|
for(j=0; j<BITVEC_NINT; j++){
|
||||||
@@ -177,35 +220,44 @@ int sqlite3BitvecSet(Bitvec *p, u32 i){
|
|||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
bitvec_set_end:
|
||||||
|
p->nSet++;
|
||||||
p->u.aHash[h] = i;
|
p->u.aHash[h] = i;
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Clear the i-th bit. Return 0 on success and an error code if
|
** Clear the i-th bit.
|
||||||
** anything goes wrong.
|
|
||||||
*/
|
*/
|
||||||
void sqlite3BitvecClear(Bitvec *p, u32 i){
|
void sqlite3BitvecClear(Bitvec *p, u32 i){
|
||||||
assert( p!=0 );
|
assert( p!=0 );
|
||||||
assert( i>0 );
|
assert( i>0 );
|
||||||
if( p->iSize<=BITVEC_NBIT ){
|
i--;
|
||||||
i--;
|
while( p->iDivisor ){
|
||||||
p->u.aBitmap[i/8] &= ~(1 << (i&7));
|
u32 bin = i/p->iDivisor;
|
||||||
}else if( p->iDivisor ){
|
i = i%p->iDivisor;
|
||||||
u32 bin = (i-1)/p->iDivisor;
|
p = p->u.apSub[bin];
|
||||||
i = (i-1)%p->iDivisor + 1;
|
if (!p) {
|
||||||
if( p->u.apSub[bin] ){
|
return;
|
||||||
sqlite3BitvecClear(p->u.apSub[bin], i);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if( p->iSize<=BITVEC_NBIT ){
|
||||||
|
p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1)));
|
||||||
}else{
|
}else{
|
||||||
unsigned int j;
|
unsigned int j;
|
||||||
u32 aiValues[BITVEC_NINT];
|
u32 aiValues[BITVEC_NINT];
|
||||||
memcpy(aiValues, p->u.aHash, sizeof(aiValues));
|
memcpy(aiValues, p->u.aHash, sizeof(aiValues));
|
||||||
memset(p->u.aHash, 0, sizeof(p->u.aHash[0])*BITVEC_NINT);
|
memset(p->u.aHash, 0, sizeof(aiValues));
|
||||||
p->nSet = 0;
|
p->nSet = 0;
|
||||||
for(j=0; j<BITVEC_NINT; j++){
|
for(j=0; j<BITVEC_NINT; j++){
|
||||||
if( aiValues[j] && aiValues[j]!=i ){
|
if( aiValues[j] && aiValues[j]!=(i+1) ){
|
||||||
sqlite3BitvecSet(p, aiValues[j]);
|
u32 h = BITVEC_HASH(aiValues[j]-1);
|
||||||
|
p->nSet++;
|
||||||
|
while( p->u.aHash[h] ){
|
||||||
|
h++;
|
||||||
|
if( h>=BITVEC_NINT ) h = 0;
|
||||||
|
}
|
||||||
|
p->u.aHash[h] = aiValues[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
** file simultaneously, or one process from reading the database while
|
** file simultaneously, or one process from reading the database while
|
||||||
** another is writing.
|
** another is writing.
|
||||||
**
|
**
|
||||||
** @(#) $Id: pager.c,v 1.505 2008/11/19 10:22:33 danielk1977 Exp $
|
** @(#) $Id: pager.c,v 1.506 2008/11/19 18:30:29 drh Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef SQLITE_OMIT_DISKIO
|
#ifndef SQLITE_OMIT_DISKIO
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
@@ -3488,7 +3488,8 @@ void sqlite3PagerDontRollback(DbPage *pPg){
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SQLITE_SECURE_DELETE
|
#ifdef SQLITE_SECURE_DELETE
|
||||||
if( (pPg->flags & PGHDR_IN_JOURNAL)!=0 || pPg->pgno>pPager->origDbSize ){
|
if( sqlite3BitvecTest(pPager->pInJournal, pPg->pgno)!=0
|
||||||
|
|| pPg->pgno>pPager->origDbSize ){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
# Unit testing of the Bitvec object.
|
# Unit testing of the Bitvec object.
|
||||||
#
|
#
|
||||||
# $Id: bitvec.test,v 1.2 2008/03/21 16:45:48 drh Exp $
|
# $Id: bitvec.test,v 1.3 2008/11/19 18:30:35 shane Exp $
|
||||||
#
|
#
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
@@ -185,7 +185,3 @@ for {set n 1} {$go} {incr n} {
|
|||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
finish_test
|
|
||||||
|
Reference in New Issue
Block a user