Annotation of embedaddon/sqlite3/src/test_quota.c, revision 1.1
1.1 ! misho 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: static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
! 178: static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
! 179:
! 180: /* Count the number of open files in a quotaGroup
! 181: */
! 182: static int quotaGroupOpenFileCount(quotaGroup *pGroup){
! 183: int N = 0;
! 184: quotaFile *pFile = pGroup->pFiles;
! 185: while( pFile ){
! 186: if( pFile->nRef ) N++;
! 187: pFile = pFile->pNext;
! 188: }
! 189: return N;
! 190: }
! 191:
! 192: /* Remove a file from a quota group.
! 193: */
! 194: static void quotaRemoveFile(quotaFile *pFile){
! 195: quotaGroup *pGroup = pFile->pGroup;
! 196: pGroup->iSize -= pFile->iSize;
! 197: *pFile->ppPrev = pFile->pNext;
! 198: if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
! 199: sqlite3_free(pFile);
! 200: }
! 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: static void quotaRemoveAllFiles(quotaGroup *pGroup){
! 206: while( pGroup->pFiles ){
! 207: assert( pGroup->pFiles->nRef==0 );
! 208: quotaRemoveFile(pGroup->pFiles);
! 209: }
! 210: }
! 211:
! 212:
! 213: /* If the reference count and threshold for a quotaGroup are both
! 214: ** zero, then destroy the quotaGroup.
! 215: */
! 216: static void quotaGroupDeref(quotaGroup *pGroup){
! 217: if( pGroup->iLimit==0 && quotaGroupOpenFileCount(pGroup)==0 ){
! 218: quotaRemoveAllFiles(pGroup);
! 219: *pGroup->ppPrev = pGroup->pNext;
! 220: if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev;
! 221: if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg);
! 222: sqlite3_free(pGroup);
! 223: }
! 224: }
! 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: static int quotaStrglob(const char *zGlob, const char *z){
! 244: int c, c2, cx;
! 245: int invert;
! 246: int seen;
! 247:
! 248: while( (c = (*(zGlob++)))!=0 ){
! 249: if( c=='*' ){
! 250: while( (c=(*(zGlob++))) == '*' || c=='?' ){
! 251: if( c=='?' && (*(z++))==0 ) return 0;
! 252: }
! 253: if( c==0 ){
! 254: return 1;
! 255: }else if( c=='[' ){
! 256: while( *z && quotaStrglob(zGlob-1,z)==0 ){
! 257: z++;
! 258: }
! 259: return (*z)!=0;
! 260: }
! 261: cx = (c=='/') ? '\\' : c;
! 262: while( (c2 = (*(z++)))!=0 ){
! 263: while( c2!=c && c2!=cx ){
! 264: c2 = *(z++);
! 265: if( c2==0 ) return 0;
! 266: }
! 267: if( quotaStrglob(zGlob,z) ) return 1;
! 268: }
! 269: return 0;
! 270: }else if( c=='?' ){
! 271: if( (*(z++))==0 ) return 0;
! 272: }else if( c=='[' ){
! 273: int prior_c = 0;
! 274: seen = 0;
! 275: invert = 0;
! 276: c = *(z++);
! 277: if( c==0 ) return 0;
! 278: c2 = *(zGlob++);
! 279: if( c2=='^' ){
! 280: invert = 1;
! 281: c2 = *(zGlob++);
! 282: }
! 283: if( c2==']' ){
! 284: if( c==']' ) seen = 1;
! 285: c2 = *(zGlob++);
! 286: }
! 287: while( c2 && c2!=']' ){
! 288: if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
! 289: c2 = *(zGlob++);
! 290: if( c>=prior_c && c<=c2 ) seen = 1;
! 291: prior_c = 0;
! 292: }else{
! 293: if( c==c2 ){
! 294: seen = 1;
! 295: }
! 296: prior_c = c2;
! 297: }
! 298: c2 = *(zGlob++);
! 299: }
! 300: if( c2==0 || (seen ^ invert)==0 ) return 0;
! 301: }else if( c=='/' ){
! 302: if( z[0]!='/' && z[0]!='\\' ) return 0;
! 303: z++;
! 304: }else{
! 305: if( c!=(*(z++)) ) return 0;
! 306: }
! 307: }
! 308: 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: static quotaGroup *quotaGroupFind(const char *zFilename){
! 317: quotaGroup *p;
! 318: for(p=gQuota.pGroup; p && quotaStrglob(p->zPattern, zFilename)==0;
! 319: p=p->pNext){}
! 320: 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: static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
! 327: quotaConn *p = (quotaConn*)pConn;
! 328: 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: 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: quotaFile *pFile = pGroup->pFiles;
! 340: while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
! 341: pFile = pFile->pNext;
! 342: }
! 343: if( pFile==0 && createFlag ){
! 344: int nName = strlen(zName);
! 345: pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
! 346: if( pFile ){
! 347: memset(pFile, 0, sizeof(*pFile));
! 348: pFile->zFilename = (char*)&pFile[1];
! 349: memcpy(pFile->zFilename, zName, nName+1);
! 350: pFile->pNext = pGroup->pFiles;
! 351: if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
! 352: pFile->ppPrev = &pGroup->pFiles;
! 353: pGroup->pFiles = pFile;
! 354: pFile->pGroup = pGroup;
! 355: }
! 356: }
! 357: 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: 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: return (char*)zUtf8; /* No-op on unix */
! 445: #endif
! 446: }
! 447:
! 448: /*
! 449: ** Deallocate any memory allocated by quota_utf8_to_mbcs().
! 450: */
! 451: 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: }
! 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: 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: 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: if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
! 485: 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: quotaEnter();
! 492: pGroup = quotaGroupFind(zName);
! 493: if( pGroup==0 ){
! 494: 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: pQuotaOpen = (quotaConn*)pConn;
! 499: pSubOpen = quotaSubOpen(pConn);
! 500: rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
! 501: if( rc==SQLITE_OK ){
! 502: pFile = quotaFindFile(pGroup, zName, 1);
! 503: if( pFile==0 ){
! 504: quotaLeave();
! 505: pSubOpen->pMethods->xClose(pSubOpen);
! 506: return SQLITE_NOMEM;
! 507: }
! 508: pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
! 509: pFile->nRef++;
! 510: pQuotaOpen->pFile = pFile;
! 511: if( pSubOpen->pMethods->iVersion==1 ){
! 512: pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1;
! 513: }else{
! 514: pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2;
! 515: }
! 516: }
! 517: }
! 518: quotaLeave();
! 519: 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: 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: sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */
! 538:
! 539: /* Do the actual file delete */
! 540: 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: if( rc==SQLITE_OK ){
! 546: quotaEnter();
! 547: pGroup = quotaGroupFind(zName);
! 548: if( pGroup ){
! 549: pFile = quotaFindFile(pGroup, zName, 0);
! 550: if( pFile ){
! 551: if( pFile->nRef ){
! 552: pFile->deleteOnClose = 1;
! 553: }else{
! 554: quotaRemoveFile(pFile);
! 555: quotaGroupDeref(pGroup);
! 556: }
! 557: }
! 558: }
! 559: quotaLeave();
! 560: }
! 561: 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: static int quotaClose(sqlite3_file *pConn){
! 572: quotaConn *p = (quotaConn*)pConn;
! 573: quotaFile *pFile = p->pFile;
! 574: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 575: int rc;
! 576: rc = pSubOpen->pMethods->xClose(pSubOpen);
! 577: quotaEnter();
! 578: pFile->nRef--;
! 579: if( pFile->nRef==0 ){
! 580: quotaGroup *pGroup = pFile->pGroup;
! 581: if( pFile->deleteOnClose ){
! 582: gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
! 583: quotaRemoveFile(pFile);
! 584: }
! 585: quotaGroupDeref(pGroup);
! 586: }
! 587: quotaLeave();
! 588: return rc;
! 589: }
! 590:
! 591: /* Pass xRead requests directory thru to the original VFS without
! 592: ** further processing.
! 593: */
! 594: static int quotaRead(
! 595: sqlite3_file *pConn,
! 596: void *pBuf,
! 597: int iAmt,
! 598: sqlite3_int64 iOfst
! 599: ){
! 600: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 601: 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: static int quotaWrite(
! 609: sqlite3_file *pConn,
! 610: const void *pBuf,
! 611: int iAmt,
! 612: sqlite3_int64 iOfst
! 613: ){
! 614: quotaConn *p = (quotaConn*)pConn;
! 615: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 616: sqlite3_int64 iEnd = iOfst+iAmt;
! 617: quotaGroup *pGroup;
! 618: quotaFile *pFile = p->pFile;
! 619: sqlite3_int64 szNew;
! 620:
! 621: if( pFile->iSize<iEnd ){
! 622: pGroup = pFile->pGroup;
! 623: quotaEnter();
! 624: szNew = pGroup->iSize - pFile->iSize + iEnd;
! 625: if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
! 626: if( pGroup->xCallback ){
! 627: pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
! 628: pGroup->pArg);
! 629: }
! 630: if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
! 631: quotaLeave();
! 632: return SQLITE_FULL;
! 633: }
! 634: }
! 635: pGroup->iSize = szNew;
! 636: pFile->iSize = iEnd;
! 637: quotaLeave();
! 638: }
! 639: 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: static int quotaTruncate(sqlite3_file *pConn, sqlite3_int64 size){
! 646: quotaConn *p = (quotaConn*)pConn;
! 647: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 648: int rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
! 649: quotaFile *pFile = p->pFile;
! 650: quotaGroup *pGroup;
! 651: if( rc==SQLITE_OK ){
! 652: quotaEnter();
! 653: pGroup = pFile->pGroup;
! 654: pGroup->iSize -= pFile->iSize;
! 655: pFile->iSize = size;
! 656: pGroup->iSize += size;
! 657: quotaLeave();
! 658: }
! 659: return rc;
! 660: }
! 661:
! 662: /* Pass xSync requests through to the original VFS without change
! 663: */
! 664: static int quotaSync(sqlite3_file *pConn, int flags){
! 665: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 666: 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: static int quotaFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
! 673: quotaConn *p = (quotaConn*)pConn;
! 674: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 675: quotaFile *pFile = p->pFile;
! 676: quotaGroup *pGroup;
! 677: sqlite3_int64 sz;
! 678: int rc;
! 679:
! 680: rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
! 681: if( rc==SQLITE_OK ){
! 682: quotaEnter();
! 683: pGroup = pFile->pGroup;
! 684: pGroup->iSize -= pFile->iSize;
! 685: pFile->iSize = sz;
! 686: pGroup->iSize += sz;
! 687: quotaLeave();
! 688: *pSize = sz;
! 689: }
! 690: return rc;
! 691: }
! 692:
! 693: /* Pass xLock requests through to the original VFS unchanged.
! 694: */
! 695: static int quotaLock(sqlite3_file *pConn, int lock){
! 696: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 697: return pSubOpen->pMethods->xLock(pSubOpen, lock);
! 698: }
! 699:
! 700: /* Pass xUnlock requests through to the original VFS unchanged.
! 701: */
! 702: static int quotaUnlock(sqlite3_file *pConn, int lock){
! 703: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 704: return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
! 705: }
! 706:
! 707: /* Pass xCheckReservedLock requests through to the original VFS unchanged.
! 708: */
! 709: static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
! 710: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 711: return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
! 712: }
! 713:
! 714: /* Pass xFileControl requests through to the original VFS unchanged.
! 715: */
! 716: static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
! 717: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 718: int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
! 719: #if defined(SQLITE_FCNTL_VFSNAME)
! 720: if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
! 721: *(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg);
! 722: }
! 723: #endif
! 724: return rc;
! 725: }
! 726:
! 727: /* Pass xSectorSize requests through to the original VFS unchanged.
! 728: */
! 729: static int quotaSectorSize(sqlite3_file *pConn){
! 730: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 731: return pSubOpen->pMethods->xSectorSize(pSubOpen);
! 732: }
! 733:
! 734: /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
! 735: */
! 736: static int quotaDeviceCharacteristics(sqlite3_file *pConn){
! 737: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 738: return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
! 739: }
! 740:
! 741: /* Pass xShmMap requests through to the original VFS unchanged.
! 742: */
! 743: 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: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 751: return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
! 752: }
! 753:
! 754: /* Pass xShmLock requests through to the original VFS unchanged.
! 755: */
! 756: 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: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 763: return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
! 764: }
! 765:
! 766: /* Pass xShmBarrier requests through to the original VFS unchanged.
! 767: */
! 768: static void quotaShmBarrier(sqlite3_file *pConn){
! 769: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 770: pSubOpen->pMethods->xShmBarrier(pSubOpen);
! 771: }
! 772:
! 773: /* Pass xShmUnmap requests through to the original VFS unchanged.
! 774: */
! 775: static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){
! 776: sqlite3_file *pSubOpen = quotaSubOpen(pConn);
! 777: 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: int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
! 793: sqlite3_vfs *pOrigVfs;
! 794: if( gQuota.isInitialized ) return SQLITE_MISUSE;
! 795: pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
! 796: if( pOrigVfs==0 ) return SQLITE_ERROR;
! 797: assert( pOrigVfs!=&gQuota.sThisVfs );
! 798: gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
! 799: if( !gQuota.pMutex ){
! 800: return SQLITE_NOMEM;
! 801: }
! 802: gQuota.isInitialized = 1;
! 803: gQuota.pOrigVfs = pOrigVfs;
! 804: gQuota.sThisVfs = *pOrigVfs;
! 805: gQuota.sThisVfs.xOpen = quotaOpen;
! 806: gQuota.sThisVfs.xDelete = quotaDelete;
! 807: gQuota.sThisVfs.szOsFile += sizeof(quotaConn);
! 808: gQuota.sThisVfs.zName = "quota";
! 809: gQuota.sIoMethodsV1.iVersion = 1;
! 810: gQuota.sIoMethodsV1.xClose = quotaClose;
! 811: gQuota.sIoMethodsV1.xRead = quotaRead;
! 812: gQuota.sIoMethodsV1.xWrite = quotaWrite;
! 813: gQuota.sIoMethodsV1.xTruncate = quotaTruncate;
! 814: gQuota.sIoMethodsV1.xSync = quotaSync;
! 815: gQuota.sIoMethodsV1.xFileSize = quotaFileSize;
! 816: gQuota.sIoMethodsV1.xLock = quotaLock;
! 817: gQuota.sIoMethodsV1.xUnlock = quotaUnlock;
! 818: gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock;
! 819: gQuota.sIoMethodsV1.xFileControl = quotaFileControl;
! 820: gQuota.sIoMethodsV1.xSectorSize = quotaSectorSize;
! 821: gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics;
! 822: gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1;
! 823: gQuota.sIoMethodsV2.iVersion = 2;
! 824: gQuota.sIoMethodsV2.xShmMap = quotaShmMap;
! 825: gQuota.sIoMethodsV2.xShmLock = quotaShmLock;
! 826: gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier;
! 827: gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap;
! 828: sqlite3_vfs_register(&gQuota.sThisVfs, makeDefault);
! 829: 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: int sqlite3_quota_shutdown(void){
! 842: quotaGroup *pGroup;
! 843: if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
! 844: for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
! 845: if( quotaGroupOpenFileCount(pGroup)>0 ) return SQLITE_MISUSE;
! 846: }
! 847: while( gQuota.pGroup ){
! 848: pGroup = gQuota.pGroup;
! 849: gQuota.pGroup = pGroup->pNext;
! 850: pGroup->iLimit = 0;
! 851: assert( quotaGroupOpenFileCount(pGroup)==0 );
! 852: quotaGroupDeref(pGroup);
! 853: }
! 854: gQuota.isInitialized = 0;
! 855: sqlite3_mutex_free(gQuota.pMutex);
! 856: sqlite3_vfs_unregister(&gQuota.sThisVfs);
! 857: memset(&gQuota, 0, sizeof(gQuota));
! 858: 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: 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: quotaEnter();
! 895: pGroup = gQuota.pGroup;
! 896: while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){
! 897: pGroup = pGroup->pNext;
! 898: }
! 899: if( pGroup==0 ){
! 900: int nPattern = strlen(zPattern);
! 901: if( iLimit<=0 ){
! 902: quotaLeave();
! 903: return SQLITE_OK;
! 904: }
! 905: pGroup = (quotaGroup *)sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
! 906: if( pGroup==0 ){
! 907: quotaLeave();
! 908: return SQLITE_NOMEM;
! 909: }
! 910: memset(pGroup, 0, sizeof(*pGroup));
! 911: pGroup->zPattern = (char*)&pGroup[1];
! 912: memcpy((char *)pGroup->zPattern, zPattern, nPattern+1);
! 913: if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext;
! 914: pGroup->pNext = gQuota.pGroup;
! 915: pGroup->ppPrev = &gQuota.pGroup;
! 916: gQuota.pGroup = pGroup;
! 917: }
! 918: pGroup->iLimit = iLimit;
! 919: pGroup->xCallback = xCallback;
! 920: if( pGroup->xDestroy && pGroup->pArg!=pArg ){
! 921: pGroup->xDestroy(pGroup->pArg);
! 922: }
! 923: pGroup->pArg = pArg;
! 924: pGroup->xDestroy = xDestroy;
! 925: quotaGroupDeref(pGroup);
! 926: quotaLeave();
! 927: 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: int sqlite3_quota_file(const char *zFilename){
! 935: char *zFull;
! 936: sqlite3_file *fd;
! 937: int rc;
! 938: int outFlags = 0;
! 939: sqlite3_int64 iSize;
! 940: 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: fd = (sqlite3_file *)sqlite3_malloc(nAlloc);
! 944: if( fd==0 ){
! 945: rc = SQLITE_NOMEM;
! 946: }else{
! 947: zFull = &((char *)fd)[gQuota.sThisVfs.szOsFile];
! 948: rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
! 949: gQuota.sThisVfs.mxPathname+1, zFull);
! 950: }
! 951:
! 952: if( rc==SQLITE_OK ){
! 953: zFull[strlen(zFull)+1] = '\0';
! 954: rc = quotaOpen(&gQuota.sThisVfs, zFull, fd,
! 955: SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags);
! 956: if( rc==SQLITE_OK ){
! 957: fd->pMethods->xFileSize(fd, &iSize);
! 958: fd->pMethods->xClose(fd);
! 959: }else if( rc==SQLITE_CANTOPEN ){
! 960: quotaGroup *pGroup;
! 961: quotaFile *pFile;
! 962: quotaEnter();
! 963: pGroup = quotaGroupFind(zFull);
! 964: if( pGroup ){
! 965: pFile = quotaFindFile(pGroup, zFull, 0);
! 966: if( pFile ) quotaRemoveFile(pFile);
! 967: }
! 968: quotaLeave();
! 969: }
! 970: }
! 971:
! 972: sqlite3_free(fd);
! 973: return rc;
! 974: }
! 975:
! 976: /*
! 977: ** Open a potentially quotaed file for I/O.
! 978: */
! 979: quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
! 980: quota_FILE *p = 0;
! 981: char *zFull = 0;
! 982: char *zFullTranslated;
! 983: int rc;
! 984: quotaGroup *pGroup;
! 985: quotaFile *pFile;
! 986:
! 987: zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
! 988: if( zFull==0 ) return 0;
! 989: rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
! 990: gQuota.sThisVfs.mxPathname+1, zFull);
! 991: if( rc ) goto quota_fopen_error;
! 992: p = (quota_FILE*)sqlite3_malloc(sizeof(*p));
! 993: if( p==0 ) goto quota_fopen_error;
! 994: memset(p, 0, sizeof(*p));
! 995: zFullTranslated = quota_utf8_to_mbcs(zFull);
! 996: if( zFullTranslated==0 ) goto quota_fopen_error;
! 997: p->f = fopen(zFullTranslated, zMode);
! 998: quota_mbcs_free(zFullTranslated);
! 999: if( p->f==0 ) goto quota_fopen_error;
! 1000: quotaEnter();
! 1001: pGroup = quotaGroupFind(zFull);
! 1002: if( pGroup ){
! 1003: pFile = quotaFindFile(pGroup, zFull, 1);
! 1004: if( pFile==0 ){
! 1005: quotaLeave();
! 1006: goto quota_fopen_error;
! 1007: }
! 1008: pFile->nRef++;
! 1009: p->pFile = pFile;
! 1010: }
! 1011: quotaLeave();
! 1012: sqlite3_free(zFull);
! 1013: return p;
! 1014:
! 1015: quota_fopen_error:
! 1016: sqlite3_free(zFull);
! 1017: if( p && p->f ) fclose(p->f);
! 1018: sqlite3_free(p);
! 1019: return 0;
! 1020: }
! 1021:
! 1022: /*
! 1023: ** Read content from a quota_FILE
! 1024: */
! 1025: 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: 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: 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: iOfst = ftell(p->f);
! 1050: iEnd = iOfst + size*nmemb;
! 1051: pFile = p->pFile;
! 1052: if( pFile && pFile->iSize<iEnd ){
! 1053: quotaGroup *pGroup = pFile->pGroup;
! 1054: quotaEnter();
! 1055: szNew = pGroup->iSize - pFile->iSize + iEnd;
! 1056: if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
! 1057: if( pGroup->xCallback ){
! 1058: pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
! 1059: pGroup->pArg);
! 1060: }
! 1061: if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
! 1062: iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
! 1063: nmemb = (iEnd - iOfst)/size;
! 1064: iEnd = iOfst + size*nmemb;
! 1065: szNew = pGroup->iSize - pFile->iSize + iEnd;
! 1066: }
! 1067: }
! 1068: pGroup->iSize = szNew;
! 1069: pFile->iSize = iEnd;
! 1070: quotaLeave();
! 1071: }
! 1072: return fwrite(pBuf, size, nmemb, p->f);
! 1073: }
! 1074:
! 1075: /*
! 1076: ** Close an open quota_FILE stream.
! 1077: */
! 1078: int sqlite3_quota_fclose(quota_FILE *p){
! 1079: int rc;
! 1080: quotaFile *pFile;
! 1081: rc = fclose(p->f);
! 1082: pFile = p->pFile;
! 1083: if( pFile ){
! 1084: quotaEnter();
! 1085: pFile->nRef--;
! 1086: if( pFile->nRef==0 ){
! 1087: quotaGroup *pGroup = pFile->pGroup;
! 1088: if( pFile->deleteOnClose ){
! 1089: gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
! 1090: quotaRemoveFile(pFile);
! 1091: }
! 1092: quotaGroupDeref(pGroup);
! 1093: }
! 1094: quotaLeave();
! 1095: }
! 1096: sqlite3_free(p);
! 1097: return rc;
! 1098: }
! 1099:
! 1100: /*
! 1101: ** Flush memory buffers for a quota_FILE to disk.
! 1102: */
! 1103: int sqlite3_quota_fflush(quota_FILE *p, int doFsync){
! 1104: int rc;
! 1105: rc = fflush(p->f);
! 1106: if( rc==0 && doFsync ){
! 1107: #if SQLITE_OS_UNIX
! 1108: rc = fsync(fileno(p->f));
! 1109: #endif
! 1110: #if SQLITE_OS_WIN
! 1111: rc = _commit(_fileno(p->f));
! 1112: #endif
! 1113: }
! 1114: return rc!=0;
! 1115: }
! 1116:
! 1117: /*
! 1118: ** Seek on a quota_FILE stream.
! 1119: */
! 1120: int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
! 1121: return fseek(p->f, offset, whence);
! 1122: }
! 1123:
! 1124: /*
! 1125: ** rewind a quota_FILE stream.
! 1126: */
! 1127: void sqlite3_quota_rewind(quota_FILE *p){
! 1128: rewind(p->f);
! 1129: }
! 1130:
! 1131: /*
! 1132: ** Tell the current location of a quota_FILE stream.
! 1133: */
! 1134: long sqlite3_quota_ftell(quota_FILE *p){
! 1135: return ftell(p->f);
! 1136: }
! 1137:
! 1138: /*
! 1139: ** Remove a managed file. Update quotas accordingly.
! 1140: */
! 1141: 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: zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
! 1152: if( zFull==0 ) return SQLITE_NOMEM;
! 1153: rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
! 1154: gQuota.sThisVfs.mxPathname+1, zFull);
! 1155: if( rc ){
! 1156: sqlite3_free(zFull);
! 1157: 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: nFull = strlen(zFull);
! 1164: if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){
! 1165: nFull--;
! 1166: zFull[nFull] = 0;
! 1167: }
! 1168:
! 1169: quotaEnter();
! 1170: pGroup = quotaGroupFind(zFull);
! 1171: if( pGroup ){
! 1172: for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){
! 1173: pNextFile = pFile->pNext;
! 1174: diff = memcmp(zFull, pFile->zFilename, nFull);
! 1175: if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){
! 1176: if( pFile->nRef ){
! 1177: pFile->deleteOnClose = 1;
! 1178: }else{
! 1179: rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
! 1180: quotaRemoveFile(pFile);
! 1181: quotaGroupDeref(pGroup);
! 1182: }
! 1183: }
! 1184: }
! 1185: }
! 1186: quotaLeave();
! 1187: sqlite3_free(zFull);
! 1188: 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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>