modernc.org/cc@v1.0.1/v2/testdata/_sqlite/src/test_quota.c (about)

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