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 */
58 /************************ Object Definitions ******************************/
60 /* Forward declaration of all object types */
61 typedef struct quotaGroup quotaGroup
;
62 typedef struct quotaConn quotaConn
;
63 typedef struct quotaFile quotaFile
;
66 ** A "quota group" is a collection of files whose collective size we want
67 ** to limit. Each quota group is defined by a GLOB pattern.
69 ** There is an instance of the following object for each defined quota
70 ** group. This object records the GLOB pattern that defines which files
71 ** belong to the quota group. The object also remembers the size limit
72 ** for the group (the quota) and the callback to be invoked when the
73 ** sum of the sizes of the files within the group goes over the limit.
75 ** A quota group must be established (using sqlite3_quota_set(...))
76 ** prior to opening any of the database connections that access files
77 ** within the quota group.
80 const char *zPattern
; /* Filename pattern to be quotaed */
81 sqlite3_int64 iLimit
; /* Upper bound on total file size */
82 sqlite3_int64 iSize
; /* Current size of all files */
83 void (*xCallback
)( /* Callback invoked when going over quota */
84 const char *zFilename
, /* Name of file whose size increases */
85 sqlite3_int64
*piLimit
, /* IN/OUT: The current limit */
86 sqlite3_int64 iSize
, /* Total size of all files in the group */
87 void *pArg
/* Client data */
89 void *pArg
; /* Third argument to the xCallback() */
90 void (*xDestroy
)(void*); /* Optional destructor for pArg */
91 quotaGroup
*pNext
, **ppPrev
; /* Doubly linked list of all quota objects */
92 quotaFile
*pFiles
; /* Files within this group */
96 ** An instance of this structure represents a single file that is part
97 ** of a quota group. A single file can be opened multiple times. In
98 ** order keep multiple openings of the same file from causing the size
99 ** of the file to count against the quota multiple times, each file
100 ** has a unique instance of this object and multiple open connections
101 ** to the same file each point to a single instance of this object.
104 char *zFilename
; /* Name of this file */
105 quotaGroup
*pGroup
; /* Quota group to which this file belongs */
106 sqlite3_int64 iSize
; /* Current size of this file */
107 int nRef
; /* Number of times this file is open */
108 int deleteOnClose
; /* True to delete this file when it closes */
109 quotaFile
*pNext
, **ppPrev
; /* Linked list of files in the same group */
113 ** An instance of the following object represents each open connection
114 ** to a file that participates in quota tracking. This object is a
115 ** subclass of sqlite3_file. The sqlite3_file object for the underlying
116 ** VFS is appended to this structure.
119 sqlite3_file base
; /* Base class - must be first */
120 quotaFile
*pFile
; /* The underlying file */
121 /* The underlying VFS sqlite3_file is appended to this object */
125 ** An instance of the following object records the state of an
126 ** open file. This object is opaque to all users - the internal
127 ** structure is only visible to the functions below.
130 FILE *f
; /* Open stdio file pointer */
131 sqlite3_int64 iOfst
; /* Current offset into the file */
132 quotaFile
*pFile
; /* The file record in the quota system */
134 char *zMbcsName
; /* Full MBCS pathname of the file */
139 /************************* Global Variables **********************************/
141 ** All global variables used by this file are containing within the following
145 /* The pOrigVfs is the real, original underlying VFS implementation.
146 ** Most operations pass-through to the real VFS. This value is read-only
147 ** during operation. It is only modified at start-time and thus does not
150 sqlite3_vfs
*pOrigVfs
;
152 /* The sThisVfs is the VFS structure used by this shim. It is initialized
153 ** at start-time and thus does not require a mutex
155 sqlite3_vfs sThisVfs
;
157 /* The sIoMethods defines the methods used by sqlite3_file objects
158 ** associated with this shim. It is initialized at start-time and does
159 ** not require a mutex.
161 ** When the underlying VFS is called to open a file, it might return
162 ** either a version 1 or a version 2 sqlite3_file object. This shim
163 ** has to create a wrapper sqlite3_file of the same version. Hence
164 ** there are two I/O method structures, one for version 1 and the other
167 sqlite3_io_methods sIoMethodsV1
;
168 sqlite3_io_methods sIoMethodsV2
;
170 /* True when this shim as been initialized.
174 /* For run-time access any of the other global data structures in this
175 ** shim, the following mutex must be held.
177 sqlite3_mutex
*pMutex
;
179 /* List of quotaGroup objects.
185 /************************* Utility Routines *********************************/
187 ** Acquire and release the mutex used to serialize access to the
188 ** list of quotaGroups.
190 static void quotaEnter(void){ sqlite3_mutex_enter(gQuota
.pMutex
); }
191 static void quotaLeave(void){ sqlite3_mutex_leave(gQuota
.pMutex
); }
193 /* Count the number of open files in a quotaGroup
195 static int quotaGroupOpenFileCount(quotaGroup
*pGroup
){
197 quotaFile
*pFile
= pGroup
->pFiles
;
199 if( pFile
->nRef
) N
++;
200 pFile
= pFile
->pNext
;
205 /* Remove a file from a quota group.
207 static void quotaRemoveFile(quotaFile
*pFile
){
208 quotaGroup
*pGroup
= pFile
->pGroup
;
209 pGroup
->iSize
-= pFile
->iSize
;
210 *pFile
->ppPrev
= pFile
->pNext
;
211 if( pFile
->pNext
) pFile
->pNext
->ppPrev
= pFile
->ppPrev
;
215 /* Remove all files from a quota group. It is always the case that
216 ** all files will be closed when this routine is called.
218 static void quotaRemoveAllFiles(quotaGroup
*pGroup
){
219 while( pGroup
->pFiles
){
220 assert( pGroup
->pFiles
->nRef
==0 );
221 quotaRemoveFile(pGroup
->pFiles
);
226 /* If the reference count and threshold for a quotaGroup are both
227 ** zero, then destroy the quotaGroup.
229 static void quotaGroupDeref(quotaGroup
*pGroup
){
230 if( pGroup
->iLimit
==0 && quotaGroupOpenFileCount(pGroup
)==0 ){
231 quotaRemoveAllFiles(pGroup
);
232 *pGroup
->ppPrev
= pGroup
->pNext
;
233 if( pGroup
->pNext
) pGroup
->pNext
->ppPrev
= pGroup
->ppPrev
;
234 if( pGroup
->xDestroy
) pGroup
->xDestroy(pGroup
->pArg
);
235 sqlite3_free(pGroup
);
240 ** Return TRUE if string z matches glob pattern zGlob.
244 ** '*' Matches any sequence of zero or more characters.
246 ** '?' Matches exactly one character.
248 ** [...] Matches one character from the enclosed list of
251 ** [^...] Matches one character not in the enclosed list.
253 ** / Matches "/" or "\\"
256 static int quotaStrglob(const char *zGlob
, const char *z
){
261 while( (c
= (*(zGlob
++)))!=0 ){
263 while( (c
=(*(zGlob
++))) == '*' || c
=='?' ){
264 if( c
=='?' && (*(z
++))==0 ) return 0;
269 while( *z
&& quotaStrglob(zGlob
-1,z
)==0 ){
274 cx
= (c
=='/') ? '\\' : c
;
275 while( (c2
= (*(z
++)))!=0 ){
276 while( c2
!=c
&& c2
!=cx
){
278 if( c2
==0 ) return 0;
280 if( quotaStrglob(zGlob
,z
) ) return 1;
284 if( (*(z
++))==0 ) return 0;
297 if( c
==']' ) seen
= 1;
300 while( c2
&& c2
!=']' ){
301 if( c2
=='-' && zGlob
[0]!=']' && zGlob
[0]!=0 && prior_c
>0 ){
303 if( c
>=prior_c
&& c
<=c2
) seen
= 1;
313 if( c2
==0 || (seen
^ invert
)==0 ) return 0;
315 if( z
[0]!='/' && z
[0]!='\\' ) return 0;
318 if( c
!=(*(z
++)) ) return 0;
325 /* Find a quotaGroup given the filename.
327 ** Return a pointer to the quotaGroup object. Return NULL if not found.
329 static quotaGroup
*quotaGroupFind(const char *zFilename
){
331 for(p
=gQuota
.pGroup
; p
&& quotaStrglob(p
->zPattern
, zFilename
)==0;
336 /* Translate an sqlite3_file* that is really a quotaConn* into
337 ** the sqlite3_file* for the underlying original VFS.
339 static sqlite3_file
*quotaSubOpen(sqlite3_file
*pConn
){
340 quotaConn
*p
= (quotaConn
*)pConn
;
341 return (sqlite3_file
*)&p
[1];
344 /* Find a file in a quota group and return a pointer to that file.
345 ** Return NULL if the file is not in the group.
347 static quotaFile
*quotaFindFile(
348 quotaGroup
*pGroup
, /* Group in which to look for the file */
349 const char *zName
, /* Full pathname of the file */
350 int createFlag
/* Try to create the file if not found */
352 quotaFile
*pFile
= pGroup
->pFiles
;
353 while( pFile
&& strcmp(pFile
->zFilename
, zName
)!=0 ){
354 pFile
= pFile
->pNext
;
356 if( pFile
==0 && createFlag
){
357 int nName
= (int)(strlen(zName
) & 0x3fffffff);
358 pFile
= (quotaFile
*)sqlite3_malloc( sizeof(*pFile
) + nName
+ 1 );
360 memset(pFile
, 0, sizeof(*pFile
));
361 pFile
->zFilename
= (char*)&pFile
[1];
362 memcpy(pFile
->zFilename
, zName
, nName
+1);
363 pFile
->pNext
= pGroup
->pFiles
;
364 if( pGroup
->pFiles
) pGroup
->pFiles
->ppPrev
= &pFile
->pNext
;
365 pFile
->ppPrev
= &pGroup
->pFiles
;
366 pGroup
->pFiles
= pFile
;
367 pFile
->pGroup
= pGroup
;
373 ** Translate UTF8 to MBCS for use in fopen() calls. Return a pointer to the
374 ** translated text.. Call quota_mbcs_free() to deallocate any memory
375 ** used to store the returned pointer when done.
377 static char *quota_utf8_to_mbcs(const char *zUtf8
){
379 size_t n
; /* Bytes in zUtf8 */
380 int nWide
; /* number of UTF-16 characters */
381 int nMbcs
; /* Bytes of MBCS */
382 LPWSTR zTmpWide
; /* The UTF16 text */
383 char *zMbcs
; /* The MBCS text */
384 int codepage
; /* Code page used by fopen() */
387 nWide
= MultiByteToWideChar(CP_UTF8
, 0, zUtf8
, -1, NULL
, 0);
388 if( nWide
==0 ) return 0;
389 zTmpWide
= (LPWSTR
)sqlite3_malloc( (nWide
+1)*sizeof(zTmpWide
[0]) );
390 if( zTmpWide
==0 ) return 0;
391 MultiByteToWideChar(CP_UTF8
, 0, zUtf8
, -1, zTmpWide
, nWide
);
392 codepage
= AreFileApisANSI() ? CP_ACP
: CP_OEMCP
;
393 nMbcs
= WideCharToMultiByte(codepage
, 0, zTmpWide
, nWide
, 0, 0, 0, 0);
394 zMbcs
= nMbcs
? (char*)sqlite3_malloc( nMbcs
+1 ) : 0;
396 WideCharToMultiByte(codepage
, 0, zTmpWide
, nWide
, zMbcs
, nMbcs
, 0, 0);
398 sqlite3_free(zTmpWide
);
401 return (char*)zUtf8
; /* No-op on unix */
406 ** Deallocate any memory allocated by quota_utf8_to_mbcs().
408 static void quota_mbcs_free(char *zOld
){
416 /************************* VFS Method Wrappers *****************************/
418 ** This is the xOpen method used for the "quota" VFS.
420 ** Most of the work is done by the underlying original VFS. This method
421 ** simply links the new file into the appropriate quota group if it is a
422 ** file that needs to be tracked.
424 static int quotaOpen(
425 sqlite3_vfs
*pVfs
, /* The quota VFS */
426 const char *zName
, /* Name of file to be opened */
427 sqlite3_file
*pConn
, /* Fill in this file descriptor */
428 int flags
, /* Flags to control the opening */
429 int *pOutFlags
/* Flags showing results of opening */
431 int rc
; /* Result code */
432 quotaConn
*pQuotaOpen
; /* The new quota file descriptor */
433 quotaFile
*pFile
; /* Corresponding quotaFile obj */
434 quotaGroup
*pGroup
; /* The group file belongs to */
435 sqlite3_file
*pSubOpen
; /* Real file descriptor */
436 sqlite3_vfs
*pOrigVfs
= gQuota
.pOrigVfs
; /* Real VFS */
438 /* If the file is not a main database file or a WAL, then use the
439 ** normal xOpen method.
441 if( (flags
& (SQLITE_OPEN_MAIN_DB
|SQLITE_OPEN_WAL
))==0 ){
442 return pOrigVfs
->xOpen(pOrigVfs
, zName
, pConn
, flags
, pOutFlags
);
445 /* If the name of the file does not match any quota group, then
446 ** use the normal xOpen method.
449 pGroup
= quotaGroupFind(zName
);
451 rc
= pOrigVfs
->xOpen(pOrigVfs
, zName
, pConn
, flags
, pOutFlags
);
453 /* If we get to this point, it means the file needs to be quota tracked.
455 pQuotaOpen
= (quotaConn
*)pConn
;
456 pSubOpen
= quotaSubOpen(pConn
);
457 rc
= pOrigVfs
->xOpen(pOrigVfs
, zName
, pSubOpen
, flags
, pOutFlags
);
459 pFile
= quotaFindFile(pGroup
, zName
, 1);
462 pSubOpen
->pMethods
->xClose(pSubOpen
);
465 pFile
->deleteOnClose
= (flags
& SQLITE_OPEN_DELETEONCLOSE
)!=0;
467 pQuotaOpen
->pFile
= pFile
;
468 if( pSubOpen
->pMethods
->iVersion
==1 ){
469 pQuotaOpen
->base
.pMethods
= &gQuota
.sIoMethodsV1
;
471 pQuotaOpen
->base
.pMethods
= &gQuota
.sIoMethodsV2
;
480 ** This is the xDelete method used for the "quota" VFS.
482 ** If the file being deleted is part of the quota group, then reduce
483 ** the size of the quota group accordingly. And remove the file from
484 ** the set of files in the quota group.
486 static int quotaDelete(
487 sqlite3_vfs
*pVfs
, /* The quota VFS */
488 const char *zName
, /* Name of file to be deleted */
489 int syncDir
/* Do a directory sync after deleting */
491 int rc
; /* Result code */
492 quotaFile
*pFile
; /* Files in the quota */
493 quotaGroup
*pGroup
; /* The group file belongs to */
494 sqlite3_vfs
*pOrigVfs
= gQuota
.pOrigVfs
; /* Real VFS */
496 /* Do the actual file delete */
497 rc
= pOrigVfs
->xDelete(pOrigVfs
, zName
, syncDir
);
499 /* If the file just deleted is a member of a quota group, then remove
500 ** it from that quota group.
504 pGroup
= quotaGroupFind(zName
);
506 pFile
= quotaFindFile(pGroup
, zName
, 0);
509 pFile
->deleteOnClose
= 1;
511 quotaRemoveFile(pFile
);
512 quotaGroupDeref(pGroup
);
522 /************************ I/O Method Wrappers *******************************/
524 /* xClose requests get passed through to the original VFS. But we
525 ** also have to unlink the quotaConn from the quotaFile and quotaGroup.
526 ** The quotaFile and/or quotaGroup are freed if they are no longer in use.
528 static int quotaClose(sqlite3_file
*pConn
){
529 quotaConn
*p
= (quotaConn
*)pConn
;
530 quotaFile
*pFile
= p
->pFile
;
531 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
533 rc
= pSubOpen
->pMethods
->xClose(pSubOpen
);
536 if( pFile
->nRef
==0 ){
537 quotaGroup
*pGroup
= pFile
->pGroup
;
538 if( pFile
->deleteOnClose
){
539 gQuota
.pOrigVfs
->xDelete(gQuota
.pOrigVfs
, pFile
->zFilename
, 0);
540 quotaRemoveFile(pFile
);
542 quotaGroupDeref(pGroup
);
548 /* Pass xRead requests directory thru to the original VFS without
549 ** further processing.
551 static int quotaRead(
557 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
558 return pSubOpen
->pMethods
->xRead(pSubOpen
, pBuf
, iAmt
, iOfst
);
561 /* Check xWrite requests to see if they expand the file. If they do,
562 ** the perform a quota check before passing them through to the
565 static int quotaWrite(
571 quotaConn
*p
= (quotaConn
*)pConn
;
572 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
573 sqlite3_int64 iEnd
= iOfst
+iAmt
;
575 quotaFile
*pFile
= p
->pFile
;
578 if( pFile
->iSize
<iEnd
){
579 pGroup
= pFile
->pGroup
;
581 szNew
= pGroup
->iSize
- pFile
->iSize
+ iEnd
;
582 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
583 if( pGroup
->xCallback
){
584 pGroup
->xCallback(pFile
->zFilename
, &pGroup
->iLimit
, szNew
,
587 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
592 pGroup
->iSize
= szNew
;
596 return pSubOpen
->pMethods
->xWrite(pSubOpen
, pBuf
, iAmt
, iOfst
);
599 /* Pass xTruncate requests thru to the original VFS. If the
600 ** success, update the file size.
602 static int quotaTruncate(sqlite3_file
*pConn
, sqlite3_int64 size
){
603 quotaConn
*p
= (quotaConn
*)pConn
;
604 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
605 int rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, size
);
606 quotaFile
*pFile
= p
->pFile
;
610 pGroup
= pFile
->pGroup
;
611 pGroup
->iSize
-= pFile
->iSize
;
613 pGroup
->iSize
+= size
;
619 /* Pass xSync requests through to the original VFS without change
621 static int quotaSync(sqlite3_file
*pConn
, int flags
){
622 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
623 return pSubOpen
->pMethods
->xSync(pSubOpen
, flags
);
626 /* Pass xFileSize requests through to the original VFS but then
627 ** update the quotaGroup with the new size before returning.
629 static int quotaFileSize(sqlite3_file
*pConn
, sqlite3_int64
*pSize
){
630 quotaConn
*p
= (quotaConn
*)pConn
;
631 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
632 quotaFile
*pFile
= p
->pFile
;
637 rc
= pSubOpen
->pMethods
->xFileSize(pSubOpen
, &sz
);
640 pGroup
= pFile
->pGroup
;
641 pGroup
->iSize
-= pFile
->iSize
;
650 /* Pass xLock requests through to the original VFS unchanged.
652 static int quotaLock(sqlite3_file
*pConn
, int lock
){
653 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
654 return pSubOpen
->pMethods
->xLock(pSubOpen
, lock
);
657 /* Pass xUnlock requests through to the original VFS unchanged.
659 static int quotaUnlock(sqlite3_file
*pConn
, int lock
){
660 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
661 return pSubOpen
->pMethods
->xUnlock(pSubOpen
, lock
);
664 /* Pass xCheckReservedLock requests through to the original VFS unchanged.
666 static int quotaCheckReservedLock(sqlite3_file
*pConn
, int *pResOut
){
667 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
668 return pSubOpen
->pMethods
->xCheckReservedLock(pSubOpen
, pResOut
);
671 /* Pass xFileControl requests through to the original VFS unchanged.
673 static int quotaFileControl(sqlite3_file
*pConn
, int op
, void *pArg
){
674 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
675 int rc
= pSubOpen
->pMethods
->xFileControl(pSubOpen
, op
, pArg
);
676 #if defined(SQLITE_FCNTL_VFSNAME)
677 if( op
==SQLITE_FCNTL_VFSNAME
&& rc
==SQLITE_OK
){
678 *(char**)pArg
= sqlite3_mprintf("quota/%z", *(char**)pArg
);
684 /* Pass xSectorSize requests through to the original VFS unchanged.
686 static int quotaSectorSize(sqlite3_file
*pConn
){
687 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
688 return pSubOpen
->pMethods
->xSectorSize(pSubOpen
);
691 /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
693 static int quotaDeviceCharacteristics(sqlite3_file
*pConn
){
694 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
695 return pSubOpen
->pMethods
->xDeviceCharacteristics(pSubOpen
);
698 /* Pass xShmMap requests through to the original VFS unchanged.
700 static int quotaShmMap(
701 sqlite3_file
*pConn
, /* Handle open on database file */
702 int iRegion
, /* Region to retrieve */
703 int szRegion
, /* Size of regions */
704 int bExtend
, /* True to extend file if necessary */
705 void volatile **pp
/* OUT: Mapped memory */
707 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
708 return pSubOpen
->pMethods
->xShmMap(pSubOpen
, iRegion
, szRegion
, bExtend
, pp
);
711 /* Pass xShmLock requests through to the original VFS unchanged.
713 static int quotaShmLock(
714 sqlite3_file
*pConn
, /* Database file holding the shared memory */
715 int ofst
, /* First lock to acquire or release */
716 int n
, /* Number of locks to acquire or release */
717 int flags
/* What to do with the lock */
719 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
720 return pSubOpen
->pMethods
->xShmLock(pSubOpen
, ofst
, n
, flags
);
723 /* Pass xShmBarrier requests through to the original VFS unchanged.
725 static void quotaShmBarrier(sqlite3_file
*pConn
){
726 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
727 pSubOpen
->pMethods
->xShmBarrier(pSubOpen
);
730 /* Pass xShmUnmap requests through to the original VFS unchanged.
732 static int quotaShmUnmap(sqlite3_file
*pConn
, int deleteFlag
){
733 sqlite3_file
*pSubOpen
= quotaSubOpen(pConn
);
734 return pSubOpen
->pMethods
->xShmUnmap(pSubOpen
, deleteFlag
);
737 /************************** Public Interfaces *****************************/
739 ** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
740 ** as the VFS that does the actual work. Use the default if
741 ** zOrigVfsName==NULL.
743 ** The quota VFS shim is named "quota". It will become the default
744 ** VFS if makeDefault is non-zero.
746 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
749 int sqlite3_quota_initialize(const char *zOrigVfsName
, int makeDefault
){
750 sqlite3_vfs
*pOrigVfs
;
751 if( gQuota
.isInitialized
) return SQLITE_MISUSE
;
752 pOrigVfs
= sqlite3_vfs_find(zOrigVfsName
);
753 if( pOrigVfs
==0 ) return SQLITE_ERROR
;
754 assert( pOrigVfs
!=&gQuota
.sThisVfs
);
755 gQuota
.pMutex
= sqlite3_mutex_alloc(SQLITE_MUTEX_FAST
);
756 if( !gQuota
.pMutex
){
759 gQuota
.isInitialized
= 1;
760 gQuota
.pOrigVfs
= pOrigVfs
;
761 gQuota
.sThisVfs
= *pOrigVfs
;
762 gQuota
.sThisVfs
.xOpen
= quotaOpen
;
763 gQuota
.sThisVfs
.xDelete
= quotaDelete
;
764 gQuota
.sThisVfs
.szOsFile
+= sizeof(quotaConn
);
765 gQuota
.sThisVfs
.zName
= "quota";
766 gQuota
.sIoMethodsV1
.iVersion
= 1;
767 gQuota
.sIoMethodsV1
.xClose
= quotaClose
;
768 gQuota
.sIoMethodsV1
.xRead
= quotaRead
;
769 gQuota
.sIoMethodsV1
.xWrite
= quotaWrite
;
770 gQuota
.sIoMethodsV1
.xTruncate
= quotaTruncate
;
771 gQuota
.sIoMethodsV1
.xSync
= quotaSync
;
772 gQuota
.sIoMethodsV1
.xFileSize
= quotaFileSize
;
773 gQuota
.sIoMethodsV1
.xLock
= quotaLock
;
774 gQuota
.sIoMethodsV1
.xUnlock
= quotaUnlock
;
775 gQuota
.sIoMethodsV1
.xCheckReservedLock
= quotaCheckReservedLock
;
776 gQuota
.sIoMethodsV1
.xFileControl
= quotaFileControl
;
777 gQuota
.sIoMethodsV1
.xSectorSize
= quotaSectorSize
;
778 gQuota
.sIoMethodsV1
.xDeviceCharacteristics
= quotaDeviceCharacteristics
;
779 gQuota
.sIoMethodsV2
= gQuota
.sIoMethodsV1
;
780 gQuota
.sIoMethodsV2
.iVersion
= 2;
781 gQuota
.sIoMethodsV2
.xShmMap
= quotaShmMap
;
782 gQuota
.sIoMethodsV2
.xShmLock
= quotaShmLock
;
783 gQuota
.sIoMethodsV2
.xShmBarrier
= quotaShmBarrier
;
784 gQuota
.sIoMethodsV2
.xShmUnmap
= quotaShmUnmap
;
785 sqlite3_vfs_register(&gQuota
.sThisVfs
, makeDefault
);
790 ** Shutdown the quota system.
792 ** All SQLite database connections must be closed before calling this
795 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
796 ** shutting down in order to free all remaining quota groups.
798 int sqlite3_quota_shutdown(void){
800 if( gQuota
.isInitialized
==0 ) return SQLITE_MISUSE
;
801 for(pGroup
=gQuota
.pGroup
; pGroup
; pGroup
=pGroup
->pNext
){
802 if( quotaGroupOpenFileCount(pGroup
)>0 ) return SQLITE_MISUSE
;
804 while( gQuota
.pGroup
){
805 pGroup
= gQuota
.pGroup
;
806 gQuota
.pGroup
= pGroup
->pNext
;
808 assert( quotaGroupOpenFileCount(pGroup
)==0 );
809 quotaGroupDeref(pGroup
);
811 gQuota
.isInitialized
= 0;
812 sqlite3_mutex_free(gQuota
.pMutex
);
813 sqlite3_vfs_unregister(&gQuota
.sThisVfs
);
814 memset(&gQuota
, 0, sizeof(gQuota
));
819 ** Create or destroy a quota group.
821 ** The quota group is defined by the zPattern. When calling this routine
822 ** with a zPattern for a quota group that already exists, this routine
823 ** merely updates the iLimit, xCallback, and pArg values for that quota
824 ** group. If zPattern is new, then a new quota group is created.
826 ** If the iLimit for a quota group is set to zero, then the quota group
827 ** is disabled and will be deleted when the last database connection using
828 ** the quota group is closed.
830 ** Calling this routine on a zPattern that does not exist and with a
831 ** zero iLimit is a no-op.
833 ** A quota group must exist with a non-zero iLimit prior to opening
834 ** database connections if those connections are to participate in the
835 ** quota group. Creating a quota group does not affect database connections
836 ** that are already open.
838 int sqlite3_quota_set(
839 const char *zPattern
, /* The filename pattern */
840 sqlite3_int64 iLimit
, /* New quota to set for this quota group */
841 void (*xCallback
)( /* Callback invoked when going over quota */
842 const char *zFilename
, /* Name of file whose size increases */
843 sqlite3_int64
*piLimit
, /* IN/OUT: The current limit */
844 sqlite3_int64 iSize
, /* Total size of all files in the group */
845 void *pArg
/* Client data */
847 void *pArg
, /* client data passed thru to callback */
848 void (*xDestroy
)(void*) /* Optional destructor for pArg */
852 pGroup
= gQuota
.pGroup
;
853 while( pGroup
&& strcmp(pGroup
->zPattern
, zPattern
)!=0 ){
854 pGroup
= pGroup
->pNext
;
857 int nPattern
= (int)(strlen(zPattern
) & 0x3fffffff);
862 pGroup
= (quotaGroup
*)sqlite3_malloc( sizeof(*pGroup
) + nPattern
+ 1 );
867 memset(pGroup
, 0, sizeof(*pGroup
));
868 pGroup
->zPattern
= (char*)&pGroup
[1];
869 memcpy((char *)pGroup
->zPattern
, zPattern
, nPattern
+1);
870 if( gQuota
.pGroup
) gQuota
.pGroup
->ppPrev
= &pGroup
->pNext
;
871 pGroup
->pNext
= gQuota
.pGroup
;
872 pGroup
->ppPrev
= &gQuota
.pGroup
;
873 gQuota
.pGroup
= pGroup
;
875 pGroup
->iLimit
= iLimit
;
876 pGroup
->xCallback
= xCallback
;
877 if( pGroup
->xDestroy
&& pGroup
->pArg
!=pArg
){
878 pGroup
->xDestroy(pGroup
->pArg
);
881 pGroup
->xDestroy
= xDestroy
;
882 quotaGroupDeref(pGroup
);
888 ** Bring the named file under quota management. Or if it is already under
889 ** management, update its size.
891 int sqlite3_quota_file(const char *zFilename
){
897 int nAlloc
= gQuota
.sThisVfs
.szOsFile
+ gQuota
.sThisVfs
.mxPathname
+2;
899 /* Allocate space for a file-handle and the full path for file zFilename */
900 fd
= (sqlite3_file
*)sqlite3_malloc(nAlloc
);
904 zFull
= &((char *)fd
)[gQuota
.sThisVfs
.szOsFile
];
905 rc
= gQuota
.pOrigVfs
->xFullPathname(gQuota
.pOrigVfs
, zFilename
,
906 gQuota
.sThisVfs
.mxPathname
+1, zFull
);
910 zFull
[strlen(zFull
)+1] = '\0';
911 rc
= quotaOpen(&gQuota
.sThisVfs
, zFull
, fd
,
912 SQLITE_OPEN_READONLY
| SQLITE_OPEN_MAIN_DB
, &outFlags
);
914 fd
->pMethods
->xFileSize(fd
, &iSize
);
915 fd
->pMethods
->xClose(fd
);
916 }else if( rc
==SQLITE_CANTOPEN
){
920 pGroup
= quotaGroupFind(zFull
);
922 pFile
= quotaFindFile(pGroup
, zFull
, 0);
923 if( pFile
) quotaRemoveFile(pFile
);
934 ** Open a potentially quotaed file for I/O.
936 quota_FILE
*sqlite3_quota_fopen(const char *zFilename
, const char *zMode
){
939 char *zFullTranslated
= 0;
944 zFull
= (char*)sqlite3_malloc(gQuota
.sThisVfs
.mxPathname
+ 1);
945 if( zFull
==0 ) return 0;
946 rc
= gQuota
.pOrigVfs
->xFullPathname(gQuota
.pOrigVfs
, zFilename
,
947 gQuota
.sThisVfs
.mxPathname
+1, zFull
);
948 if( rc
) goto quota_fopen_error
;
949 p
= (quota_FILE
*)sqlite3_malloc(sizeof(*p
));
950 if( p
==0 ) goto quota_fopen_error
;
951 memset(p
, 0, sizeof(*p
));
952 zFullTranslated
= quota_utf8_to_mbcs(zFull
);
953 if( zFullTranslated
==0 ) goto quota_fopen_error
;
954 p
->f
= fopen(zFullTranslated
, zMode
);
955 if( p
->f
==0 ) goto quota_fopen_error
;
957 pGroup
= quotaGroupFind(zFull
);
959 pFile
= quotaFindFile(pGroup
, zFull
, 1);
962 goto quota_fopen_error
;
970 p
->zMbcsName
= zFullTranslated
;
975 quota_mbcs_free(zFullTranslated
);
977 if( p
&& p
->f
) fclose(p
->f
);
983 ** Read content from a quota_FILE
985 size_t sqlite3_quota_fread(
986 void *pBuf
, /* Store the content here */
987 size_t size
, /* Size of each element */
988 size_t nmemb
, /* Number of elements to read */
989 quota_FILE
*p
/* Read from this quota_FILE object */
991 return fread(pBuf
, size
, nmemb
, p
->f
);
995 ** Write content into a quota_FILE. Invoke the quota callback and block
996 ** the write if we exceed quota.
998 size_t sqlite3_quota_fwrite(
999 const void *pBuf
, /* Take content to write from here */
1000 size_t size
, /* Size of each element */
1001 size_t nmemb
, /* Number of elements */
1002 quota_FILE
*p
/* Write to this quota_FILE objecct */
1004 sqlite3_int64 iOfst
;
1006 sqlite3_int64 szNew
;
1010 iOfst
= ftell(p
->f
);
1011 iEnd
= iOfst
+ size
*nmemb
;
1013 if( pFile
&& pFile
->iSize
<iEnd
){
1014 quotaGroup
*pGroup
= pFile
->pGroup
;
1016 szNew
= pGroup
->iSize
- pFile
->iSize
+ iEnd
;
1017 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
1018 if( pGroup
->xCallback
){
1019 pGroup
->xCallback(pFile
->zFilename
, &pGroup
->iLimit
, szNew
,
1022 if( szNew
>pGroup
->iLimit
&& pGroup
->iLimit
>0 ){
1023 iEnd
= pGroup
->iLimit
- pGroup
->iSize
+ pFile
->iSize
;
1024 nmemb
= (size_t)((iEnd
- iOfst
)/size
);
1025 iEnd
= iOfst
+ size
*nmemb
;
1026 szNew
= pGroup
->iSize
- pFile
->iSize
+ iEnd
;
1029 pGroup
->iSize
= szNew
;
1030 pFile
->iSize
= iEnd
;
1035 rc
= fwrite(pBuf
, size
, nmemb
, p
->f
);
1037 /* If the write was incomplete, adjust the file size and group size
1039 if( rc
<nmemb
&& pFile
){
1040 size_t nWritten
= rc
;
1041 sqlite3_int64 iNewEnd
= iOfst
+ size
*nWritten
;
1042 if( iNewEnd
<iEnd
) iNewEnd
= iEnd
;
1044 pFile
->pGroup
->iSize
+= iNewEnd
- pFile
->iSize
;
1045 pFile
->iSize
= iNewEnd
;
1052 ** Close an open quota_FILE stream.
1054 int sqlite3_quota_fclose(quota_FILE
*p
){
1062 if( pFile
->nRef
==0 ){
1063 quotaGroup
*pGroup
= pFile
->pGroup
;
1064 if( pFile
->deleteOnClose
){
1065 gQuota
.pOrigVfs
->xDelete(gQuota
.pOrigVfs
, pFile
->zFilename
, 0);
1066 quotaRemoveFile(pFile
);
1068 quotaGroupDeref(pGroup
);
1073 quota_mbcs_free(p
->zMbcsName
);
1080 ** Flush memory buffers for a quota_FILE to disk.
1082 int sqlite3_quota_fflush(quota_FILE
*p
, int doFsync
){
1085 if( rc
==0 && doFsync
){
1087 rc
= fsync(fileno(p
->f
));
1090 rc
= _commit(_fileno(p
->f
));
1097 ** Seek on a quota_FILE stream.
1099 int sqlite3_quota_fseek(quota_FILE
*p
, long offset
, int whence
){
1100 return fseek(p
->f
, offset
, whence
);
1104 ** rewind a quota_FILE stream.
1106 void sqlite3_quota_rewind(quota_FILE
*p
){
1111 ** Tell the current location of a quota_FILE stream.
1113 long sqlite3_quota_ftell(quota_FILE
*p
){
1118 ** Test the error indicator for the given file.
1120 int sqlite3_quota_ferror(quota_FILE
*p
){
1121 return ferror(p
->f
);
1125 ** Truncate a file to szNew bytes.
1127 int sqlite3_quota_ftruncate(quota_FILE
*p
, sqlite3_int64 szNew
){
1128 quotaFile
*pFile
= p
->pFile
;
1130 if( (pFile
= p
->pFile
)!=0 && pFile
->iSize
<szNew
){
1132 if( pFile
->iSize
<szNew
){
1133 /* This routine cannot be used to extend a file that is under
1134 ** quota management. Only true truncation is allowed. */
1137 pGroup
= pFile
->pGroup
;
1139 pGroup
->iSize
+= szNew
- pFile
->iSize
;
1143 rc
= ftruncate(fileno(p
->f
), szNew
);
1146 # if defined(__MINGW32__) && defined(SQLITE_TEST)
1147 /* _chsize_s() is missing from MingW (as of 2012-11-06). Use
1148 ** _chsize() as a work-around for testing purposes. */
1149 rc
= _chsize(_fileno(p
->f
), (long)szNew
);
1151 rc
= _chsize_s(_fileno(p
->f
), szNew
);
1154 if( pFile
&& rc
==0 ){
1155 quotaGroup
*pGroup
= pFile
->pGroup
;
1157 pGroup
->iSize
+= szNew
- pFile
->iSize
;
1158 pFile
->iSize
= szNew
;
1165 ** Determine the time that the given file was last modified, in
1166 ** seconds size 1970. Write the result into *pTime. Return 0 on
1167 ** success and non-zero on any kind of error.
1169 int sqlite3_quota_file_mtime(quota_FILE
*p
, time_t *pTime
){
1173 rc
= fstat(fileno(p
->f
), &buf
);
1176 struct _stati64 buf
;
1177 rc
= _stati64(p
->zMbcsName
, &buf
);
1179 if( rc
==0 ) *pTime
= buf
.st_mtime
;
1184 ** Return the true size of the file, as reported by the operating
1187 sqlite3_int64
sqlite3_quota_file_truesize(quota_FILE
*p
){
1191 rc
= fstat(fileno(p
->f
), &buf
);
1194 struct _stati64 buf
;
1195 rc
= _stati64(p
->zMbcsName
, &buf
);
1197 return rc
==0 ? buf
.st_size
: -1;
1201 ** Return the size of the file, as it is known to the quota subsystem.
1203 sqlite3_int64
sqlite3_quota_file_size(quota_FILE
*p
){
1204 return p
->pFile
? p
->pFile
->iSize
: -1;
1208 ** Determine the amount of data in bytes available for reading
1209 ** in the given file.
1211 long sqlite3_quota_file_available(quota_FILE
*p
){
1216 if ( pos1
< 0 ) return -1;
1217 rc
= fseek(f
, 0, SEEK_END
);
1218 if ( rc
!= 0 ) return -1;
1220 if ( pos2
< 0 ) return -1;
1221 rc
= fseek(f
, pos1
, SEEK_SET
);
1222 if ( rc
!= 0 ) return -1;
1227 ** Remove a managed file. Update quotas accordingly.
1229 int sqlite3_quota_remove(const char *zFilename
){
1230 char *zFull
; /* Full pathname for zFilename */
1231 size_t nFull
; /* Number of bytes in zFilename */
1232 int rc
; /* Result code */
1233 quotaGroup
*pGroup
; /* Group containing zFilename */
1234 quotaFile
*pFile
; /* A file in the group */
1235 quotaFile
*pNextFile
; /* next file in the group */
1236 int diff
; /* Difference between filenames */
1237 char c
; /* First character past end of pattern */
1239 zFull
= (char*)sqlite3_malloc(gQuota
.sThisVfs
.mxPathname
+ 1);
1240 if( zFull
==0 ) return SQLITE_NOMEM
;
1241 rc
= gQuota
.pOrigVfs
->xFullPathname(gQuota
.pOrigVfs
, zFilename
,
1242 gQuota
.sThisVfs
.mxPathname
+1, zFull
);
1244 sqlite3_free(zFull
);
1248 /* Figure out the length of the full pathname. If the name ends with
1249 ** / (or \ on windows) then remove the trailing /.
1251 nFull
= strlen(zFull
);
1252 if( nFull
>0 && (zFull
[nFull
-1]=='/' || zFull
[nFull
-1]=='\\') ){
1258 pGroup
= quotaGroupFind(zFull
);
1260 for(pFile
=pGroup
->pFiles
; pFile
&& rc
==SQLITE_OK
; pFile
=pNextFile
){
1261 pNextFile
= pFile
->pNext
;
1262 diff
= strncmp(zFull
, pFile
->zFilename
, nFull
);
1263 if( diff
==0 && ((c
= pFile
->zFilename
[nFull
])==0 || c
=='/' || c
=='\\') ){
1265 pFile
->deleteOnClose
= 1;
1267 rc
= gQuota
.pOrigVfs
->xDelete(gQuota
.pOrigVfs
, pFile
->zFilename
, 0);
1268 quotaRemoveFile(pFile
);
1269 quotaGroupDeref(pGroup
);
1275 sqlite3_free(zFull
);
1279 /***************************** Test Code ***********************************/
1281 #if defined(INCLUDE_SQLITE_TCL_H)
1282 # include "sqlite_tcl.h"
1285 # ifndef SQLITE_TCLAPI
1286 # define SQLITE_TCLAPI
1291 ** Argument passed to a TCL quota-over-limit callback.
1293 typedef struct TclQuotaCallback TclQuotaCallback
;
1294 struct TclQuotaCallback
{
1295 Tcl_Interp
*interp
; /* Interpreter in which to run the script */
1296 Tcl_Obj
*pScript
; /* Script to be run */
1299 extern const char *sqlite3ErrName(int);
1303 ** This is the callback from a quota-over-limit.
1305 static void tclQuotaCallback(
1306 const char *zFilename
, /* Name of file whose size increases */
1307 sqlite3_int64
*piLimit
, /* IN/OUT: The current limit */
1308 sqlite3_int64 iSize
, /* Total size of all files in the group */
1309 void *pArg
/* Client data */
1311 TclQuotaCallback
*p
; /* Callback script object */
1312 Tcl_Obj
*pEval
; /* Script to evaluate */
1313 Tcl_Obj
*pVarname
; /* Name of variable to pass as 2nd arg */
1314 unsigned int rnd
; /* Random part of pVarname */
1315 int rc
; /* Tcl error code */
1317 p
= (TclQuotaCallback
*)pArg
;
1320 pVarname
= Tcl_NewStringObj("::piLimit_", -1);
1321 Tcl_IncrRefCount(pVarname
);
1322 sqlite3_randomness(sizeof(rnd
), (void *)&rnd
);
1323 Tcl_AppendObjToObj(pVarname
, Tcl_NewIntObj((int)(rnd
&0x7FFFFFFF)));
1324 Tcl_ObjSetVar2(p
->interp
, pVarname
, 0, Tcl_NewWideIntObj(*piLimit
), 0);
1326 pEval
= Tcl_DuplicateObj(p
->pScript
);
1327 Tcl_IncrRefCount(pEval
);
1328 Tcl_ListObjAppendElement(0, pEval
, Tcl_NewStringObj(zFilename
, -1));
1329 Tcl_ListObjAppendElement(0, pEval
, pVarname
);
1330 Tcl_ListObjAppendElement(0, pEval
, Tcl_NewWideIntObj(iSize
));
1331 rc
= Tcl_EvalObjEx(p
->interp
, pEval
, TCL_EVAL_GLOBAL
);
1335 Tcl_Obj
*pLimit
= Tcl_ObjGetVar2(p
->interp
, pVarname
, 0, 0);
1336 rc
= Tcl_GetWideIntFromObj(p
->interp
, pLimit
, &x
);
1338 Tcl_UnsetVar(p
->interp
, Tcl_GetString(pVarname
), 0);
1341 Tcl_DecrRefCount(pEval
);
1342 Tcl_DecrRefCount(pVarname
);
1343 if( rc
!=TCL_OK
) Tcl_BackgroundError(p
->interp
);
1347 ** Destructor for a TCL quota-over-limit callback.
1349 static void tclCallbackDestructor(void *pObj
){
1350 TclQuotaCallback
*p
= (TclQuotaCallback
*)pObj
;
1352 Tcl_DecrRefCount(p
->pScript
);
1353 sqlite3_free((char *)p
);
1358 ** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT
1360 static int SQLITE_TCLAPI
test_quota_initialize(
1364 Tcl_Obj
*CONST objv
[]
1366 const char *zName
; /* Name of new quota VFS */
1367 int makeDefault
; /* True to make the new VFS the default */
1368 int rc
; /* Value returned by quota_initialize() */
1370 /* Process arguments */
1372 Tcl_WrongNumArgs(interp
, 1, objv
, "NAME MAKEDEFAULT");
1375 zName
= Tcl_GetString(objv
[1]);
1376 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &makeDefault
) ) return TCL_ERROR
;
1377 if( zName
[0]=='\0' ) zName
= 0;
1379 /* Call sqlite3_quota_initialize() */
1380 rc
= sqlite3_quota_initialize(zName
, makeDefault
);
1381 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1387 ** tclcmd: sqlite3_quota_shutdown
1389 static int SQLITE_TCLAPI
test_quota_shutdown(
1393 Tcl_Obj
*CONST objv
[]
1395 int rc
; /* Value returned by quota_shutdown() */
1398 Tcl_WrongNumArgs(interp
, 1, objv
, "");
1402 /* Call sqlite3_quota_shutdown() */
1403 rc
= sqlite3_quota_shutdown();
1404 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1410 ** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT
1412 static int SQLITE_TCLAPI
test_quota_set(
1416 Tcl_Obj
*CONST objv
[]
1418 const char *zPattern
; /* File pattern to configure */
1419 Tcl_WideInt iLimit
; /* Initial quota in bytes */
1420 Tcl_Obj
*pScript
; /* Tcl script to invoke to increase quota */
1421 int rc
; /* Value returned by quota_set() */
1422 TclQuotaCallback
*p
; /* Callback object */
1423 int nScript
; /* Length of callback script */
1424 void (*xDestroy
)(void*); /* Optional destructor for pArg */
1425 void (*xCallback
)(const char *, sqlite3_int64
*, sqlite3_int64
, void *);
1427 /* Process arguments */
1429 Tcl_WrongNumArgs(interp
, 1, objv
, "PATTERN LIMIT SCRIPT");
1432 zPattern
= Tcl_GetString(objv
[1]);
1433 if( Tcl_GetWideIntFromObj(interp
, objv
[2], &iLimit
) ) return TCL_ERROR
;
1435 Tcl_GetStringFromObj(pScript
, &nScript
);
1438 /* Allocate a TclQuotaCallback object */
1439 p
= (TclQuotaCallback
*)sqlite3_malloc(sizeof(TclQuotaCallback
));
1441 Tcl_SetResult(interp
, (char *)"SQLITE_NOMEM", TCL_STATIC
);
1444 memset(p
, 0, sizeof(TclQuotaCallback
));
1446 Tcl_IncrRefCount(pScript
);
1447 p
->pScript
= pScript
;
1448 xDestroy
= tclCallbackDestructor
;
1449 xCallback
= tclQuotaCallback
;
1456 /* Invoke sqlite3_quota_set() */
1457 rc
= sqlite3_quota_set(zPattern
, iLimit
, xCallback
, (void*)p
, xDestroy
);
1459 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1464 ** tclcmd: sqlite3_quota_file FILENAME
1466 static int SQLITE_TCLAPI
test_quota_file(
1470 Tcl_Obj
*CONST objv
[]
1472 const char *zFilename
; /* File pattern to configure */
1473 int rc
; /* Value returned by quota_file() */
1475 /* Process arguments */
1477 Tcl_WrongNumArgs(interp
, 1, objv
, "FILENAME");
1480 zFilename
= Tcl_GetString(objv
[1]);
1482 /* Invoke sqlite3_quota_file() */
1483 rc
= sqlite3_quota_file(zFilename
);
1485 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1490 ** tclcmd: sqlite3_quota_dump
1492 static int SQLITE_TCLAPI
test_quota_dump(
1496 Tcl_Obj
*CONST objv
[]
1499 Tcl_Obj
*pGroupTerm
;
1504 pResult
= Tcl_NewObj();
1506 for(pGroup
=gQuota
.pGroup
; pGroup
; pGroup
=pGroup
->pNext
){
1507 pGroupTerm
= Tcl_NewObj();
1508 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1509 Tcl_NewStringObj(pGroup
->zPattern
, -1));
1510 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1511 Tcl_NewWideIntObj(pGroup
->iLimit
));
1512 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1513 Tcl_NewWideIntObj(pGroup
->iSize
));
1514 for(pFile
=pGroup
->pFiles
; pFile
; pFile
=pFile
->pNext
){
1517 pFileTerm
= Tcl_NewObj();
1518 sqlite3_snprintf(sizeof(zTemp
), zTemp
, "%s", pFile
->zFilename
);
1519 for(i
=0; zTemp
[i
]; i
++){ if( zTemp
[i
]=='\\' ) zTemp
[i
] = '/'; }
1520 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1521 Tcl_NewStringObj(zTemp
, -1));
1522 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1523 Tcl_NewWideIntObj(pFile
->iSize
));
1524 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1525 Tcl_NewWideIntObj(pFile
->nRef
));
1526 Tcl_ListObjAppendElement(interp
, pFileTerm
,
1527 Tcl_NewWideIntObj(pFile
->deleteOnClose
));
1528 Tcl_ListObjAppendElement(interp
, pGroupTerm
, pFileTerm
);
1530 Tcl_ListObjAppendElement(interp
, pResult
, pGroupTerm
);
1533 Tcl_SetObjResult(interp
, pResult
);
1538 ** tclcmd: sqlite3_quota_fopen FILENAME MODE
1540 static int SQLITE_TCLAPI
test_quota_fopen(
1544 Tcl_Obj
*CONST objv
[]
1546 const char *zFilename
; /* File pattern to configure */
1547 const char *zMode
; /* Mode string */
1548 quota_FILE
*p
; /* Open string object */
1549 char zReturn
[50]; /* Name of pointer to return */
1551 /* Process arguments */
1553 Tcl_WrongNumArgs(interp
, 1, objv
, "FILENAME MODE");
1556 zFilename
= Tcl_GetString(objv
[1]);
1557 zMode
= Tcl_GetString(objv
[2]);
1558 p
= sqlite3_quota_fopen(zFilename
, zMode
);
1559 sqlite3_snprintf(sizeof(zReturn
), zReturn
, "%p", p
);
1560 Tcl_SetResult(interp
, zReturn
, TCL_VOLATILE
);
1564 /* Defined in test1.c */
1565 extern void *sqlite3TestTextToPtr(const char*);
1568 ** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
1570 static int SQLITE_TCLAPI
test_quota_fread(
1574 Tcl_Obj
*CONST objv
[]
1583 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE SIZE NELEM");
1586 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1587 if( Tcl_GetIntFromObj(interp
, objv
[2], &sz
) ) return TCL_ERROR
;
1588 if( Tcl_GetIntFromObj(interp
, objv
[3], &nElem
) ) return TCL_ERROR
;
1589 zBuf
= (char*)sqlite3_malloc( sz
*nElem
+ 1 );
1591 Tcl_SetResult(interp
, "out of memory", TCL_STATIC
);
1594 got
= sqlite3_quota_fread(zBuf
, sz
, nElem
, p
);
1596 Tcl_SetResult(interp
, zBuf
, TCL_VOLATILE
);
1602 ** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
1604 static int SQLITE_TCLAPI
test_quota_fwrite(
1608 Tcl_Obj
*CONST objv
[]
1617 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE SIZE NELEM CONTENT");
1620 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1621 if( Tcl_GetIntFromObj(interp
, objv
[2], &sz
) ) return TCL_ERROR
;
1622 if( Tcl_GetIntFromObj(interp
, objv
[3], &nElem
) ) return TCL_ERROR
;
1623 zBuf
= Tcl_GetString(objv
[4]);
1624 got
= sqlite3_quota_fwrite(zBuf
, sz
, nElem
, p
);
1625 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(got
));
1630 ** tclcmd: sqlite3_quota_fclose HANDLE
1632 static int SQLITE_TCLAPI
test_quota_fclose(
1636 Tcl_Obj
*CONST objv
[]
1642 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1645 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1646 rc
= sqlite3_quota_fclose(p
);
1647 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1652 ** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
1654 static int SQLITE_TCLAPI
test_quota_fflush(
1658 Tcl_Obj
*CONST objv
[]
1664 if( objc
!=2 && objc
!=3 ){
1665 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE ?HARDSYNC?");
1668 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1670 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &doSync
) ) return TCL_ERROR
;
1672 rc
= sqlite3_quota_fflush(p
, doSync
);
1673 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1678 ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
1680 static int SQLITE_TCLAPI
test_quota_fseek(
1684 Tcl_Obj
*CONST objv
[]
1688 const char *zWhence
;
1693 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE OFFSET WHENCE");
1696 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1697 if( Tcl_GetIntFromObj(interp
, objv
[2], &ofst
) ) return TCL_ERROR
;
1698 zWhence
= Tcl_GetString(objv
[3]);
1699 if( strcmp(zWhence
, "SEEK_SET")==0 ){
1701 }else if( strcmp(zWhence
, "SEEK_CUR")==0 ){
1703 }else if( strcmp(zWhence
, "SEEK_END")==0 ){
1706 Tcl_AppendResult(interp
,
1707 "WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0);
1710 rc
= sqlite3_quota_fseek(p
, ofst
, whence
);
1711 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1716 ** tclcmd: sqlite3_quota_rewind HANDLE
1718 static int SQLITE_TCLAPI
test_quota_rewind(
1722 Tcl_Obj
*CONST objv
[]
1726 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1729 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1730 sqlite3_quota_rewind(p
);
1735 ** tclcmd: sqlite3_quota_ftell HANDLE
1737 static int SQLITE_TCLAPI
test_quota_ftell(
1741 Tcl_Obj
*CONST objv
[]
1746 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1749 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1750 x
= sqlite3_quota_ftell(p
);
1751 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(x
));
1756 ** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE
1758 static int SQLITE_TCLAPI
test_quota_ftruncate(
1762 Tcl_Obj
*CONST objv
[]
1769 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE SIZE");
1772 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1773 if( Tcl_GetWideIntFromObj(interp
, objv
[2], &w
) ) return TCL_ERROR
;
1774 x
= (sqlite3_int64
)w
;
1775 rc
= sqlite3_quota_ftruncate(p
, x
);
1776 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1781 ** tclcmd: sqlite3_quota_file_size HANDLE
1783 static int SQLITE_TCLAPI
test_quota_file_size(
1787 Tcl_Obj
*CONST objv
[]
1792 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1795 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1796 x
= sqlite3_quota_file_size(p
);
1797 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(x
));
1802 ** tclcmd: sqlite3_quota_file_truesize HANDLE
1804 static int SQLITE_TCLAPI
test_quota_file_truesize(
1808 Tcl_Obj
*CONST objv
[]
1813 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1816 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1817 x
= sqlite3_quota_file_truesize(p
);
1818 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(x
));
1823 ** tclcmd: sqlite3_quota_file_mtime HANDLE
1825 static int SQLITE_TCLAPI
test_quota_file_mtime(
1829 Tcl_Obj
*CONST objv
[]
1834 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1837 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1839 sqlite3_quota_file_mtime(p
, &t
);
1840 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(t
));
1846 ** tclcmd: sqlite3_quota_remove FILENAME
1848 static int SQLITE_TCLAPI
test_quota_remove(
1852 Tcl_Obj
*CONST objv
[]
1854 const char *zFilename
; /* File pattern to configure */
1857 Tcl_WrongNumArgs(interp
, 1, objv
, "FILENAME");
1860 zFilename
= Tcl_GetString(objv
[1]);
1861 rc
= sqlite3_quota_remove(zFilename
);
1862 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1867 ** tclcmd: sqlite3_quota_glob PATTERN TEXT
1869 ** Test the glob pattern matching. Return 1 if TEXT matches PATTERN
1870 ** and return 0 if it does not.
1872 static int SQLITE_TCLAPI
test_quota_glob(
1876 Tcl_Obj
*CONST objv
[]
1878 const char *zPattern
; /* The glob pattern */
1879 const char *zText
; /* Text to compare agains the pattern */
1882 Tcl_WrongNumArgs(interp
, 1, objv
, "PATTERN TEXT");
1885 zPattern
= Tcl_GetString(objv
[1]);
1886 zText
= Tcl_GetString(objv
[2]);
1887 rc
= quotaStrglob(zPattern
, zText
);
1888 Tcl_SetObjResult(interp
, Tcl_NewIntObj(rc
));
1893 ** tclcmd: sqlite3_quota_file_available HANDLE
1895 ** Return the number of bytes from the current file point to the end of
1898 static int SQLITE_TCLAPI
test_quota_file_available(
1902 Tcl_Obj
*CONST objv
[]
1907 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1910 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1911 x
= sqlite3_quota_file_available(p
);
1912 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(x
));
1917 ** tclcmd: sqlite3_quota_ferror HANDLE
1919 ** Return true if the file handle is in the error state.
1921 static int SQLITE_TCLAPI
test_quota_ferror(
1925 Tcl_Obj
*CONST objv
[]
1930 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE");
1933 p
= sqlite3TestTextToPtr(Tcl_GetString(objv
[1]));
1934 x
= sqlite3_quota_ferror(p
);
1935 Tcl_SetObjResult(interp
, Tcl_NewIntObj(x
));
1940 ** This routine registers the custom TCL commands defined in this
1941 ** module. This should be the only procedure visible from outside
1944 int Sqlitequota_Init(Tcl_Interp
*interp
){
1947 Tcl_ObjCmdProc
*xProc
;
1949 { "sqlite3_quota_initialize", test_quota_initialize
},
1950 { "sqlite3_quota_shutdown", test_quota_shutdown
},
1951 { "sqlite3_quota_set", test_quota_set
},
1952 { "sqlite3_quota_file", test_quota_file
},
1953 { "sqlite3_quota_dump", test_quota_dump
},
1954 { "sqlite3_quota_fopen", test_quota_fopen
},
1955 { "sqlite3_quota_fread", test_quota_fread
},
1956 { "sqlite3_quota_fwrite", test_quota_fwrite
},
1957 { "sqlite3_quota_fclose", test_quota_fclose
},
1958 { "sqlite3_quota_fflush", test_quota_fflush
},
1959 { "sqlite3_quota_fseek", test_quota_fseek
},
1960 { "sqlite3_quota_rewind", test_quota_rewind
},
1961 { "sqlite3_quota_ftell", test_quota_ftell
},
1962 { "sqlite3_quota_ftruncate", test_quota_ftruncate
},
1963 { "sqlite3_quota_file_size", test_quota_file_size
},
1964 { "sqlite3_quota_file_truesize", test_quota_file_truesize
},
1965 { "sqlite3_quota_file_mtime", test_quota_file_mtime
},
1966 { "sqlite3_quota_remove", test_quota_remove
},
1967 { "sqlite3_quota_glob", test_quota_glob
},
1968 { "sqlite3_quota_file_available",test_quota_file_available
},
1969 { "sqlite3_quota_ferror", test_quota_ferror
},
1973 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
1974 Tcl_CreateObjCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);