1 : /*
2 : ** 2010 September 31
3 : **
4 : ** The author disclaims copyright to this source code. In place of
5 : ** a legal notice, here is a blessing:
6 : **
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.
10 : **
11 : *************************************************************************
12 : **
13 : ** This file contains a VFS "shim" - a layer that sits in between the
14 : ** pager and the real VFS.
15 : **
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.
22 : **
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.
29 : */
30 : #include "test_quota.h"
31 : #include <string.h>
32 : #include <assert.h>
33 :
34 : /*
35 : ** For an build without mutexes, no-op the mutex calls.
36 : */
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 */
46 :
47 :
48 : /************************ Object Definitions ******************************/
49 :
50 : /* Forward declaration of all object types */
51 : typedef struct quotaGroup quotaGroup;
52 : typedef struct quotaConn quotaConn;
53 : typedef struct quotaFile quotaFile;
54 :
55 : /*
56 : ** A "quota group" is a collection of files whose collective size we want
57 : ** to limit. Each quota group is defined by a GLOB pattern.
58 : **
59 : ** There is an instance of the following object for each defined quota
60 : ** group. This object records the GLOB pattern that defines which files
61 : ** belong to the quota group. The object also remembers the size limit
62 : ** for the group (the quota) and the callback to be invoked when the
63 : ** sum of the sizes of the files within the group goes over the limit.
64 : **
65 : ** A quota group must be established (using sqlite3_quota_set(...))
66 : ** prior to opening any of the database connections that access files
67 : ** within the quota group.
68 : */
69 : struct quotaGroup {
70 : const char *zPattern; /* Filename pattern to be quotaed */
71 : sqlite3_int64 iLimit; /* Upper bound on total file size */
72 : sqlite3_int64 iSize; /* Current size of all files */
73 : void (*xCallback)( /* Callback invoked when going over quota */
74 : const char *zFilename, /* Name of file whose size increases */
75 : sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
76 : sqlite3_int64 iSize, /* Total size of all files in the group */
77 : void *pArg /* Client data */
78 : );
79 : void *pArg; /* Third argument to the xCallback() */
80 : void (*xDestroy)(void*); /* Optional destructor for pArg */
81 : quotaGroup *pNext, **ppPrev; /* Doubly linked list of all quota objects */
82 : quotaFile *pFiles; /* Files within this group */
83 : };
84 :
85 : /*
86 : ** An instance of this structure represents a single file that is part
87 : ** of a quota group. A single file can be opened multiple times. In
88 : ** order keep multiple openings of the same file from causing the size
89 : ** of the file to count against the quota multiple times, each file
90 : ** has a unique instance of this object and multiple open connections
91 : ** to the same file each point to a single instance of this object.
92 : */
93 : struct quotaFile {
94 : char *zFilename; /* Name of this file */
95 : quotaGroup *pGroup; /* Quota group to which this file belongs */
96 : sqlite3_int64 iSize; /* Current size of this file */
97 : int nRef; /* Number of times this file is open */
98 : int deleteOnClose; /* True to delete this file when it closes */
99 : quotaFile *pNext, **ppPrev; /* Linked list of files in the same group */
100 : };
101 :
102 : /*
103 : ** An instance of the following object represents each open connection
104 : ** to a file that participates in quota tracking. This object is a
105 : ** subclass of sqlite3_file. The sqlite3_file object for the underlying
106 : ** VFS is appended to this structure.
107 : */
108 : struct quotaConn {
109 : sqlite3_file base; /* Base class - must be first */
110 : quotaFile *pFile; /* The underlying file */
111 : /* The underlying VFS sqlite3_file is appended to this object */
112 : };
113 :
114 : /*
115 : ** An instance of the following object records the state of an
116 : ** open file. This object is opaque to all users - the internal
117 : ** structure is only visible to the functions below.
118 : */
119 : struct quota_FILE {
120 : FILE *f; /* Open stdio file pointer */
121 : sqlite3_int64 iOfst; /* Current offset into the file */
122 : quotaFile *pFile; /* The file record in the quota system */
123 : };
124 :
125 :
126 : /************************* Global Variables **********************************/
127 : /*
128 : ** All global variables used by this file are containing within the following
129 : ** gQuota structure.
130 : */
131 : static struct {
132 : /* The pOrigVfs is the real, original underlying VFS implementation.
133 : ** Most operations pass-through to the real VFS. This value is read-only
134 : ** during operation. It is only modified at start-time and thus does not
135 : ** require a mutex.
136 : */
137 : sqlite3_vfs *pOrigVfs;
138 :
139 : /* The sThisVfs is the VFS structure used by this shim. It is initialized
140 : ** at start-time and thus does not require a mutex
141 : */
142 : sqlite3_vfs sThisVfs;
143 :
144 : /* The sIoMethods defines the methods used by sqlite3_file objects
145 : ** associated with this shim. It is initialized at start-time and does
146 : ** not require a mutex.
147 : **
148 : ** When the underlying VFS is called to open a file, it might return
149 : ** either a version 1 or a version 2 sqlite3_file object. This shim
150 : ** has to create a wrapper sqlite3_file of the same version. Hence
151 : ** there are two I/O method structures, one for version 1 and the other
152 : ** for version 2.
153 : */
154 : sqlite3_io_methods sIoMethodsV1;
155 : sqlite3_io_methods sIoMethodsV2;
156 :
157 : /* True when this shim as been initialized.
158 : */
159 : int isInitialized;
160 :
161 : /* For run-time access any of the other global data structures in this
162 : ** shim, the following mutex must be held.
163 : */
164 : sqlite3_mutex *pMutex;
165 :
166 : /* List of quotaGroup objects.
167 : */
168 : quotaGroup *pGroup;
169 :
170 : } gQuota;
171 :
172 : /************************* Utility Routines *********************************/
173 : /*
174 : ** Acquire and release the mutex used to serialize access to the
175 : ** list of quotaGroups.
176 : */
177 6156 : static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
178 6156 : static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
179 :
180 : /* Count the number of open files in a quotaGroup
181 : */
182 150 : static int quotaGroupOpenFileCount(quotaGroup *pGroup){
183 150 : int N = 0;
184 150 : quotaFile *pFile = pGroup->pFiles;
185 456 : while( pFile ){
186 156 : if( pFile->nRef ) N++;
187 156 : pFile = pFile->pNext;
188 : }
189 150 : return N;
190 : }
191 :
192 : /* Remove a file from a quota group.
193 : */
194 52 : static void quotaRemoveFile(quotaFile *pFile){
195 52 : quotaGroup *pGroup = pFile->pGroup;
196 52 : pGroup->iSize -= pFile->iSize;
197 52 : *pFile->ppPrev = pFile->pNext;
198 52 : if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
199 52 : sqlite3_free(pFile);
200 52 : }
201 :
202 : /* Remove all files from a quota group. It is always the case that
203 : ** all files will be closed when this routine is called.
204 : */
205 50 : static void quotaRemoveAllFiles(quotaGroup *pGroup){
206 152 : while( pGroup->pFiles ){
207 52 : assert( pGroup->pFiles->nRef==0 );
208 52 : quotaRemoveFile(pGroup->pFiles);
209 : }
210 50 : }
211 :
212 :
213 : /* If the reference count and threshold for a quotaGroup are both
214 : ** zero, then destroy the quotaGroup.
215 : */
216 591 : static void quotaGroupDeref(quotaGroup *pGroup){
217 591 : if( pGroup->iLimit==0 && quotaGroupOpenFileCount(pGroup)==0 ){
218 50 : quotaRemoveAllFiles(pGroup);
219 50 : *pGroup->ppPrev = pGroup->pNext;
220 50 : if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev;
221 50 : if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg);
222 50 : sqlite3_free(pGroup);
223 : }
224 591 : }
225 :
226 : /*
227 : ** Return TRUE if string z matches glob pattern zGlob.
228 : **
229 : ** Globbing rules:
230 : **
231 : ** '*' Matches any sequence of zero or more characters.
232 : **
233 : ** '?' Matches exactly one character.
234 : **
235 : ** [...] Matches one character from the enclosed list of
236 : ** characters.
237 : **
238 : ** [^...] Matches one character not in the enclosed list.
239 : **
240 : ** / Matches "/" or "\\"
241 : **
242 : */
243 1117 : static int quotaStrglob(const char *zGlob, const char *z){
244 : int c, c2, cx;
245 : int invert;
246 : int seen;
247 :
248 37978 : while( (c = (*(zGlob++)))!=0 ){
249 36861 : if( c=='*' ){
250 2234 : while( (c=(*(zGlob++))) == '*' || c=='?' ){
251 0 : if( c=='?' && (*(z++))==0 ) return 0;
252 : }
253 1117 : if( c==0 ){
254 1117 : return 1;
255 0 : }else if( c=='[' ){
256 0 : while( *z && quotaStrglob(zGlob-1,z)==0 ){
257 0 : z++;
258 : }
259 0 : return (*z)!=0;
260 : }
261 0 : cx = (c=='/') ? '\\' : c;
262 0 : while( (c2 = (*(z++)))!=0 ){
263 0 : while( c2!=c && c2!=cx ){
264 0 : c2 = *(z++);
265 0 : if( c2==0 ) return 0;
266 : }
267 0 : if( quotaStrglob(zGlob,z) ) return 1;
268 : }
269 0 : return 0;
270 35744 : }else if( c=='?' ){
271 0 : if( (*(z++))==0 ) return 0;
272 35744 : }else if( c=='[' ){
273 0 : int prior_c = 0;
274 0 : seen = 0;
275 0 : invert = 0;
276 0 : c = *(z++);
277 0 : if( c==0 ) return 0;
278 0 : c2 = *(zGlob++);
279 0 : if( c2=='^' ){
280 0 : invert = 1;
281 0 : c2 = *(zGlob++);
282 : }
283 0 : if( c2==']' ){
284 0 : if( c==']' ) seen = 1;
285 0 : c2 = *(zGlob++);
286 : }
287 0 : while( c2 && c2!=']' ){
288 0 : if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
289 0 : c2 = *(zGlob++);
290 0 : if( c>=prior_c && c<=c2 ) seen = 1;
291 0 : prior_c = 0;
292 : }else{
293 0 : if( c==c2 ){
294 0 : seen = 1;
295 : }
296 0 : prior_c = c2;
297 : }
298 0 : c2 = *(zGlob++);
299 : }
300 0 : if( c2==0 || (seen ^ invert)==0 ) return 0;
301 35744 : }else if( c=='/' ){
302 5585 : if( z[0]!='/' && z[0]!='\\' ) return 0;
303 5585 : z++;
304 : }else{
305 30159 : if( c!=(*(z++)) ) return 0;
306 : }
307 : }
308 0 : return *z==0;
309 : }
310 :
311 :
312 : /* Find a quotaGroup given the filename.
313 : **
314 : ** Return a pointer to the quotaGroup object. Return NULL if not found.
315 : */
316 1117 : static quotaGroup *quotaGroupFind(const char *zFilename){
317 : quotaGroup *p;
318 1117 : for(p=gQuota.pGroup; p && quotaStrglob(p->zPattern, zFilename)==0;
319 : p=p->pNext){}
320 1117 : return p;
321 : }
322 :
323 : /* Translate an sqlite3_file* that is really a quotaConn* into
324 : ** the sqlite3_file* for the underlying original VFS.
325 : */
326 19824 : static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
327 19824 : quotaConn *p = (quotaConn*)pConn;
328 19824 : return (sqlite3_file*)&p[1];
329 : }
330 :
331 : /* Find a file in a quota group and return a pointer to that file.
332 : ** Return NULL if the file is not in the group.
333 : */
334 1117 : static quotaFile *quotaFindFile(
335 : quotaGroup *pGroup, /* Group in which to look for the file */
336 : const char *zName, /* Full pathname of the file */
337 : int createFlag /* Try to create the file if not found */
338 : ){
339 1117 : quotaFile *pFile = pGroup->pFiles;
340 2881 : while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
341 647 : pFile = pFile->pNext;
342 : }
343 1117 : if( pFile==0 && createFlag ){
344 52 : int nName = strlen(zName);
345 52 : pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
346 52 : if( pFile ){
347 52 : memset(pFile, 0, sizeof(*pFile));
348 52 : pFile->zFilename = (char*)&pFile[1];
349 52 : memcpy(pFile->zFilename, zName, nName+1);
350 52 : pFile->pNext = pGroup->pFiles;
351 52 : if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
352 52 : pFile->ppPrev = &pGroup->pFiles;
353 52 : pGroup->pFiles = pFile;
354 52 : pFile->pGroup = pGroup;
355 : }
356 : }
357 1117 : return pFile;
358 : }
359 :
360 : /*
361 : ** Figure out if we are dealing with Unix, Windows, or some other
362 : ** operating system. After the following block of preprocess macros,
363 : ** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
364 : ** will defined to either 1 or 0. One of the four will be 1. The other
365 : ** three will be 0.
366 : */
367 : #if defined(SQLITE_OS_OTHER)
368 : # if SQLITE_OS_OTHER==1
369 : # undef SQLITE_OS_UNIX
370 : # define SQLITE_OS_UNIX 0
371 : # undef SQLITE_OS_WIN
372 : # define SQLITE_OS_WIN 0
373 : # undef SQLITE_OS_OS2
374 : # define SQLITE_OS_OS2 0
375 : # else
376 : # undef SQLITE_OS_OTHER
377 : # endif
378 : #endif
379 : #if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
380 : # define SQLITE_OS_OTHER 0
381 : # ifndef SQLITE_OS_WIN
382 : # if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) \
383 : || defined(__MINGW32__) || defined(__BORLANDC__)
384 : # define SQLITE_OS_WIN 1
385 : # define SQLITE_OS_UNIX 0
386 : # define SQLITE_OS_OS2 0
387 : # elif defined(__EMX__) || defined(_OS2) || defined(OS2) \
388 : || defined(_OS2_) || defined(__OS2__)
389 : # define SQLITE_OS_WIN 0
390 : # define SQLITE_OS_UNIX 0
391 : # define SQLITE_OS_OS2 1
392 : # else
393 : # define SQLITE_OS_WIN 0
394 : # define SQLITE_OS_UNIX 1
395 : # define SQLITE_OS_OS2 0
396 : # endif
397 : # else
398 : # define SQLITE_OS_UNIX 0
399 : # define SQLITE_OS_OS2 0
400 : # endif
401 : #else
402 : # ifndef SQLITE_OS_WIN
403 : # define SQLITE_OS_WIN 0
404 : # endif
405 : #endif
406 :
407 : #if SQLITE_OS_UNIX
408 : # include <unistd.h>
409 : #endif
410 : #if SQLITE_OS_WIN
411 : # include <windows.h>
412 : # include <io.h>
413 : #endif
414 :
415 : /*
416 : ** Translate UTF8 to MBCS for use in fopen() calls. Return a pointer to the
417 : ** translated text.. Call quota_mbcs_free() to deallocate any memory
418 : ** used to store the returned pointer when done.
419 : */
420 0 : static char *quota_utf8_to_mbcs(const char *zUtf8){
421 : #if SQLITE_OS_WIN
422 : int n; /* Bytes in zUtf8 */
423 : int nWide; /* number of UTF-16 characters */
424 : int nMbcs; /* Bytes of MBCS */
425 : LPWSTR zTmpWide; /* The UTF16 text */
426 : char *zMbcs; /* The MBCS text */
427 : int codepage; /* Code page used by fopen() */
428 :
429 : n = strlen(zUtf8);
430 : nWide = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0);
431 : if( nWide==0 ) return 0;
432 : zTmpWide = (LPWSTR)sqlite3_malloc( (nWide+1)*sizeof(zTmpWide[0]) );
433 : if( zTmpWide==0 ) return 0;
434 : MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zTmpWide, nWide);
435 : codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
436 : nMbcs = WideCharToMultiByte(codepage, 0, zTmpWide, nWide, 0, 0, 0, 0);
437 : zMbcs = nMbcs ? (char*)sqlite3_malloc( nMbcs+1 ) : 0;
438 : if( zMbcs ){
439 : WideCharToMultiByte(codepage, 0, zTmpWide, nWide, zMbcs, nMbcs, 0, 0);
440 : }
441 : sqlite3_free(zTmpWide);
442 : return zMbcs;
443 : #else
444 0 : return (char*)zUtf8; /* No-op on unix */
445 : #endif
446 : }
447 :
448 : /*
449 : ** Deallocate any memory allocated by quota_utf8_to_mbcs().
450 : */
451 0 : static void quota_mbcs_free(char *zOld){
452 : #if SQLITE_OS_WIN
453 : sqlite3_free(zOld);
454 : #else
455 : /* No-op on unix */
456 : #endif
457 0 : }
458 :
459 : /************************* VFS Method Wrappers *****************************/
460 : /*
461 : ** This is the xOpen method used for the "quota" VFS.
462 : **
463 : ** Most of the work is done by the underlying original VFS. This method
464 : ** simply links the new file into the appropriate quota group if it is a
465 : ** file that needs to be tracked.
466 : */
467 1016 : static int quotaOpen(
468 : sqlite3_vfs *pVfs, /* The quota VFS */
469 : const char *zName, /* Name of file to be opened */
470 : sqlite3_file *pConn, /* Fill in this file descriptor */
471 : int flags, /* Flags to control the opening */
472 : int *pOutFlags /* Flags showing results of opening */
473 : ){
474 : int rc; /* Result code */
475 : quotaConn *pQuotaOpen; /* The new quota file descriptor */
476 : quotaFile *pFile; /* Corresponding quotaFile obj */
477 : quotaGroup *pGroup; /* The group file belongs to */
478 : sqlite3_file *pSubOpen; /* Real file descriptor */
479 1016 : sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */
480 :
481 : /* If the file is not a main database file or a WAL, then use the
482 : ** normal xOpen method.
483 : */
484 1016 : if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
485 525 : return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
486 : }
487 :
488 : /* If the name of the file does not match any quota group, then
489 : ** use the normal xOpen method.
490 : */
491 491 : quotaEnter();
492 491 : pGroup = quotaGroupFind(zName);
493 491 : if( pGroup==0 ){
494 0 : rc = pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
495 : }else{
496 : /* If we get to this point, it means the file needs to be quota tracked.
497 : */
498 491 : pQuotaOpen = (quotaConn*)pConn;
499 491 : pSubOpen = quotaSubOpen(pConn);
500 491 : rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
501 491 : if( rc==SQLITE_OK ){
502 491 : pFile = quotaFindFile(pGroup, zName, 1);
503 491 : if( pFile==0 ){
504 0 : quotaLeave();
505 0 : pSubOpen->pMethods->xClose(pSubOpen);
506 0 : return SQLITE_NOMEM;
507 : }
508 491 : pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
509 491 : pFile->nRef++;
510 491 : pQuotaOpen->pFile = pFile;
511 491 : if( pSubOpen->pMethods->iVersion==1 ){
512 0 : pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1;
513 : }else{
514 491 : pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2;
515 : }
516 : }
517 : }
518 491 : quotaLeave();
519 491 : return rc;
520 : }
521 :
522 : /*
523 : ** This is the xDelete method used for the "quota" VFS.
524 : **
525 : ** If the file being deleted is part of the quota group, then reduce
526 : ** the size of the quota group accordingly. And remove the file from
527 : ** the set of files in the quota group.
528 : */
529 626 : static int quotaDelete(
530 : sqlite3_vfs *pVfs, /* The quota VFS */
531 : const char *zName, /* Name of file to be deleted */
532 : int syncDir /* Do a directory sync after deleting */
533 : ){
534 : int rc; /* Result code */
535 : quotaFile *pFile; /* Files in the quota */
536 : quotaGroup *pGroup; /* The group file belongs to */
537 626 : sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */
538 :
539 : /* Do the actual file delete */
540 626 : rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
541 :
542 : /* If the file just deleted is a member of a quota group, then remove
543 : ** it from that quota group.
544 : */
545 626 : if( rc==SQLITE_OK ){
546 626 : quotaEnter();
547 626 : pGroup = quotaGroupFind(zName);
548 626 : if( pGroup ){
549 626 : pFile = quotaFindFile(pGroup, zName, 0);
550 626 : if( pFile ){
551 0 : if( pFile->nRef ){
552 0 : pFile->deleteOnClose = 1;
553 : }else{
554 0 : quotaRemoveFile(pFile);
555 0 : quotaGroupDeref(pGroup);
556 : }
557 : }
558 : }
559 626 : quotaLeave();
560 : }
561 626 : return rc;
562 : }
563 :
564 :
565 : /************************ I/O Method Wrappers *******************************/
566 :
567 : /* xClose requests get passed through to the original VFS. But we
568 : ** also have to unlink the quotaConn from the quotaFile and quotaGroup.
569 : ** The quotaFile and/or quotaGroup are freed if they are no longer in use.
570 : */
571 491 : static int quotaClose(sqlite3_file *pConn){
572 491 : quotaConn *p = (quotaConn*)pConn;
573 491 : quotaFile *pFile = p->pFile;
574 491 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
575 : int rc;
576 491 : rc = pSubOpen->pMethods->xClose(pSubOpen);
577 491 : quotaEnter();
578 491 : pFile->nRef--;
579 491 : if( pFile->nRef==0 ){
580 491 : quotaGroup *pGroup = pFile->pGroup;
581 491 : if( pFile->deleteOnClose ){
582 0 : gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
583 0 : quotaRemoveFile(pFile);
584 : }
585 491 : quotaGroupDeref(pGroup);
586 : }
587 491 : quotaLeave();
588 491 : return rc;
589 : }
590 :
591 : /* Pass xRead requests directory thru to the original VFS without
592 : ** further processing.
593 : */
594 3469 : static int quotaRead(
595 : sqlite3_file *pConn,
596 : void *pBuf,
597 : int iAmt,
598 : sqlite3_int64 iOfst
599 : ){
600 3469 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
601 3469 : return pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
602 : }
603 :
604 : /* Check xWrite requests to see if they expand the file. If they do,
605 : ** the perform a quota check before passing them through to the
606 : ** original VFS.
607 : */
608 2719 : static int quotaWrite(
609 : sqlite3_file *pConn,
610 : const void *pBuf,
611 : int iAmt,
612 : sqlite3_int64 iOfst
613 : ){
614 2719 : quotaConn *p = (quotaConn*)pConn;
615 2719 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
616 2719 : sqlite3_int64 iEnd = iOfst+iAmt;
617 : quotaGroup *pGroup;
618 2719 : quotaFile *pFile = p->pFile;
619 : sqlite3_int64 szNew;
620 :
621 2719 : if( pFile->iSize<iEnd ){
622 834 : pGroup = pFile->pGroup;
623 834 : quotaEnter();
624 834 : szNew = pGroup->iSize - pFile->iSize + iEnd;
625 834 : if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
626 0 : if( pGroup->xCallback ){
627 : pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
628 0 : pGroup->pArg);
629 : }
630 0 : if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
631 0 : quotaLeave();
632 0 : return SQLITE_FULL;
633 : }
634 : }
635 834 : pGroup->iSize = szNew;
636 834 : pFile->iSize = iEnd;
637 834 : quotaLeave();
638 : }
639 2719 : return pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
640 : }
641 :
642 : /* Pass xTruncate requests thru to the original VFS. If the
643 : ** success, update the file size.
644 : */
645 0 : static int quotaTruncate(sqlite3_file *pConn, sqlite3_int64 size){
646 0 : quotaConn *p = (quotaConn*)pConn;
647 0 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
648 0 : int rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
649 0 : quotaFile *pFile = p->pFile;
650 : quotaGroup *pGroup;
651 0 : if( rc==SQLITE_OK ){
652 0 : quotaEnter();
653 0 : pGroup = pFile->pGroup;
654 0 : pGroup->iSize -= pFile->iSize;
655 0 : pFile->iSize = size;
656 0 : pGroup->iSize += size;
657 0 : quotaLeave();
658 : }
659 0 : return rc;
660 : }
661 :
662 : /* Pass xSync requests through to the original VFS without change
663 : */
664 466 : static int quotaSync(sqlite3_file *pConn, int flags){
665 466 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
666 466 : return pSubOpen->pMethods->xSync(pSubOpen, flags);
667 : }
668 :
669 : /* Pass xFileSize requests through to the original VFS but then
670 : ** update the quotaGroup with the new size before returning.
671 : */
672 3664 : static int quotaFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
673 3664 : quotaConn *p = (quotaConn*)pConn;
674 3664 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
675 3664 : quotaFile *pFile = p->pFile;
676 : quotaGroup *pGroup;
677 : sqlite3_int64 sz;
678 : int rc;
679 :
680 3664 : rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
681 3664 : if( rc==SQLITE_OK ){
682 3664 : quotaEnter();
683 3664 : pGroup = pFile->pGroup;
684 3664 : pGroup->iSize -= pFile->iSize;
685 3664 : pFile->iSize = sz;
686 3664 : pGroup->iSize += sz;
687 3664 : quotaLeave();
688 3664 : *pSize = sz;
689 : }
690 3664 : return rc;
691 : }
692 :
693 : /* Pass xLock requests through to the original VFS unchanged.
694 : */
695 2322 : static int quotaLock(sqlite3_file *pConn, int lock){
696 2322 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
697 2322 : return pSubOpen->pMethods->xLock(pSubOpen, lock);
698 : }
699 :
700 : /* Pass xUnlock requests through to the original VFS unchanged.
701 : */
702 2347 : static int quotaUnlock(sqlite3_file *pConn, int lock){
703 2347 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
704 2347 : return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
705 : }
706 :
707 : /* Pass xCheckReservedLock requests through to the original VFS unchanged.
708 : */
709 0 : static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
710 0 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
711 0 : return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
712 : }
713 :
714 : /* Pass xFileControl requests through to the original VFS unchanged.
715 : */
716 57 : static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
717 57 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
718 57 : int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
719 : #if defined(SQLITE_FCNTL_VFSNAME)
720 57 : if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
721 0 : *(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg);
722 : }
723 : #endif
724 57 : return rc;
725 : }
726 :
727 : /* Pass xSectorSize requests through to the original VFS unchanged.
728 : */
729 0 : static int quotaSectorSize(sqlite3_file *pConn){
730 0 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
731 0 : return pSubOpen->pMethods->xSectorSize(pSubOpen);
732 : }
733 :
734 : /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
735 : */
736 3798 : static int quotaDeviceCharacteristics(sqlite3_file *pConn){
737 3798 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
738 3798 : return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
739 : }
740 :
741 : /* Pass xShmMap requests through to the original VFS unchanged.
742 : */
743 0 : static int quotaShmMap(
744 : sqlite3_file *pConn, /* Handle open on database file */
745 : int iRegion, /* Region to retrieve */
746 : int szRegion, /* Size of regions */
747 : int bExtend, /* True to extend file if necessary */
748 : void volatile **pp /* OUT: Mapped memory */
749 : ){
750 0 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
751 0 : return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
752 : }
753 :
754 : /* Pass xShmLock requests through to the original VFS unchanged.
755 : */
756 0 : static int quotaShmLock(
757 : sqlite3_file *pConn, /* Database file holding the shared memory */
758 : int ofst, /* First lock to acquire or release */
759 : int n, /* Number of locks to acquire or release */
760 : int flags /* What to do with the lock */
761 : ){
762 0 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
763 0 : return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
764 : }
765 :
766 : /* Pass xShmBarrier requests through to the original VFS unchanged.
767 : */
768 0 : static void quotaShmBarrier(sqlite3_file *pConn){
769 0 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
770 0 : pSubOpen->pMethods->xShmBarrier(pSubOpen);
771 0 : }
772 :
773 : /* Pass xShmUnmap requests through to the original VFS unchanged.
774 : */
775 0 : static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){
776 0 : sqlite3_file *pSubOpen = quotaSubOpen(pConn);
777 0 : return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
778 : }
779 :
780 : /************************** Public Interfaces *****************************/
781 : /*
782 : ** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
783 : ** as the VFS that does the actual work. Use the default if
784 : ** zOrigVfsName==NULL.
785 : **
786 : ** The quota VFS shim is named "quota". It will become the default
787 : ** VFS if makeDefault is non-zero.
788 : **
789 : ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
790 : ** during start-up.
791 : */
792 803 : int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
793 : sqlite3_vfs *pOrigVfs;
794 803 : if( gQuota.isInitialized ) return SQLITE_MISUSE;
795 803 : pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
796 803 : if( pOrigVfs==0 ) return SQLITE_ERROR;
797 803 : assert( pOrigVfs!=&gQuota.sThisVfs );
798 803 : gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
799 803 : if( !gQuota.pMutex ){
800 0 : return SQLITE_NOMEM;
801 : }
802 803 : gQuota.isInitialized = 1;
803 803 : gQuota.pOrigVfs = pOrigVfs;
804 803 : gQuota.sThisVfs = *pOrigVfs;
805 803 : gQuota.sThisVfs.xOpen = quotaOpen;
806 803 : gQuota.sThisVfs.xDelete = quotaDelete;
807 803 : gQuota.sThisVfs.szOsFile += sizeof(quotaConn);
808 803 : gQuota.sThisVfs.zName = "quota";
809 803 : gQuota.sIoMethodsV1.iVersion = 1;
810 803 : gQuota.sIoMethodsV1.xClose = quotaClose;
811 803 : gQuota.sIoMethodsV1.xRead = quotaRead;
812 803 : gQuota.sIoMethodsV1.xWrite = quotaWrite;
813 803 : gQuota.sIoMethodsV1.xTruncate = quotaTruncate;
814 803 : gQuota.sIoMethodsV1.xSync = quotaSync;
815 803 : gQuota.sIoMethodsV1.xFileSize = quotaFileSize;
816 803 : gQuota.sIoMethodsV1.xLock = quotaLock;
817 803 : gQuota.sIoMethodsV1.xUnlock = quotaUnlock;
818 803 : gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock;
819 803 : gQuota.sIoMethodsV1.xFileControl = quotaFileControl;
820 803 : gQuota.sIoMethodsV1.xSectorSize = quotaSectorSize;
821 803 : gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics;
822 803 : gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1;
823 803 : gQuota.sIoMethodsV2.iVersion = 2;
824 803 : gQuota.sIoMethodsV2.xShmMap = quotaShmMap;
825 803 : gQuota.sIoMethodsV2.xShmLock = quotaShmLock;
826 803 : gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier;
827 803 : gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap;
828 803 : sqlite3_vfs_register(&gQuota.sThisVfs, makeDefault);
829 803 : return SQLITE_OK;
830 : }
831 :
832 : /*
833 : ** Shutdown the quota system.
834 : **
835 : ** All SQLite database connections must be closed before calling this
836 : ** routine.
837 : **
838 : ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
839 : ** shutting down in order to free all remaining quota groups.
840 : */
841 794 : int sqlite3_quota_shutdown(void){
842 : quotaGroup *pGroup;
843 794 : if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
844 844 : for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
845 50 : if( quotaGroupOpenFileCount(pGroup)>0 ) return SQLITE_MISUSE;
846 : }
847 1638 : while( gQuota.pGroup ){
848 50 : pGroup = gQuota.pGroup;
849 50 : gQuota.pGroup = pGroup->pNext;
850 50 : pGroup->iLimit = 0;
851 50 : assert( quotaGroupOpenFileCount(pGroup)==0 );
852 50 : quotaGroupDeref(pGroup);
853 : }
854 794 : gQuota.isInitialized = 0;
855 794 : sqlite3_mutex_free(gQuota.pMutex);
856 794 : sqlite3_vfs_unregister(&gQuota.sThisVfs);
857 794 : memset(&gQuota, 0, sizeof(gQuota));
858 794 : return SQLITE_OK;
859 : }
860 :
861 : /*
862 : ** Create or destroy a quota group.
863 : **
864 : ** The quota group is defined by the zPattern. When calling this routine
865 : ** with a zPattern for a quota group that already exists, this routine
866 : ** merely updates the iLimit, xCallback, and pArg values for that quota
867 : ** group. If zPattern is new, then a new quota group is created.
868 : **
869 : ** If the iLimit for a quota group is set to zero, then the quota group
870 : ** is disabled and will be deleted when the last database connection using
871 : ** the quota group is closed.
872 : **
873 : ** Calling this routine on a zPattern that does not exist and with a
874 : ** zero iLimit is a no-op.
875 : **
876 : ** A quota group must exist with a non-zero iLimit prior to opening
877 : ** database connections if those connections are to participate in the
878 : ** quota group. Creating a quota group does not affect database connections
879 : ** that are already open.
880 : */
881 50 : int sqlite3_quota_set(
882 : const char *zPattern, /* The filename pattern */
883 : sqlite3_int64 iLimit, /* New quota to set for this quota group */
884 : void (*xCallback)( /* Callback invoked when going over quota */
885 : const char *zFilename, /* Name of file whose size increases */
886 : sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
887 : sqlite3_int64 iSize, /* Total size of all files in the group */
888 : void *pArg /* Client data */
889 : ),
890 : void *pArg, /* client data passed thru to callback */
891 : void (*xDestroy)(void*) /* Optional destructor for pArg */
892 : ){
893 : quotaGroup *pGroup;
894 50 : quotaEnter();
895 50 : pGroup = gQuota.pGroup;
896 100 : while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){
897 0 : pGroup = pGroup->pNext;
898 : }
899 50 : if( pGroup==0 ){
900 50 : int nPattern = strlen(zPattern);
901 50 : if( iLimit<=0 ){
902 0 : quotaLeave();
903 0 : return SQLITE_OK;
904 : }
905 50 : pGroup = (quotaGroup *)sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
906 50 : if( pGroup==0 ){
907 0 : quotaLeave();
908 0 : return SQLITE_NOMEM;
909 : }
910 50 : memset(pGroup, 0, sizeof(*pGroup));
911 50 : pGroup->zPattern = (char*)&pGroup[1];
912 50 : memcpy((char *)pGroup->zPattern, zPattern, nPattern+1);
913 50 : if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext;
914 50 : pGroup->pNext = gQuota.pGroup;
915 50 : pGroup->ppPrev = &gQuota.pGroup;
916 50 : gQuota.pGroup = pGroup;
917 : }
918 50 : pGroup->iLimit = iLimit;
919 50 : pGroup->xCallback = xCallback;
920 50 : if( pGroup->xDestroy && pGroup->pArg!=pArg ){
921 0 : pGroup->xDestroy(pGroup->pArg);
922 : }
923 50 : pGroup->pArg = pArg;
924 50 : pGroup->xDestroy = xDestroy;
925 50 : quotaGroupDeref(pGroup);
926 50 : quotaLeave();
927 50 : return SQLITE_OK;
928 : }
929 :
930 : /*
931 : ** Bring the named file under quota management. Or if it is already under
932 : ** management, update its size.
933 : */
934 0 : int sqlite3_quota_file(const char *zFilename){
935 : char *zFull;
936 : sqlite3_file *fd;
937 : int rc;
938 0 : int outFlags = 0;
939 : sqlite3_int64 iSize;
940 0 : int nAlloc = gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+2;
941 :
942 : /* Allocate space for a file-handle and the full path for file zFilename */
943 0 : fd = (sqlite3_file *)sqlite3_malloc(nAlloc);
944 0 : if( fd==0 ){
945 0 : rc = SQLITE_NOMEM;
946 : }else{
947 0 : zFull = &((char *)fd)[gQuota.sThisVfs.szOsFile];
948 : rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
949 0 : gQuota.sThisVfs.mxPathname+1, zFull);
950 : }
951 :
952 0 : if( rc==SQLITE_OK ){
953 0 : zFull[strlen(zFull)+1] = '\0';
954 : rc = quotaOpen(&gQuota.sThisVfs, zFull, fd,
955 0 : SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags);
956 0 : if( rc==SQLITE_OK ){
957 0 : fd->pMethods->xFileSize(fd, &iSize);
958 0 : fd->pMethods->xClose(fd);
959 0 : }else if( rc==SQLITE_CANTOPEN ){
960 : quotaGroup *pGroup;
961 : quotaFile *pFile;
962 0 : quotaEnter();
963 0 : pGroup = quotaGroupFind(zFull);
964 0 : if( pGroup ){
965 0 : pFile = quotaFindFile(pGroup, zFull, 0);
966 0 : if( pFile ) quotaRemoveFile(pFile);
967 : }
968 0 : quotaLeave();
969 : }
970 : }
971 :
972 0 : sqlite3_free(fd);
973 0 : return rc;
974 : }
975 :
976 : /*
977 : ** Open a potentially quotaed file for I/O.
978 : */
979 0 : quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
980 0 : quota_FILE *p = 0;
981 0 : char *zFull = 0;
982 : char *zFullTranslated;
983 : int rc;
984 : quotaGroup *pGroup;
985 : quotaFile *pFile;
986 :
987 0 : zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
988 0 : if( zFull==0 ) return 0;
989 : rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
990 0 : gQuota.sThisVfs.mxPathname+1, zFull);
991 0 : if( rc ) goto quota_fopen_error;
992 0 : p = (quota_FILE*)sqlite3_malloc(sizeof(*p));
993 0 : if( p==0 ) goto quota_fopen_error;
994 0 : memset(p, 0, sizeof(*p));
995 0 : zFullTranslated = quota_utf8_to_mbcs(zFull);
996 0 : if( zFullTranslated==0 ) goto quota_fopen_error;
997 0 : p->f = fopen(zFullTranslated, zMode);
998 0 : quota_mbcs_free(zFullTranslated);
999 0 : if( p->f==0 ) goto quota_fopen_error;
1000 0 : quotaEnter();
1001 0 : pGroup = quotaGroupFind(zFull);
1002 0 : if( pGroup ){
1003 0 : pFile = quotaFindFile(pGroup, zFull, 1);
1004 0 : if( pFile==0 ){
1005 0 : quotaLeave();
1006 0 : goto quota_fopen_error;
1007 : }
1008 0 : pFile->nRef++;
1009 0 : p->pFile = pFile;
1010 : }
1011 0 : quotaLeave();
1012 0 : sqlite3_free(zFull);
1013 0 : return p;
1014 :
1015 : quota_fopen_error:
1016 0 : sqlite3_free(zFull);
1017 0 : if( p && p->f ) fclose(p->f);
1018 0 : sqlite3_free(p);
1019 0 : return 0;
1020 : }
1021 :
1022 : /*
1023 : ** Read content from a quota_FILE
1024 : */
1025 0 : size_t sqlite3_quota_fread(
1026 : void *pBuf, /* Store the content here */
1027 : size_t size, /* Size of each element */
1028 : size_t nmemb, /* Number of elements to read */
1029 : quota_FILE *p /* Read from this quota_FILE object */
1030 : ){
1031 0 : return fread(pBuf, size, nmemb, p->f);
1032 : }
1033 :
1034 : /*
1035 : ** Write content into a quota_FILE. Invoke the quota callback and block
1036 : ** the write if we exceed quota.
1037 : */
1038 0 : size_t sqlite3_quota_fwrite(
1039 : void *pBuf, /* Take content to write from here */
1040 : size_t size, /* Size of each element */
1041 : size_t nmemb, /* Number of elements */
1042 : quota_FILE *p /* Write to this quota_FILE objecct */
1043 : ){
1044 : sqlite3_int64 iOfst;
1045 : sqlite3_int64 iEnd;
1046 : sqlite3_int64 szNew;
1047 : quotaFile *pFile;
1048 :
1049 0 : iOfst = ftell(p->f);
1050 0 : iEnd = iOfst + size*nmemb;
1051 0 : pFile = p->pFile;
1052 0 : if( pFile && pFile->iSize<iEnd ){
1053 0 : quotaGroup *pGroup = pFile->pGroup;
1054 0 : quotaEnter();
1055 0 : szNew = pGroup->iSize - pFile->iSize + iEnd;
1056 0 : if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
1057 0 : if( pGroup->xCallback ){
1058 : pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
1059 0 : pGroup->pArg);
1060 : }
1061 0 : if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
1062 0 : iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
1063 0 : nmemb = (iEnd - iOfst)/size;
1064 0 : iEnd = iOfst + size*nmemb;
1065 0 : szNew = pGroup->iSize - pFile->iSize + iEnd;
1066 : }
1067 : }
1068 0 : pGroup->iSize = szNew;
1069 0 : pFile->iSize = iEnd;
1070 0 : quotaLeave();
1071 : }
1072 0 : return fwrite(pBuf, size, nmemb, p->f);
1073 : }
1074 :
1075 : /*
1076 : ** Close an open quota_FILE stream.
1077 : */
1078 0 : int sqlite3_quota_fclose(quota_FILE *p){
1079 : int rc;
1080 : quotaFile *pFile;
1081 0 : rc = fclose(p->f);
1082 0 : pFile = p->pFile;
1083 0 : if( pFile ){
1084 0 : quotaEnter();
1085 0 : pFile->nRef--;
1086 0 : if( pFile->nRef==0 ){
1087 0 : quotaGroup *pGroup = pFile->pGroup;
1088 0 : if( pFile->deleteOnClose ){
1089 0 : gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
1090 0 : quotaRemoveFile(pFile);
1091 : }
1092 0 : quotaGroupDeref(pGroup);
1093 : }
1094 0 : quotaLeave();
1095 : }
1096 0 : sqlite3_free(p);
1097 0 : return rc;
1098 : }
1099 :
1100 : /*
1101 : ** Flush memory buffers for a quota_FILE to disk.
1102 : */
1103 0 : int sqlite3_quota_fflush(quota_FILE *p, int doFsync){
1104 : int rc;
1105 0 : rc = fflush(p->f);
1106 0 : if( rc==0 && doFsync ){
1107 : #if SQLITE_OS_UNIX
1108 0 : rc = fsync(fileno(p->f));
1109 : #endif
1110 : #if SQLITE_OS_WIN
1111 : rc = _commit(_fileno(p->f));
1112 : #endif
1113 : }
1114 0 : return rc!=0;
1115 : }
1116 :
1117 : /*
1118 : ** Seek on a quota_FILE stream.
1119 : */
1120 0 : int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
1121 0 : return fseek(p->f, offset, whence);
1122 : }
1123 :
1124 : /*
1125 : ** rewind a quota_FILE stream.
1126 : */
1127 0 : void sqlite3_quota_rewind(quota_FILE *p){
1128 0 : rewind(p->f);
1129 0 : }
1130 :
1131 : /*
1132 : ** Tell the current location of a quota_FILE stream.
1133 : */
1134 0 : long sqlite3_quota_ftell(quota_FILE *p){
1135 0 : return ftell(p->f);
1136 : }
1137 :
1138 : /*
1139 : ** Remove a managed file. Update quotas accordingly.
1140 : */
1141 0 : int sqlite3_quota_remove(const char *zFilename){
1142 : char *zFull; /* Full pathname for zFilename */
1143 : int nFull; /* Number of bytes in zFilename */
1144 : int rc; /* Result code */
1145 : quotaGroup *pGroup; /* Group containing zFilename */
1146 : quotaFile *pFile; /* A file in the group */
1147 : quotaFile *pNextFile; /* next file in the group */
1148 : int diff; /* Difference between filenames */
1149 : char c; /* First character past end of pattern */
1150 :
1151 0 : zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
1152 0 : if( zFull==0 ) return SQLITE_NOMEM;
1153 : rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
1154 0 : gQuota.sThisVfs.mxPathname+1, zFull);
1155 0 : if( rc ){
1156 0 : sqlite3_free(zFull);
1157 0 : return rc;
1158 : }
1159 :
1160 : /* Figure out the length of the full pathname. If the name ends with
1161 : ** / (or \ on windows) then remove the trailing /.
1162 : */
1163 0 : nFull = strlen(zFull);
1164 0 : if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){
1165 0 : nFull--;
1166 0 : zFull[nFull] = 0;
1167 : }
1168 :
1169 0 : quotaEnter();
1170 0 : pGroup = quotaGroupFind(zFull);
1171 0 : if( pGroup ){
1172 0 : for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){
1173 0 : pNextFile = pFile->pNext;
1174 0 : diff = memcmp(zFull, pFile->zFilename, nFull);
1175 0 : if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){
1176 0 : if( pFile->nRef ){
1177 0 : pFile->deleteOnClose = 1;
1178 : }else{
1179 0 : rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
1180 0 : quotaRemoveFile(pFile);
1181 0 : quotaGroupDeref(pGroup);
1182 : }
1183 : }
1184 : }
1185 : }
1186 0 : quotaLeave();
1187 0 : sqlite3_free(zFull);
1188 0 : return rc;
1189 : }
1190 :
1191 : /***************************** Test Code ***********************************/
1192 : #ifdef SQLITE_TEST
1193 : #include <tcl.h>
1194 :
1195 : /*
1196 : ** Argument passed to a TCL quota-over-limit callback.
1197 : */
1198 : typedef struct TclQuotaCallback TclQuotaCallback;
1199 : struct TclQuotaCallback {
1200 : Tcl_Interp *interp; /* Interpreter in which to run the script */
1201 : Tcl_Obj *pScript; /* Script to be run */
1202 : };
1203 :
1204 : extern const char *sqlite3TestErrorName(int);
1205 :
1206 :
1207 : /*
1208 : ** This is the callback from a quota-over-limit.
1209 : */
1210 : static void tclQuotaCallback(
1211 : const char *zFilename, /* Name of file whose size increases */
1212 : sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
1213 : sqlite3_int64 iSize, /* Total size of all files in the group */
1214 : void *pArg /* Client data */
1215 : ){
1216 : TclQuotaCallback *p; /* Callback script object */
1217 : Tcl_Obj *pEval; /* Script to evaluate */
1218 : Tcl_Obj *pVarname; /* Name of variable to pass as 2nd arg */
1219 : unsigned int rnd; /* Random part of pVarname */
1220 : int rc; /* Tcl error code */
1221 :
1222 : p = (TclQuotaCallback *)pArg;
1223 : if( p==0 ) return;
1224 :
1225 : pVarname = Tcl_NewStringObj("::piLimit_", -1);
1226 : Tcl_IncrRefCount(pVarname);
1227 : sqlite3_randomness(sizeof(rnd), (void *)&rnd);
1228 : Tcl_AppendObjToObj(pVarname, Tcl_NewIntObj((int)(rnd&0x7FFFFFFF)));
1229 : Tcl_ObjSetVar2(p->interp, pVarname, 0, Tcl_NewWideIntObj(*piLimit), 0);
1230 :
1231 : pEval = Tcl_DuplicateObj(p->pScript);
1232 : Tcl_IncrRefCount(pEval);
1233 : Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zFilename, -1));
1234 : Tcl_ListObjAppendElement(0, pEval, pVarname);
1235 : Tcl_ListObjAppendElement(0, pEval, Tcl_NewWideIntObj(iSize));
1236 : rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
1237 :
1238 : if( rc==TCL_OK ){
1239 : Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0);
1240 : rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit);
1241 : Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0);
1242 : }
1243 :
1244 : Tcl_DecrRefCount(pEval);
1245 : Tcl_DecrRefCount(pVarname);
1246 : if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp);
1247 : }
1248 :
1249 : /*
1250 : ** Destructor for a TCL quota-over-limit callback.
1251 : */
1252 : static void tclCallbackDestructor(void *pObj){
1253 : TclQuotaCallback *p = (TclQuotaCallback*)pObj;
1254 : if( p ){
1255 : Tcl_DecrRefCount(p->pScript);
1256 : sqlite3_free((char *)p);
1257 : }
1258 : }
1259 :
1260 : /*
1261 : ** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT
1262 : */
1263 : static int test_quota_initialize(
1264 : void * clientData,
1265 : Tcl_Interp *interp,
1266 : int objc,
1267 : Tcl_Obj *CONST objv[]
1268 : ){
1269 : const char *zName; /* Name of new quota VFS */
1270 : int makeDefault; /* True to make the new VFS the default */
1271 : int rc; /* Value returned by quota_initialize() */
1272 :
1273 : /* Process arguments */
1274 : if( objc!=3 ){
1275 : Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
1276 : return TCL_ERROR;
1277 : }
1278 : zName = Tcl_GetString(objv[1]);
1279 : if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
1280 : if( zName[0]=='\0' ) zName = 0;
1281 :
1282 : /* Call sqlite3_quota_initialize() */
1283 : rc = sqlite3_quota_initialize(zName, makeDefault);
1284 : Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
1285 :
1286 : return TCL_OK;
1287 : }
1288 :
1289 : /*
1290 : ** tclcmd: sqlite3_quota_shutdown
1291 : */
1292 : static int test_quota_shutdown(
1293 : void * clientData,
1294 : Tcl_Interp *interp,
1295 : int objc,
1296 : Tcl_Obj *CONST objv[]
1297 : ){
1298 : int rc; /* Value returned by quota_shutdown() */
1299 :
1300 : if( objc!=1 ){
1301 : Tcl_WrongNumArgs(interp, 1, objv, "");
1302 : return TCL_ERROR;
1303 : }
1304 :
1305 : /* Call sqlite3_quota_shutdown() */
1306 : rc = sqlite3_quota_shutdown();
1307 : Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
1308 :
1309 : return TCL_OK;
1310 : }
1311 :
1312 : /*
1313 : ** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT
1314 : */
1315 : static int test_quota_set(
1316 : void * clientData,
1317 : Tcl_Interp *interp,
1318 : int objc,
1319 : Tcl_Obj *CONST objv[]
1320 : ){
1321 : const char *zPattern; /* File pattern to configure */
1322 : sqlite3_int64 iLimit; /* Initial quota in bytes */
1323 : Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */
1324 : int rc; /* Value returned by quota_set() */
1325 : TclQuotaCallback *p; /* Callback object */
1326 : int nScript; /* Length of callback script */
1327 : void (*xDestroy)(void*); /* Optional destructor for pArg */
1328 : void (*xCallback)(const char *, sqlite3_int64 *, sqlite3_int64, void *);
1329 :
1330 : /* Process arguments */
1331 : if( objc!=4 ){
1332 : Tcl_WrongNumArgs(interp, 1, objv, "PATTERN LIMIT SCRIPT");
1333 : return TCL_ERROR;
1334 : }
1335 : zPattern = Tcl_GetString(objv[1]);
1336 : if( Tcl_GetWideIntFromObj(interp, objv[2], &iLimit) ) return TCL_ERROR;
1337 : pScript = objv[3];
1338 : Tcl_GetStringFromObj(pScript, &nScript);
1339 :
1340 : if( nScript>0 ){
1341 : /* Allocate a TclQuotaCallback object */
1342 : p = (TclQuotaCallback *)sqlite3_malloc(sizeof(TclQuotaCallback));
1343 : if( !p ){
1344 : Tcl_SetResult(interp, (char *)"SQLITE_NOMEM", TCL_STATIC);
1345 : return TCL_OK;
1346 : }
1347 : memset(p, 0, sizeof(TclQuotaCallback));
1348 : p->interp = interp;
1349 : Tcl_IncrRefCount(pScript);
1350 : p->pScript = pScript;
1351 : xDestroy = tclCallbackDestructor;
1352 : xCallback = tclQuotaCallback;
1353 : }else{
1354 : p = 0;
1355 : xDestroy = 0;
1356 : xCallback = 0;
1357 : }
1358 :
1359 : /* Invoke sqlite3_quota_set() */
1360 : rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy);
1361 :
1362 : Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
1363 : return TCL_OK;
1364 : }
1365 :
1366 : /*
1367 : ** tclcmd: sqlite3_quota_file FILENAME
1368 : */
1369 : static int test_quota_file(
1370 : void * clientData,
1371 : Tcl_Interp *interp,
1372 : int objc,
1373 : Tcl_Obj *CONST objv[]
1374 : ){
1375 : const char *zFilename; /* File pattern to configure */
1376 : int rc; /* Value returned by quota_file() */
1377 :
1378 : /* Process arguments */
1379 : if( objc!=2 ){
1380 : Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
1381 : return TCL_ERROR;
1382 : }
1383 : zFilename = Tcl_GetString(objv[1]);
1384 :
1385 : /* Invoke sqlite3_quota_file() */
1386 : rc = sqlite3_quota_file(zFilename);
1387 :
1388 : Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
1389 : return TCL_OK;
1390 : }
1391 :
1392 : /*
1393 : ** tclcmd: sqlite3_quota_dump
1394 : */
1395 : static int test_quota_dump(
1396 : void * clientData,
1397 : Tcl_Interp *interp,
1398 : int objc,
1399 : Tcl_Obj *CONST objv[]
1400 : ){
1401 : Tcl_Obj *pResult;
1402 : Tcl_Obj *pGroupTerm;
1403 : Tcl_Obj *pFileTerm;
1404 : quotaGroup *pGroup;
1405 : quotaFile *pFile;
1406 :
1407 : pResult = Tcl_NewObj();
1408 : quotaEnter();
1409 : for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
1410 : pGroupTerm = Tcl_NewObj();
1411 : Tcl_ListObjAppendElement(interp, pGroupTerm,
1412 : Tcl_NewStringObj(pGroup->zPattern, -1));
1413 : Tcl_ListObjAppendElement(interp, pGroupTerm,
1414 : Tcl_NewWideIntObj(pGroup->iLimit));
1415 : Tcl_ListObjAppendElement(interp, pGroupTerm,
1416 : Tcl_NewWideIntObj(pGroup->iSize));
1417 : for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
1418 : int i;
1419 : char zTemp[1000];
1420 : pFileTerm = Tcl_NewObj();
1421 : sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename);
1422 : for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; }
1423 : Tcl_ListObjAppendElement(interp, pFileTerm,
1424 : Tcl_NewStringObj(zTemp, -1));
1425 : Tcl_ListObjAppendElement(interp, pFileTerm,
1426 : Tcl_NewWideIntObj(pFile->iSize));
1427 : Tcl_ListObjAppendElement(interp, pFileTerm,
1428 : Tcl_NewWideIntObj(pFile->nRef));
1429 : Tcl_ListObjAppendElement(interp, pFileTerm,
1430 : Tcl_NewWideIntObj(pFile->deleteOnClose));
1431 : Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm);
1432 : }
1433 : Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
1434 : }
1435 : quotaLeave();
1436 : Tcl_SetObjResult(interp, pResult);
1437 : return TCL_OK;
1438 : }
1439 :
1440 : /*
1441 : ** tclcmd: sqlite3_quota_fopen FILENAME MODE
1442 : */
1443 : static int test_quota_fopen(
1444 : void * clientData,
1445 : Tcl_Interp *interp,
1446 : int objc,
1447 : Tcl_Obj *CONST objv[]
1448 : ){
1449 : const char *zFilename; /* File pattern to configure */
1450 : const char *zMode; /* Mode string */
1451 : quota_FILE *p; /* Open string object */
1452 : char zReturn[50]; /* Name of pointer to return */
1453 :
1454 : /* Process arguments */
1455 : if( objc!=3 ){
1456 : Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE");
1457 : return TCL_ERROR;
1458 : }
1459 : zFilename = Tcl_GetString(objv[1]);
1460 : zMode = Tcl_GetString(objv[2]);
1461 : p = sqlite3_quota_fopen(zFilename, zMode);
1462 : sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p);
1463 : Tcl_SetResult(interp, zReturn, TCL_VOLATILE);
1464 : return TCL_OK;
1465 : }
1466 :
1467 : /* Defined in test1.c */
1468 : extern void *sqlite3TestTextToPtr(const char*);
1469 :
1470 : /*
1471 : ** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
1472 : */
1473 : static int test_quota_fread(
1474 : void * clientData,
1475 : Tcl_Interp *interp,
1476 : int objc,
1477 : Tcl_Obj *CONST objv[]
1478 : ){
1479 : quota_FILE *p;
1480 : char *zBuf;
1481 : int sz;
1482 : int nElem;
1483 : int got;
1484 :
1485 : if( objc!=4 ){
1486 : Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM");
1487 : return TCL_ERROR;
1488 : }
1489 : p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
1490 : if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
1491 : if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
1492 : zBuf = (char*)sqlite3_malloc( sz*nElem + 1 );
1493 : if( zBuf==0 ){
1494 : Tcl_SetResult(interp, "out of memory", TCL_STATIC);
1495 : return TCL_ERROR;
1496 : }
1497 : got = sqlite3_quota_fread(zBuf, sz, nElem, p);
1498 : if( got<0 ) got = 0;
1499 : zBuf[got*sz] = 0;
1500 : Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
1501 : sqlite3_free(zBuf);
1502 : return TCL_OK;
1503 : }
1504 :
1505 : /*
1506 : ** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
1507 : */
1508 : static int test_quota_fwrite(
1509 : void * clientData,
1510 : Tcl_Interp *interp,
1511 : int objc,
1512 : Tcl_Obj *CONST objv[]
1513 : ){
1514 : quota_FILE *p;
1515 : char *zBuf;
1516 : int sz;
1517 : int nElem;
1518 : int got;
1519 :
1520 : if( objc!=5 ){
1521 : Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT");
1522 : return TCL_ERROR;
1523 : }
1524 : p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
1525 : if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
1526 : if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
1527 : zBuf = Tcl_GetString(objv[4]);
1528 : got = sqlite3_quota_fwrite(zBuf, sz, nElem, p);
1529 : Tcl_SetObjResult(interp, Tcl_NewIntObj(got));
1530 : return TCL_OK;
1531 : }
1532 :
1533 : /*
1534 : ** tclcmd: sqlite3_quota_fclose HANDLE
1535 : */
1536 : static int test_quota_fclose(
1537 : void * clientData,
1538 : Tcl_Interp *interp,
1539 : int objc,
1540 : Tcl_Obj *CONST objv[]
1541 : ){
1542 : quota_FILE *p;
1543 : int rc;
1544 :
1545 : if( objc!=2 ){
1546 : Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
1547 : return TCL_ERROR;
1548 : }
1549 : p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
1550 : rc = sqlite3_quota_fclose(p);
1551 : Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1552 : return TCL_OK;
1553 : }
1554 :
1555 : /*
1556 : ** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
1557 : */
1558 : static int test_quota_fflush(
1559 : void * clientData,
1560 : Tcl_Interp *interp,
1561 : int objc,
1562 : Tcl_Obj *CONST objv[]
1563 : ){
1564 : quota_FILE *p;
1565 : int rc;
1566 : int doSync = 0;
1567 :
1568 : if( objc!=2 && objc!=3 ){
1569 : Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?");
1570 : return TCL_ERROR;
1571 : }
1572 : p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
1573 : if( objc==3 ){
1574 : if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR;
1575 : }
1576 : rc = sqlite3_quota_fflush(p, doSync);
1577 : Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1578 : return TCL_OK;
1579 : }
1580 :
1581 : /*
1582 : ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
1583 : */
1584 : static int test_quota_fseek(
1585 : void * clientData,
1586 : Tcl_Interp *interp,
1587 : int objc,
1588 : Tcl_Obj *CONST objv[]
1589 : ){
1590 : quota_FILE *p;
1591 : int ofst;
1592 : const char *zWhence;
1593 : int whence;
1594 : int rc;
1595 :
1596 : if( objc!=4 ){
1597 : Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE");
1598 : return TCL_ERROR;
1599 : }
1600 : p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
1601 : if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR;
1602 : zWhence = Tcl_GetString(objv[3]);
1603 : if( strcmp(zWhence, "SEEK_SET")==0 ){
1604 : whence = SEEK_SET;
1605 : }else if( strcmp(zWhence, "SEEK_CUR")==0 ){
1606 : whence = SEEK_CUR;
1607 : }else if( strcmp(zWhence, "SEEK_END")==0 ){
1608 : whence = SEEK_END;
1609 : }else{
1610 : Tcl_AppendResult(interp,
1611 : "WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0);
1612 : return TCL_ERROR;
1613 : }
1614 : rc = sqlite3_quota_fseek(p, ofst, whence);
1615 : Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1616 : return TCL_OK;
1617 : }
1618 :
1619 : /*
1620 : ** tclcmd: sqlite3_quota_rewind HANDLE
1621 : */
1622 : static int test_quota_rewind(
1623 : void * clientData,
1624 : Tcl_Interp *interp,
1625 : int objc,
1626 : Tcl_Obj *CONST objv[]
1627 : ){
1628 : quota_FILE *p;
1629 : if( objc!=2 ){
1630 : Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
1631 : return TCL_ERROR;
1632 : }
1633 : p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
1634 : sqlite3_quota_rewind(p);
1635 : return TCL_OK;
1636 : }
1637 :
1638 : /*
1639 : ** tclcmd: sqlite3_quota_ftell HANDLE
1640 : */
1641 : static int test_quota_ftell(
1642 : void * clientData,
1643 : Tcl_Interp *interp,
1644 : int objc,
1645 : Tcl_Obj *CONST objv[]
1646 : ){
1647 : quota_FILE *p;
1648 : sqlite3_int64 x;
1649 : if( objc!=2 ){
1650 : Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
1651 : return TCL_ERROR;
1652 : }
1653 : p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
1654 : x = sqlite3_quota_ftell(p);
1655 : Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
1656 : return TCL_OK;
1657 : }
1658 :
1659 : /*
1660 : ** tclcmd: sqlite3_quota_remove FILENAME
1661 : */
1662 : static int test_quota_remove(
1663 : void * clientData,
1664 : Tcl_Interp *interp,
1665 : int objc,
1666 : Tcl_Obj *CONST objv[]
1667 : ){
1668 : const char *zFilename; /* File pattern to configure */
1669 : int rc;
1670 : if( objc!=2 ){
1671 : Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
1672 : return TCL_ERROR;
1673 : }
1674 : zFilename = Tcl_GetString(objv[1]);
1675 : rc = sqlite3_quota_remove(zFilename);
1676 : Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1677 : return TCL_OK;
1678 : }
1679 :
1680 : /*
1681 : ** tclcmd: sqlite3_quota_glob PATTERN TEXT
1682 : **
1683 : ** Test the glob pattern matching. Return 1 if TEXT matches PATTERN
1684 : ** and return 0 if it does not.
1685 : */
1686 : static int test_quota_glob(
1687 : void * clientData,
1688 : Tcl_Interp *interp,
1689 : int objc,
1690 : Tcl_Obj *CONST objv[]
1691 : ){
1692 : const char *zPattern; /* The glob pattern */
1693 : const char *zText; /* Text to compare agains the pattern */
1694 : int rc;
1695 : if( objc!=3 ){
1696 : Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT");
1697 : return TCL_ERROR;
1698 : }
1699 : zPattern = Tcl_GetString(objv[1]);
1700 : zText = Tcl_GetString(objv[2]);
1701 : rc = quotaStrglob(zPattern, zText);
1702 : Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1703 : return TCL_OK;
1704 : }
1705 :
1706 : /*
1707 : ** This routine registers the custom TCL commands defined in this
1708 : ** module. This should be the only procedure visible from outside
1709 : ** of this module.
1710 : */
1711 : int Sqlitequota_Init(Tcl_Interp *interp){
1712 : static struct {
1713 : char *zName;
1714 : Tcl_ObjCmdProc *xProc;
1715 : } aCmd[] = {
1716 : { "sqlite3_quota_initialize", test_quota_initialize },
1717 : { "sqlite3_quota_shutdown", test_quota_shutdown },
1718 : { "sqlite3_quota_set", test_quota_set },
1719 : { "sqlite3_quota_file", test_quota_file },
1720 : { "sqlite3_quota_dump", test_quota_dump },
1721 : { "sqlite3_quota_fopen", test_quota_fopen },
1722 : { "sqlite3_quota_fread", test_quota_fread },
1723 : { "sqlite3_quota_fwrite", test_quota_fwrite },
1724 : { "sqlite3_quota_fclose", test_quota_fclose },
1725 : { "sqlite3_quota_fflush", test_quota_fflush },
1726 : { "sqlite3_quota_fseek", test_quota_fseek },
1727 : { "sqlite3_quota_rewind", test_quota_rewind },
1728 : { "sqlite3_quota_ftell", test_quota_ftell },
1729 : { "sqlite3_quota_remove", test_quota_remove },
1730 : { "sqlite3_quota_glob", test_quota_glob },
1731 : };
1732 : int i;
1733 :
1734 : for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
1735 : Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
1736 : }
1737 :
1738 : return TCL_OK;
1739 : }
1740 : #endif
|