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 #include <tcl.h> 1282 1283 /* 1284 ** Argument passed to a TCL quota-over-limit callback. 1285 */ 1286 typedef struct TclQuotaCallback TclQuotaCallback; 1287 struct TclQuotaCallback { 1288 Tcl_Interp *interp; /* Interpreter in which to run the script */ 1289 Tcl_Obj *pScript; /* Script to be run */ 1290 }; 1291 1292 extern const char *sqlite3ErrName(int); 1293 1294 1295 /* 1296 ** This is the callback from a quota-over-limit. 1297 */ 1298 static void tclQuotaCallback( 1299 const char *zFilename, /* Name of file whose size increases */ 1300 sqlite3_int64 *piLimit, /* IN/OUT: The current limit */ 1301 sqlite3_int64 iSize, /* Total size of all files in the group */ 1302 void *pArg /* Client data */ 1303 ){ 1304 TclQuotaCallback *p; /* Callback script object */ 1305 Tcl_Obj *pEval; /* Script to evaluate */ 1306 Tcl_Obj *pVarname; /* Name of variable to pass as 2nd arg */ 1307 unsigned int rnd; /* Random part of pVarname */ 1308 int rc; /* Tcl error code */ 1309 1310 p = (TclQuotaCallback *)pArg; 1311 if( p==0 ) return; 1312 1313 pVarname = Tcl_NewStringObj("::piLimit_", -1); 1314 Tcl_IncrRefCount(pVarname); 1315 sqlite3_randomness(sizeof(rnd), (void *)&rnd); 1316 Tcl_AppendObjToObj(pVarname, Tcl_NewIntObj((int)(rnd&0x7FFFFFFF))); 1317 Tcl_ObjSetVar2(p->interp, pVarname, 0, Tcl_NewWideIntObj(*piLimit), 0); 1318 1319 pEval = Tcl_DuplicateObj(p->pScript); 1320 Tcl_IncrRefCount(pEval); 1321 Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zFilename, -1)); 1322 Tcl_ListObjAppendElement(0, pEval, pVarname); 1323 Tcl_ListObjAppendElement(0, pEval, Tcl_NewWideIntObj(iSize)); 1324 rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); 1325 1326 if( rc==TCL_OK ){ 1327 Tcl_WideInt x; 1328 Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0); 1329 rc = Tcl_GetWideIntFromObj(p->interp, pLimit, &x); 1330 *piLimit = x; 1331 Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0); 1332 } 1333 1334 Tcl_DecrRefCount(pEval); 1335 Tcl_DecrRefCount(pVarname); 1336 if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp); 1337 } 1338 1339 /* 1340 ** Destructor for a TCL quota-over-limit callback. 1341 */ 1342 static void tclCallbackDestructor(void *pObj){ 1343 TclQuotaCallback *p = (TclQuotaCallback*)pObj; 1344 if( p ){ 1345 Tcl_DecrRefCount(p->pScript); 1346 sqlite3_free((char *)p); 1347 } 1348 } 1349 1350 /* 1351 ** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT 1352 */ 1353 static int test_quota_initialize( 1354 void * clientData, 1355 Tcl_Interp *interp, 1356 int objc, 1357 Tcl_Obj *CONST objv[] 1358 ){ 1359 const char *zName; /* Name of new quota VFS */ 1360 int makeDefault; /* True to make the new VFS the default */ 1361 int rc; /* Value returned by quota_initialize() */ 1362 1363 /* Process arguments */ 1364 if( objc!=3 ){ 1365 Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT"); 1366 return TCL_ERROR; 1367 } 1368 zName = Tcl_GetString(objv[1]); 1369 if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR; 1370 if( zName[0]=='\0' ) zName = 0; 1371 1372 /* Call sqlite3_quota_initialize() */ 1373 rc = sqlite3_quota_initialize(zName, makeDefault); 1374 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); 1375 1376 return TCL_OK; 1377 } 1378 1379 /* 1380 ** tclcmd: sqlite3_quota_shutdown 1381 */ 1382 static int test_quota_shutdown( 1383 void * clientData, 1384 Tcl_Interp *interp, 1385 int objc, 1386 Tcl_Obj *CONST objv[] 1387 ){ 1388 int rc; /* Value returned by quota_shutdown() */ 1389 1390 if( objc!=1 ){ 1391 Tcl_WrongNumArgs(interp, 1, objv, ""); 1392 return TCL_ERROR; 1393 } 1394 1395 /* Call sqlite3_quota_shutdown() */ 1396 rc = sqlite3_quota_shutdown(); 1397 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); 1398 1399 return TCL_OK; 1400 } 1401 1402 /* 1403 ** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT 1404 */ 1405 static int test_quota_set( 1406 void * clientData, 1407 Tcl_Interp *interp, 1408 int objc, 1409 Tcl_Obj *CONST objv[] 1410 ){ 1411 const char *zPattern; /* File pattern to configure */ 1412 Tcl_WideInt iLimit; /* Initial quota in bytes */ 1413 Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */ 1414 int rc; /* Value returned by quota_set() */ 1415 TclQuotaCallback *p; /* Callback object */ 1416 int nScript; /* Length of callback script */ 1417 void (*xDestroy)(void*); /* Optional destructor for pArg */ 1418 void (*xCallback)(const char *, sqlite3_int64 *, sqlite3_int64, void *); 1419 1420 /* Process arguments */ 1421 if( objc!=4 ){ 1422 Tcl_WrongNumArgs(interp, 1, objv, "PATTERN LIMIT SCRIPT"); 1423 return TCL_ERROR; 1424 } 1425 zPattern = Tcl_GetString(objv[1]); 1426 if( Tcl_GetWideIntFromObj(interp, objv[2], &iLimit) ) return TCL_ERROR; 1427 pScript = objv[3]; 1428 Tcl_GetStringFromObj(pScript, &nScript); 1429 1430 if( nScript>0 ){ 1431 /* Allocate a TclQuotaCallback object */ 1432 p = (TclQuotaCallback *)sqlite3_malloc(sizeof(TclQuotaCallback)); 1433 if( !p ){ 1434 Tcl_SetResult(interp, (char *)"SQLITE_NOMEM", TCL_STATIC); 1435 return TCL_OK; 1436 } 1437 memset(p, 0, sizeof(TclQuotaCallback)); 1438 p->interp = interp; 1439 Tcl_IncrRefCount(pScript); 1440 p->pScript = pScript; 1441 xDestroy = tclCallbackDestructor; 1442 xCallback = tclQuotaCallback; 1443 }else{ 1444 p = 0; 1445 xDestroy = 0; 1446 xCallback = 0; 1447 } 1448 1449 /* Invoke sqlite3_quota_set() */ 1450 rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy); 1451 1452 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); 1453 return TCL_OK; 1454 } 1455 1456 /* 1457 ** tclcmd: sqlite3_quota_file FILENAME 1458 */ 1459 static int test_quota_file( 1460 void * clientData, 1461 Tcl_Interp *interp, 1462 int objc, 1463 Tcl_Obj *CONST objv[] 1464 ){ 1465 const char *zFilename; /* File pattern to configure */ 1466 int rc; /* Value returned by quota_file() */ 1467 1468 /* Process arguments */ 1469 if( objc!=2 ){ 1470 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); 1471 return TCL_ERROR; 1472 } 1473 zFilename = Tcl_GetString(objv[1]); 1474 1475 /* Invoke sqlite3_quota_file() */ 1476 rc = sqlite3_quota_file(zFilename); 1477 1478 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); 1479 return TCL_OK; 1480 } 1481 1482 /* 1483 ** tclcmd: sqlite3_quota_dump 1484 */ 1485 static int test_quota_dump( 1486 void * clientData, 1487 Tcl_Interp *interp, 1488 int objc, 1489 Tcl_Obj *CONST objv[] 1490 ){ 1491 Tcl_Obj *pResult; 1492 Tcl_Obj *pGroupTerm; 1493 Tcl_Obj *pFileTerm; 1494 quotaGroup *pGroup; 1495 quotaFile *pFile; 1496 1497 pResult = Tcl_NewObj(); 1498 quotaEnter(); 1499 for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){ 1500 pGroupTerm = Tcl_NewObj(); 1501 Tcl_ListObjAppendElement(interp, pGroupTerm, 1502 Tcl_NewStringObj(pGroup->zPattern, -1)); 1503 Tcl_ListObjAppendElement(interp, pGroupTerm, 1504 Tcl_NewWideIntObj(pGroup->iLimit)); 1505 Tcl_ListObjAppendElement(interp, pGroupTerm, 1506 Tcl_NewWideIntObj(pGroup->iSize)); 1507 for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){ 1508 int i; 1509 char zTemp[1000]; 1510 pFileTerm = Tcl_NewObj(); 1511 sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename); 1512 for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; } 1513 Tcl_ListObjAppendElement(interp, pFileTerm, 1514 Tcl_NewStringObj(zTemp, -1)); 1515 Tcl_ListObjAppendElement(interp, pFileTerm, 1516 Tcl_NewWideIntObj(pFile->iSize)); 1517 Tcl_ListObjAppendElement(interp, pFileTerm, 1518 Tcl_NewWideIntObj(pFile->nRef)); 1519 Tcl_ListObjAppendElement(interp, pFileTerm, 1520 Tcl_NewWideIntObj(pFile->deleteOnClose)); 1521 Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm); 1522 } 1523 Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); 1524 } 1525 quotaLeave(); 1526 Tcl_SetObjResult(interp, pResult); 1527 return TCL_OK; 1528 } 1529 1530 /* 1531 ** tclcmd: sqlite3_quota_fopen FILENAME MODE 1532 */ 1533 static int test_quota_fopen( 1534 void * clientData, 1535 Tcl_Interp *interp, 1536 int objc, 1537 Tcl_Obj *CONST objv[] 1538 ){ 1539 const char *zFilename; /* File pattern to configure */ 1540 const char *zMode; /* Mode string */ 1541 quota_FILE *p; /* Open string object */ 1542 char zReturn[50]; /* Name of pointer to return */ 1543 1544 /* Process arguments */ 1545 if( objc!=3 ){ 1546 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE"); 1547 return TCL_ERROR; 1548 } 1549 zFilename = Tcl_GetString(objv[1]); 1550 zMode = Tcl_GetString(objv[2]); 1551 p = sqlite3_quota_fopen(zFilename, zMode); 1552 sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p); 1553 Tcl_SetResult(interp, zReturn, TCL_VOLATILE); 1554 return TCL_OK; 1555 } 1556 1557 /* Defined in test1.c */ 1558 extern void *sqlite3TestTextToPtr(const char*); 1559 1560 /* 1561 ** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM 1562 */ 1563 static int test_quota_fread( 1564 void * clientData, 1565 Tcl_Interp *interp, 1566 int objc, 1567 Tcl_Obj *CONST objv[] 1568 ){ 1569 quota_FILE *p; 1570 char *zBuf; 1571 int sz; 1572 int nElem; 1573 size_t got; 1574 1575 if( objc!=4 ){ 1576 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM"); 1577 return TCL_ERROR; 1578 } 1579 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1580 if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR; 1581 if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR; 1582 zBuf = (char*)sqlite3_malloc( sz*nElem + 1 ); 1583 if( zBuf==0 ){ 1584 Tcl_SetResult(interp, "out of memory", TCL_STATIC); 1585 return TCL_ERROR; 1586 } 1587 got = sqlite3_quota_fread(zBuf, sz, nElem, p); 1588 zBuf[got*sz] = 0; 1589 Tcl_SetResult(interp, zBuf, TCL_VOLATILE); 1590 sqlite3_free(zBuf); 1591 return TCL_OK; 1592 } 1593 1594 /* 1595 ** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT 1596 */ 1597 static int test_quota_fwrite( 1598 void * clientData, 1599 Tcl_Interp *interp, 1600 int objc, 1601 Tcl_Obj *CONST objv[] 1602 ){ 1603 quota_FILE *p; 1604 char *zBuf; 1605 int sz; 1606 int nElem; 1607 size_t got; 1608 1609 if( objc!=5 ){ 1610 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT"); 1611 return TCL_ERROR; 1612 } 1613 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1614 if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR; 1615 if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR; 1616 zBuf = Tcl_GetString(objv[4]); 1617 got = sqlite3_quota_fwrite(zBuf, sz, nElem, p); 1618 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(got)); 1619 return TCL_OK; 1620 } 1621 1622 /* 1623 ** tclcmd: sqlite3_quota_fclose HANDLE 1624 */ 1625 static int test_quota_fclose( 1626 void * clientData, 1627 Tcl_Interp *interp, 1628 int objc, 1629 Tcl_Obj *CONST objv[] 1630 ){ 1631 quota_FILE *p; 1632 int rc; 1633 1634 if( objc!=2 ){ 1635 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1636 return TCL_ERROR; 1637 } 1638 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1639 rc = sqlite3_quota_fclose(p); 1640 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); 1641 return TCL_OK; 1642 } 1643 1644 /* 1645 ** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC? 1646 */ 1647 static int test_quota_fflush( 1648 void * clientData, 1649 Tcl_Interp *interp, 1650 int objc, 1651 Tcl_Obj *CONST objv[] 1652 ){ 1653 quota_FILE *p; 1654 int rc; 1655 int doSync = 0; 1656 1657 if( objc!=2 && objc!=3 ){ 1658 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?"); 1659 return TCL_ERROR; 1660 } 1661 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1662 if( objc==3 ){ 1663 if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR; 1664 } 1665 rc = sqlite3_quota_fflush(p, doSync); 1666 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); 1667 return TCL_OK; 1668 } 1669 1670 /* 1671 ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE 1672 */ 1673 static int test_quota_fseek( 1674 void * clientData, 1675 Tcl_Interp *interp, 1676 int objc, 1677 Tcl_Obj *CONST objv[] 1678 ){ 1679 quota_FILE *p; 1680 int ofst; 1681 const char *zWhence; 1682 int whence; 1683 int rc; 1684 1685 if( objc!=4 ){ 1686 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE"); 1687 return TCL_ERROR; 1688 } 1689 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1690 if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR; 1691 zWhence = Tcl_GetString(objv[3]); 1692 if( strcmp(zWhence, "SEEK_SET")==0 ){ 1693 whence = SEEK_SET; 1694 }else if( strcmp(zWhence, "SEEK_CUR")==0 ){ 1695 whence = SEEK_CUR; 1696 }else if( strcmp(zWhence, "SEEK_END")==0 ){ 1697 whence = SEEK_END; 1698 }else{ 1699 Tcl_AppendResult(interp, 1700 "WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0); 1701 return TCL_ERROR; 1702 } 1703 rc = sqlite3_quota_fseek(p, ofst, whence); 1704 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); 1705 return TCL_OK; 1706 } 1707 1708 /* 1709 ** tclcmd: sqlite3_quota_rewind HANDLE 1710 */ 1711 static int test_quota_rewind( 1712 void * clientData, 1713 Tcl_Interp *interp, 1714 int objc, 1715 Tcl_Obj *CONST objv[] 1716 ){ 1717 quota_FILE *p; 1718 if( objc!=2 ){ 1719 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1720 return TCL_ERROR; 1721 } 1722 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1723 sqlite3_quota_rewind(p); 1724 return TCL_OK; 1725 } 1726 1727 /* 1728 ** tclcmd: sqlite3_quota_ftell HANDLE 1729 */ 1730 static int test_quota_ftell( 1731 void * clientData, 1732 Tcl_Interp *interp, 1733 int objc, 1734 Tcl_Obj *CONST objv[] 1735 ){ 1736 quota_FILE *p; 1737 sqlite3_int64 x; 1738 if( objc!=2 ){ 1739 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1740 return TCL_ERROR; 1741 } 1742 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1743 x = sqlite3_quota_ftell(p); 1744 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); 1745 return TCL_OK; 1746 } 1747 1748 /* 1749 ** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE 1750 */ 1751 static int test_quota_ftruncate( 1752 void * clientData, 1753 Tcl_Interp *interp, 1754 int objc, 1755 Tcl_Obj *CONST objv[] 1756 ){ 1757 quota_FILE *p; 1758 sqlite3_int64 x; 1759 Tcl_WideInt w; 1760 int rc; 1761 if( objc!=3 ){ 1762 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE"); 1763 return TCL_ERROR; 1764 } 1765 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1766 if( Tcl_GetWideIntFromObj(interp, objv[2], &w) ) return TCL_ERROR; 1767 x = (sqlite3_int64)w; 1768 rc = sqlite3_quota_ftruncate(p, x); 1769 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); 1770 return TCL_OK; 1771 } 1772 1773 /* 1774 ** tclcmd: sqlite3_quota_file_size HANDLE 1775 */ 1776 static int test_quota_file_size( 1777 void * clientData, 1778 Tcl_Interp *interp, 1779 int objc, 1780 Tcl_Obj *CONST objv[] 1781 ){ 1782 quota_FILE *p; 1783 sqlite3_int64 x; 1784 if( objc!=2 ){ 1785 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1786 return TCL_ERROR; 1787 } 1788 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1789 x = sqlite3_quota_file_size(p); 1790 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); 1791 return TCL_OK; 1792 } 1793 1794 /* 1795 ** tclcmd: sqlite3_quota_file_truesize HANDLE 1796 */ 1797 static int test_quota_file_truesize( 1798 void * clientData, 1799 Tcl_Interp *interp, 1800 int objc, 1801 Tcl_Obj *CONST objv[] 1802 ){ 1803 quota_FILE *p; 1804 sqlite3_int64 x; 1805 if( objc!=2 ){ 1806 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1807 return TCL_ERROR; 1808 } 1809 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1810 x = sqlite3_quota_file_truesize(p); 1811 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); 1812 return TCL_OK; 1813 } 1814 1815 /* 1816 ** tclcmd: sqlite3_quota_file_mtime HANDLE 1817 */ 1818 static int test_quota_file_mtime( 1819 void * clientData, 1820 Tcl_Interp *interp, 1821 int objc, 1822 Tcl_Obj *CONST objv[] 1823 ){ 1824 quota_FILE *p; 1825 time_t t; 1826 if( objc!=2 ){ 1827 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1828 return TCL_ERROR; 1829 } 1830 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1831 t = 0; 1832 sqlite3_quota_file_mtime(p, &t); 1833 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(t)); 1834 return TCL_OK; 1835 } 1836 1837 1838 /* 1839 ** tclcmd: sqlite3_quota_remove FILENAME 1840 */ 1841 static int test_quota_remove( 1842 void * clientData, 1843 Tcl_Interp *interp, 1844 int objc, 1845 Tcl_Obj *CONST objv[] 1846 ){ 1847 const char *zFilename; /* File pattern to configure */ 1848 int rc; 1849 if( objc!=2 ){ 1850 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); 1851 return TCL_ERROR; 1852 } 1853 zFilename = Tcl_GetString(objv[1]); 1854 rc = sqlite3_quota_remove(zFilename); 1855 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); 1856 return TCL_OK; 1857 } 1858 1859 /* 1860 ** tclcmd: sqlite3_quota_glob PATTERN TEXT 1861 ** 1862 ** Test the glob pattern matching. Return 1 if TEXT matches PATTERN 1863 ** and return 0 if it does not. 1864 */ 1865 static int test_quota_glob( 1866 void * clientData, 1867 Tcl_Interp *interp, 1868 int objc, 1869 Tcl_Obj *CONST objv[] 1870 ){ 1871 const char *zPattern; /* The glob pattern */ 1872 const char *zText; /* Text to compare agains the pattern */ 1873 int rc; 1874 if( objc!=3 ){ 1875 Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT"); 1876 return TCL_ERROR; 1877 } 1878 zPattern = Tcl_GetString(objv[1]); 1879 zText = Tcl_GetString(objv[2]); 1880 rc = quotaStrglob(zPattern, zText); 1881 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); 1882 return TCL_OK; 1883 } 1884 1885 /* 1886 ** tclcmd: sqlite3_quota_file_available HANDLE 1887 ** 1888 ** Return the number of bytes from the current file point to the end of 1889 ** the file. 1890 */ 1891 static int test_quota_file_available( 1892 void * clientData, 1893 Tcl_Interp *interp, 1894 int objc, 1895 Tcl_Obj *CONST objv[] 1896 ){ 1897 quota_FILE *p; 1898 sqlite3_int64 x; 1899 if( objc!=2 ){ 1900 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1901 return TCL_ERROR; 1902 } 1903 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1904 x = sqlite3_quota_file_available(p); 1905 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); 1906 return TCL_OK; 1907 } 1908 1909 /* 1910 ** tclcmd: sqlite3_quota_ferror HANDLE 1911 ** 1912 ** Return true if the file handle is in the error state. 1913 */ 1914 static int test_quota_ferror( 1915 void * clientData, 1916 Tcl_Interp *interp, 1917 int objc, 1918 Tcl_Obj *CONST objv[] 1919 ){ 1920 quota_FILE *p; 1921 int x; 1922 if( objc!=2 ){ 1923 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); 1924 return TCL_ERROR; 1925 } 1926 p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 1927 x = sqlite3_quota_ferror(p); 1928 Tcl_SetObjResult(interp, Tcl_NewIntObj(x)); 1929 return TCL_OK; 1930 } 1931 1932 /* 1933 ** This routine registers the custom TCL commands defined in this 1934 ** module. This should be the only procedure visible from outside 1935 ** of this module. 1936 */ 1937 int Sqlitequota_Init(Tcl_Interp *interp){ 1938 static struct { 1939 char *zName; 1940 Tcl_ObjCmdProc *xProc; 1941 } aCmd[] = { 1942 { "sqlite3_quota_initialize", test_quota_initialize }, 1943 { "sqlite3_quota_shutdown", test_quota_shutdown }, 1944 { "sqlite3_quota_set", test_quota_set }, 1945 { "sqlite3_quota_file", test_quota_file }, 1946 { "sqlite3_quota_dump", test_quota_dump }, 1947 { "sqlite3_quota_fopen", test_quota_fopen }, 1948 { "sqlite3_quota_fread", test_quota_fread }, 1949 { "sqlite3_quota_fwrite", test_quota_fwrite }, 1950 { "sqlite3_quota_fclose", test_quota_fclose }, 1951 { "sqlite3_quota_fflush", test_quota_fflush }, 1952 { "sqlite3_quota_fseek", test_quota_fseek }, 1953 { "sqlite3_quota_rewind", test_quota_rewind }, 1954 { "sqlite3_quota_ftell", test_quota_ftell }, 1955 { "sqlite3_quota_ftruncate", test_quota_ftruncate }, 1956 { "sqlite3_quota_file_size", test_quota_file_size }, 1957 { "sqlite3_quota_file_truesize", test_quota_file_truesize }, 1958 { "sqlite3_quota_file_mtime", test_quota_file_mtime }, 1959 { "sqlite3_quota_remove", test_quota_remove }, 1960 { "sqlite3_quota_glob", test_quota_glob }, 1961 { "sqlite3_quota_file_available",test_quota_file_available }, 1962 { "sqlite3_quota_ferror", test_quota_ferror }, 1963 }; 1964 int i; 1965 1966 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 1967 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 1968 } 1969 1970 return TCL_OK; 1971 } 1972 #endif 1973