diff options
-rw-r--r-- | manifest | 16 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/test_quota.c | 206 | ||||
-rw-r--r-- | src/test_quota.h | 158 |
4 files changed, 354 insertions, 28 deletions
@@ -1,5 +1,5 @@ -C Remove\sunused\sfields\sfrom\sthe\sParse\sobject.\s\sDocumentation\sand\sformatting\nimprovements\son\sdata\sstructure\sdefinitions. -D 2011-11-29T15:40:32.491 +C Add\sstdio-like\sI/O\sinterfaces\sto\sthe\stest_quota\sVFS.\s\sThis\sis\sa\sprototype\nchange\sfor\sdiscussion\sand\sis\smostly\suntested.\s\sThis\sis\san\salternative\sto\nadding\sstdio-like\sI/O\sinterfaces\sin\sthe\score.\s\sThere\sis\sno\sguarantee\sthat\nthis\scode\swill\smake\sit\sinto\sthe\strunk.\s\sIf\sit\sdoes\sget\sto\strunk,\sthere\ncould\sbe\smany\schanges\sto\sthe\sinterface\sfirst. +D 2011-12-01T18:44:21.630 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -220,7 +220,8 @@ F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32 F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 -F src/test_quota.c a391c866217e92986c6f523f05b08aa6956c8419 +F src/test_quota.c f3ed8e130fff8e824a320a80668cfaffd6cb55ff +F src/test_quota.h 118dba604ae5b6903acdd40d2b94a1f319047612 F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f @@ -976,7 +977,10 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P b10d091ec02e94643e865743129e2a21147b3136 -R 7d0a9d11e01c28eaf1d831b1553005fb +P 431556cac0b2c86d7f6a60412ff1023feeaafedf +R e31672c1519f90c43111109b685a8ffb +T *branch * quota-stdio +T *sym-quota-stdio * +T -sym-trunk * U drh -Z fa710425b85d2a4b5e2c971fb970b087 +Z 0283eb6e7b14cfb0e1559cbc303ec8df diff --git a/manifest.uuid b/manifest.uuid index b014af186..f43d772bd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -431556cac0b2c86d7f6a60412ff1023feeaafedf
\ No newline at end of file +bd3ce723f1b5be52be46ede8614ca316f56e7e6f
\ No newline at end of file diff --git a/src/test_quota.c b/src/test_quota.c index 74d1a6d3b..1866e7998 100644 --- a/src/test_quota.c +++ b/src/test_quota.c @@ -27,7 +27,7 @@ ** files within the group is less than the new quota, then the write ** continues as if nothing had happened. */ -#include "sqlite3.h" +#include "test_quota.h" #include <string.h> #include <assert.h> @@ -111,6 +111,18 @@ struct quotaConn { /* The underlying VFS sqlite3_file is appended to this object */ }; +/* +** An instance of the following object records the state of an +** open file. This object is opaque to all users - the internal +** structure is only visible to the functions below. +*/ +struct quota_FILE { + FILE *f; /* Open stdio file pointer */ + sqlite3_int64 iOfst; /* Current offset into the file */ + quotaFile *pFile; /* The file record in the quota system */ +}; + + /************************* Global Variables **********************************/ /* ** All global variables used by this file are containing within the following @@ -313,11 +325,29 @@ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){ /* Find a file in a quota group and return a pointer to that file. ** Return NULL if the file is not in the group. */ -static quotaFile *quotaFindFile(quotaGroup *pGroup, const char *zName){ +static quotaFile *quotaFindFile( + quotaGroup *pGroup, /* Group in which to look for the file */ + const char *zName, /* Full pathname of the file */ + int createFlag /* Try to create the file if not found */ +){ quotaFile *pFile = pGroup->pFiles; while( pFile && strcmp(pFile->zFilename, zName)!=0 ){ pFile = pFile->pNext; } + if( pFile==0 && createFlag ){ + int nName = strlen(zName); + pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 ); + if( pFile ){ + memset(pFile, 0, sizeof(*pFile)); + pFile->zFilename = (char*)&pFile[1]; + memcpy(pFile->zFilename, zName, nName+1); + pFile->pNext = pGroup->pFiles; + if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext; + pFile->ppPrev = &pGroup->pFiles; + pGroup->pFiles = pFile; + pFile->pGroup = pGroup; + } + } return pFile; } @@ -364,25 +394,13 @@ static int quotaOpen( pSubOpen = quotaSubOpen(pConn); rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags); if( rc==SQLITE_OK ){ - pFile = quotaFindFile(pGroup, zName); + pFile = quotaFindFile(pGroup, zName, 1); if( pFile==0 ){ - int nName = strlen(zName); - pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 ); - if( pFile==0 ){ - quotaLeave(); - pSubOpen->pMethods->xClose(pSubOpen); - return SQLITE_NOMEM; - } - memset(pFile, 0, sizeof(*pFile)); - pFile->zFilename = (char*)&pFile[1]; - memcpy(pFile->zFilename, zName, nName+1); - pFile->pNext = pGroup->pFiles; - if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext; - pFile->ppPrev = &pGroup->pFiles; - pGroup->pFiles = pFile; - pFile->pGroup = pGroup; - pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0; + quotaLeave(); + pSubOpen->pMethods->xClose(pSubOpen); + return SQLITE_NOMEM; } + pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0; pFile->nRef++; pQuotaOpen->pFile = pFile; if( pSubOpen->pMethods->iVersion==1 ){ @@ -423,7 +441,7 @@ static int quotaDelete( quotaEnter(); pGroup = quotaGroupFind(zName); if( pGroup ){ - pFile = quotaFindFile(pGroup, zName); + pFile = quotaFindFile(pGroup, zName, 0); if( pFile ){ if( pFile->nRef ){ pFile->deleteOnClose = 1; @@ -823,7 +841,7 @@ int sqlite3_quota_file(const char *zFilename){ quotaEnter(); pGroup = quotaGroupFind(zFull); if( pGroup ){ - pFile = quotaFindFile(pGroup, zFull); + pFile = quotaFindFile(pGroup, zFull, 0); if( pFile ) quotaRemoveFile(pFile); } quotaLeave(); @@ -832,6 +850,152 @@ int sqlite3_quota_file(const char *zFilename){ return rc; } +/* +** Open a potentially quotaed file for I/O. +*/ +quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){ + quota_FILE *p = 0; + char *zFull = 0; + int rc; + quotaGroup *pGroup; + quotaFile *pFile; + + p = sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1); + if( p==0 ) return 0; + zFull = (char*)&p[1]; + rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, + gQuota.sThisVfs.mxPathname+1, zFull); + if( rc ) goto quota_fopen_error; + p = sqlite3_malloc(sizeof(*p)); + if( p==0 ) goto quota_fopen_error; + memset(p, 0, sizeof(*p)); + p->f = fopen(zFull, zMode); + if( p->f==0 ) goto quota_fopen_error; + quotaEnter(); + pGroup = quotaGroupFind(zFull); + if( pGroup ){ + pFile = quotaFindFile(pGroup, zFull, 1); + if( pFile==0 ){ + quotaLeave(); + goto quota_fopen_error; + } + pFile->nRef++; + p->pFile = pFile; + } + quotaLeave(); + sqlite3_free(zFull); + return p; + +quota_fopen_error: + sqlite3_free(zFull); + if( p && p->f ) fclose(p->f); + sqlite3_free(p); + return 0; +} + +/* +** Read content from a quota_FILE +*/ +size_t sqlite3_quota_fread( + void *pBuf, /* Store the content here */ + size_t size, /* Size of each element */ + size_t nmemb, /* Number of elements to read */ + quota_FILE *p /* Read from this quota_FILE object */ +){ + return fread(pBuf, size, nmemb, p->f); +} + +/* +** Write content into a quota_FILE. Invoke the quota callback and block +** the write if we exceed quota. +*/ +size_t sqlite3_quota_fwrite( + void *pBuf, /* Take content to write from here */ + size_t size, /* Size of each element */ + size_t nmemb, /* Number of elements */ + quota_FILE *p /* Write to this quota_FILE objecct */ +){ + sqlite3_int64 iOfst; + sqlite3_int64 iEnd; + sqlite3_int64 szNew; + quotaFile *pFile; + + iOfst = ftell(p->f); + iEnd = iOfst + size*nmemb; + pFile = p->pFile; + if( pFile->iSize<iEnd ){ + quotaGroup *pGroup = pFile->pGroup; + quotaEnter(); + szNew = pGroup->iSize - pFile->iSize + iEnd; + if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ + if( pGroup->xCallback ){ + pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, + pGroup->pArg); + } + if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ + iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize; + nmemb = (iEnd - iOfst)/size; + iEnd = iOfst + size*nmemb; + szNew = pGroup->iSize - pFile->iSize + iEnd; + } + } + pGroup->iSize = szNew; + pFile->iSize = iEnd; + quotaLeave(); + } + return fwrite(pBuf, size, nmemb, p->f); +} + +/* +** Close an open quota_FILE stream. +*/ +int sqlite3_quota_fclose(quota_FILE *p){ + int rc; + quotaFile *pFile; + rc = fclose(p->f); + pFile = p->pFile; + quotaEnter(); + pFile->nRef--; + if( pFile->nRef==0 ){ + quotaGroup *pGroup = pFile->pGroup; + if( pFile->deleteOnClose ) quotaRemoveFile(pFile); + quotaGroupDeref(pGroup); + } + quotaLeave(); + sqlite3_free(p); + return rc; +} + +/* +** Seek on a quota_FILE stream. +*/ +int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){ + return fseek(p->f, offset, whence); +} + +/* +** rewind a quota_FILE stream. +*/ +void sqlite3_quota_rewind(quota_FILE *p){ + rewind(p->f); +} + +/* +** Tell the current location of a quota_FILE stream. +*/ +long sqlite3_quota_ftell(quota_FILE *p){ + return ftell(p->f); +} + +/* +** Remove a file. Update quotas accordingly. +*/ +int sqlite3_quota_remove(const char *zFilename){ + int rc = remove(zFilename); + sqlite3_quota_file(zFilename); + return rc; +} + /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST diff --git a/src/test_quota.h b/src/test_quota.h new file mode 100644 index 000000000..df78e7eb7 --- /dev/null +++ b/src/test_quota.h @@ -0,0 +1,158 @@ +/* +** 2011 December 1 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the interface definition for the quota a VFS shim. +** +** This particular shim enforces a quota system on files. One or more +** database files are in a "quota group" that is defined by a GLOB +** pattern. A quota is set for the combined size of all files in the +** the group. A quota of zero means "no limit". If the total size +** of all files in the quota group is greater than the limit, then +** write requests that attempt to enlarge a file fail with SQLITE_FULL. +** +** However, before returning SQLITE_FULL, the write requests invoke +** a callback function that is configurable for each quota group. +** This callback has the opportunity to enlarge the quota. If the +** callback does enlarge the quota such that the total size of all +** files within the group is less than the new quota, then the write +** continues as if nothing had happened. +*/ +#ifndef _QUOTA_H_ +#include "sqlite3.h" +#include <stdio.h> + +/* +** Initialize the quota VFS shim. Use the VFS named zOrigVfsName +** as the VFS that does the actual work. Use the default if +** zOrigVfsName==NULL. +** +** The quota VFS shim is named "quota". It will become the default +** VFS if makeDefault is non-zero. +** +** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once +** during start-up. +*/ +int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault); + +/* +** Shutdown the quota system. +** +** All SQLite database connections must be closed before calling this +** routine. +** +** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while +** shutting down in order to free all remaining quota groups. +*/ +int sqlite3_quota_shutdown(void); + +/* +** Create or destroy a quota group. +** +** The quota group is defined by the zPattern. When calling this routine +** with a zPattern for a quota group that already exists, this routine +** merely updates the iLimit, xCallback, and pArg values for that quota +** group. If zPattern is new, then a new quota group is created. +** +** The zPattern is always compared against the full pathname of the file. +** Even if APIs are called with relative pathnames, SQLite converts the +** name to a full pathname before comparing it against zPattern. zPattern +** is a standard glob pattern with the following matching rules: +** +** '*' Matches any sequence of zero or more characters. +** +** '?' Matches exactly one character. +** +** [...] Matches one character from the enclosed list of +** characters. +** +** [^...] Matches one character not in the enclosed list. +** +** Note that, unlike unix shell globbing, the directory separator "/" +** can match a wildcard. So, for example, the pattern "/abc/xyz/" "*" +** matches any files anywhere in the directory hierarchy beneath +** /abc/xyz +** +** If the iLimit for a quota group is set to zero, then the quota group +** is disabled and will be deleted when the last database connection using +** the quota group is closed. +** +** Calling this routine on a zPattern that does not exist and with a +** zero iLimit is a no-op. +** +** A quota group must exist with a non-zero iLimit prior to opening +** database connections if those connections are to participate in the +** quota group. Creating a quota group does not affect database connections +** that are already open. +*/ +int sqlite3_quota_set( + const char *zPattern, /* The filename pattern */ + sqlite3_int64 iLimit, /* New quota to set for this quota group */ + void (*xCallback)( /* Callback invoked when going over quota */ + const char *zFilename, /* Name of file whose size increases */ + sqlite3_int64 *piLimit, /* IN/OUT: The current limit */ + sqlite3_int64 iSize, /* Total size of all files in the group */ + void *pArg /* Client data */ + ), + void *pArg, /* client data passed thru to callback */ + void (*xDestroy)(void*) /* Optional destructor for pArg */ +); + +/* +** Bring the named file under quota management. Or if it is already under +** management, update its size. +*/ +int sqlite3_quota_file(const char *zFilename); + +/* +** The following object serves the same role as FILE in the standard C +** library. It represents an open connection to a file on disk for I/O. +*/ +typedef struct quota_FILE quota_FILE; + +/* +** Create a new quota_FILE object used to read and/or write to the +** file zFilename. The zMode parameter is as with standard library zMode. +*/ +quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode); + +/* +** Perform I/O against a quota_FILE object. When doing writes, the +** quota mechanism may result in a short write, in order to prevent +** the sum of sizes of all files from going over quota. +*/ +size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*); +size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*); + +/* +** Close a quota_FILE object and free all associated resources. The +** file remains under quota management. +*/ +int sqlite3_quota_fclose(quota_FILE*); + +/* +** Move the read/write pointer for a quota_FILE object. Or tell the +** current location of the read/write pointer. +*/ +int sqlite3_quota_fseek(quota_FILE*, long, int); +void sqlite3_quota_rewind(quota_FILE*); +long sqlite3_quota_ftell(quota_FILE*); + +/* +** Delete a file from the disk. If that file is under quota management, +** then adjust quotas accordingly. +** +** The file being deleted must not be open for reading or writing or as +** a database when it is deleted. +*/ +int sqlite3_quota_remove(const char *zFilename); + +#endif /* _QUOTA_H_ */ |