LCOV - code coverage report
Current view: directory - db/sqlite3/src - test_quota.c (source / functions) Found Hit Coverage
Test: app.info Lines: 455 230 50.5 %
Date: 2012-06-02 Functions: 43 24 55.8 %

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

Generated by: LCOV version 1.7