4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
13 ** This file contains a VFS "shim" - a layer that sits in between the
14 ** pager and the real VFS.
16 ** This particular shim enforces a quota system on files. One or more
17 ** database files are in a "quota group" that is defined by a GLOB
18 ** pattern. A quota is set for the combined size of all files in the
19 ** the group. A quota of zero means "no limit". If the total size
20 ** of all files in the quota group is greater than the limit, then
21 ** write requests that attempt to enlarge a file fail with SQLITE_FULL.
23 ** However, before returning SQLITE_FULL, the write requests invoke
24 ** a callback function that is configurable for each quota group.
25 ** This callback has the opportunity to enlarge the quota. If the
26 ** callback does enlarge the quota such that the total size of all
27 ** files within the group is less than the new quota, then the write
28 ** continues as if nothing had happened.
30 #include "test_quota.h"
35 ** For an build without mutexes, no-op the mutex calls.
37 #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
38 #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
39 #define sqlite3_mutex_free(X)
40 #define sqlite3_mutex_enter(X)
41 #define sqlite3_mutex_try(X) SQLITE_OK
42 #define sqlite3_mutex_leave(X)
43 #define sqlite3_mutex_held(X) ((void)(X),1)
44 #define sqlite3_mutex_notheld(X) ((void)(X),1)
45 #endif /* SQLITE_THREADSAFE==0 */
49 ** Figure out if we are dealing with Unix, Windows, or some other
50 ** operating system. After the following block of preprocess macros,
51 ** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
52 ** will defined to either 1 or 0. One of the four will be 1. The other
55 #if defined(SQLITE_OS_OTHER)
56 # if SQLITE_OS_OTHER==1
57 # undef SQLITE_OS_UNIX
58 # define SQLITE_OS_UNIX 0
60 # define SQLITE_OS_WIN 0
62 # define SQLITE_OS_OS2 0
64 # undef SQLITE_OS_OTHER
67 #if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
68 # define SQLITE_OS_OTHER 0
69 # ifndef SQLITE_OS_WIN
70 # if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) \
71 || defined(__MINGW32__) || defined(__BORLANDC__)
72 # define SQLITE_OS_WIN 1
73 # define SQLITE_OS_UNIX 0
74 # define SQLITE_OS_OS2 0
75 # elif defined(__EMX__) || defined(_OS2) || defined(OS2) \
76 || defined(_OS2_) || defined(__OS2__)
77 # define SQLITE_OS_WIN 0
78 # define SQLITE_OS_UNIX 0
79 # define SQLITE_OS_OS2 1
81 # define SQLITE_OS_WIN 0
82 # define SQLITE_OS_UNIX 1
83 # define SQLITE_OS_OS2 0
86 # define SQLITE_OS_UNIX 0
87 # define SQLITE_OS_OS2 0
90 # ifndef SQLITE_OS_WIN
91 # define SQLITE_OS_WIN 0
104 /************************ Object Definitions ******************************/
106 /* Forward declaration of all object types */
107 typedef struct quotaGroup quotaGroup
;
108 typedef struct quotaConn quotaConn
;
109 typedef struct quotaFile quotaFile
;
112 ** A "quota group" is a collection of files whose collective size we want
113 ** to limit. Each quota group is defined by a GLOB pattern.
115 ** There is an instance of the following object for each defined quota
116 ** group. This object records the GLOB pattern that defines which files
117 ** belong to the quota group. The object also remembers the size limit
118 ** for the group (the quota) and the callback to be invoked when the
119 ** sum of the sizes of the files within the group goes over the limit.
121 ** A quota group must be established (using sqlite3_quota_set(...))
122 ** prior to opening any of the database connections that access files
123 ** within the quota group.
126 const char *zPattern
; /* Filename pattern to be quotaed */
127 sqlite3_int64 iLimit
; /* Upper bound on total file size */
128 sqlite3_int64 iSize
; /* Current size of all files */
129 void (*xCallback
)( /* Callback invoked when going over quota */
130 const char *zFilename
, /* Name of file whose size increases */
131 sqlite3_int64
*piLimit
, /* IN/OUT: The current limit */
132 sqlite3_int64 iSize
, /* Total size of all files in the group */
133 void *pArg
/* Client data */
135 void *pArg
; /* Third argument to the xCallback() */
136 void (*xDestroy
)(void*); /* Optional destructor for pArg */
137 quotaGroup
*pNext
, **ppPrev
; /* Doubly linked list of all quota objects */
138 quotaFile
*pFiles
; /* Files within this group */
142 ** An instance of this structure represents a single file that is part
143 ** of a quota group. A single file can be opened multiple times. In
144 ** order keep multiple openings of the same file from causing the size
145 ** of the file to count against the quota multiple times, each file
146 ** has a unique instance of this object and multiple open connections
147 ** to the same file each point to a single instance of this object.
150 char *zFilename
; /* Name of this file */
151 quotaGroup
*pGroup
; /* Quota group to which this file belongs */
152 sqlite3_int64 iSize
; /* Current size of this file */
153 int nRef
; /* Number of times this file is open */
154 int deleteOnClose
; /* True to delete this file when it closes */
155 quotaFile
*pNext
, **ppPrev
; /* Linked list of files in the same group */
159 ** An instance of the following object represents each open connection
160 ** to a file that participates in quota tracking. This object is a
161 ** subclass of sqlite3_file. The sqlite3_file object for the underlying
162 ** VFS is appended to this structure.
165 sqlite3_file base
; /* Base class - must be first */
166 quotaFile
*pFile
; /* The underlying file */
167 /* The underlying VFS sqlite3_file is appended to this object */
171 ** An instance of the following object records the state of an
172 ** open file. This object is opaque to all users - the internal
173 ** structure is only visible to the functions below.
176 FILE *f
; /* Open stdio file pointer */
177 sqlite3_int64 iOfst
; /* Current offset into the file */
178 quotaFile
*pFile
; /* The file record in the quota system */
180 char *zMbcsName
; /* Full MBCS pathname of the file */
185 /************************* Global Variables **********************************/
187 ** All global variables used by this file are containing within the following
191 /* The pOrigVfs is the real, original underlying VFS implementation.
192 ** Most operations pass-through to the real VFS. This value is read-only
193 ** during operation. It is only modified at start-time and thus does not
196 sqlite3_vfs
*pOrigVfs
;
198 /* The sThisVfs is the VFS structure used by this shim. It is initialized
199 ** at start-time and thus does not require a mutex
201 sqlite3_vfs sThisVfs
;
203 /* The sIoMethods defines the methods used by sqlite3_file objects
204 ** associated with this shim. It is initialized at start-time and does
205 ** not require a mutex.
207 ** When the underlying VFS is called to open a file, it might return
208 ** either a version 1 or a version 2 sqlite3_file object. This shim
209 ** has to create a wrapper sqlite3_file of the same version. Hence
210 ** there are two I/O method structures, one for version 1 and the other
213 sqlite3_io_methods sIoMethodsV1
;
214 sqlite3_io_methods sIoMethodsV2
;
216 /* True when this shim as been initialized.
220 /* For run-time access any of the other global data structures in this
221 ** shim, the following mutex must be held.
223 sqlite3_mutex
*pMutex
;
225 /* List of quotaGroup objects.
231 /************************* Utility Routines *********************************/
233 ** Acquire and release the mutex used to serialize access to the
234 ** list of quotaGroups.
236 static void quotaEnter(void){ sqlite3_mutex_enter(gQuota
.pMutex
); }
237 static void quotaLeave(void){ sqlite3_mutex_leave(gQuota
.pMutex
); }
239 /* Count the number of open files in a quotaGroup
241 static int quotaGroupOpenFileCount(quotaGroup
*pGroup
){
243 quotaFile
*pFile
= pGroup
->pFiles
;
245 if( pFile
->nRef
) N
++;
246 pFile
= pFile
->pNext
;
251 /* Remove a file from a quota group.
253 static void quotaRemoveFile(quotaFile
*pFile
){
254 quotaGroup
*pGroup
= pFile
->pGroup
;
255 pGroup
->iSize
-= pFile
->iSize
;
256 *pFile
->ppPrev
= pFile
->pNext
;
257 if( pFile
->pNext
) pFile
->pNext
->ppPrev
= pFile
->ppPrev
;
261 /* Remove all files from a quota group. It is always the case that
262 ** all files will be closed when this routine is called.
264 static void quotaRemoveAllFiles(quotaGroup
*pGroup
){
265 while( pGroup
->pFiles
){
266 assert( pGroup
->pFiles
->nRef
==0 );
267 quotaRemoveFile(pGroup
->pFiles
);
272 /* If the reference count and threshold for a quotaGroup are both
273 ** zero, then destroy the quotaGroup.
275 static void quotaGroupDeref(quotaGroup
*pGroup
){
276 if( pGroup
->iLimit
==0 && quotaGroupOpenFileCount(pGroup
)==0 ){
277 quotaRemoveAllFiles(pGroup
);
278 *pGroup
->ppPrev
= pGroup
->pNext
;
279 if( pGroup
->pNext
) pGroup
->pNext
->ppPrev
= pGroup
->ppPrev
;
280 if( pGroup
->xDestroy
) pGroup
->xDestroy(pGroup
->pArg
);
281 sqlite3_free(pGroup
);
286 ** Return TRUE if string z matches glob pattern zGlob.
290 ** '*' Matches any sequence of zero or more characters.
292 ** '?' Matches exactly one character.
294 ** [...] Matches one character from the enclosed list of
297 ** [^...] Matches one character not in the enclosed list.
299 ** / Matches "/" or "\\"
302 static int quotaStrglob(const char *zGlob
, const char *z
){
307 while( (c
= (*(zGlob
++)))!=0 ){
309 while( (c
=(*(zGlob
++))) == '*' || c
=='?' ){
310 if( c
=='?' && (*(z
++))==0 ) return 0;
315 while( *z
&& quotaStrglob(zGlob
-1,z
)==0 ){
320 cx
= (c
=='/') ? '\\' : c
;
321 while( (c2
= (*(z
++)))!=0 ){
322 while( c2
!=c
&& c2
!=cx
){
324 if( c2
==0 ) return 0;
326 if( quotaStrglob(zGlob
,z
) ) return 1;
330 if( (*(z
++))==0 ) return 0;
343 if( c
==']' ) seen
= 1;
346 while( c2
&& c2
!=']' ){
347 if( c2
=='-' && zGlob
[0]!=']' && zGlob
[0]!=0 && prior_c
>0 ){
349 if( c
>=prior_c
&& c
<=c2
) seen
= 1;
359 if( c2
==0 || (seen
^ invert
)==0 ) return 0;
361 if( z
[0]!='/' && z
[0]!='\\' ) return 0;
364 if( c
!=(*(z
++)) ) return 0;
371 /* Find a quotaGroup given the filename.
373 ** Return a pointer to the quotaGroup object. Return NULL if not found.
375 static quotaGroup
*quotaGroupFind(const char *zFilename
){
377 for(p
=gQuota
.pGroup
; p
&& quotaStrglob(p
->zPattern
, zFilename
)==0;
382 /* Translate an sqlite3_file* that is really a quotaConn* into
383 ** the sqlite3_file* for the underlying original VFS.
385 static sqlite3_file
*quotaSubOpen(sqlite3_file
*pConn
){
386 quotaConn
*p
= (quotaConn
*)pConn
;
387 return (sqlite3_file
*)&p
[1];
390 /* Find a file in a quota group and return a pointer to that file.
391 ** Return NULL if the file is not in the group.
393 static quotaFile
*quotaFindFile(
394 quotaGroup
*pGroup
, /* Group in which to look for the file */
395 const char *zName
, /* Full pathname of the file */
396 int createFlag
/* Try to create the file if not found */
398 quotaFile
*pFile
= pGroup
->pFiles
;
399 while( pFile
&& strcmp(pFile
->zFilename
, zName
)!=0 ){
400 pFile
= pFile
->pNext
;
402 if( pFile
==0 && createFlag
){
403 int nName
= (int)(strlen(zName
) & 0x3fffffff);
404 pFile
= (quotaFile
*)sqlite3_malloc( sizeof(*pFile
) + nName
+ 1 );
406 memset(pFile
, 0, sizeof(*pFile
));
407 pFile
->zFilename
= (char*)&pFile
[1];
408 memcpy(pFile
->zFilename
, zName
, nName
+1);
409 pFile
->pNext
= pGroup
->pFiles
;
410 if( pGroup
->pFiles
) pGroup
->pFiles
->ppPrev
= &pFile
->pNext
;
411 pFile
->ppPrev
= &pGroup
->pFiles
;
412 pGroup
->pFiles
= pFile
;
413 pFile
->pGroup
= pGroup
;
419 ** Translate UTF8 to MBCS for use in fopen() calls. Return a pointer to the
420 ** translated text.. Call quota_mbcs_free() to deallocate any memory
421 ** used to store the returned pointer when done.
423 static char *quota_utf8_to_mbcs(const char *zUtf8
){
425 size_t n
; /* Bytes in zUtf8 */
426 int nWide
; /* number of UTF-16 characters */
427 int nMbcs
; /* Bytes of MBCS */
428 LPWSTR zTmpWide
; /* The UTF16 text */
429 char *zMbcs
; /* The MBCS text */
430 int codepage
; /* Code page used by fopen() */
433 nWide
= MultiByteToWideChar(CP_UTF8
, 0, zUtf8
, -1, NULL
, 0);
434 if( nWide
==0 ) return 0;
435 zTmpWide
= (LPWSTR
)sqlite3_malloc( (nWide
+1)*sizeof(zTmpWide
[0]) );
436 if( zTmpWide
==0 ) return 0;
437 MultiByteToWideChar(CP_UTF8
, 0, zUtf8
, -1, zTmpWide
, nWide
);
438 codepage
= AreFileApisANSI() ? CP_ACP
: CP_OEMCP
;
439 nMbcs
= WideCharToMultiByte(codepage
, 0, zTmpWide
, nWide
, 0, 0, 0, 0);
440 zMbcs
= nMbcs
? (char*)sqlite3_malloc( nMbcs
+1 ) : 0;
442 WideCharToMultiByte(codepage
, 0, zTmpWide
, nWide
, zMbcs
, nMbcs
, 0, 0);
444 sqlite3_free(zTmpWide
);
447 return (char*)zUtf8
; /* No-op on unix */
452 ** Deallocate any memory allocated by quota_utf8_to_mbcs().
454 static void quota_mbcs_free(char *zOld
){
462 /************************* VFS Method Wrappers *****************************/
464 ** This is the xOpen method used for the "quota" VFS.
466 ** Most of the work is done by the underlying original VFS. This method
467 ** simply links the new file into the appropriate quota group if it is a
468 ** file that needs to be tracked.
470 static int quotaOpen(
471 sqlite3_vfs
*pVfs
, /* The quota VFS */
472 const char *zName
, /* Name of file to be opened */
473 sqlite3_file
*pConn
, /* Fill in this file descriptor */
474 int flags
, /* Flags to control the opening */
475 int *pOutFlags
/* Flags showing results of opening */
477 int rc
; /* Result code */
478 quotaConn
*pQuotaOpen
; /* The new quota file descriptor */
479 quotaFile
*pFile
; /* Corresponding quotaFile obj */
480 quotaGroup
*pGroup
; /* The group file belongs to */
481 sqlite3_file
*pSubOpen
; /* Real file descriptor */
482 sqlite3_vfs
*pOrigVfs
= gQuota
.pOrigVfs
; /* Real VFS */
484 /* If the file is not a main database file or a WAL, then use the
485 ** normal xOpen method.
487 if( (flags
& (SQLITE_OPEN_MAIN_DB
|SQLITE_OPEN_WAL
))==0 ){
488 return pOrigVfs
->xOpen(pOrigVfs
, zName
, pConn
, flags
, pOutFlags
);
491 /* If the name of the file does not match any quota group, then
492 ** use the normal xOpen method.
495 pGroup
= quotaGroupFind(zName
);
497 rc
= pOrigVfs
->xOpen(pOrigVfs
, zName
, pConn
, flags
, pOutFlags
);
499 /* If we get to this point, it means the file needs to be quota tracked.
501 pQuotaOpen
= (quotaConn
*)pConn
;
502 pSubOpen
= quotaSubOpen(pConn
);
503 rc
= pOrigVfs
->xOpen(pOrigVfs
, zName
, pSubOpen
, flags
, pOutFlags
);
505 pFile
= quotaFindFile(pGroup
, zName
, 1);
508 pSubOpen
->pMethods
->xClose(pSubOpen
);
511 pFile
->deleteOnClose
= (flags
& SQLITE_OPEN_DELETEONCLOSE
)!=0;
513 pQuotaOpen
->pFile
= pFile
;
514 if( pSubOpen
->pMethods
->iVersion
==1 ){
515 pQuotaOpen
->base
.pMethods
= &gQuota
.sIoMethodsV1
;
517 pQuotaOpen
->base
.pMethods
= &gQuota
.sIoMethodsV2
;
526 ** This is the xDelete method used for the "quota" VFS.
528 ** If the file being deleted is part of the quota group, then reduce
529 ** the size of the quota group accordingly. And remove the file from
530 ** the set of files in the quota group.
532 static int quotaDelete(
533 sqlite3_vfs
*pVfs
, /* The quota VFS */
534 const char *zName
, /* Name of file to be deleted */
535 int syncDir
/* Do a directory sync after deleting */
537 int rc
; /* Result code */
538 quotaFile
*pFile
; /* Files in the quota */
539 quotaGroup
*pGroup
; /* The group file belongs to */
540 sqlite3_vfs
*pOrigVfs
= gQuota
.pOrigVfs
; /* Real VFS */
542 /* Do the actual file delete */
543 rc
= pOrigVfs
->xDelete(pOrigVfs
, zName
, syncDir
);
545 /* If the file just deleted is a member of a quota group, then remove
546 ** it from that quota group.
550 pGroup
= quotaGroupFind(zName
);
552 pFile
= quotaFindFile(pGroup
, zName
, 0);
555 pFile
->deleteOnClose
= 1;
557 quotaRemoveFile(pFile
);
558 quotaGroupDeref(pGroup
);
568 /************************ I/O Method Wrappers *******************************/
570 /* xClose requests get passed through to the original VFS. But we
571 ** also have to unlink the quotaConn from the quotaFile and quotaGroup.
572 ** The quotaFile and/or quotaGroup are freed if they are no longer in use.
574 static int quotaClose(sqlite3_file
*pConn
){
575 quotaConn
*p
= (quotaConn
*)pConn
;
576 quotaFile
*pFile
= p
->pFile
;
577 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
579 rc
= pSubOpen
->pMethods
->xClose(pSubOpen
);
582 if( pFile
->nRef
==0 ){
583 quotaGroup
*pGroup
= pFile
->pGroup
;
584 if( pFile
->deleteOnClose
){
585 gQuota
.pOrigVfs
->xDelete(gQuota
.pOrigVfs
, pFile
->zFilename
, 0);
586 quotaRemoveFile(pFile
);
588 quotaGroupDeref(pGroup
);
594 /* Pass xRead requests directory thru to the original VFS without
595 ** further processing.
597 static int quotaRead(
603 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
604 return pSubOpen
->pMethods
->xRead(pSubOpen
, pBuf
, iAmt
, iOfst
);
607 /* Check xWrite requests to see if they expand the file. If they do,
608 ** the perform a quota check before passing them through to the
611 static int quotaWrite(
617 quotaConn
*p
= (quotaConn
*)pConn
;
618 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
619 sqlite3_int64 iEnd
= iOfst
+iAmt
;
621 quotaFile
*pFile
= p
->pFile
;
624 if( pFile
->iSize
<iEnd
){
625 pGroup
= pFile
->pGroup
;
627 szNew
= pGroup
->iSize
- pFile
->iSize
+ iEnd
;
628 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
629 if( pGroup
->xCallback
){
630 pGroup
->xCallback(pFile
->zFilename
, &pGroup
->iLimit
, szNew
,
633 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
638 pGroup
->iSize
= szNew
;
642 return pSubOpen
->pMethods
->xWrite(pSubOpen
, pBuf
, iAmt
, iOfst
);
645 /* Pass xTruncate requests thru to the original VFS. If the
646 ** success, update the file size.
648 static int quotaTruncate(sqlite3_file
*pConn
, sqlite3_int64 size
){
649 quotaConn
*p
= (quotaConn
*)pConn
;
650 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
651 int rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, size
);
652 quotaFile
*pFile
= p
->pFile
;
656 pGroup
= pFile
->pGroup
;
657 pGroup
->iSize
-= pFile
->iSize
;
659 pGroup
->iSize
+= size
;
665 /* Pass xSync requests through to the original VFS without change
667 static int quotaSync(sqlite3_file
*pConn
, int flags
){
668 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
669 return pSubOpen
->pMethods
->xSync(pSubOpen
, flags
);
672 /* Pass xFileSize requests through to the original VFS but then
673 ** update the quotaGroup with the new size before returning.
675 static int quotaFileSize(sqlite3_file
*pConn
, sqlite3_int64
*pSize
){
676 quotaConn
*p
= (quotaConn
*)pConn
;
677 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
678 quotaFile
*pFile
= p
->pFile
;
683 rc
= pSubOpen
->pMethods
->xFileSize(pSubOpen
, &sz
);
686 pGroup
= pFile
->pGroup
;
687 pGroup
->iSize
-= pFile
->iSize
;
696 /* Pass xLock requests through to the original VFS unchanged.
698 static int quotaLock(sqlite3_file
*pConn
, int lock
){
699 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
700 return pSubOpen
->pMethods
->xLock(pSubOpen
, lock
);
703 /* Pass xUnlock requests through to the original VFS unchanged.
705 static int quotaUnlock(sqlite3_file
*pConn
, int lock
){
706 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
707 return pSubOpen
->pMethods
->xUnlock(pSubOpen
, lock
);
710 /* Pass xCheckReservedLock requests through to the original VFS unchanged.
712 static int quotaCheckReservedLock(sqlite3_file
*pConn
, int *pResOut
){
713 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
714 return pSubOpen
->pMethods
->xCheckReservedLock(pSubOpen
, pResOut
);
717 /* Pass xFileControl requests through to the original VFS unchanged.
719 static int quotaFileControl(sqlite3_file
*pConn
, int op
, void *pArg
){
720 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
721 int rc
= pSubOpen
->pMethods
->xFileControl(pSubOpen
, op
, pArg
);
722 #if defined(SQLITE_FCNTL_VFSNAME)
723 if( op
==SQLITE_FCNTL_VFSNAME
&& rc
==SQLITE_OK
){
724 *(char**)pArg
= sqlite3_mprintf("quota/%z", *(char**)pArg
);
730 /* Pass xSectorSize requests through to the original VFS unchanged.
732 static int quotaSectorSize(sqlite3_file
*pConn
){
733 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
734 return pSubOpen
->pMethods
->xSectorSize(pSubOpen
);
737 /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
739 static int quotaDeviceCharacteristics(sqlite3_file
*pConn
){
740 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
741 return pSubOpen
->pMethods
->xDeviceCharacteristics(pSubOpen
);
744 /* Pass xShmMap requests through to the original VFS unchanged.
746 static int quotaShmMap(
747 sqlite3_file
*pConn
, /* Handle open on database file */
748 int iRegion
, /* Region to retrieve */
749 int szRegion
, /* Size of regions */
750 int bExtend
, /* True to extend file if necessary */
751 void volatile **pp
/* OUT: Mapped memory */
753 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
754 return pSubOpen
->pMethods
->xShmMap(pSubOpen
, iRegion
, szRegion
, bExtend
, pp
);
757 /* Pass xShmLock requests through to the original VFS unchanged.
759 static int quotaShmLock(
760 sqlite3_file
*pConn
, /* Database file holding the shared memory */
761 int ofst
, /* First lock to acquire or release */
762 int n
, /* Number of locks to acquire or release */
763 int flags
/* What to do with the lock */
765 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
766 return pSubOpen
->pMethods
->xShmLock(pSubOpen
, ofst
, n
, flags
);
769 /* Pass xShmBarrier requests through to the original VFS unchanged.
771 static void quotaShmBarrier(sqlite3_file
*pConn
){
772 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
773 pSubOpen
->pMethods
->xShmBarrier(pSubOpen
);
776 /* Pass xShmUnmap requests through to the original VFS unchanged.
778 static int quotaShmUnmap(sqlite3_file
*pConn
, int deleteFlag
){
779 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
780 return pSubOpen
->pMethods
->xShmUnmap(pSubOpen
, deleteFlag
);
783 /************************** Public Interfaces *****************************/
785 ** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
786 ** as the VFS that does the actual work. Use the default if
787 ** zOrigVfsName==NULL.
789 ** The quota VFS shim is named "quota". It will become the default
790 ** VFS if makeDefault is non-zero.
792 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
795 int sqlite3_quota_initialize(const char *zOrigVfsName
, int makeDefault
){
796 sqlite3_vfs
*pOrigVfs
;
797 if( gQuota
.isInitialized
) return SQLITE_MISUSE
;
798 pOrigVfs
= sqlite3_vfs_find(zOrigVfsName
);
799 if( pOrigVfs
==0 ) return SQLITE_ERROR
;
800 assert( pOrigVfs
!=&gQuota
.sThisVfs
);
801 gQuota
.pMutex
= sqlite3_mutex_alloc(SQLITE_MUTEX_FAST
);
802 if( !gQuota
.pMutex
){
805 gQuota
.isInitialized
= 1;
806 gQuota
.pOrigVfs
= pOrigVfs
;
807 gQuota
.sThisVfs
= *pOrigVfs
;
808 gQuota
.sThisVfs
.xOpen
= quotaOpen
;
809 gQuota
.sThisVfs
.xDelete
= quotaDelete
;
810 gQuota
.sThisVfs
.szOsFile
+= sizeof(quotaConn
);
811 gQuota
.sThisVfs
.zName
= "quota";
812 gQuota
.sIoMethodsV1
.iVersion
= 1;
813 gQuota
.sIoMethodsV1
.xClose
= quotaClose
;
814 gQuota
.sIoMethodsV1
.xRead
= quotaRead
;
815 gQuota
.sIoMethodsV1
.xWrite
= quotaWrite
;
816 gQuota
.sIoMethodsV1
.xTruncate
= quotaTruncate
;
817 gQuota
.sIoMethodsV1
.xSync
= quotaSync
;
818 gQuota
.sIoMethodsV1
.xFileSize
= quotaFileSize
;
819 gQuota
.sIoMethodsV1
.xLock
= quotaLock
;
820 gQuota
.sIoMethodsV1
.xUnlock
= quotaUnlock
;
821 gQuota
.sIoMethodsV1
.xCheckReservedLock
= quotaCheckReservedLock
;
822 gQuota
.sIoMethodsV1
.xFileControl
= quotaFileControl
;
823 gQuota
.sIoMethodsV1
.xSectorSize
= quotaSectorSize
;
824 gQuota
.sIoMethodsV1
.xDeviceCharacteristics
= quotaDeviceCharacteristics
;
825 gQuota
.sIoMethodsV2
= gQuota
.sIoMethodsV1
;
826 gQuota
.sIoMethodsV2
.iVersion
= 2;
827 gQuota
.sIoMethodsV2
.xShmMap
= quotaShmMap
;
828 gQuota
.sIoMethodsV2
.xShmLock
= quotaShmLock
;
829 gQuota
.sIoMethodsV2
.xShmBarrier
= quotaShmBarrier
;
830 gQuota
.sIoMethodsV2
.xShmUnmap
= quotaShmUnmap
;
831 sqlite3_vfs_register(&gQuota
.sThisVfs
, makeDefault
);
836 ** Shutdown the quota system.
838 ** All SQLite database connections must be closed before calling this
841 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
842 ** shutting down in order to free all remaining quota groups.
844 int sqlite3_quota_shutdown(void){
846 if( gQuota
.isInitialized
==0 ) return SQLITE_MISUSE
;
847 for(pGroup
=gQuota
.pGroup
; pGroup
; pGroup
=pGroup
->pNext
){
848 if( quotaGroupOpenFileCount(pGroup
)>0 ) return SQLITE_MISUSE
;
850 while( gQuota
.pGroup
){
851 pGroup
= gQuota
.pGroup
;
852 gQuota
.pGroup
= pGroup
->pNext
;
854 assert( quotaGroupOpenFileCount(pGroup
)==0 );
855 quotaGroupDeref(pGroup
);
857 gQuota
.isInitialized
= 0;
858 sqlite3_mutex_free(gQuota
.pMutex
);
859 sqlite3_vfs_unregister(&gQuota
.sThisVfs
);
860 memset(&gQuota
, 0, sizeof(gQuota
));
865 ** Create or destroy a quota group.
867 ** The quota group is defined by the zPattern. When calling this routine
868 ** with a zPattern for a quota group that already exists, this routine
869 ** merely updates the iLimit, xCallback, and pArg values for that quota
870 ** group. If zPattern is new, then a new quota group is created.
872 ** If the iLimit for a quota group is set to zero, then the quota group
873 ** is disabled and will be deleted when the last database connection using
874 ** the quota group is closed.
876 ** Calling this routine on a zPattern that does not exist and with a
877 ** zero iLimit is a no-op.
879 ** A quota group must exist with a non-zero iLimit prior to opening
880 ** database connections if those connections are to participate in the
881 ** quota group. Creating a quota group does not affect database connections
882 ** that are already open.
884 int sqlite3_quota_set(
885 const char *zPattern
, /* The filename pattern */
886 sqlite3_int64 iLimit
, /* New quota to set for this quota group */
887 void (*xCallback
)( /* Callback invoked when going over quota */
888 const char *zFilename
, /* Name of file whose size increases */
889 sqlite3_int64
*piLimit
, /* IN/OUT: The current limit */
890 sqlite3_int64 iSize
, /* Total size of all files in the group */
891 void *pArg
/* Client data */
893 void *pArg
, /* client data passed thru to callback */
894 void (*xDestroy
)(void*) /* Optional destructor for pArg */
898 pGroup
= gQuota
.pGroup
;
899 while( pGroup
&& strcmp(pGroup
->zPattern
, zPattern
)!=0 ){
900 pGroup
= pGroup
->pNext
;
903 int nPattern
= (int)(strlen(zPattern
) & 0x3fffffff);
908 pGroup
= (quotaGroup
*)sqlite3_malloc( sizeof(*pGroup
) + nPattern
+ 1 );
913 memset(pGroup
, 0, sizeof(*pGroup
));
914 pGroup
->zPattern
= (char*)&pGroup
[1];
915 memcpy((char *)pGroup
->zPattern
, zPattern
, nPattern
+1);
916 if( gQuota
.pGroup
) gQuota
.pGroup
->ppPrev
= &pGroup
->pNext
;
917 pGroup
->pNext
= gQuota
.pGroup
;
918 pGroup
->ppPrev
= &gQuota
.pGroup
;
919 gQuota
.pGroup
= pGroup
;
921 pGroup
->iLimit
= iLimit
;
922 pGroup
->xCallback
= xCallback
;
923 if( pGroup
->xDestroy
&& pGroup
->pArg
!=pArg
){
924 pGroup
->xDestroy(pGroup
->pArg
);
927 pGroup
->xDestroy
= xDestroy
;
928 quotaGroupDeref(pGroup
);
934 ** Bring the named file under quota management. Or if it is already under
935 ** management, update its size.
937 int sqlite3_quota_file(const char *zFilename
){
943 int nAlloc
= gQuota
.sThisVfs
.szOsFile
+ gQuota
.sThisVfs
.mxPathname
+2;
945 /* Allocate space for a file-handle and the full path for file zFilename */
946 fd
= (sqlite3_file
*)sqlite3_malloc(nAlloc
);
950 zFull
= &((char *)fd
)[gQuota
.sThisVfs
.szOsFile
];
951 rc
= gQuota
.pOrigVfs
->xFullPathname(gQuota
.pOrigVfs
, zFilename
,
952 gQuota
.sThisVfs
.mxPathname
+1, zFull
);
956 zFull
[strlen(zFull
)+1] = '\0';
957 rc
= quotaOpen(&gQuota
.sThisVfs
, zFull
, fd
,
958 SQLITE_OPEN_READONLY
| SQLITE_OPEN_MAIN_DB
, &outFlags
);
960 fd
->pMethods
->xFileSize(fd
, &iSize
);
961 fd
->pMethods
->xClose(fd
);
962 }else if( rc
==SQLITE_CANTOPEN
){
966 pGroup
= quotaGroupFind(zFull
);
968 pFile
= quotaFindFile(pGroup
, zFull
, 0);
969 if( pFile
) quotaRemoveFile(pFile
);
980 ** Open a potentially quotaed file for I/O.
982 quota_FILE
*sqlite3_quota_fopen(const char *zFilename
, const char *zMode
){
985 char *zFullTranslated
= 0;
990 zFull
= (char*)sqlite3_malloc(gQuota
.sThisVfs
.mxPathname
+ 1);
991 if( zFull
==0 ) return 0;
992 rc
= gQuota
.pOrigVfs
->xFullPathname(gQuota
.pOrigVfs
, zFilename
,
993 gQuota
.sThisVfs
.mxPathname
+1, zFull
);
994 if( rc
) goto quota_fopen_error
;
995 p
= (quota_FILE
*)sqlite3_malloc(sizeof(*p
));
996 if( p
==0 ) goto quota_fopen_error
;
997 memset(p
, 0, sizeof(*p
));
998 zFullTranslated
= quota_utf8_to_mbcs(zFull
);
999 if( zFullTranslated
==0 ) goto quota_fopen_error
;
1000 p
->f
= fopen(zFullTranslated
, zMode
);
1001 if( p
->f
==0 ) goto quota_fopen_error
;
1003 pGroup
= quotaGroupFind(zFull
);
1005 pFile
= quotaFindFile(pGroup
, zFull
, 1);
1008 goto quota_fopen_error
;
1014 sqlite3_free(zFull
);
1016 p
->zMbcsName
= zFullTranslated
;
1021 quota_mbcs_free(zFullTranslated
);
1022 sqlite3_free(zFull
);
1023 if( p
&& p
->f
) fclose(p
->f
);
1029 ** Read content from a quota_FILE
1031 size_t sqlite3_quota_fread(
1032 void *pBuf
, /* Store the content here */
1033 size_t size
, /* Size of each element */
1034 size_t nmemb
, /* Number of elements to read */
1035 quota_FILE
*p
/* Read from this quota_FILE object */
1037 return fread(pBuf
, size
, nmemb
, p
->f
);
1041 ** Write content into a quota_FILE. Invoke the quota callback and block
1042 ** the write if we exceed quota.
1044 size_t sqlite3_quota_fwrite(
1045 void *pBuf
, /* Take content to write from here */
1046 size_t size
, /* Size of each element */
1047 size_t nmemb
, /* Number of elements */
1048 quota_FILE
*p
/* Write to this quota_FILE objecct */
1050 sqlite3_int64 iOfst
;
1052 sqlite3_int64 szNew
;
1056 iOfst
= ftell(p
->f
);
1057 iEnd
= iOfst
+ size
*nmemb
;
1059 if( pFile
&& pFile
->iSize
<iEnd
){
1060 quotaGroup
*pGroup
= pFile
->pGroup
;
1062 szNew
= pGroup
->iSize
- pFile
->iSize
+ iEnd
;
1063 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
1064 if( pGroup
->xCallback
){
1065 pGroup
->xCallback(pFile
->zFilename
, &pGroup
->iLimit
, szNew
,
1068 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
1069 iEnd
= pGroup
->iLimit
- pGroup
->iSize
+ pFile
->iSize
;
1070 nmemb
= (size_t)((iEnd
- iOfst
)/size
);
1071 iEnd
= iOfst
+ size
*nmemb
;
1072 szNew
= pGroup
->iSize
- pFile
->iSize
+ iEnd
;
1075 pGroup
->iSize
= szNew
;
1076 pFile
->iSize
= iEnd
;
1081 rc
= fwrite(pBuf
, size
, nmemb
, p
->f
);
1083 /* If the write was incomplete, adjust the file size and group size
1085 if( rc
<nmemb
&& pFile
){
1086 size_t nWritten
= rc
>=0 ? rc
: 0;
1087 sqlite3_int64 iNewEnd
= iOfst
+ size
*nWritten
;
1088 if( iNewEnd
<iEnd
) iNewEnd
= iEnd
;
1090 pFile
->pGroup
->iSize
+= iNewEnd
- pFile
->iSize
;
1091 pFile
->iSize
= iNewEnd
;
1098 ** Close an open quota_FILE stream.
1100 int sqlite3_quota_fclose(quota_FILE
*p
){
1108 if( pFile
->nRef
==0 ){
1109 quotaGroup
*pGroup
= pFile
->pGroup
;
1110 if( pFile
->deleteOnClose
){
1111 gQuota
.pOrigVfs
->xDelete(gQuota
.pOrigVfs
, pFile
->zFilename
, 0);
1112 quotaRemoveFile(pFile
);
1114 quotaGroupDeref(pGroup
);
1119 quota_mbcs_free(p
->zMbcsName
);
1126 ** Flush memory buffers for a quota_FILE to disk.
1128 int sqlite3_quota_fflush(quota_FILE
*p
, int doFsync
){
1131 if( rc
==0 && doFsync
){
1133 rc
= fsync(fileno(p
->f
));
1136 rc
= _commit(_fileno(p
->f
));
1143 ** Seek on a quota_FILE stream.
1145 int sqlite3_quota_fseek(quota_FILE
*p
, long offset
, int whence
){
1146 return fseek(p
->f
, offset
, whence
);
1150 ** rewind a quota_FILE stream.
1152 void sqlite3_quota_rewind(quota_FILE
*p
){
1157 ** Tell the current location of a quota_FILE stream.
1159 long sqlite3_quota_ftell(quota_FILE
*p
){
1164 ** Truncate a file to szNew bytes.
1166 int sqlite3_quota_ftruncate(quota_FILE
*p
, sqlite3_int64 szNew
){
1167 quotaFile
*pFile
= p
->pFile
;
1169 if( (pFile
= p
->pFile
)!=0 && pFile
->iSize
<szNew
){
1171 if( pFile
->iSize
<szNew
){
1172 /* This routine cannot be used to extend a file that is under
1173 ** quota management. Only true truncation is allowed. */
1176 pGroup
= pFile
->pGroup
;
1178 pGroup
->iSize
+= szNew
- pFile
->iSize
;
1182 rc
= ftruncate(fileno(p
->f
), szNew
);
1185 rc
= _chsize_s(_fileno(p
->f
), szNew
);
1187 if( pFile
&& rc
==0 ){
1188 quotaGroup
*pGroup
= pFile
->pGroup
;
1190 pGroup
->iSize
+= szNew
- pFile
->iSize
;
1191 pFile
->iSize
= szNew
;
1198 ** Determine the time that the given file was last modified, in
1199 ** seconds size 1970. Write the result into *pTime. Return 0 on
1200 ** success and non-zero on any kind of error.
1202 int sqlite3_quota_file_mtime(quota_FILE
*p
, time_t *pTime
){
1206 rc
= fstat(fileno(p
->f
), &buf
);
1209 struct _stati64 buf
;
1210 rc
= _stati64(p
->zMbcsName
, &buf
);
1212 if( rc
==0 ) *pTime
= buf
.st_mtime
;
1217 ** Return the true size of the file, as reported by the operating
1220 sqlite3_int64
sqlite3_quota_file_truesize(quota_FILE
*p
){
1224 rc
= fstat(fileno(p
->f
), &buf
);
1227 struct _stati64 buf
;
1228 rc
= _stati64(p
->zMbcsName
, &buf
);
1230 return rc
==0 ? buf
.st_size
: -1;
1234 ** Return the size of the file, as it is known to the quota subsystem.
1236 sqlite3_int64
sqlite3_quota_file_size(quota_FILE
*p
){
1237 return p
->pFile
? p
->pFile
->iSize
: -1;
1241 ** Remove a managed file. Update quotas accordingly.
1243 int sqlite3_quota_remove(const char *zFilename
){
1244 char *zFull
; /* Full pathname for zFilename */
1245 size_t nFull
; /* Number of bytes in zFilename */
1246 int rc
; /* Result code */
1247 quotaGroup
*pGroup
; /* Group containing zFilename */
1248 quotaFile
*pFile
; /* A file in the group */
1249 quotaFile
*pNextFile
; /* next file in the group */
1250 int diff
; /* Difference between filenames */
1251 char c
; /* First character past end of pattern */
1253 zFull
= (char*)sqlite3_malloc(gQuota
.sThisVfs
.mxPathname
+ 1);
1254 if( zFull
==0 ) return SQLITE_NOMEM
;
1255 rc
= gQuota
.pOrigVfs
->xFullPathname(gQuota
.pOrigVfs
, zFilename
,
1256 gQuota
.sThisVfs
.mxPathname
+1, zFull
);
1258 sqlite3_free(zFull
);
1262 /* Figure out the length of the full pathname. If the name ends with
1263 ** / (or \ on windows) then remove the trailing /.
1265 nFull
= strlen(zFull
);
1266 if( nFull
>0 && (zFull
[nFull
-1]=='/' || zFull
[nFull
-1]=='\\') ){
1272 pGroup
= quotaGroupFind(zFull
);
1274 for(pFile
=pGroup
->pFiles
; pFile
&& rc
==SQLITE_OK
; pFile
=pNextFile
){
1275 pNextFile
= pFile
->pNext
;
1276 diff
= memcmp(zFull
, pFile
->zFilename
, nFull
);
1277 if( diff
==0 && ((c
= pFile
->zFilename
[nFull
])==0 || c
=='/' || c
=='\\') ){
1279 pFile
->deleteOnClose
= 1;
1281 rc
= gQuota
.pOrigVfs
->xDelete(gQuota
.pOrigVfs
, pFile
->zFilename
, 0);
1282 quotaRemoveFile(pFile
);
1283 quotaGroupDeref(pGroup
);
1289 sqlite3_free(zFull
);
1293 /***************************** Test Code ***********************************/
1298 ** Argument passed to a TCL quota-over-limit callback.
1300 typedef struct TclQuotaCallback TclQuotaCallback
;
1301 struct TclQuotaCallback
{
1302 Tcl_Interp
*interp
; /* Interpreter in which to run the script */
1303 Tcl_Obj
*pScript
; /* Script to be run */
1306 extern const char *sqlite3TestErrorName(int);
1310 ** This is the callback from a quota-over-limit.
1312 static void tclQuotaCallback(
1313 const char *zFilename
, /* Name of file whose size increases */
1314 sqlite3_int64
*piLimit
, /* IN/OUT: The current limit */
1315 sqlite3_int64 iSize
, /* Total size of all files in the group */
1316 void *pArg
/* Client data */
1318 TclQuotaCallback
*p
; /* Callback script object */
1319 Tcl_Obj
*pEval
; /* Script to evaluate */
1320 Tcl_Obj
*pVarname
; /* Name of variable to pass as 2nd arg */
1321 unsigned int rnd
; /* Random part of pVarname */
1322 int rc
; /* Tcl error code */
1324 p
= (TclQuotaCallback
*)pArg
;
1327 pVarname
= Tcl_NewStringObj("::piLimit_", -1);
1328 Tcl_IncrRefCount(pVarname
);
1329 sqlite3_randomness(sizeof(rnd
), (void *)&rnd
);
1330 Tcl_AppendObjToObj(pVarname
, Tcl_NewIntObj((int)(rnd
&0x7FFFFFFF)));
1331 Tcl_ObjSetVar2(p
->interp
, pVarname
, 0, Tcl_NewWideIntObj(*piLimit
), 0);
1333 pEval
= Tcl_DuplicateObj(p
->pScript
);
1334 Tcl_IncrRefCount(pEval
);
1335 Tcl_ListObjAppendElement(0, pEval
, Tcl_NewStringObj(zFilename
, -1));
1336 Tcl_ListObjAppendElement(0, pEval
, pVarname
);
1337 Tcl_ListObjAppendElement(0, pEval
, Tcl_NewWideIntObj(iSize
));
1338 rc
= Tcl_EvalObjEx(p
->interp
, pEval
, TCL_EVAL_GLOBAL
);
1341 Tcl_Obj
*pLimit
= Tcl_ObjGetVar2(p
->interp
, pVarname
, 0, 0);
1342 rc
= Tcl_GetWideIntFromObj(p
->interp
, pLimit
, piLimit
);
1343 Tcl_UnsetVar(p
->interp
, Tcl_GetString(pVarname
), 0);
1346 Tcl_DecrRefCount(pEval
);
1347 Tcl_DecrRefCount(pVarname
);
1348 if( rc
!=TCL_OK
) Tcl_BackgroundError(p
->interp
);
1352 ** Destructor for a TCL quota-over-limit callback.
1354 static void tclCallbackDestructor(void *pObj
){
1355 TclQuotaCallback
*p
= (TclQuotaCallback
*)pObj
;
1357 Tcl_DecrRefCount(p
->pScript
);
1358 sqlite3_free((char *)p
);
1363 ** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT
1365 static int test_quota_initialize(
1369 Tcl_Obj
*CONST objv
[]
1371 const char *zName
; /* Name of new quota VFS */
1372 int makeDefault
; /* True to make the new VFS the default */
1373 int rc
; /* Value returned by quota_initialize() */
1375 /* Process arguments */
1377 Tcl_WrongNumArgs(interp
, 1, objv
, "NAME MAKEDEFAULT");
1380 zName
= Tcl_GetString(objv
[1]);
1381 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &makeDefault
) ) return TCL_ERROR
;
1382 if( zName
[0]=='\0' ) zName
= 0;
1384 /* Call sqlite3_quota_initialize() */
1385 rc
= sqlite3_quota_initialize(zName
, makeDefault
);
1386 Tcl_SetResult(interp
, (char *)sqlite3TestErrorName(rc
), TCL_STATIC
);
1392 ** tclcmd: sqlite3_quota_shutdown
1394 static int test_quota_shutdown(
1398 Tcl_Obj
*CONST objv
[]
1400 int rc
; /* Value returned by quota_shutdown() */
1403 Tcl_WrongNumArgs(interp
, 1, objv
, "");
1407 /* Call sqlite3_quota_shutdown() */
1408 rc
= sqlite3_quota_shutdown();
1409 Tcl_SetResult(interp
, (char *)sqlite3TestErrorName(rc
), TCL_STATIC
);
1415 ** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT
1417 static int test_quota_set(
1421 Tcl_Obj
*CONST objv
[]
1423 const char *zPattern
; /* File pattern to configure */
1424 sqlite3_int64 iLimit
; /* Initial quota in bytes */
1425 Tcl_Obj
*pScript
; /* Tcl script to invoke to increase quota */
1426 int rc
; /* Value returned by quota_set() */
1427 TclQuotaCallback
*p
; /* Callback object */
1428 int nScript
; /* Length of callback script */
1429 void (*xDestroy
)(void*); /* Optional destructor for pArg */
1430 void (*xCallback
)(const char *, sqlite3_int64
*, sqlite3_int64
, void *);
1432 /* Process arguments */
1434 Tcl_WrongNumArgs(interp
, 1, objv
, "PATTERN LIMIT SCRIPT");
1437 zPattern
= Tcl_GetString(objv
[1]);
1438 if( Tcl_GetWideIntFromObj(interp
, objv
[2], &iLimit
) ) return TCL_ERROR
;
1440 Tcl_GetStringFromObj(pScript
, &nScript
);
1443 /* Allocate a TclQuotaCallback object */
1444 p
= (TclQuotaCallback
*)sqlite3_malloc(sizeof(TclQuotaCallback
));
1446 Tcl_SetResult(interp
, (char *)"SQLITE_NOMEM", TCL_STATIC
);
1449 memset(p
, 0, sizeof(TclQuotaCallback
));
1451 Tcl_IncrRefCount(pScript
);
1452 p
->pScript
= pScript
;
1453 xDestroy
= tclCallbackDestructor
;
1454 xCallback
= tclQuotaCallback
;
1461 /* Invoke sqlite3_quota_set() */
1462 rc
= sqlite3_quota_set(zPattern
, iLimit
, xCallback
, (void*)p
, xDestroy
);
1464 Tcl_SetResult(interp
, (char *)sqlite3TestErrorName(rc
), TCL_STATIC
);
1469 ** tclcmd: sqlite3_quota_file FILENAME
1471 static int test_quota_file(
1475 Tcl_Obj
*CONST objv
[]
1477 const char *zFilename
; /* File pattern to configure */
1478 int rc
; /* Value returned by quota_file() */
1480 /* Process arguments */
1482 Tcl_WrongNumArgs(interp
, 1, objv
, "FILENAME");
1485 zFilename
= Tcl_GetString(objv
[1]);
1487 /* Invoke sqlite3_quota_file() */
1488 rc
= sqlite3_quota_file(zFilename
);
1490 Tcl_SetResult(interp
, (char *)sqlite3TestErrorName(rc
), TCL_STATIC
);
1495 ** tclcmd: sqlite3_quota_dump
1497 static int test_quota_dump(
1501 Tcl_Obj
*CONST objv
[]
1504 Tcl_Obj
*pGroupTerm
;
1509 pResult
= Tcl_NewObj();
1511 for(pGroup
=gQuota
.pGroup
; pGroup
; pGroup
=pGroup
->pNext
){
1512 pGroupTerm
= Tcl_NewObj();
1513 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1514 Tcl_NewStringObj(pGroup
->zPattern
, -1));
1515 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1516 Tcl_NewWideIntObj(pGroup
->iLimit
));
1517 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1518 Tcl_NewWideIntObj(pGroup
->iSize
));
1519 for(pFile
=pGroup
->pFiles
; pFile
; pFile
=pFile
->pNext
){
1522 pFileTerm
= Tcl_NewObj();
1523 sqlite3_snprintf(sizeof(zTemp
), zTemp
, "%s", pFile
->zFilename
);
1524 for(i
=0; zTemp
[i
]; i
++){ if( zTemp
[i
]=='\\' ) zTemp
[i
] = '/'; }
1525 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1526 Tcl_NewStringObj(zTemp
, -1));
1527 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1528 Tcl_NewWideIntObj(pFile
->iSize
));
1529 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1530 Tcl_NewWideIntObj(pFile
->nRef
));
1531 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1532 Tcl_NewWideIntObj(pFile
->deleteOnClose
));
1533 Tcl_ListObjAppendElement(interp
, pGroupTerm
, pFileTerm
);
1535 Tcl_ListObjAppendElement(interp
, pResult
, pGroupTerm
);
1538 Tcl_SetObjResult(interp
, pResult
);
1543 ** tclcmd: sqlite3_quota_fopen FILENAME MODE
1545 static int test_quota_fopen(
1549 Tcl_Obj
*CONST objv
[]
1551 const char *zFilename
; /* File pattern to configure */
1552 const char *zMode
; /* Mode string */
1553 quota_FILE
*p
; /* Open string object */
1554 char zReturn
[50]; /* Name of pointer to return */
1556 /* Process arguments */
1558 Tcl_WrongNumArgs(interp
, 1, objv
, "FILENAME MODE");
1561 zFilename
= Tcl_GetString(objv
[1]);
1562 zMode
= Tcl_GetString(objv
[2]);
1563 p
= sqlite3_quota_fopen(zFilename
, zMode
);
1564 sqlite3_snprintf(sizeof(zReturn
), zReturn
, "%p", p
);
1565 Tcl_SetResult(interp
, zReturn
, TCL_VOLATILE
);
1569 /* Defined in test1.c */
1570 extern void *sqlite3TestTextToPtr(const char*);
1573 ** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
1575 static int test_quota_fread(
1579 Tcl_Obj
*CONST objv
[]
1588 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE SIZE NELEM");
1591 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1592 if( Tcl_GetIntFromObj(interp
, objv
[2], &sz
) ) return TCL_ERROR
;
1593 if( Tcl_GetIntFromObj(interp
, objv
[3], &nElem
) ) return TCL_ERROR
;
1594 zBuf
= (char*)sqlite3_malloc( sz
*nElem
+ 1 );
1596 Tcl_SetResult(interp
, "out of memory", TCL_STATIC
);
1599 got
= sqlite3_quota_fread(zBuf
, sz
, nElem
, p
);
1600 if( got
<0 ) got
= 0;
1602 Tcl_SetResult(interp
, zBuf
, TCL_VOLATILE
);
1608 ** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
1610 static int test_quota_fwrite(
1614 Tcl_Obj
*CONST objv
[]
1623 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE SIZE NELEM CONTENT");
1626 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1627 if( Tcl_GetIntFromObj(interp
, objv
[2], &sz
) ) return TCL_ERROR
;
1628 if( Tcl_GetIntFromObj(interp
, objv
[3], &nElem
) ) return TCL_ERROR
;
1629 zBuf
= Tcl_GetString(objv
[4]);
1630 got
= sqlite3_quota_fwrite(zBuf
, sz
, nElem
, p
);
1631 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(got
));
1636 ** tclcmd: sqlite3_quota_fclose HANDLE
1638 static int test_quota_fclose(
1642 Tcl_Obj
*CONST objv
[]
1648 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1651 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1652 rc
= sqlite3_quota_fclose(p
);
1653 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1658 ** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
1660 static int test_quota_fflush(
1664 Tcl_Obj
*CONST objv
[]
1670 if( objc
!=2 && objc
!=3 ){
1671 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE ?HARDSYNC?");
1674 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1676 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &doSync
) ) return TCL_ERROR
;
1678 rc
= sqlite3_quota_fflush(p
, doSync
);
1679 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1684 ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
1686 static int test_quota_fseek(
1690 Tcl_Obj
*CONST objv
[]
1694 const char *zWhence
;
1699 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE OFFSET WHENCE");
1702 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1703 if( Tcl_GetIntFromObj(interp
, objv
[2], &ofst
) ) return TCL_ERROR
;
1704 zWhence
= Tcl_GetString(objv
[3]);
1705 if( strcmp(zWhence
, "SEEK_SET")==0 ){
1707 }else if( strcmp(zWhence
, "SEEK_CUR")==0 ){
1709 }else if( strcmp(zWhence
, "SEEK_END")==0 ){
1712 Tcl_AppendResult(interp
,
1713 "WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0);
1716 rc
= sqlite3_quota_fseek(p
, ofst
, whence
);
1717 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1722 ** tclcmd: sqlite3_quota_rewind HANDLE
1724 static int test_quota_rewind(
1728 Tcl_Obj
*CONST objv
[]
1732 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1735 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1736 sqlite3_quota_rewind(p
);
1741 ** tclcmd: sqlite3_quota_ftell HANDLE
1743 static int test_quota_ftell(
1747 Tcl_Obj
*CONST objv
[]
1752 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1755 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1756 x
= sqlite3_quota_ftell(p
);
1757 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(x
));
1762 ** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE
1764 static int test_quota_ftruncate(
1768 Tcl_Obj
*CONST objv
[]
1775 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE SIZE");
1778 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1779 if( Tcl_GetWideIntFromObj(interp
, objv
[2], &w
) ) return TCL_ERROR
;
1780 x
= (sqlite3_int64
)w
;
1781 rc
= sqlite3_quota_ftruncate(p
, x
);
1782 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1787 ** tclcmd: sqlite3_quota_file_size HANDLE
1789 static int test_quota_file_size(
1793 Tcl_Obj
*CONST objv
[]
1798 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1801 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1802 x
= sqlite3_quota_file_size(p
);
1803 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(x
));
1808 ** tclcmd: sqlite3_quota_file_truesize HANDLE
1810 static int test_quota_file_truesize(
1814 Tcl_Obj
*CONST objv
[]
1819 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1822 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1823 x
= sqlite3_quota_file_truesize(p
);
1824 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(x
));
1829 ** tclcmd: sqlite3_quota_file_mtime HANDLE
1831 static int test_quota_file_mtime(
1835 Tcl_Obj
*CONST objv
[]
1840 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1843 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1845 sqlite3_quota_file_mtime(p
, &t
);
1846 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(t
));
1852 ** tclcmd: sqlite3_quota_remove FILENAME
1854 static int test_quota_remove(
1858 Tcl_Obj
*CONST objv
[]
1860 const char *zFilename
; /* File pattern to configure */
1863 Tcl_WrongNumArgs(interp
, 1, objv
, "FILENAME");
1866 zFilename
= Tcl_GetString(objv
[1]);
1867 rc
= sqlite3_quota_remove(zFilename
);
1868 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1873 ** tclcmd: sqlite3_quota_glob PATTERN TEXT
1875 ** Test the glob pattern matching. Return 1 if TEXT matches PATTERN
1876 ** and return 0 if it does not.
1878 static int test_quota_glob(
1882 Tcl_Obj
*CONST objv
[]
1884 const char *zPattern
; /* The glob pattern */
1885 const char *zText
; /* Text to compare agains the pattern */
1888 Tcl_WrongNumArgs(interp
, 1, objv
, "PATTERN TEXT");
1891 zPattern
= Tcl_GetString(objv
[1]);
1892 zText
= Tcl_GetString(objv
[2]);
1893 rc
= quotaStrglob(zPattern
, zText
);
1894 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1899 ** This routine registers the custom TCL commands defined in this
1900 ** module. This should be the only procedure visible from outside
1903 int Sqlitequota_Init(Tcl_Interp
*interp
){
1906 Tcl_ObjCmdProc
*xProc
;
1908 { "sqlite3_quota_initialize", test_quota_initialize
},
1909 { "sqlite3_quota_shutdown", test_quota_shutdown
},
1910 { "sqlite3_quota_set", test_quota_set
},
1911 { "sqlite3_quota_file", test_quota_file
},
1912 { "sqlite3_quota_dump", test_quota_dump
},
1913 { "sqlite3_quota_fopen", test_quota_fopen
},
1914 { "sqlite3_quota_fread", test_quota_fread
},
1915 { "sqlite3_quota_fwrite", test_quota_fwrite
},
1916 { "sqlite3_quota_fclose", test_quota_fclose
},
1917 { "sqlite3_quota_fflush", test_quota_fflush
},
1918 { "sqlite3_quota_fseek", test_quota_fseek
},
1919 { "sqlite3_quota_rewind", test_quota_rewind
},
1920 { "sqlite3_quota_ftell", test_quota_ftell
},
1921 { "sqlite3_quota_ftruncate", test_quota_ftruncate
},
1922 { "sqlite3_quota_file_size", test_quota_file_size
},
1923 { "sqlite3_quota_file_truesize", test_quota_file_truesize
},
1924 { "sqlite3_quota_file_mtime", test_quota_file_mtime
},
1925 { "sqlite3_quota_remove", test_quota_remove
},
1926 { "sqlite3_quota_glob", test_quota_glob
},
1930 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
1931 Tcl_CreateObjCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);