xref: /sqlite-3.40.0/src/test_quota.c (revision 748a52cd)
1*748a52cdSdrh /*
2*748a52cdSdrh ** 2010 September 31
3*748a52cdSdrh **
4*748a52cdSdrh ** The author disclaims copyright to this source code.  In place of
5*748a52cdSdrh ** a legal notice, here is a blessing:
6*748a52cdSdrh **
7*748a52cdSdrh **    May you do good and not evil.
8*748a52cdSdrh **    May you find forgiveness for yourself and forgive others.
9*748a52cdSdrh **    May you share freely, never taking more than you give.
10*748a52cdSdrh **
11*748a52cdSdrh *************************************************************************
12*748a52cdSdrh **
13*748a52cdSdrh ** This file contains a VFS "shim" - a layer that sits in between the
14*748a52cdSdrh ** pager and the real VFS.
15*748a52cdSdrh **
16*748a52cdSdrh ** This particular shim enforces a quota system on files.  One or more
17*748a52cdSdrh ** database files are in a "quota group" that is defined by a GLOB
18*748a52cdSdrh ** pattern.  A quota is set for the combined size of all files in the
19*748a52cdSdrh ** the group.  A quota of zero means "no limit".  If the total size
20*748a52cdSdrh ** of all files in the quota group is met or exceeded, then new
21*748a52cdSdrh ** write requests that attempt to enlarge a file fail with SQLITE_FULL.
22*748a52cdSdrh **
23*748a52cdSdrh ** However, before returning SQLITE_FULL, the write requests invoke
24*748a52cdSdrh ** a callback function that is configurable for each quota group.
25*748a52cdSdrh ** This callback has the opportunity to enlarge the quota.  If the
26*748a52cdSdrh ** callback does enlarge the quota such that the total size of all
27*748a52cdSdrh ** files within the group is less than the new quota, then the write
28*748a52cdSdrh ** continues as if nothing had happened.
29*748a52cdSdrh */
30*748a52cdSdrh #include "sqlite3.h"
31*748a52cdSdrh 
32*748a52cdSdrh /************************ Object Definitions ******************************/
33*748a52cdSdrh 
34*748a52cdSdrh /*
35*748a52cdSdrh ** This module contains a table of filename patterns that have size
36*748a52cdSdrh ** quotas.  The quota applies to the sum of the sizes of all open
37*748a52cdSdrh ** database files whose names match the GLOB pattern.
38*748a52cdSdrh **
39*748a52cdSdrh ** Each quota is an instance of the following object.  Quotas must
40*748a52cdSdrh ** be established (using sqlite3_quota_set()) prior to opening any
41*748a52cdSdrh ** of the database connections that access files governed by the
42*748a52cdSdrh ** quota.
43*748a52cdSdrh **
44*748a52cdSdrh ** Each entry in the quota table is an instance of the following object.
45*748a52cdSdrh */
46*748a52cdSdrh typedef struct quotaGroup quotaGroup;
47*748a52cdSdrh struct quotaGroup {
48*748a52cdSdrh   const char *zPattern;          /* Filename pattern to be quotaed */
49*748a52cdSdrh   sqlite3_int64 iLimit;          /* Upper bound on total file size */
50*748a52cdSdrh   sqlite3_int64 iSize;           /* Current size of all files */
51*748a52cdSdrh   void (*xCallback)(             /* Callback invoked when going over quota */
52*748a52cdSdrh      const char *zFilename,         /* Name of file whose size increases */
53*748a52cdSdrh      sqlite3_int64 *piLimit,        /* IN/OUT: The current limit */
54*748a52cdSdrh      sqlite3_int64 iSize,           /* Total size of all files in the group */
55*748a52cdSdrh      void *pArg                     /* Client data */
56*748a52cdSdrh   );
57*748a52cdSdrh   void *pArg;                    /* Third argument to the xCallback() */
58*748a52cdSdrh   int nRef;                      /* Number of files in the group references. */
59*748a52cdSdrh   quotaGroup *pNext, **ppPrev;   /* Doubly linked list of all quota objects */
60*748a52cdSdrh };
61*748a52cdSdrh 
62*748a52cdSdrh /*
63*748a52cdSdrh ** An instance of the following object represents each file that
64*748a52cdSdrh ** participates in quota tracking.  The sqlite3_file object for the
65*748a52cdSdrh ** underlying VFS is appended to this structure.
66*748a52cdSdrh */
67*748a52cdSdrh typedef struct quotaFile quotaFile;
68*748a52cdSdrh struct quotaFile {
69*748a52cdSdrh   sqlite3_file base;            /* Base class - must be first */
70*748a52cdSdrh   const char *zFilename;        /* Name of this file */
71*748a52cdSdrh   quotaGroup *pGroup;           /* Upper bound on file size */
72*748a52cdSdrh   /* The underlying VFS sqlite3_file is appended to this object */
73*748a52cdSdrh };
74*748a52cdSdrh 
75*748a52cdSdrh /************************* Global Variables **********************************/
76*748a52cdSdrh /*
77*748a52cdSdrh ** All global variables used by this file are containing within the following
78*748a52cdSdrh ** gQuota structure.
79*748a52cdSdrh */
80*748a52cdSdrh static struct {
81*748a52cdSdrh   /* The pOrigVfs is a pointer to the real underlying VFS implementation.
82*748a52cdSdrh   ** Most operations pass-through to the real VFS.  This value is read-only
83*748a52cdSdrh   ** during operation.  It is only modified at start-time and thus does not
84*748a52cdSdrh   ** require a mutex.
85*748a52cdSdrh   */
86*748a52cdSdrh   sqlite3_vfs *pOrigVfs;
87*748a52cdSdrh 
88*748a52cdSdrh   /* The sThisVfs is the VFS structure used by this shim.  It is initialized
89*748a52cdSdrh   ** at start-time and thus does not require a mutex
90*748a52cdSdrh   */
91*748a52cdSdrh   sqlite3_vfs sThisVfs;
92*748a52cdSdrh 
93*748a52cdSdrh   /* The sIoMethods defines the methods used by sqlite3_file objects
94*748a52cdSdrh   ** associated with this shim.  It is initialized at start-time and does
95*748a52cdSdrh   ** not require a mutex.
96*748a52cdSdrh   **
97*748a52cdSdrh   ** When the underlying VFS is called to open a file, it might return
98*748a52cdSdrh   ** either a version 1 or a version 2 sqlite3_file object.  This shim
99*748a52cdSdrh   ** has to create a wrapper sqlite3_file of the same version.  Hence
100*748a52cdSdrh   ** there are two I/O method structures, one for version 1 and the other
101*748a52cdSdrh   ** for version 2.
102*748a52cdSdrh   */
103*748a52cdSdrh   sqlite3_io_methods sIoMethodsV1;
104*748a52cdSdrh   sqlite3_io_methods sIoMethodsV2;
105*748a52cdSdrh 
106*748a52cdSdrh   /* True when this shim as been initialized.
107*748a52cdSdrh   */
108*748a52cdSdrh   int isInitialized;
109*748a52cdSdrh 
110*748a52cdSdrh   /* For run-time access any of the other global data structures in this
111*748a52cdSdrh   ** shim, the following mutex must be held.
112*748a52cdSdrh   */
113*748a52cdSdrh   sqlite3_mutex *pMutex;
114*748a52cdSdrh 
115*748a52cdSdrh   /* List of quotaGroup objects.
116*748a52cdSdrh   */
117*748a52cdSdrh   quotaGroup *pGroup;
118*748a52cdSdrh 
119*748a52cdSdrh } gQuota;
120*748a52cdSdrh 
121*748a52cdSdrh /************************* Utility Routines *********************************/
122*748a52cdSdrh /*
123*748a52cdSdrh ** Acquire and release the mutex used to serialize access to the
124*748a52cdSdrh ** list of quotaGroups.
125*748a52cdSdrh */
126*748a52cdSdrh static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
127*748a52cdSdrh static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
128*748a52cdSdrh 
129*748a52cdSdrh 
130*748a52cdSdrh /* If the reference count and threshold for a quotaGroup are both
131*748a52cdSdrh ** zero, then destroy the quotaGroup.
132*748a52cdSdrh */
133*748a52cdSdrh static void quotaGroupDeref(quotaFile *p){
134*748a52cdSdrh   if( p->nRef==0 && p->iLimit==0 ){
135*748a52cdSdrh     if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
136*748a52cdSdrh     if( p->ppPrev ) *p->ppPrev = p->pNext;
137*748a52cdSdrh     sqlite3_free(p);
138*748a52cdSdrh   }
139*748a52cdSdrh }
140*748a52cdSdrh 
141*748a52cdSdrh /*
142*748a52cdSdrh ** Return TRUE if string z matches glob pattern zGlob.
143*748a52cdSdrh **
144*748a52cdSdrh ** Globbing rules:
145*748a52cdSdrh **
146*748a52cdSdrh **      '*'       Matches any sequence of zero or more characters.
147*748a52cdSdrh **
148*748a52cdSdrh **      '?'       Matches exactly one character.
149*748a52cdSdrh **
150*748a52cdSdrh **     [...]      Matches one character from the enclosed list of
151*748a52cdSdrh **                characters.
152*748a52cdSdrh **
153*748a52cdSdrh **     [^...]     Matches one character not in the enclosed list.
154*748a52cdSdrh **
155*748a52cdSdrh */
156*748a52cdSdrh static int strglob(const char *zGlob, const char *z){
157*748a52cdSdrh   int c, c2;
158*748a52cdSdrh   int invert;
159*748a52cdSdrh   int seen;
160*748a52cdSdrh 
161*748a52cdSdrh   while( (c = (*(zGlob++)))!=0 ){
162*748a52cdSdrh     if( c=='*' ){
163*748a52cdSdrh       while( (c=(*(zGlob++))) == '*' || c=='?' ){
164*748a52cdSdrh         if( c=='?' && (*(z++))==0 ) return 0;
165*748a52cdSdrh       }
166*748a52cdSdrh       if( c==0 ){
167*748a52cdSdrh         return 1;
168*748a52cdSdrh       }else if( c=='[' ){
169*748a52cdSdrh         while( *z && th3strglob(zGlob-1,z)==0 ){
170*748a52cdSdrh           z++;
171*748a52cdSdrh         }
172*748a52cdSdrh         return (*z)!=0;
173*748a52cdSdrh       }
174*748a52cdSdrh       while( (c2 = (*(z++)))!=0 ){
175*748a52cdSdrh         while( c2!=c ){
176*748a52cdSdrh           c2 = *(z++);
177*748a52cdSdrh           if( c2==0 ) return 0;
178*748a52cdSdrh         }
179*748a52cdSdrh         if( th3strglob(zGlob,z) ) return 1;
180*748a52cdSdrh       }
181*748a52cdSdrh       return 0;
182*748a52cdSdrh     }else if( c=='?' ){
183*748a52cdSdrh       if( (*(z++))==0 ) return 0;
184*748a52cdSdrh     }else if( c=='[' ){
185*748a52cdSdrh       int prior_c = 0;
186*748a52cdSdrh       seen = 0;
187*748a52cdSdrh       invert = 0;
188*748a52cdSdrh       c = *(z++);
189*748a52cdSdrh       if( c==0 ) return 0;
190*748a52cdSdrh       c2 = *(zGlob++);
191*748a52cdSdrh       if( c2=='^' ){
192*748a52cdSdrh         invert = 1;
193*748a52cdSdrh         c2 = *(zGlob++);
194*748a52cdSdrh       }
195*748a52cdSdrh       if( c2==']' ){
196*748a52cdSdrh         if( c==']' ) seen = 1;
197*748a52cdSdrh         c2 = *(zGlob++);
198*748a52cdSdrh       }
199*748a52cdSdrh       while( c2 && c2!=']' ){
200*748a52cdSdrh         if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
201*748a52cdSdrh           c2 = *(zGlob++);
202*748a52cdSdrh           if( c>=prior_c && c<=c2 ) seen = 1;
203*748a52cdSdrh           prior_c = 0;
204*748a52cdSdrh         }else{
205*748a52cdSdrh           if( c==c2 ){
206*748a52cdSdrh             seen = 1;
207*748a52cdSdrh           }
208*748a52cdSdrh           prior_c = c2;
209*748a52cdSdrh         }
210*748a52cdSdrh         c2 = *(zGlob++);
211*748a52cdSdrh       }
212*748a52cdSdrh       if( c2==0 || (seen ^ invert)==0 ) return 0;
213*748a52cdSdrh     }else{
214*748a52cdSdrh       if( c!=(*(z++)) ) return 0;
215*748a52cdSdrh     }
216*748a52cdSdrh   }
217*748a52cdSdrh   return *z==0;
218*748a52cdSdrh }
219*748a52cdSdrh 
220*748a52cdSdrh 
221*748a52cdSdrh /* Find a quotaGroup given the filename.
222*748a52cdSdrh ** Return a pointer to the quotaFile object.  return NULL if not found.
223*748a52cdSdrh */
224*748a52cdSdrh static quotaGroup *quotaGroupFind(const char *zFilename){
225*748a52cdSdrh   quotaGroup *p;
226*748a52cdSdrh   for(p=pGroup; p && strglob(p->zPattern, zFilename)==0; p=p->pNext){}
227*748a52cdSdrh   return p;
228*748a52cdSdrh }
229*748a52cdSdrh 
230*748a52cdSdrh /* Translate an sqlite3_file* that is really a quotaFile* into
231*748a52cdSdrh ** an sqlite3_file* for the underlying original VFS.
232*748a52cdSdrh */
233*748a52cdSdrh static sqlite3_file *quotaSubFile(sqlite3_file *pFile){
234*748a52cdSdrh   quotaFile *p = (quotaFile*)pFile;
235*748a52cdSdrh   return (sqlite3_file*)&p[1];
236*748a52cdSdrh }
237*748a52cdSdrh 
238*748a52cdSdrh /************************* VFS Method Wrappers *****************************/
239*748a52cdSdrh /*
240*748a52cdSdrh ** This is the xOpen method used for the "quota" VFS.
241*748a52cdSdrh **
242*748a52cdSdrh ** Most of the work is done by the underlying original VFS.  This method
243*748a52cdSdrh ** simply links the new file into the quota group if it is a file that
244*748a52cdSdrh ** needs to be tracked.
245*748a52cdSdrh */
246*748a52cdSdrh static int quotaOpen(
247*748a52cdSdrh   sqlite3_vfs *pVfs,
248*748a52cdSdrh   const char *zName,
249*748a52cdSdrh   sqlite3_file *pFile,
250*748a52cdSdrh   int flags,
251*748a52cdSdrh   int *pOutFlags
252*748a52cdSdrh ){
253*748a52cdSdrh   int rc;
254*748a52cdSdrh   quotaFile *pQuotaFile;
255*748a52cdSdrh   sqlite3_file *pSubFile;
256*748a52cdSdrh 
257*748a52cdSdrh   /* If the file is not a main database file or a WAL, then use the
258*748a52cdSdrh   ** normal xOpen method.
259*748a52cdSdrh   */
260*748a52cdSdrh   if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_WAL))==0 ){
261*748a52cdSdrh     return  gQuota.pOrigVfs->xOpen(gQuota.pOrigVfs, zName,
262*748a52cdSdrh                                    pFile, flags, pOutFlags);
263*748a52cdSdrh   }
264*748a52cdSdrh 
265*748a52cdSdrh   /* If the name of the file does not match any quota group, then
266*748a52cdSdrh   ** use the normal xOpen method.
267*748a52cdSdrh   */
268*748a52cdSdrh   quotaEnter();
269*748a52cdSdrh   pGroup = quotaFindGroup(zName);
270*748a52cdSdrh   if( pGroup==0 ){
271*748a52cdSdrh     return  gQuota.pOrigVfs->xOpen(gQuota.pOrigVfs, zName,
272*748a52cdSdrh                                    pFile, flags, pOutFlags);
273*748a52cdSdrh   }
274*748a52cdSdrh 
275*748a52cdSdrh   /* If we get to this point, it means the file needs
276*748a52cdSdrh   ** to be quota tracked.
277*748a52cdSdrh   */
278*748a52cdSdrh   pQuotaFile = (quotaFile*)pFile;
279*748a52cdSdrh   pSubFile = quotaSubFile(pFile);
280*748a52cdSdrh   rc = gQuota.pOrigVfs->xOpen(gQuota.pOrigVfs, zName,
281*748a52cdSdrh                               pSubFile, flags, pOutFlags);
282*748a52cdSdrh   if( rc==SQLITE_OK ){
283*748a52cdSdrh     pQuotaFile->iSize = 0;
284*748a52cdSdrh     pQuotaFile->pGroup = pGroup;
285*748a52cdSdrh     pGroup->nRef++;
286*748a52cdSdrh     quotaLeave();
287*748a52cdSdrh     pQuotaFile->zFilename = zName;
288*748a52cdSdrh     if( pSubFile->pMethods.iVersion==1 ){
289*748a52cdSdrh       pQuotaFile->base.pMethods = &gQuota.sIoMethodsV1;
290*748a52cdSdrh     }else{
291*748a52cdSdrh       pQuotaFile->base.pMethods = &gQuota.sIoMethodsV2;
292*748a52cdSdrh     }
293*748a52cdSdrh   }
294*748a52cdSdrh   return rc;
295*748a52cdSdrh }
296*748a52cdSdrh 
297*748a52cdSdrh /************************ I/O Method Wrappers *******************************/
298*748a52cdSdrh 
299*748a52cdSdrh /* xClose requests get passed through to the original VFS.  But we
300*748a52cdSdrh ** also have to unlink the quotaFile from the quotaGroup.
301*748a52cdSdrh */
302*748a52cdSdrh static int quotaClose(sqlite3_file *pFile){
303*748a52cdSdrh   quotaFile *p = (quotaFile*)pFile;
304*748a52cdSdrh   quotaGroup *pGroup = p->pGroup;
305*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
306*748a52cdSdrh   int rc;
307*748a52cdSdrh   rc = pSubFile->pMethods.xClose(pSubFile);
308*748a52cdSdrh   quotaEnter();
309*748a52cdSdrh   pGroup->nRef--;
310*748a52cdSdrh   pGroup->iSize -= p->iSize;
311*748a52cdSdrh   quotaGroupDeref(pGroup);
312*748a52cdSdrh   quotaLeave();
313*748a52cdSdrh   return rc;
314*748a52cdSdrh }
315*748a52cdSdrh 
316*748a52cdSdrh /* Pass xRead requests directory thru to the original VFS without
317*748a52cdSdrh ** further processing.
318*748a52cdSdrh */
319*748a52cdSdrh static int quotaRead(
320*748a52cdSdrh   sqlite3_file *pFile,
321*748a52cdSdrh   void *pBuf,
322*748a52cdSdrh   int iAmt,
323*748a52cdSdrh   sqlite3_int64 iOfst
324*748a52cdSdrh ){
325*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
326*748a52cdSdrh   return pSubFile->pMethods.xRead(pSubFile, pBuf, iAmt, iOfst);
327*748a52cdSdrh }
328*748a52cdSdrh 
329*748a52cdSdrh /* Check xWrite requests to see if they expand the file.  If they do,
330*748a52cdSdrh ** the perform a quota check before passing them through to the
331*748a52cdSdrh ** original VFS.
332*748a52cdSdrh */
333*748a52cdSdrh static int quotaWrite(
334*748a52cdSdrh   sqlite3_file *pFile,
335*748a52cdSdrh   void *pBuf,
336*748a52cdSdrh   int iAmt,
337*748a52cdSdrh   sqlite3_int64 iOfst
338*748a52cdSdrh ){
339*748a52cdSdrh   quotaFile *p = (quotaFile*)pFile;
340*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
341*748a52cdSdrh   sqlite3_int64 iEnd = iOfst+iAmt;
342*748a52cdSdrh   quotaGroup *pGroup;
343*748a52cdSdrh   sqlite3_int64 szNew;
344*748a52cdSdrh 
345*748a52cdSdrh   if( p->iSize<iEnd ){
346*748a52cdSdrh     pGroup = p->pGroup;
347*748a52cdSdrh     quotaEnter();
348*748a52cdSdrh     szNew = pGroup->iSize - p->iSize + iEnd;
349*748a52cdSdrh     if( szNew >= pGroup->iLimit && pGroup->iLimit > 0 ){
350*748a52cdSdrh       if( pGroup->xCallback ){
351*748a52cdSdrh         pGroup->xCallback(p->zFilename, &pGroup->iLimit, szNew,
352*748a52cdSdrh                           pGroup->pArg);
353*748a52cdSdrh       }
354*748a52cdSdrh       if( szNew >= pGroup->iLimit && pGroup->iLimit > 0 ){
355*748a52cdSdrh         quotaLeave();
356*748a52cdSdrh         return SQLITE_FULL;
357*748a52cdSdrh       }
358*748a52cdSdrh     }
359*748a52cdSdrh     pGroup->iSize = szNew;
360*748a52cdSdrh     quotaLeave();
361*748a52cdSdrh   }
362*748a52cdSdrh   return pSubFile->pMethods.xWrite(pSubFile, pBuf, iAmt, iOfst);
363*748a52cdSdrh }
364*748a52cdSdrh 
365*748a52cdSdrh /* Pass xTruncate requests thru to the original VFS.  If the
366*748a52cdSdrh ** success, update the file size.
367*748a52cdSdrh */
368*748a52cdSdrh static int quotaTruncate(sqlite3_file *pFile, sqlite3_int64 size){
369*748a52cdSdrh   quotaFile *p = (quotaFile*)pFile);
370*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
371*748a52cdSdrh   int rc = pSubFile->pMethods.xTruncate(pSubFile, size);
372*748a52cdSdrh   quotaGroup *pGroup = p->pGroup;
373*748a52cdSdrh   if( rc==SQLITE_OK ){
374*748a52cdSdrh     quotaEnter();
375*748a52cdSdrh     pGroup->iSize -= p->iSize;
376*748a52cdSdrh     p->iSize = size;
377*748a52cdSdrh     pGroup->iSize += size;
378*748a52cdSdrh     quotaLeave();
379*748a52cdSdrh   }
380*748a52cdSdrh   return rc;
381*748a52cdSdrh }
382*748a52cdSdrh 
383*748a52cdSdrh /* Pass xSync requests through to the original VFS without change
384*748a52cdSdrh */
385*748a52cdSdrh static int quotaSync(sqlite3_file *pFile, int flags){
386*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
387*748a52cdSdrh   return pSubFile->pMethods.xSync(pSubFile, flags);
388*748a52cdSdrh }
389*748a52cdSdrh 
390*748a52cdSdrh /* Pass xFileSize requests through to the original VFS but then
391*748a52cdSdrh ** update the quotaGroup with the new size before returning.
392*748a52cdSdrh */
393*748a52cdSdrh static int quotaFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){
394*748a52cdSdrh   quotaFile *p = (quotaFile*)pFile;
395*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
396*748a52cdSdrh   quotaGroup *pGroup;
397*748a52cdSdrh   sqlite3_int64 sz;
398*748a52cdSdrh   int rc;
399*748a52cdSdrh 
400*748a52cdSdrh   rc = pSubFile->pMethods.xFileSize(pSubFile, &sz);
401*748a52cdSdrh   if( rc==SQLITE_OK ){
402*748a52cdSdrh     pGroup = p->pGroup;
403*748a52cdSdrh     quotaEnter();
404*748a52cdSdrh     pGroup->iSize -= p->iSize;
405*748a52cdSdrh     p->iSize = sz;
406*748a52cdSdrh     pGroup->iSize += sz;
407*748a52cdSdrh     quotaLeave();
408*748a52cdSdrh     *pSize = sz;
409*748a52cdSdrh   }
410*748a52cdSdrh   return rc;
411*748a52cdSdrh }
412*748a52cdSdrh 
413*748a52cdSdrh /* Pass xLock requests through to the original VFS unchanged.
414*748a52cdSdrh */
415*748a52cdSdrh static int quotaLock(sqlite3_file *pFile, int lock){
416*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
417*748a52cdSdrh   return pSubFile->pMethods.xLock(pSubFile, lock);
418*748a52cdSdrh }
419*748a52cdSdrh 
420*748a52cdSdrh /* Pass xUnlock requests through to the original VFS unchanged.
421*748a52cdSdrh */
422*748a52cdSdrh static int quotaUnlock(sqlite3_file *pFile, int lock){
423*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
424*748a52cdSdrh   return pSubFile->pMethods.xUnlock(pSubFile, lock);
425*748a52cdSdrh }
426*748a52cdSdrh 
427*748a52cdSdrh /* Pass xCheckReservedLock requests through to the original VFS unchanged.
428*748a52cdSdrh */
429*748a52cdSdrh static int quotaCheckReservedLock(sqlite3_file *pFile, int *pResOut){
430*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
431*748a52cdSdrh   return pSubFile->pMethods.xCheckReservedLock(pSubFile, pResOut);
432*748a52cdSdrh }
433*748a52cdSdrh 
434*748a52cdSdrh /* Pass xFileControl requests through to the original VFS unchanged.
435*748a52cdSdrh */
436*748a52cdSdrh static int quotaFileControl(sqlite3_file *pFile, int op, void *pArg){
437*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
438*748a52cdSdrh   return pSubFile->pMethods.xFileControl(pSubFile, op, pArg);
439*748a52cdSdrh }
440*748a52cdSdrh 
441*748a52cdSdrh /* Pass xSectorSize requests through to the original VFS unchanged.
442*748a52cdSdrh */
443*748a52cdSdrh static int quotaSectorSize(sqlite3_file *pFile){
444*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
445*748a52cdSdrh   return pSubFile->pMethods.xSectorSize(pSubFile);
446*748a52cdSdrh }
447*748a52cdSdrh 
448*748a52cdSdrh /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
449*748a52cdSdrh */
450*748a52cdSdrh static int quotaDeviceCharacteristics(sqlite3_file *pFile){
451*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
452*748a52cdSdrh   return pSubFile->pMethods.xDeviceCharacteristics(pSubFile);
453*748a52cdSdrh }
454*748a52cdSdrh 
455*748a52cdSdrh /* Pass xShmMap requests through to the original VFS unchanged.
456*748a52cdSdrh */
457*748a52cdSdrh static int quotaShmMap(
458*748a52cdSdrh   sqlite3_file *pFile,            /* Handle open on database file */
459*748a52cdSdrh   int iRegion,                    /* Region to retrieve */
460*748a52cdSdrh   int szRegion,                   /* Size of regions */
461*748a52cdSdrh   int bExtend,                    /* True to extend file if necessary */
462*748a52cdSdrh   void volatile **pp              /* OUT: Mapped memory */
463*748a52cdSdrh ){
464*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
465*748a52cdSdrh   return pSubFile->pMethods.xShmMap(pSubFile, iRegion, szRegion, bExtend, pp);
466*748a52cdSdrh }
467*748a52cdSdrh 
468*748a52cdSdrh /* Pass xShmLock requests through to the original VFS unchanged.
469*748a52cdSdrh */
470*748a52cdSdrh static int quotaShmLock(
471*748a52cdSdrh   sqlite3_file *pFile,       /* Database file holding the shared memory */
472*748a52cdSdrh   int ofst,                  /* First lock to acquire or release */
473*748a52cdSdrh   int n,                     /* Number of locks to acquire or release */
474*748a52cdSdrh   int flags                  /* What to do with the lock */
475*748a52cdSdrh ){
476*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
477*748a52cdSdrh   return pSubFile->pMethods.xShmLock(pSubFile, ofst, n, flags);
478*748a52cdSdrh }
479*748a52cdSdrh 
480*748a52cdSdrh /* Pass xShmBarrier requests through to the original VFS unchanged.
481*748a52cdSdrh */
482*748a52cdSdrh static int quotaShmBarrier(sqlite3_file *pFile){
483*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
484*748a52cdSdrh   return pSubFile->pMethods.xShmBarrier(pSubFile);
485*748a52cdSdrh }
486*748a52cdSdrh 
487*748a52cdSdrh /* Pass xShmUnmap requests through to the original VFS unchanged.
488*748a52cdSdrh */
489*748a52cdSdrh static int quotaShmUnmap(sqlite3_file *pFile, int deleteFlag){
490*748a52cdSdrh   sqlite3_file *pSubFile = quotaSubFile(pFile);
491*748a52cdSdrh   return pSubFile->pMethods.xShmUnmap(pSubFile, deleteFlag);
492*748a52cdSdrh }
493*748a52cdSdrh 
494*748a52cdSdrh /************************** Public Interfaces *****************************/
495*748a52cdSdrh /*
496*748a52cdSdrh ** Initialize the quota VFS shim.  Use the VFS named zOrigVfsName
497*748a52cdSdrh ** as the VFS that does the actual work.  Use the default if
498*748a52cdSdrh ** zOrigVfsName==NULL.
499*748a52cdSdrh **
500*748a52cdSdrh ** The quota VFS shim is named "quota".  It will become the default
501*748a52cdSdrh ** VFS if makeDefault is non-zero.
502*748a52cdSdrh **
503*748a52cdSdrh ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
504*748a52cdSdrh ** during start-up.
505*748a52cdSdrh */
506*748a52cdSdrh int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
507*748a52cdSdrh   sqlite3_vfs *pOrigVfs;
508*748a52cdSdrh   if( gQuota.isInitialize ) return SQLITE_MISUSE;
509*748a52cdSdrh   gQuota.isInitialized = 1;
510*748a52cdSdrh   pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
511*748a52cdSdrh   if( pOrigVfs==0 ) return SQLITE_ERROR;
512*748a52cdSdrh   gQuota.pOrigVfs = pOrigVfs;
513*748a52cdSdrh   gQuota.sThisVfs = *pOrigVfs;
514*748a52cdSdrh   gQuota.sThisVfs.xOpen = quotaOpen;
515*748a52cdSdrh   gQuota.sThisVfs.szOsFile += sizeof(quotaFile);
516*748a52cdSdrh   gQuota.sThisVfs.zName = "quota";
517*748a52cdSdrh   gQuota.sIoMethodsV1.iVersion = 1;
518*748a52cdSdrh   gQuota.sIoMethodsV1.xClose = quotaClose;
519*748a52cdSdrh   gQuota.sIoMethodsV1.xRead = quotaRead;
520*748a52cdSdrh   gQuota.sIoMethodsV1.xWrite = quotaWrite;
521*748a52cdSdrh   gQuota.sIoMethodsV1.xTruncate = quotaTruncate;
522*748a52cdSdrh   gQuota.sIoMethodsV1.xSync = quotaSync;
523*748a52cdSdrh   gQuota.sIoMethodsV1.xFileSize = quotaFileSize;
524*748a52cdSdrh   gQuota.sIoMethodsV1.xLock = quotaLock;
525*748a52cdSdrh   gQuota.sIoMethodsV1.xUnlock = quotaUnlock;
526*748a52cdSdrh   gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock;
527*748a52cdSdrh   gQuota.sIoMethodsV1.xFileControl = quotaFileControl;
528*748a52cdSdrh   gQuota.sIoMethodsV1.xSectorSize = quotaSectoSize;
529*748a52cdSdrh   gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics;
530*748a52cdSdrh   gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1;
531*748a52cdSdrh   gQuota.sIoMethodsV2.iVersion = 2;
532*748a52cdSdrh   gQuota.sIoMethodsV2.xShmMap = quotaShmMap;
533*748a52cdSdrh   gQuota.sIoMethodsV2.xShmLock = quotaShmLock;
534*748a52cdSdrh   gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier;
535*748a52cdSdrh   gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap;
536*748a52cdSdrh   gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
537*748a52cdSdrh }
538*748a52cdSdrh 
539*748a52cdSdrh /*
540*748a52cdSdrh ** Shutdown the quota system.
541*748a52cdSdrh **
542*748a52cdSdrh ** All SQLite database connections must be closed before calling this
543*748a52cdSdrh ** routine.
544*748a52cdSdrh **
545*748a52cdSdrh ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly one while
546*748a52cdSdrh ** shutting down in order to free all remaining quota groups.
547*748a52cdSdrh */
548*748a52cdSdrh int sqlite3_quota_shutdown(void){
549*748a52cdSdrh   if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
550*748a52cdSdrh   gQuota.isInitialized = 0;
551*748a52cdSdrh   sqlite3_mutex_free(gQuota.pMutex);
552*748a52cdSdrh   while( gQuota.pGroup ){
553*748a52cdSdrh     quotaGroup *p = gQuota.pGroup;
554*748a52cdSdrh     gQuota.pGroup = p->pNext;
555*748a52cdSdrh     if( p->nRef ) return SQLITE_MISUSE;
556*748a52cdSdrh     sqlite3_free(p);
557*748a52cdSdrh   }
558*748a52cdSdrh   memset(&gQuota, 0, sizeof(gQuota));
559*748a52cdSdrh }
560*748a52cdSdrh 
561*748a52cdSdrh /*
562*748a52cdSdrh ** Create or destroy a quota group.
563*748a52cdSdrh **
564*748a52cdSdrh ** The quota group is defined by the zPattern.  When calling this routine
565*748a52cdSdrh ** with a zPattern for a quota group that already exists, this routine
566*748a52cdSdrh ** merely updates the iLimit, xCallback, and pArg values for that quota
567*748a52cdSdrh ** group.  If zPattern is new, then a new quota group is created.
568*748a52cdSdrh **
569*748a52cdSdrh ** If the iLimit for a quota group is set to zero, then the quota group
570*748a52cdSdrh ** is disabled and will be deleted when the last database connection using
571*748a52cdSdrh ** the quota group is closed.
572*748a52cdSdrh **
573*748a52cdSdrh ** Calling this routine on a zPattern that does not exist and with a
574*748a52cdSdrh ** zero iLimit is a no-op.
575*748a52cdSdrh **
576*748a52cdSdrh ** A quota group must exist with a non-zero iLimit prior to opening
577*748a52cdSdrh ** database connections if those connections are to participate in the
578*748a52cdSdrh ** quota group.  Creating a quota group does not effect database connections
579*748a52cdSdrh ** that are already open.
580*748a52cdSdrh */
581*748a52cdSdrh int sqlite3_quota_set(
582*748a52cdSdrh   const char *zPattern,          /* The filename pattern */
583*748a52cdSdrh   sqlite3_int64 iLimit,          /* New quota to set for this quota group */
584*748a52cdSdrh   void (*xCallback)(             /* Callback invoked when going over quota */
585*748a52cdSdrh      const char *zFilename,         /* Name of file whose size increases */
586*748a52cdSdrh      sqlite3_int64 *piLimit,        /* IN/OUT: The current limit */
587*748a52cdSdrh      sqlite3_int64 iSize,           /* Total size of all files in the group */
588*748a52cdSdrh      void *pArg                     /* Client data */
589*748a52cdSdrh   );
590*748a52cdSdrh   void *pArg                     /* client data passed thru to callback */
591*748a52cdSdrh ){
592*748a52cdSdrh   quotaGroup *pGroup;
593*748a52cdSdrh   quotaEnter();
594*748a52cdSdrh   pGroup = gQuota.pGroup;
595*748a52cdSdrh   while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){
596*748a52cdSdrh     pGroup = pGroup->pNext;
597*748a52cdSdrh   }
598*748a52cdSdrh   if( pGroup==0 && iLimit>0 ){
599*748a52cdSdrh     int nPattern = strlen(zPattern);
600*748a52cdSdrh     pGroup = sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
601*748a52cdSdrh     if( pGroup==0 ){
602*748a52cdSdrh       quotaLeave();
603*748a52cdSdrh       return SQLITE_NOMEM;
604*748a52cdSdrh     }
605*748a52cdSdrh     memset(pGroup, 0, sizeof(*pGroup));
606*748a52cdSdrh     pGroup->zPattern = (char*)&pGroup[1];
607*748a52cdSdrh     memcpy(pGroup->zPattern, zPattern, nPattern+1);
608*748a52cdSdrh     pGroup->pNext = gQuota.pGroup;
609*748a52cdSdrh     if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext;
610*748a52cdSdrh     pGroup->ppPrev = &gQuota.pNext;
611*748a52cdSdrh   }
612*748a52cdSdrh   pGroup->iLimit = iLimit;
613*748a52cdSdrh   pGroup->xCallback = xCallback;
614*748a52cdSdrh   pGroup->pArg = pArg;
615*748a52cdSdrh   quotaGroupDeref(pGroup);
616*748a52cdSdrh   quotaLeave();
617*748a52cdSdrh   return SQLITE_OK;
618*748a52cdSdrh }
619*748a52cdSdrh 
620*748a52cdSdrh 
621*748a52cdSdrh /***************************** Test Code ***********************************/
622*748a52cdSdrh #ifdef SQLITE_TEST
623*748a52cdSdrh #include <tcl.h>
624*748a52cdSdrh /*
625*748a52cdSdrh ** This routine registers the custom TCL commands defined in this
626*748a52cdSdrh ** module.  This should be the only procedure visible from outside
627*748a52cdSdrh ** of this module.
628*748a52cdSdrh */
629*748a52cdSdrh int Sqlitequota_Init(Tcl_Interp *interp){
630*748a52cdSdrh #ifdef SQLITE_ENABLE_ASYNCIO
631*748a52cdSdrh #endif  /* SQLITE_ENABLE_ASYNCIO */
632*748a52cdSdrh   return TCL_OK;
633*748a52cdSdrh }
634*748a52cdSdrh #endif
635