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