author | Jan Varga <jan.varga@gmail.com> |
Mon, 17 Dec 2012 20:25:10 +0100 | |
changeset 125404 | 6328b64258aeb42d066414861ccc0e389846f4ac |
parent 125403 | a09a149d8a44af3f18d84cdfcd2aacf7db42f7df |
child 125405 | 249e7db380b0e4a2da0df37aa34844c364fd42ee |
push id | 2151 |
push user | lsblakk@mozilla.com |
push date | Tue, 19 Feb 2013 18:06:57 +0000 |
treeherder | mozilla-beta@4952e88741ec [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bent, asuth, vladan |
bugs | 787804 |
milestone | 20.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/db/sqlite3/README.MOZILLA +++ b/db/sqlite3/README.MOZILLA @@ -4,18 +4,18 @@ This is sqlite 3.7.14.1 See http://www.sqlite.org/ for more info. We have a mozilla-specific Makefile.in in src/ (normally no Makefile.in there) that we use to build. To move to a new version: -Copy the sqlite3.h and sqlite3.c files from the amalgamation of sqlite. Also, -copy test_quota.h and test_quota.c from the full source package. +Copy the sqlite3.h and sqlite3.c files from the amalgamation of sqlite. +(We no longer use test_quota.h and test_quota.c) Be sure to update SQLITE_VERSION accordingly in $(topsrcdir)/configure.in as well as the version number at the top of this file. -- Paul O’Shannessy <paul@oshannessy.com>, 01/2011 We are using an experimental quota management feature included in test_quota.c. This file is not compiled into mozsqlite, but instead included directly into
--- a/db/sqlite3/src/sqlite.def +++ b/db/sqlite3/src/sqlite.def @@ -127,16 +127,17 @@ EXPORTS sqlite3_stmt_readonly sqlite3_stmt_status sqlite3_thread_cleanup sqlite3_total_changes sqlite3_trace sqlite3_transfer_bindings sqlite3_unlock_notify sqlite3_update_hook + sqlite3_uri_parameter sqlite3_user_data sqlite3_value_blob sqlite3_value_bytes sqlite3_value_bytes16 sqlite3_value_double sqlite3_value_int sqlite3_value_int64 sqlite3_value_numeric_type
deleted file mode 100644 --- a/db/sqlite3/src/test_quota.c +++ /dev/null @@ -1,2001 +0,0 @@ -/* -** 2010 September 31 -** -** 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 a VFS "shim" - a layer that sits in between the -** pager and the real VFS. -** -** 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. -*/ -#include "test_quota.h" -#include <string.h> -#include <assert.h> - -/* -** For an build without mutexes, no-op the mutex calls. -*/ -#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 -#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) -#define sqlite3_mutex_free(X) -#define sqlite3_mutex_enter(X) -#define sqlite3_mutex_try(X) SQLITE_OK -#define sqlite3_mutex_leave(X) -#define sqlite3_mutex_held(X) ((void)(X),1) -#define sqlite3_mutex_notheld(X) ((void)(X),1) -#endif /* SQLITE_THREADSAFE==0 */ - - -/* -** Figure out if we are dealing with Unix, Windows, or some other -** operating system. After the following block of preprocess macros, -** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER -** will defined to either 1 or 0. One of the four will be 1. The other -** three will be 0. -*/ -#if defined(SQLITE_OS_OTHER) -# if SQLITE_OS_OTHER==1 -# undef SQLITE_OS_UNIX -# define SQLITE_OS_UNIX 0 -# undef SQLITE_OS_WIN -# define SQLITE_OS_WIN 0 -# else -# undef SQLITE_OS_OTHER -# endif -#endif -#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) -# define SQLITE_OS_OTHER 0 -# ifndef SQLITE_OS_WIN -# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) \ - || defined(__MINGW32__) || defined(__BORLANDC__) -# define SQLITE_OS_WIN 1 -# define SQLITE_OS_UNIX 0 -# else -# define SQLITE_OS_WIN 0 -# define SQLITE_OS_UNIX 1 -# endif -# else -# define SQLITE_OS_UNIX 0 -# endif -#else -# ifndef SQLITE_OS_WIN -# define SQLITE_OS_WIN 0 -# endif -#endif - -#if SQLITE_OS_UNIX -# include <unistd.h> -#endif -#if SQLITE_OS_WIN -# include <windows.h> -# include <io.h> -#endif - - -/************************ Object Definitions ******************************/ - -/* Forward declaration of all object types */ -typedef struct quotaGroup quotaGroup; -typedef struct quotaConn quotaConn; -typedef struct quotaFile quotaFile; - -/* -** A "quota group" is a collection of files whose collective size we want -** to limit. Each quota group is defined by a GLOB pattern. -** -** There is an instance of the following object for each defined quota -** group. This object records the GLOB pattern that defines which files -** belong to the quota group. The object also remembers the size limit -** for the group (the quota) and the callback to be invoked when the -** sum of the sizes of the files within the group goes over the limit. -** -** A quota group must be established (using sqlite3_quota_set(...)) -** prior to opening any of the database connections that access files -** within the quota group. -*/ -struct quotaGroup { - const char *zPattern; /* Filename pattern to be quotaed */ - sqlite3_int64 iLimit; /* Upper bound on total file size */ - sqlite3_int64 iSize; /* Current size of all files */ - 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; /* Third argument to the xCallback() */ - void (*xDestroy)(void*); /* Optional destructor for pArg */ - quotaGroup *pNext, **ppPrev; /* Doubly linked list of all quota objects */ - quotaFile *pFiles; /* Files within this group */ -}; - -/* -** An instance of this structure represents a single file that is part -** of a quota group. A single file can be opened multiple times. In -** order keep multiple openings of the same file from causing the size -** of the file to count against the quota multiple times, each file -** has a unique instance of this object and multiple open connections -** to the same file each point to a single instance of this object. -*/ -struct quotaFile { - char *zFilename; /* Name of this file */ - quotaGroup *pGroup; /* Quota group to which this file belongs */ - sqlite3_int64 iSize; /* Current size of this file */ - int nRef; /* Number of times this file is open */ - int deleteOnClose; /* True to delete this file when it closes */ - quotaFile *pNext, **ppPrev; /* Linked list of files in the same group */ -}; - -/* -** An instance of the following object represents each open connection -** to a file that participates in quota tracking. This object is a -** subclass of sqlite3_file. The sqlite3_file object for the underlying -** VFS is appended to this structure. -*/ -struct quotaConn { - sqlite3_file base; /* Base class - must be first */ - quotaFile *pFile; /* The underlying file */ - /* 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 */ -#if SQLITE_OS_WIN - char *zMbcsName; /* Full MBCS pathname of the file */ -#endif -}; - - -/************************* Global Variables **********************************/ -/* -** All global variables used by this file are containing within the following -** gQuota structure. -*/ -static struct { - /* The pOrigVfs is the real, original underlying VFS implementation. - ** Most operations pass-through to the real VFS. This value is read-only - ** during operation. It is only modified at start-time and thus does not - ** require a mutex. - */ - sqlite3_vfs *pOrigVfs; - - /* The sThisVfs is the VFS structure used by this shim. It is initialized - ** at start-time and thus does not require a mutex - */ - sqlite3_vfs sThisVfs; - - /* The sIoMethods defines the methods used by sqlite3_file objects - ** associated with this shim. It is initialized at start-time and does - ** not require a mutex. - ** - ** When the underlying VFS is called to open a file, it might return - ** either a version 1 or a version 2 sqlite3_file object. This shim - ** has to create a wrapper sqlite3_file of the same version. Hence - ** there are two I/O method structures, one for version 1 and the other - ** for version 2. - */ - sqlite3_io_methods sIoMethodsV1; - sqlite3_io_methods sIoMethodsV2; - - /* True when this shim as been initialized. - */ - int isInitialized; - - /* For run-time access any of the other global data structures in this - ** shim, the following mutex must be held. - */ - sqlite3_mutex *pMutex; - - /* List of quotaGroup objects. - */ - quotaGroup *pGroup; - -} gQuota; - -/************************* Utility Routines *********************************/ -/* -** Acquire and release the mutex used to serialize access to the -** list of quotaGroups. -*/ -static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); } -static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); } - -/* Count the number of open files in a quotaGroup -*/ -static int quotaGroupOpenFileCount(quotaGroup *pGroup){ - int N = 0; - quotaFile *pFile = pGroup->pFiles; - while( pFile ){ - if( pFile->nRef ) N++; - pFile = pFile->pNext; - } - return N; -} - -/* Remove a file from a quota group. -*/ -static void quotaRemoveFile(quotaFile *pFile){ - quotaGroup *pGroup = pFile->pGroup; - pGroup->iSize -= pFile->iSize; - *pFile->ppPrev = pFile->pNext; - if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev; - sqlite3_free(pFile); -} - -/* Remove all files from a quota group. It is always the case that -** all files will be closed when this routine is called. -*/ -static void quotaRemoveAllFiles(quotaGroup *pGroup){ - while( pGroup->pFiles ){ - assert( pGroup->pFiles->nRef==0 ); - quotaRemoveFile(pGroup->pFiles); - } -} - - -/* If the reference count and threshold for a quotaGroup are both -** zero, then destroy the quotaGroup. -*/ -static void quotaGroupDeref(quotaGroup *pGroup){ - if( pGroup->iLimit==0 && quotaGroupOpenFileCount(pGroup)==0 ){ - quotaRemoveAllFiles(pGroup); - *pGroup->ppPrev = pGroup->pNext; - if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev; - if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg); - sqlite3_free(pGroup); - } -} - -/* -** Return TRUE if string z matches glob pattern zGlob. -** -** Globbing 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. -** -** / Matches "/" or "\\" -** -*/ -static int quotaStrglob(const char *zGlob, const char *z){ - int c, c2, cx; - int invert; - int seen; - - while( (c = (*(zGlob++)))!=0 ){ - if( c=='*' ){ - while( (c=(*(zGlob++))) == '*' || c=='?' ){ - if( c=='?' && (*(z++))==0 ) return 0; - } - if( c==0 ){ - return 1; - }else if( c=='[' ){ - while( *z && quotaStrglob(zGlob-1,z)==0 ){ - z++; - } - return (*z)!=0; - } - cx = (c=='/') ? '\\' : c; - while( (c2 = (*(z++)))!=0 ){ - while( c2!=c && c2!=cx ){ - c2 = *(z++); - if( c2==0 ) return 0; - } - if( quotaStrglob(zGlob,z) ) return 1; - } - return 0; - }else if( c=='?' ){ - if( (*(z++))==0 ) return 0; - }else if( c=='[' ){ - int prior_c = 0; - seen = 0; - invert = 0; - c = *(z++); - if( c==0 ) return 0; - c2 = *(zGlob++); - if( c2=='^' ){ - invert = 1; - c2 = *(zGlob++); - } - if( c2==']' ){ - if( c==']' ) seen = 1; - c2 = *(zGlob++); - } - while( c2 && c2!=']' ){ - if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ - c2 = *(zGlob++); - if( c>=prior_c && c<=c2 ) seen = 1; - prior_c = 0; - }else{ - if( c==c2 ){ - seen = 1; - } - prior_c = c2; - } - c2 = *(zGlob++); - } - if( c2==0 || (seen ^ invert)==0 ) return 0; - }else if( c=='/' ){ - if( z[0]!='/' && z[0]!='\\' ) return 0; - z++; - }else{ - if( c!=(*(z++)) ) return 0; - } - } - return *z==0; -} - - -/* Find a quotaGroup given the filename. -** -** Return a pointer to the quotaGroup object. Return NULL if not found. -*/ -static quotaGroup *quotaGroupFind(const char *zFilename){ - quotaGroup *p; - for(p=gQuota.pGroup; p && quotaStrglob(p->zPattern, zFilename)==0; - p=p->pNext){} - return p; -} - -/* Translate an sqlite3_file* that is really a quotaConn* into -** the sqlite3_file* for the underlying original VFS. -*/ -static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){ - quotaConn *p = (quotaConn*)pConn; - return (sqlite3_file*)&p[1]; -} - -/* 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, /* 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 = (int)(strlen(zName) & 0x3fffffff); - 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; -} -/* -** Translate UTF8 to MBCS for use in fopen() calls. Return a pointer to the -** translated text.. Call quota_mbcs_free() to deallocate any memory -** used to store the returned pointer when done. -*/ -static char *quota_utf8_to_mbcs(const char *zUtf8){ -#if SQLITE_OS_WIN - size_t n; /* Bytes in zUtf8 */ - int nWide; /* number of UTF-16 characters */ - int nMbcs; /* Bytes of MBCS */ - LPWSTR zTmpWide; /* The UTF16 text */ - char *zMbcs; /* The MBCS text */ - int codepage; /* Code page used by fopen() */ - - n = strlen(zUtf8); - nWide = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0); - if( nWide==0 ) return 0; - zTmpWide = (LPWSTR)sqlite3_malloc( (nWide+1)*sizeof(zTmpWide[0]) ); - if( zTmpWide==0 ) return 0; - MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zTmpWide, nWide); - codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; - nMbcs = WideCharToMultiByte(codepage, 0, zTmpWide, nWide, 0, 0, 0, 0); - zMbcs = nMbcs ? (char*)sqlite3_malloc( nMbcs+1 ) : 0; - if( zMbcs ){ - WideCharToMultiByte(codepage, 0, zTmpWide, nWide, zMbcs, nMbcs, 0, 0); - } - sqlite3_free(zTmpWide); - return zMbcs; -#else - return (char*)zUtf8; /* No-op on unix */ -#endif -} - -/* -** Deallocate any memory allocated by quota_utf8_to_mbcs(). -*/ -static void quota_mbcs_free(char *zOld){ -#if SQLITE_OS_WIN - sqlite3_free(zOld); -#else - /* No-op on unix */ -#endif -} - -/************************* VFS Method Wrappers *****************************/ -/* -** This is the xOpen method used for the "quota" VFS. -** -** Most of the work is done by the underlying original VFS. This method -** simply links the new file into the appropriate quota group if it is a -** file that needs to be tracked. -*/ -static int quotaOpen( - sqlite3_vfs *pVfs, /* The quota VFS */ - const char *zName, /* Name of file to be opened */ - sqlite3_file *pConn, /* Fill in this file descriptor */ - int flags, /* Flags to control the opening */ - int *pOutFlags /* Flags showing results of opening */ -){ - int rc; /* Result code */ - quotaConn *pQuotaOpen; /* The new quota file descriptor */ - quotaFile *pFile; /* Corresponding quotaFile obj */ - quotaGroup *pGroup; /* The group file belongs to */ - sqlite3_file *pSubOpen; /* Real file descriptor */ - sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */ - - /* If the file is not a main database file or a WAL, then use the - ** normal xOpen method. - */ - if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){ - return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags); - } - - /* If the name of the file does not match any quota group, then - ** use the normal xOpen method. - */ - quotaEnter(); - pGroup = quotaGroupFind(zName); - if( pGroup==0 ){ - rc = pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags); - }else{ - /* If we get to this point, it means the file needs to be quota tracked. - */ - pQuotaOpen = (quotaConn*)pConn; - pSubOpen = quotaSubOpen(pConn); - rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags); - if( rc==SQLITE_OK ){ - pFile = quotaFindFile(pGroup, zName, 1); - if( pFile==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 ){ - pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1; - }else{ - pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2; - } - } - } - quotaLeave(); - return rc; -} - -/* -** This is the xDelete method used for the "quota" VFS. -** -** If the file being deleted is part of the quota group, then reduce -** the size of the quota group accordingly. And remove the file from -** the set of files in the quota group. -*/ -static int quotaDelete( - sqlite3_vfs *pVfs, /* The quota VFS */ - const char *zName, /* Name of file to be deleted */ - int syncDir /* Do a directory sync after deleting */ -){ - int rc; /* Result code */ - quotaFile *pFile; /* Files in the quota */ - quotaGroup *pGroup; /* The group file belongs to */ - sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */ - - /* Do the actual file delete */ - rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir); - - /* If the file just deleted is a member of a quota group, then remove - ** it from that quota group. - */ - if( rc==SQLITE_OK ){ - quotaEnter(); - pGroup = quotaGroupFind(zName); - if( pGroup ){ - pFile = quotaFindFile(pGroup, zName, 0); - if( pFile ){ - if( pFile->nRef ){ - pFile->deleteOnClose = 1; - }else{ - quotaRemoveFile(pFile); - quotaGroupDeref(pGroup); - } - } - } - quotaLeave(); - } - return rc; -} - - -/************************ I/O Method Wrappers *******************************/ - -/* xClose requests get passed through to the original VFS. But we -** also have to unlink the quotaConn from the quotaFile and quotaGroup. -** The quotaFile and/or quotaGroup are freed if they are no longer in use. -*/ -static int quotaClose(sqlite3_file *pConn){ - quotaConn *p = (quotaConn*)pConn; - quotaFile *pFile = p->pFile; - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - int rc; - rc = pSubOpen->pMethods->xClose(pSubOpen); - quotaEnter(); - pFile->nRef--; - if( pFile->nRef==0 ){ - quotaGroup *pGroup = pFile->pGroup; - if( pFile->deleteOnClose ){ - gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); - quotaRemoveFile(pFile); - } - quotaGroupDeref(pGroup); - } - quotaLeave(); - return rc; -} - -/* Pass xRead requests directory thru to the original VFS without -** further processing. -*/ -static int quotaRead( - sqlite3_file *pConn, - void *pBuf, - int iAmt, - sqlite3_int64 iOfst -){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst); -} - -/* Check xWrite requests to see if they expand the file. If they do, -** the perform a quota check before passing them through to the -** original VFS. -*/ -static int quotaWrite( - sqlite3_file *pConn, - const void *pBuf, - int iAmt, - sqlite3_int64 iOfst -){ - quotaConn *p = (quotaConn*)pConn; - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - sqlite3_int64 iEnd = iOfst+iAmt; - quotaGroup *pGroup; - quotaFile *pFile = p->pFile; - sqlite3_int64 szNew; - - if( pFile->iSize<iEnd ){ - 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 ){ - quotaLeave(); - return SQLITE_FULL; - } - } - pGroup->iSize = szNew; - pFile->iSize = iEnd; - quotaLeave(); - } - return pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst); -} - -/* Pass xTruncate requests thru to the original VFS. If the -** success, update the file size. -*/ -static int quotaTruncate(sqlite3_file *pConn, sqlite3_int64 size){ - quotaConn *p = (quotaConn*)pConn; - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - int rc = pSubOpen->pMethods->xTruncate(pSubOpen, size); - quotaFile *pFile = p->pFile; - quotaGroup *pGroup; - if( rc==SQLITE_OK ){ - quotaEnter(); - pGroup = pFile->pGroup; - pGroup->iSize -= pFile->iSize; - pFile->iSize = size; - pGroup->iSize += size; - quotaLeave(); - } - return rc; -} - -/* Pass xSync requests through to the original VFS without change -*/ -static int quotaSync(sqlite3_file *pConn, int flags){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xSync(pSubOpen, flags); -} - -/* Pass xFileSize requests through to the original VFS but then -** update the quotaGroup with the new size before returning. -*/ -static int quotaFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ - quotaConn *p = (quotaConn*)pConn; - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - quotaFile *pFile = p->pFile; - quotaGroup *pGroup; - sqlite3_int64 sz; - int rc; - - rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); - if( rc==SQLITE_OK ){ - quotaEnter(); - pGroup = pFile->pGroup; - pGroup->iSize -= pFile->iSize; - pFile->iSize = sz; - pGroup->iSize += sz; - quotaLeave(); - *pSize = sz; - } - return rc; -} - -/* Pass xLock requests through to the original VFS unchanged. -*/ -static int quotaLock(sqlite3_file *pConn, int lock){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xLock(pSubOpen, lock); -} - -/* Pass xUnlock requests through to the original VFS unchanged. -*/ -static int quotaUnlock(sqlite3_file *pConn, int lock){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xUnlock(pSubOpen, lock); -} - -/* Pass xCheckReservedLock requests through to the original VFS unchanged. -*/ -static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); -} - -/* Pass xFileControl requests through to the original VFS unchanged. -*/ -static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); -#if defined(SQLITE_FCNTL_VFSNAME) - if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){ - *(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg); - } -#endif - return rc; -} - -/* Pass xSectorSize requests through to the original VFS unchanged. -*/ -static int quotaSectorSize(sqlite3_file *pConn){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xSectorSize(pSubOpen); -} - -/* Pass xDeviceCharacteristics requests through to the original VFS unchanged. -*/ -static int quotaDeviceCharacteristics(sqlite3_file *pConn){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen); -} - -/* Pass xShmMap requests through to the original VFS unchanged. -*/ -static int quotaShmMap( - sqlite3_file *pConn, /* Handle open on database file */ - int iRegion, /* Region to retrieve */ - int szRegion, /* Size of regions */ - int bExtend, /* True to extend file if necessary */ - void volatile **pp /* OUT: Mapped memory */ -){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp); -} - -/* Pass xShmLock requests through to the original VFS unchanged. -*/ -static int quotaShmLock( - sqlite3_file *pConn, /* Database file holding the shared memory */ - int ofst, /* First lock to acquire or release */ - int n, /* Number of locks to acquire or release */ - int flags /* What to do with the lock */ -){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags); -} - -/* Pass xShmBarrier requests through to the original VFS unchanged. -*/ -static void quotaShmBarrier(sqlite3_file *pConn){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - pSubOpen->pMethods->xShmBarrier(pSubOpen); -} - -/* Pass xShmUnmap requests through to the original VFS unchanged. -*/ -static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){ - sqlite3_file *pSubOpen = quotaSubOpen(pConn); - return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); -} - -/************************** Public Interfaces *****************************/ -/* -** 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){ - sqlite3_vfs *pOrigVfs; - if( gQuota.isInitialized ) return SQLITE_MISUSE; - pOrigVfs = sqlite3_vfs_find(zOrigVfsName); - if( pOrigVfs==0 ) return SQLITE_ERROR; - assert( pOrigVfs!=&gQuota.sThisVfs ); - gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( !gQuota.pMutex ){ - return SQLITE_NOMEM; - } - gQuota.isInitialized = 1; - gQuota.pOrigVfs = pOrigVfs; - gQuota.sThisVfs = *pOrigVfs; - gQuota.sThisVfs.xOpen = quotaOpen; - gQuota.sThisVfs.xDelete = quotaDelete; - gQuota.sThisVfs.szOsFile += sizeof(quotaConn); - gQuota.sThisVfs.zName = "quota"; - gQuota.sIoMethodsV1.iVersion = 1; - gQuota.sIoMethodsV1.xClose = quotaClose; - gQuota.sIoMethodsV1.xRead = quotaRead; - gQuota.sIoMethodsV1.xWrite = quotaWrite; - gQuota.sIoMethodsV1.xTruncate = quotaTruncate; - gQuota.sIoMethodsV1.xSync = quotaSync; - gQuota.sIoMethodsV1.xFileSize = quotaFileSize; - gQuota.sIoMethodsV1.xLock = quotaLock; - gQuota.sIoMethodsV1.xUnlock = quotaUnlock; - gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock; - gQuota.sIoMethodsV1.xFileControl = quotaFileControl; - gQuota.sIoMethodsV1.xSectorSize = quotaSectorSize; - gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics; - gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1; - gQuota.sIoMethodsV2.iVersion = 2; - gQuota.sIoMethodsV2.xShmMap = quotaShmMap; - gQuota.sIoMethodsV2.xShmLock = quotaShmLock; - gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier; - gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap; - sqlite3_vfs_register(&gQuota.sThisVfs, makeDefault); - return SQLITE_OK; -} - -/* -** 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){ - quotaGroup *pGroup; - if( gQuota.isInitialized==0 ) return SQLITE_MISUSE; - for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){ - if( quotaGroupOpenFileCount(pGroup)>0 ) return SQLITE_MISUSE; - } - while( gQuota.pGroup ){ - pGroup = gQuota.pGroup; - gQuota.pGroup = pGroup->pNext; - pGroup->iLimit = 0; - assert( quotaGroupOpenFileCount(pGroup)==0 ); - quotaGroupDeref(pGroup); - } - gQuota.isInitialized = 0; - sqlite3_mutex_free(gQuota.pMutex); - sqlite3_vfs_unregister(&gQuota.sThisVfs); - memset(&gQuota, 0, sizeof(gQuota)); - return SQLITE_OK; -} - -/* -** 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. -** -** 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 */ -){ - quotaGroup *pGroup; - quotaEnter(); - pGroup = gQuota.pGroup; - while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){ - pGroup = pGroup->pNext; - } - if( pGroup==0 ){ - int nPattern = (int)(strlen(zPattern) & 0x3fffffff); - if( iLimit<=0 ){ - quotaLeave(); - return SQLITE_OK; - } - pGroup = (quotaGroup *)sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 ); - if( pGroup==0 ){ - quotaLeave(); - return SQLITE_NOMEM; - } - memset(pGroup, 0, sizeof(*pGroup)); - pGroup->zPattern = (char*)&pGroup[1]; - memcpy((char *)pGroup->zPattern, zPattern, nPattern+1); - if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext; - pGroup->pNext = gQuota.pGroup; - pGroup->ppPrev = &gQuota.pGroup; - gQuota.pGroup = pGroup; - } - pGroup->iLimit = iLimit; - pGroup->xCallback = xCallback; - if( pGroup->xDestroy && pGroup->pArg!=pArg ){ - pGroup->xDestroy(pGroup->pArg); - } - pGroup->pArg = pArg; - pGroup->xDestroy = xDestroy; - quotaGroupDeref(pGroup); - quotaLeave(); - return SQLITE_OK; -} - -/* -** Bring the named file under quota management. Or if it is already under -** management, update its size. -*/ -int sqlite3_quota_file(const char *zFilename){ - char *zFull; - sqlite3_file *fd; - int rc; - int outFlags = 0; - sqlite3_int64 iSize; - int nAlloc = gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+2; - - /* Allocate space for a file-handle and the full path for file zFilename */ - fd = (sqlite3_file *)sqlite3_malloc(nAlloc); - if( fd==0 ){ - rc = SQLITE_NOMEM; - }else{ - zFull = &((char *)fd)[gQuota.sThisVfs.szOsFile]; - rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, - gQuota.sThisVfs.mxPathname+1, zFull); - } - - if( rc==SQLITE_OK ){ - zFull[strlen(zFull)+1] = '\0'; - rc = quotaOpen(&gQuota.sThisVfs, zFull, fd, - SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags); - if( rc==SQLITE_OK ){ - fd->pMethods->xFileSize(fd, &iSize); - fd->pMethods->xClose(fd); - }else if( rc==SQLITE_CANTOPEN ){ - quotaGroup *pGroup; - quotaFile *pFile; - quotaEnter(); - pGroup = quotaGroupFind(zFull); - if( pGroup ){ - pFile = quotaFindFile(pGroup, zFull, 0); - if( pFile ) quotaRemoveFile(pFile); - } - quotaLeave(); - } - } - - sqlite3_free(fd); - 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; - char *zFullTranslated = 0; - int rc; - quotaGroup *pGroup; - quotaFile *pFile; - - zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1); - if( zFull==0 ) return 0; - rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, - gQuota.sThisVfs.mxPathname+1, zFull); - if( rc ) goto quota_fopen_error; - p = (quota_FILE*)sqlite3_malloc(sizeof(*p)); - if( p==0 ) goto quota_fopen_error; - memset(p, 0, sizeof(*p)); - zFullTranslated = quota_utf8_to_mbcs(zFull); - if( zFullTranslated==0 ) goto quota_fopen_error; - p->f = fopen(zFullTranslated, 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); -#if SQLITE_OS_WIN - p->zMbcsName = zFullTranslated; -#endif - return p; - -quota_fopen_error: - quota_mbcs_free(zFullTranslated); - 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( - const 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; - size_t rc; - - iOfst = ftell(p->f); - iEnd = iOfst + size*nmemb; - pFile = p->pFile; - if( pFile && 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 = (size_t)((iEnd - iOfst)/size); - iEnd = iOfst + size*nmemb; - szNew = pGroup->iSize - pFile->iSize + iEnd; - } - } - pGroup->iSize = szNew; - pFile->iSize = iEnd; - quotaLeave(); - }else{ - pFile = 0; - } - rc = fwrite(pBuf, size, nmemb, p->f); - - /* If the write was incomplete, adjust the file size and group size - ** downward */ - if( rc<nmemb && pFile ){ - size_t nWritten = rc>=0 ? rc : 0; - sqlite3_int64 iNewEnd = iOfst + size*nWritten; - if( iNewEnd<iEnd ) iNewEnd = iEnd; - quotaEnter(); - pFile->pGroup->iSize += iNewEnd - pFile->iSize; - pFile->iSize = iNewEnd; - quotaLeave(); - } - return rc; -} - -/* -** Close an open quota_FILE stream. -*/ -int sqlite3_quota_fclose(quota_FILE *p){ - int rc; - quotaFile *pFile; - rc = fclose(p->f); - pFile = p->pFile; - if( pFile ){ - quotaEnter(); - pFile->nRef--; - if( pFile->nRef==0 ){ - quotaGroup *pGroup = pFile->pGroup; - if( pFile->deleteOnClose ){ - gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); - quotaRemoveFile(pFile); - } - quotaGroupDeref(pGroup); - } - quotaLeave(); - } -#if SQLITE_OS_WIN - quota_mbcs_free(p->zMbcsName); -#endif - sqlite3_free(p); - return rc; -} - -/* -** Flush memory buffers for a quota_FILE to disk. -*/ -int sqlite3_quota_fflush(quota_FILE *p, int doFsync){ - int rc; - rc = fflush(p->f); - if( rc==0 && doFsync ){ -#if SQLITE_OS_UNIX - rc = fsync(fileno(p->f)); -#endif -#if SQLITE_OS_WIN - rc = _commit(_fileno(p->f)); -#endif - } - return rc!=0; -} - -/* -** 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); -} - -/* -** Test the error indicator for the given file. -*/ -int sqlite3_quota_ferror(quota_FILE *p){ - return ferror(p->f); -} - -/* -** Truncate a file to szNew bytes. -*/ -int sqlite3_quota_ftruncate(quota_FILE *p, sqlite3_int64 szNew){ - quotaFile *pFile = p->pFile; - int rc; - if( (pFile = p->pFile)!=0 && pFile->iSize<szNew ){ - quotaGroup *pGroup; - if( pFile->iSize<szNew ){ - /* This routine cannot be used to extend a file that is under - ** quota management. Only true truncation is allowed. */ - return -1; - } - pGroup = pFile->pGroup; - quotaEnter(); - pGroup->iSize += szNew - pFile->iSize; - quotaLeave(); - } -#if SQLITE_OS_UNIX - rc = ftruncate(fileno(p->f), szNew); -#endif -#if SQLITE_OS_WIN - rc = _chsize_s(_fileno(p->f), szNew); -#endif - if( pFile && rc==0 ){ - quotaGroup *pGroup = pFile->pGroup; - quotaEnter(); - pGroup->iSize += szNew - pFile->iSize; - pFile->iSize = szNew; - quotaLeave(); - } - return rc; -} - -/* -** Determine the time that the given file was last modified, in -** seconds size 1970. Write the result into *pTime. Return 0 on -** success and non-zero on any kind of error. -*/ -int sqlite3_quota_file_mtime(quota_FILE *p, time_t *pTime){ - int rc; -#if SQLITE_OS_UNIX - struct stat buf; - rc = fstat(fileno(p->f), &buf); -#endif -#if SQLITE_OS_WIN - struct _stati64 buf; - rc = _stati64(p->zMbcsName, &buf); -#endif - if( rc==0 ) *pTime = buf.st_mtime; - return rc; -} - -/* -** Return the true size of the file, as reported by the operating -** system. -*/ -sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE *p){ - int rc; -#if SQLITE_OS_UNIX - struct stat buf; - rc = fstat(fileno(p->f), &buf); -#endif -#if SQLITE_OS_WIN - struct _stati64 buf; - rc = _stati64(p->zMbcsName, &buf); -#endif - return rc==0 ? buf.st_size : -1; -} - -/* -** Return the size of the file, as it is known to the quota subsystem. -*/ -sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){ - return p->pFile ? p->pFile->iSize : -1; -} - -/* -** Determine the amount of data in bytes available for reading -** in the given file. -*/ -long sqlite3_quota_file_available(quota_FILE *p){ - FILE* f = p->f; - long pos1, pos2; - int rc; - pos1 = ftell(f); - if ( pos1 < 0 ) return -1; - rc = fseek(f, 0, SEEK_END); - if ( rc != 0 ) return -1; - pos2 = ftell(f); - if ( pos2 < 0 ) return -1; - rc = fseek(f, pos1, SEEK_SET); - if ( rc != 0 ) return -1; - return pos2 - pos1; -} - -/* -** Remove a managed file. Update quotas accordingly. -*/ -int sqlite3_quota_remove(const char *zFilename){ - char *zFull; /* Full pathname for zFilename */ - size_t nFull; /* Number of bytes in zFilename */ - int rc; /* Result code */ - quotaGroup *pGroup; /* Group containing zFilename */ - quotaFile *pFile; /* A file in the group */ - quotaFile *pNextFile; /* next file in the group */ - int diff; /* Difference between filenames */ - char c; /* First character past end of pattern */ - - zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1); - if( zFull==0 ) return SQLITE_NOMEM; - rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, - gQuota.sThisVfs.mxPathname+1, zFull); - if( rc ){ - sqlite3_free(zFull); - return rc; - } - - /* Figure out the length of the full pathname. If the name ends with - ** / (or \ on windows) then remove the trailing /. - */ - nFull = strlen(zFull); - if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){ - nFull--; - zFull[nFull] = 0; - } - - quotaEnter(); - pGroup = quotaGroupFind(zFull); - if( pGroup ){ - for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){ - pNextFile = pFile->pNext; - diff = memcmp(zFull, pFile->zFilename, nFull); - if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){ - if( pFile->nRef ){ - pFile->deleteOnClose = 1; - }else{ - rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); - quotaRemoveFile(pFile); - quotaGroupDeref(pGroup); - } - } - } - } - quotaLeave(); - sqlite3_free(zFull); - return rc; -} - -/***************************** Test Code ***********************************/ -#ifdef SQLITE_TEST -#include <tcl.h> - -/* -** Argument passed to a TCL quota-over-limit callback. -*/ -typedef struct TclQuotaCallback TclQuotaCallback; -struct TclQuotaCallback { - Tcl_Interp *interp; /* Interpreter in which to run the script */ - Tcl_Obj *pScript; /* Script to be run */ -}; - -extern const char *sqlite3TestErrorName(int); - - -/* -** This is the callback from a quota-over-limit. -*/ -static void tclQuotaCallback( - 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 */ -){ - TclQuotaCallback *p; /* Callback script object */ - Tcl_Obj *pEval; /* Script to evaluate */ - Tcl_Obj *pVarname; /* Name of variable to pass as 2nd arg */ - unsigned int rnd; /* Random part of pVarname */ - int rc; /* Tcl error code */ - - p = (TclQuotaCallback *)pArg; - if( p==0 ) return; - - pVarname = Tcl_NewStringObj("::piLimit_", -1); - Tcl_IncrRefCount(pVarname); - sqlite3_randomness(sizeof(rnd), (void *)&rnd); - Tcl_AppendObjToObj(pVarname, Tcl_NewIntObj((int)(rnd&0x7FFFFFFF))); - Tcl_ObjSetVar2(p->interp, pVarname, 0, Tcl_NewWideIntObj(*piLimit), 0); - - pEval = Tcl_DuplicateObj(p->pScript); - Tcl_IncrRefCount(pEval); - Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zFilename, -1)); - Tcl_ListObjAppendElement(0, pEval, pVarname); - Tcl_ListObjAppendElement(0, pEval, Tcl_NewWideIntObj(iSize)); - rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); - - if( rc==TCL_OK ){ - Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0); - rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit); - Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0); - } - - Tcl_DecrRefCount(pEval); - Tcl_DecrRefCount(pVarname); - if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp); -} - -/* -** Destructor for a TCL quota-over-limit callback. -*/ -static void tclCallbackDestructor(void *pObj){ - TclQuotaCallback *p = (TclQuotaCallback*)pObj; - if( p ){ - Tcl_DecrRefCount(p->pScript); - sqlite3_free((char *)p); - } -} - -/* -** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT -*/ -static int test_quota_initialize( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - const char *zName; /* Name of new quota VFS */ - int makeDefault; /* True to make the new VFS the default */ - int rc; /* Value returned by quota_initialize() */ - - /* Process arguments */ - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT"); - return TCL_ERROR; - } - zName = Tcl_GetString(objv[1]); - if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR; - if( zName[0]=='\0' ) zName = 0; - - /* Call sqlite3_quota_initialize() */ - rc = sqlite3_quota_initialize(zName, makeDefault); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); - - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_shutdown -*/ -static int test_quota_shutdown( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int rc; /* Value returned by quota_shutdown() */ - - if( objc!=1 ){ - Tcl_WrongNumArgs(interp, 1, objv, ""); - return TCL_ERROR; - } - - /* Call sqlite3_quota_shutdown() */ - rc = sqlite3_quota_shutdown(); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); - - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT -*/ -static int test_quota_set( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - const char *zPattern; /* File pattern to configure */ - sqlite3_int64 iLimit; /* Initial quota in bytes */ - Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */ - int rc; /* Value returned by quota_set() */ - TclQuotaCallback *p; /* Callback object */ - int nScript; /* Length of callback script */ - void (*xDestroy)(void*); /* Optional destructor for pArg */ - void (*xCallback)(const char *, sqlite3_int64 *, sqlite3_int64, void *); - - /* Process arguments */ - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "PATTERN LIMIT SCRIPT"); - return TCL_ERROR; - } - zPattern = Tcl_GetString(objv[1]); - if( Tcl_GetWideIntFromObj(interp, objv[2], &iLimit) ) return TCL_ERROR; - pScript = objv[3]; - Tcl_GetStringFromObj(pScript, &nScript); - - if( nScript>0 ){ - /* Allocate a TclQuotaCallback object */ - p = (TclQuotaCallback *)sqlite3_malloc(sizeof(TclQuotaCallback)); - if( !p ){ - Tcl_SetResult(interp, (char *)"SQLITE_NOMEM", TCL_STATIC); - return TCL_OK; - } - memset(p, 0, sizeof(TclQuotaCallback)); - p->interp = interp; - Tcl_IncrRefCount(pScript); - p->pScript = pScript; - xDestroy = tclCallbackDestructor; - xCallback = tclQuotaCallback; - }else{ - p = 0; - xDestroy = 0; - xCallback = 0; - } - - /* Invoke sqlite3_quota_set() */ - rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy); - - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_file FILENAME -*/ -static int test_quota_file( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - const char *zFilename; /* File pattern to configure */ - int rc; /* Value returned by quota_file() */ - - /* Process arguments */ - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); - return TCL_ERROR; - } - zFilename = Tcl_GetString(objv[1]); - - /* Invoke sqlite3_quota_file() */ - rc = sqlite3_quota_file(zFilename); - - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_dump -*/ -static int test_quota_dump( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - Tcl_Obj *pResult; - Tcl_Obj *pGroupTerm; - Tcl_Obj *pFileTerm; - quotaGroup *pGroup; - quotaFile *pFile; - - pResult = Tcl_NewObj(); - quotaEnter(); - for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){ - pGroupTerm = Tcl_NewObj(); - Tcl_ListObjAppendElement(interp, pGroupTerm, - Tcl_NewStringObj(pGroup->zPattern, -1)); - Tcl_ListObjAppendElement(interp, pGroupTerm, - Tcl_NewWideIntObj(pGroup->iLimit)); - Tcl_ListObjAppendElement(interp, pGroupTerm, - Tcl_NewWideIntObj(pGroup->iSize)); - for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){ - int i; - char zTemp[1000]; - pFileTerm = Tcl_NewObj(); - sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename); - for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; } - Tcl_ListObjAppendElement(interp, pFileTerm, - Tcl_NewStringObj(zTemp, -1)); - Tcl_ListObjAppendElement(interp, pFileTerm, - Tcl_NewWideIntObj(pFile->iSize)); - Tcl_ListObjAppendElement(interp, pFileTerm, - Tcl_NewWideIntObj(pFile->nRef)); - Tcl_ListObjAppendElement(interp, pFileTerm, - Tcl_NewWideIntObj(pFile->deleteOnClose)); - Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm); - } - Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); - } - quotaLeave(); - Tcl_SetObjResult(interp, pResult); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_fopen FILENAME MODE -*/ -static int test_quota_fopen( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - const char *zFilename; /* File pattern to configure */ - const char *zMode; /* Mode string */ - quota_FILE *p; /* Open string object */ - char zReturn[50]; /* Name of pointer to return */ - - /* Process arguments */ - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE"); - return TCL_ERROR; - } - zFilename = Tcl_GetString(objv[1]); - zMode = Tcl_GetString(objv[2]); - p = sqlite3_quota_fopen(zFilename, zMode); - sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p); - Tcl_SetResult(interp, zReturn, TCL_VOLATILE); - return TCL_OK; -} - -/* Defined in test1.c */ -extern void *sqlite3TestTextToPtr(const char*); - -/* -** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM -*/ -static int test_quota_fread( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - char *zBuf; - int sz; - int nElem; - size_t got; - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR; - if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR; - zBuf = (char*)sqlite3_malloc( sz*nElem + 1 ); - if( zBuf==0 ){ - Tcl_SetResult(interp, "out of memory", TCL_STATIC); - return TCL_ERROR; - } - got = sqlite3_quota_fread(zBuf, sz, nElem, p); - if( got<0 ) got = 0; - zBuf[got*sz] = 0; - Tcl_SetResult(interp, zBuf, TCL_VOLATILE); - sqlite3_free(zBuf); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT -*/ -static int test_quota_fwrite( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - char *zBuf; - int sz; - int nElem; - size_t got; - - if( objc!=5 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR; - if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR; - zBuf = Tcl_GetString(objv[4]); - got = sqlite3_quota_fwrite(zBuf, sz, nElem, p); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(got)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_fclose HANDLE -*/ -static int test_quota_fclose( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - int rc; - - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite3_quota_fclose(p); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC? -*/ -static int test_quota_fflush( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - int rc; - int doSync = 0; - - if( objc!=2 && objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - if( objc==3 ){ - if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR; - } - rc = sqlite3_quota_fflush(p, doSync); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE -*/ -static int test_quota_fseek( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - int ofst; - const char *zWhence; - int whence; - int rc; - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR; - zWhence = Tcl_GetString(objv[3]); - if( strcmp(zWhence, "SEEK_SET")==0 ){ - whence = SEEK_SET; - }else if( strcmp(zWhence, "SEEK_CUR")==0 ){ - whence = SEEK_CUR; - }else if( strcmp(zWhence, "SEEK_END")==0 ){ - whence = SEEK_END; - }else{ - Tcl_AppendResult(interp, - "WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0); - return TCL_ERROR; - } - rc = sqlite3_quota_fseek(p, ofst, whence); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_rewind HANDLE -*/ -static int test_quota_rewind( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - sqlite3_quota_rewind(p); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_ftell HANDLE -*/ -static int test_quota_ftell( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - sqlite3_int64 x; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - x = sqlite3_quota_ftell(p); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE -*/ -static int test_quota_ftruncate( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - sqlite3_int64 x; - Tcl_WideInt w; - int rc; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - if( Tcl_GetWideIntFromObj(interp, objv[2], &w) ) return TCL_ERROR; - x = (sqlite3_int64)w; - rc = sqlite3_quota_ftruncate(p, x); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_file_size HANDLE -*/ -static int test_quota_file_size( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - sqlite3_int64 x; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - x = sqlite3_quota_file_size(p); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_file_truesize HANDLE -*/ -static int test_quota_file_truesize( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - sqlite3_int64 x; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - x = sqlite3_quota_file_truesize(p); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_file_mtime HANDLE -*/ -static int test_quota_file_mtime( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - time_t t; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - t = 0; - sqlite3_quota_file_mtime(p, &t); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(t)); - return TCL_OK; -} - - -/* -** tclcmd: sqlite3_quota_remove FILENAME -*/ -static int test_quota_remove( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - const char *zFilename; /* File pattern to configure */ - int rc; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); - return TCL_ERROR; - } - zFilename = Tcl_GetString(objv[1]); - rc = sqlite3_quota_remove(zFilename); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_glob PATTERN TEXT -** -** Test the glob pattern matching. Return 1 if TEXT matches PATTERN -** and return 0 if it does not. -*/ -static int test_quota_glob( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - const char *zPattern; /* The glob pattern */ - const char *zText; /* Text to compare agains the pattern */ - int rc; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT"); - return TCL_ERROR; - } - zPattern = Tcl_GetString(objv[1]); - zText = Tcl_GetString(objv[2]); - rc = quotaStrglob(zPattern, zText); - Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_file_available HANDLE -** -** Return the number of bytes from the current file point to the end of -** the file. -*/ -static int test_quota_file_available( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - sqlite3_int64 x; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - x = sqlite3_quota_file_available(p); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); - return TCL_OK; -} - -/* -** tclcmd: sqlite3_quota_ferror HANDLE -** -** Return true if the file handle is in the error state. -*/ -static int test_quota_ferror( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - quota_FILE *p; - int x; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); - return TCL_ERROR; - } - p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - x = sqlite3_quota_ferror(p); - Tcl_SetObjResult(interp, Tcl_NewIntObj(x)); - return TCL_OK; -} - -/* -** This routine registers the custom TCL commands defined in this -** module. This should be the only procedure visible from outside -** of this module. -*/ -int Sqlitequota_Init(Tcl_Interp *interp){ - static struct { - char *zName; - Tcl_ObjCmdProc *xProc; - } aCmd[] = { - { "sqlite3_quota_initialize", test_quota_initialize }, - { "sqlite3_quota_shutdown", test_quota_shutdown }, - { "sqlite3_quota_set", test_quota_set }, - { "sqlite3_quota_file", test_quota_file }, - { "sqlite3_quota_dump", test_quota_dump }, - { "sqlite3_quota_fopen", test_quota_fopen }, - { "sqlite3_quota_fread", test_quota_fread }, - { "sqlite3_quota_fwrite", test_quota_fwrite }, - { "sqlite3_quota_fclose", test_quota_fclose }, - { "sqlite3_quota_fflush", test_quota_fflush }, - { "sqlite3_quota_fseek", test_quota_fseek }, - { "sqlite3_quota_rewind", test_quota_rewind }, - { "sqlite3_quota_ftell", test_quota_ftell }, - { "sqlite3_quota_ftruncate", test_quota_ftruncate }, - { "sqlite3_quota_file_size", test_quota_file_size }, - { "sqlite3_quota_file_truesize", test_quota_file_truesize }, - { "sqlite3_quota_file_mtime", test_quota_file_mtime }, - { "sqlite3_quota_remove", test_quota_remove }, - { "sqlite3_quota_glob", test_quota_glob }, - { "sqlite3_quota_file_available",test_quota_file_available }, - { "sqlite3_quota_ferror", test_quota_ferror }, - }; - int i; - - for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ - Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); - } - - return TCL_OK; -} -#endif
deleted file mode 100644 --- a/db/sqlite3/src/test_quota.h +++ /dev/null @@ -1,274 +0,0 @@ -/* -** 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> -#include <sys/types.h> -#include <sys/stat.h> -#if SQLITE_OS_UNIX -# include <unistd.h> -#endif -#if SQLITE_OS_WIN -# include <windows.h> -#endif - -/* Make this callable from C++ */ -#ifdef __cplusplus -extern "C" { -#endif - -/* -** 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 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. "]" can be part of the list if it is -** the first character. Within the list "X-Y" matches -** characters X or Y or any character in between the -** two. Ex: "[0-9]" matches any digit. -** -** [^...] Matches one character not in the enclosed list. -** -** / Matches either / or \. This allows glob patterns -** containing / to work on both unix and windows. -** -** 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. -** -** The glob algorithm works on bytes. Multi-byte UTF8 characters are -** matched as if each byte were a separate character. -** -** 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. -** -** The patterns that define the various quota groups should be distinct. -** If the same filename matches more than one quota group pattern, then -** the behavior of this package is undefined. -*/ -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, assuming its name matches -** the glob pattern of some quota group. Or if it is already under -** management, update its size. If zFilename does not match the glob -** pattern of any quota group, this routine is a no-op. -*/ -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. -** -** A single quota_FILE should not be used by two or more threads at the -** same time. Multiple threads can be using different quota_FILE objects -** simultaneously, but not the same quota_FILE object. -*/ -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(const void*, size_t, size_t, quota_FILE*); - -/* -** Flush all written content held in memory buffers out to disk. -** This is the equivalent of fflush() in the standard library. -** -** If the hardSync parameter is true (non-zero) then this routine -** also forces OS buffers to disk - the equivalent of fsync(). -** -** This routine return zero on success and non-zero if something goes -** wrong. -*/ -int sqlite3_quota_fflush(quota_FILE*, int hardSync); - -/* -** 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*); - -/* -** Test the error indicator for the given file. -** -** Return non-zero if the error indicator is set. -*/ -int sqlite3_quota_ferror(quota_FILE*); - -/* -** Truncate a file previously opened by sqlite3_quota_fopen(). Return -** zero on success and non-zero on any kind of failure. -** -** The newSize argument must be less than or equal to the current file size. -** Any attempt to "truncate" a file to a larger size results in -** undefined behavior. -*/ -int sqlite3_quota_ftruncate(quota_FILE*, sqlite3_int64 newSize); - -/* -** Return the last modification time of the opened file, in seconds -** since 1970. -*/ -int sqlite3_quota_file_mtime(quota_FILE*, time_t *pTime); - -/* -** Return the size of the file as it is known to the quota system. -** -** This size might be different from the true size of the file on -** disk if some outside process has modified the file without using the -** quota mechanism, or if calls to sqlite3_quota_fwrite() have occurred -** which have increased the file size, but those writes have not yet been -** forced to disk using sqlite3_quota_fflush(). -** -** Return -1 if the file is not participating in quota management. -*/ -sqlite3_int64 sqlite3_quota_file_size(quota_FILE*); - -/* -** Return the true size of the file. -** -** The true size should be the same as the size of the file as known -** to the quota system, however the sizes might be different if the -** file has been extended or truncated via some outside process or if -** pending writes have not yet been flushed to disk. -** -** Return -1 if the file does not exist or if the size of the file -** cannot be determined for some reason. -*/ -sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*); - -/* -** Determine the amount of data in bytes available for reading -** in the given file. -** -** Return -1 if the amount cannot be determined for some reason. -*/ -long sqlite3_quota_file_available(quota_FILE*); - -/* -** Delete a file from the disk, if that file is under quota management. -** Adjust quotas accordingly. -** -** If zFilename is the name of a directory that matches one of the -** quota glob patterns, then all files under quota management that -** are contained within that directory are deleted. -** -** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.) -** When deleting a directory of files, if the deletion of any one -** file fails (for example due to an I/O error), then this routine -** returns immediately, with the error code, and does not try to -** delete any of the other files in the specified directory. -** -** All files are removed from quota management and deleted from disk. -** However, no attempt is made to remove empty directories. -** -** This routine is a no-op for files that are not under quota management. -*/ -int sqlite3_quota_remove(const char *zFilename); - -#ifdef __cplusplus -} /* end of the 'extern "C"' block */ -#endif -#endif /* _QUOTA_H_ */
--- a/dom/Makefile.in +++ b/dom/Makefile.in @@ -53,16 +53,17 @@ PARALLEL_DIRS += \ phonenumberutils \ alarm \ devicestorage \ encoding \ file \ media \ messages \ power \ + quota \ settings \ sms \ mms \ src \ time \ locales \ network \ permission \
--- a/dom/dom-config.mk +++ b/dom/dom-config.mk @@ -3,16 +3,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DOM_SRCDIRS = \ dom/base \ dom/battery \ dom/encoding \ dom/file \ dom/power \ + dom/quota \ dom/media \ dom/network/src \ dom/settings \ dom/phonenumberutils \ dom/sms/src \ dom/contacts \ dom/permission \ dom/alarm \
--- a/dom/file/FileStreamWrappers.cpp +++ b/dom/file/FileStreamWrappers.cpp @@ -3,17 +3,16 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileStreamWrappers.h" #include "nsIFileStorage.h" #include "nsISeekableStream.h" -#include "nsIStandardFileStream.h" #include "mozilla/Attributes.h" #include "FileHelper.h" USING_FILE_NAMESPACE namespace { @@ -241,26 +240,16 @@ NS_IMPL_ISUPPORTS_INHERITED1(FileOutputS NS_IMETHODIMP FileOutputStreamWrapper::Close() { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); nsresult rv = NS_OK; if (!mFirstTime) { - // We must flush buffers of the stream on the same thread on which we wrote - // some data. - nsCOMPtr<nsIStandardFileStream> sstream = do_QueryInterface(mFileStream); - if (sstream) { - rv = sstream->FlushBuffers(); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to flush buffers of the stream!"); - } - } - NS_ASSERTION(PR_GetCurrentThread() == mWriteThread, "Unsetting thread locals on wrong thread!"); mFileHelper->mFileStorage->UnsetThreadLocals(); } if (mFlags & NOTIFY_CLOSE) { nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
--- a/dom/file/LockedFile.cpp +++ b/dom/file/LockedFile.cpp @@ -948,30 +948,30 @@ FinishHelper::Run() } nsIFileStorage* fileStorage = mLockedFile->mFileHandle->mFileStorage; if (fileStorage->IsStorageInvalidated()) { mAborted = true; } for (uint32_t index = 0; index < mParallelStreams.Length(); index++) { - nsCOMPtr<nsIOutputStream> ostream = + nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mParallelStreams[index]); - if (NS_FAILED(ostream->Close())) { + if (NS_FAILED(stream->Close())) { NS_WARNING("Failed to close stream!"); } mParallelStreams[index] = nullptr; } if (mStream) { - nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mStream); + nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream); - if (NS_FAILED(ostream->Close())) { + if (NS_FAILED(stream->Close())) { NS_WARNING("Failed to close stream!"); } mStream = nullptr; } return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); }
--- a/dom/file/nsIFileStorage.h +++ b/dom/file/nsIFileStorage.h @@ -5,24 +5,27 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsIFileStorage_h__ #define nsIFileStorage_h__ #include "nsISupports.h" #define NS_FILESTORAGE_IID \ - {0xbba9c2ff, 0x85c9, 0x47c1, \ - { 0xaf, 0xce, 0x0a, 0x7e, 0x6f, 0x21, 0x50, 0x95 } } + {0xa0801944, 0x2f1c, 0x4203, \ + { 0x9c, 0xaa, 0xaa, 0x47, 0xe0, 0x0c, 0x67, 0x92 } } class nsIFileStorage : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_FILESTORAGE_IID) + virtual const nsACString& + StorageOrigin() = 0; + virtual nsISupports* StorageId() = 0; virtual bool IsStorageInvalidated() = 0; virtual bool IsStorageShuttingDown() = 0; @@ -31,25 +34,28 @@ public: SetThreadLocals() = 0; virtual void UnsetThreadLocals() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIFileStorage, NS_FILESTORAGE_IID) -#define NS_DECL_NSIFILESTORAGE \ - virtual nsISupports* \ - StorageId(); \ - \ - virtual bool \ - IsStorageInvalidated(); \ - \ - virtual bool \ - IsStorageShuttingDown(); \ - \ - virtual void \ - SetThreadLocals(); \ - \ - virtual void \ - UnsetThreadLocals(); +#define NS_DECL_NSIFILESTORAGE \ + virtual const nsACString& \ + StorageOrigin() MOZ_OVERRIDE; \ + \ + virtual nsISupports* \ + StorageId() MOZ_OVERRIDE; \ + \ + virtual bool \ + IsStorageInvalidated() MOZ_OVERRIDE; \ + \ + virtual bool \ + IsStorageShuttingDown() MOZ_OVERRIDE; \ + \ + virtual void \ + SetThreadLocals() MOZ_OVERRIDE; \ + \ + virtual void \ + UnsetThreadLocals() MOZ_OVERRIDE; #endif // nsIFileStorage_h__
--- a/dom/indexedDB/FileManager.cpp +++ b/dom/indexedDB/FileManager.cpp @@ -2,27 +2,29 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileManager.h" #include "mozIStorageConnection.h" -#include "mozIStorageServiceQuotaManagement.h" #include "mozIStorageStatement.h" +#include "nsIInputStream.h" #include "nsISimpleEnumerator.h" #include "mozStorageCID.h" #include "mozStorageHelper.h" #include "FileInfo.h" #include "IndexedDatabaseManager.h" #include "OpenDatabaseHelper.h" +#include "IndexedDatabaseInlines.h" + #define JOURNAL_DIRECTORY_NAME "journals" USING_INDEXEDDB_NAMESPACE namespace { PLDHashOperator EnumerateToTArray(const uint64_t& aKey, @@ -257,23 +259,21 @@ FileManager::GetFileForId(nsIFile* aDire rv = file->Append(id); NS_ENSURE_SUCCESS(rv, nullptr); return file.forget(); } // static nsresult -FileManager::InitDirectory(mozIStorageServiceQuotaManagement* aService, - nsIFile* aDirectory, +FileManager::InitDirectory(nsIFile* aDirectory, nsIFile* aDatabaseFile, - FactoryPrivilege aPrivilege) + const nsACString& aOrigin) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aService, "Null service!"); NS_ASSERTION(aDirectory, "Null directory!"); NS_ASSERTION(aDatabaseFile, "Null database file!"); bool exists; nsresult rv = aDirectory->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); if (!exists) { @@ -305,18 +305,18 @@ FileManager::InitDirectory(mozIStorageSe NS_ENSURE_SUCCESS(rv, rv); bool hasElements; rv = entries->HasMoreElements(&hasElements); NS_ENSURE_SUCCESS(rv, rv); if (hasElements) { nsCOMPtr<mozIStorageConnection> connection; - rv = OpenDatabaseHelper::CreateDatabaseConnection( - NullString(), aDatabaseFile, aDirectory, getter_AddRefs(connection)); + rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile, + aDirectory, NullString(), aOrigin, getter_AddRefs(connection)); NS_ENSURE_SUCCESS(rv, rv); mozStorageTransaction transaction(connection, false); rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( "CREATE VIRTUAL TABLE fs USING filesystem;" )); NS_ENSURE_SUCCESS(rv, rv); @@ -372,22 +372,27 @@ FileManager::InitDirectory(mozIStorageSe "DROP TABLE fs;" )); NS_ENSURE_SUCCESS(rv, rv); transaction.Commit(); } } - if (aPrivilege == Chrome) { - return NS_OK; - } + return NS_OK; +} + +// static +nsresult +FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) +{ + uint64_t usage = 0; nsCOMPtr<nsISimpleEnumerator> entries; - rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); NS_ENSURE_SUCCESS(rv, rv); bool hasMore; while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { nsCOMPtr<nsISupports> entry; rv = entries->GetNext(getter_AddRefs(entry)); NS_ENSURE_SUCCESS(rv, rv); @@ -397,14 +402,18 @@ FileManager::InitDirectory(mozIStorageSe nsString leafName; rv = file->GetLeafName(leafName); NS_ENSURE_SUCCESS(rv, rv); if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { continue; } - rv = aService->UpdateQuotaInformationForFile(file); + int64_t fileSize; + rv = file->GetFileSize(&fileSize); NS_ENSURE_SUCCESS(rv, rv); + + IncrementUsage(&usage, uint64_t(fileSize)); } + *aUsage = usage; return NS_OK; }
--- a/dom/indexedDB/FileManager.h +++ b/dom/indexedDB/FileManager.h @@ -19,32 +19,37 @@ BEGIN_INDEXEDDB_NAMESPACE class FileInfo; class FileManager { friend class FileInfo; public: - FileManager(const nsACString& aOrigin, + FileManager(const nsACString& aOrigin, FactoryPrivilege aPrivilege, const nsAString& aDatabaseName) - : mOrigin(aOrigin), mDatabaseName(aDatabaseName), mLastFileId(0), - mInvalidated(false) + : mOrigin(aOrigin), mPrivilege(aPrivilege), mDatabaseName(aDatabaseName), + mLastFileId(0), mInvalidated(false) { } ~FileManager() { } NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager) const nsACString& Origin() const { return mOrigin; } + const FactoryPrivilege& Privilege() const + { + return mPrivilege; + } + const nsAString& DatabaseName() const { return mDatabaseName; } bool Invalidated() const { return mInvalidated; @@ -63,22 +68,25 @@ public: already_AddRefed<FileInfo> GetFileInfo(int64_t aId); already_AddRefed<FileInfo> GetNewFileInfo(); static already_AddRefed<nsIFile> GetFileForId(nsIFile* aDirectory, int64_t aId); - static nsresult InitDirectory(mozIStorageServiceQuotaManagement* aService, - nsIFile* aDirectory, nsIFile* aDatabaseFile, - FactoryPrivilege aPrivilege); + static nsresult InitDirectory(nsIFile* aDirectory, + nsIFile* aDatabaseFile, + const nsACString& aOrigin); + + static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage); private: nsCString mOrigin; + FactoryPrivilege mPrivilege; nsString mDatabaseName; nsString mDirectoryPath; nsString mJournalDirectoryPath; int64_t mLastFileId; // Protected by IndexedDatabaseManager::FileMutex()
deleted file mode 100644 --- a/dom/indexedDB/FileStream.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "FileStream.h" - -#include "nsIFile.h" - -#include "nsThreadUtils.h" -#include "test_quota.h" - -USING_INDEXEDDB_NAMESPACE - -NS_IMPL_THREADSAFE_ADDREF(FileStream) -NS_IMPL_THREADSAFE_RELEASE(FileStream) - -NS_INTERFACE_MAP_BEGIN(FileStream) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardFileStream) - NS_INTERFACE_MAP_ENTRY(nsISeekableStream) - NS_INTERFACE_MAP_ENTRY(nsIInputStream) - NS_INTERFACE_MAP_ENTRY(nsIOutputStream) - NS_INTERFACE_MAP_ENTRY(nsIStandardFileStream) - NS_INTERFACE_MAP_ENTRY(nsIFileMetadata) -NS_INTERFACE_MAP_END - -NS_IMETHODIMP -FileStream::Seek(int32_t aWhence, int64_t aOffset) -{ - // TODO: Add support for 64 bit file sizes, bug 752431 - NS_ENSURE_TRUE(aOffset <= INT32_MAX, NS_ERROR_INVALID_ARG); - - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - int whence; - switch (aWhence) { - case nsISeekableStream::NS_SEEK_SET: - whence = SEEK_SET; - break; - case nsISeekableStream::NS_SEEK_CUR: - whence = SEEK_CUR; - break; - case nsISeekableStream::NS_SEEK_END: - whence = SEEK_END; - break; - default: - return NS_ERROR_INVALID_ARG; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - int rc = sqlite3_quota_fseek(mQuotaFile, aOffset, whence); - NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR); - - return NS_OK; -} - -NS_IMETHODIMP -FileStream::Tell(int64_t* aResult) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - long rc = sqlite3_quota_ftell(mQuotaFile); - NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR); - - *aResult = rc; - return NS_OK; -} - -NS_IMETHODIMP -FileStream::SetEOF() -{ - int64_t pos; - nsresult rv = Tell(&pos); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - int rc = sqlite3_quota_ftruncate(mQuotaFile, pos); - NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR); - - return NS_OK; -} - - -NS_IMETHODIMP -FileStream::Close() -{ - CleanUpOpen(); - - if (mQuotaFile) { - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - int rc = sqlite3_quota_fclose(mQuotaFile); - mQuotaFile = nullptr; - - NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR); - } - - return NS_OK; -} - -NS_IMETHODIMP -FileStream::Available(uint64_t* aResult) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - long rc = sqlite3_quota_file_available(mQuotaFile); - NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR); - - *aResult = rc; - return NS_OK; -} - -NS_IMETHODIMP -FileStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - size_t bytesRead = sqlite3_quota_fread(aBuf, 1, aCount, mQuotaFile); - if (bytesRead < aCount && sqlite3_quota_ferror(mQuotaFile)) { - return NS_BASE_STREAM_OSERROR; - } - - *aResult = bytesRead; - return NS_OK; -} - -NS_IMETHODIMP -FileStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, - uint32_t aCount, uint32_t* aResult) -{ - NS_NOTREACHED("Don't call me!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -FileStream::IsNonBlocking(bool *aNonBlocking) -{ - *aNonBlocking = false; - return NS_OK; -} - -NS_IMETHODIMP -FileStream::Write(const char* aBuf, uint32_t aCount, uint32_t *aResult) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - size_t bytesWritten = sqlite3_quota_fwrite(aBuf, 1, aCount, mQuotaFile); - if (bytesWritten < aCount) { - return NS_BASE_STREAM_OSERROR; - } - - *aResult = bytesWritten; - return NS_OK; -} - -NS_IMETHODIMP -FileStream::Flush() -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - int rc = sqlite3_quota_fflush(mQuotaFile, 1); - NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR); - - return NS_OK; -} - -NS_IMETHODIMP -FileStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -FileStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) -{ - NS_NOTREACHED("Don't call me!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -FileStream::Init(nsIFile* aFile, const nsAString& aMode, int32_t aFlags) -{ - NS_ASSERTION(!mQuotaFile && !mDeferredOpen, "Already initialized!"); - - nsresult rv = aFile->GetPath(mFilePath); - NS_ENSURE_SUCCESS(rv, rv); - - mMode = aMode; - mFlags = aFlags; - - if (mFlags & nsIStandardFileStream::FLAGS_DEFER_OPEN) { - mDeferredOpen = true; - return NS_OK; - } - - return DoOpen(); -} - -NS_IMETHODIMP -FileStream::GetSize(int64_t* _retval) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - // TODO: Use sqlite3_quota_file_size() here, bug 760783 - int64_t rc = sqlite3_quota_file_truesize(mQuotaFile); - - NS_ASSERTION(rc >= 0, "The file is not under quota management!"); - - *_retval = rc; - return NS_OK; -} - -NS_IMETHODIMP -FileStream::GetLastModified(int64_t* _retval) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - time_t mtime; - int rc = sqlite3_quota_file_mtime(mQuotaFile, &mtime); - NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR); - - *_retval = mtime * PR_MSEC_PER_SEC; - return NS_OK; -} - -NS_IMETHODIMP -FileStream::FlushBuffers() -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mQuotaFile) { - return NS_BASE_STREAM_CLOSED; - } - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - int rc = sqlite3_quota_fflush(mQuotaFile, 0); - NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR); - - return NS_OK; -} - -nsresult -FileStream::DoOpen() -{ - NS_ASSERTION(!mFilePath.IsEmpty(), "Must have a file path"); - - NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!"); - - quota_FILE* quotaFile = - sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(mFilePath).get(), - NS_ConvertUTF16toUTF8(mMode).get()); - - CleanUpOpen(); - - if (!quotaFile) { - return NS_BASE_STREAM_OSERROR; - } - - mQuotaFile = quotaFile; - - return NS_OK; -}
deleted file mode 100644 --- a/dom/indexedDB/FileStream.h +++ /dev/null @@ -1,140 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_filestream_h__ -#define mozilla_dom_indexeddb_filestream_h__ - -#include "IndexedDatabase.h" - -#include "nsIFileStreams.h" -#include "nsIInputStream.h" -#include "nsIOutputStream.h" -#include "nsISeekableStream.h" -#include "nsIStandardFileStream.h" - -class nsIFile; -struct quota_FILE; - -BEGIN_INDEXEDDB_NAMESPACE - -class FileStream : public nsISeekableStream, - public nsIInputStream, - public nsIOutputStream, - public nsIStandardFileStream, - public nsIFileMetadata -{ -public: - FileStream() - : mFlags(0), - mDeferredOpen(false), - mQuotaFile(nullptr) - { } - - virtual ~FileStream() - { - Close(); - } - - NS_DECL_ISUPPORTS - NS_DECL_NSISEEKABLESTREAM - NS_DECL_NSISTANDARDFILESTREAM - NS_DECL_NSIFILEMETADATA - - // nsIInputStream - NS_IMETHOD - Close(); - - NS_IMETHOD - Available(uint64_t* _retval); - - NS_IMETHOD - Read(char* aBuf, uint32_t aCount, uint32_t* _retval); - - NS_IMETHOD - ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount, - uint32_t* _retval); - - NS_IMETHOD - IsNonBlocking(bool* _retval); - - // nsIOutputStream - - // Close() already declared - - NS_IMETHOD - Flush(); - - NS_IMETHOD - Write(const char* aBuf, uint32_t aCount, uint32_t* _retval); - - NS_IMETHOD - WriteFrom(nsIInputStream* aFromStream, uint32_t aCount, uint32_t* _retval); - - NS_IMETHOD - WriteSegments(nsReadSegmentFun aReader, void* aClosure, uint32_t aCount, - uint32_t* _retval); - - // IsNonBlocking() already declared - -protected: - /** - * Cleans up data prepared in Init. - */ - void - CleanUpOpen() - { - mFilePath.Truncate(); - mDeferredOpen = false; - } - - /** - * Open the file. This is called either from Init - * or from DoPendingOpen (if FLAGS_DEFER_OPEN is used when initializing this - * stream). The default behavior of DoOpen is to open the file and save the - * file descriptor. - */ - virtual nsresult - DoOpen(); - - /** - * If there is a pending open, do it now. It's important for this to be - * inlined since we do it in almost every stream API call. - */ - nsresult - DoPendingOpen() - { - if (!mDeferredOpen) { - return NS_OK; - } - - return DoOpen(); - } - - /** - * Data we need to do an open. - */ - nsString mFilePath; - nsString mMode; - - /** - * Flags describing our behavior. See the IDL file for possible values. - */ - int32_t mFlags; - - /** - * Whether we have a pending open (see FLAGS_DEFER_OPEN in the IDL file). - */ - bool mDeferredOpen; - - /** - * File descriptor for opened file. - */ - quota_FILE* mQuotaFile; -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_filestream_h__
--- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -774,16 +774,22 @@ IDBDatabase::Close() NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); CloseInternal(false); NS_ASSERTION(mClosed, "Should have set the closed flag!"); return NS_OK; } +const nsACString& +IDBDatabase::StorageOrigin() +{ + return Origin(); +} + nsISupports* IDBDatabase::StorageId() { return Id(); } bool IDBDatabase::IsStorageInvalidated()
--- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -248,41 +248,61 @@ IDBFactory::Create(ContentParent* aConte NS_HOLD_JS_OBJECTS(factory, IDBFactory); factory->mRootedOwningObject = true; factory.forget(aFactory); return NS_OK; } // static +already_AddRefed<nsIFileURL> +IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin) +{ + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); + NS_ENSURE_SUCCESS(rv, nullptr); + + nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri); + NS_ASSERTION(fileUrl, "This should always succeed!"); + + rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("origin=") + aOrigin); + NS_ENSURE_SUCCESS(rv, nullptr); + + return fileUrl.forget(); +} + +// static already_AddRefed<mozIStorageConnection> -IDBFactory::GetConnection(const nsAString& aDatabaseFilePath) +IDBFactory::GetConnection(const nsAString& aDatabaseFilePath, + const nsACString& aOrigin) { NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")), "Bad file path!"); nsCOMPtr<nsIFile> dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); NS_ENSURE_TRUE(dbFile, nullptr); nsresult rv = dbFile->InitWithPath(aDatabaseFilePath); NS_ENSURE_SUCCESS(rv, nullptr); bool exists; rv = dbFile->Exists(&exists); NS_ENSURE_SUCCESS(rv, nullptr); NS_ENSURE_TRUE(exists, nullptr); - nsCOMPtr<mozIStorageServiceQuotaManagement> ss = + nsCOMPtr<nsIFileURL> dbFileUrl = GetDatabaseFileURL(dbFile, aOrigin); + NS_ENSURE_TRUE(dbFileUrl, nullptr); + + nsCOMPtr<mozIStorageService> ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(ss, nullptr); nsCOMPtr<mozIStorageConnection> connection; - rv = ss->OpenDatabaseWithVFS(dbFile, NS_LITERAL_CSTRING("quota"), - getter_AddRefs(connection)); + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); NS_ENSURE_SUCCESS(rv, nullptr); // Turn on foreign key constraints and recursive triggers. // The "INSERT OR REPLACE" statement doesn't fire the update trigger, // instead it fires only the insert trigger. This confuses the update // refcount function. This behavior changes with enabled recursive triggers, // so the statement fires the delete trigger first and then the insert // trigger.
--- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -10,16 +10,18 @@ #include "mozilla/dom/indexedDB/IndexedDatabase.h" #include "mozIStorageConnection.h" #include "nsIIDBFactory.h" #include "nsCycleCollectionParticipant.h" class nsIAtom; +class nsIFile; +class nsIFileURL; class nsPIDOMWindow; namespace mozilla { namespace dom { class ContentParent; } } @@ -70,18 +72,22 @@ public: ContentParent* aContentParent, IDBFactory** aFactory); // Called when using IndexedDB from a JS component or a JSM in a different // process. static nsresult Create(ContentParent* aContentParent, IDBFactory** aFactory); + static already_AddRefed<nsIFileURL> + GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin); + static already_AddRefed<mozIStorageConnection> - GetConnection(const nsAString& aDatabaseFilePath); + GetConnection(const nsAString& aDatabaseFilePath, + const nsACString& aOrigin); static nsresult LoadDatabaseInformation(mozIStorageConnection* aConnection, nsIAtom* aDatabaseId, uint64_t* aVersion, ObjectStoreInfoArray& aObjectStores); static nsresult
--- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -1,25 +1,24 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "IDBFileHandle.h" -#include "nsIStandardFileStream.h" - #include "mozilla/dom/file/File.h" +#include "mozilla/dom/quota/FileStreams.h" #include "nsDOMClassInfoID.h" -#include "FileStream.h" #include "IDBDatabase.h" USING_INDEXEDDB_NAMESPACE +USING_QUOTA_NAMESPACE namespace { inline already_AddRefed<nsIFile> GetFileFor(FileInfo* aFileInfo) { @@ -63,32 +62,32 @@ IDBFileHandle::Create(IDBDatabase* aData fileInfo.swap(newFile->mFileInfo); return newFile.forget(); } already_AddRefed<nsISupports> IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly) { - nsRefPtr<FileStream> stream = new FileStream(); + const nsACString& origin = mFileStorage->StorageOrigin(); + + nsCOMPtr<nsISupports> result; - nsString streamMode; if (aReadOnly) { - streamMode.AssignLiteral("rb"); + nsRefPtr<FileInputStream> stream = FileInputStream::Create( + origin, aFile, -1, -1, nsIFileInputStream::DEFER_OPEN); + result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream); } else { - streamMode.AssignLiteral("r+b"); + nsRefPtr<FileStream> stream = FileStream::Create( + origin, aFile, -1, -1, nsIFileStream::DEFER_OPEN); + result = NS_ISUPPORTS_CAST(nsIFileStream*, stream); } + NS_ENSURE_TRUE(result, nullptr); - nsresult rv = stream->Init(aFile, streamMode, - nsIStandardFileStream::FLAGS_DEFER_OPEN); - NS_ENSURE_SUCCESS(rv, nullptr); - - nsCOMPtr<nsISupports> result = - NS_ISUPPORTS_CAST(nsIStandardFileStream*, stream); return result.forget(); } already_AddRefed<nsIDOMFile> IDBFileHandle::CreateFileObject(mozilla::dom::file::LockedFile* aLockedFile, uint32_t aFileSize) { nsCOMPtr<nsIDOMFile> file = new mozilla::dom::file::File(
--- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -12,30 +12,29 @@ #include "nsIJSContextStack.h" #include "nsIOutputStream.h" #include "jsfriendapi.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/ipc/Blob.h" +#include "mozilla/dom/quota/FileStreams.h" #include "mozilla/storage.h" #include "nsContentUtils.h" #include "nsDOMClassInfo.h" #include "nsDOMFile.h" #include "nsDOMLists.h" #include "nsEventDispatcher.h" #include "nsJSUtils.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "snappy/snappy.h" -#include "test_quota.h" #include "AsyncConnectionHelper.h" -#include "FileStream.h" #include "IDBCursor.h" #include "IDBEvents.h" #include "IDBFileHandle.h" #include "IDBIndex.h" #include "IDBKeyRange.h" #include "IDBTransaction.h" #include "DatabaseInfo.h" #include "DictionaryHelpers.h" @@ -46,16 +45,17 @@ #include "IndexedDatabaseInlines.h" #define FILE_COPY_BUFFER_SIZE 32768 USING_INDEXEDDB_NAMESPACE using namespace mozilla::dom; using namespace mozilla::dom::indexedDB::ipc; +using mozilla::dom::quota::FileOutputStream; namespace { inline bool IgnoreNothing(PRUnichar c) { return false; @@ -2729,19 +2729,19 @@ AddHelper::DoDatabaseWork(mozIStorageCon rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); // Now we can copy the blob nativeFile = fileManager->GetFileForId(directory, id); NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsRefPtr<FileStream> outputStream = new FileStream(); - rv = outputStream->Init(nativeFile, NS_LITERAL_STRING("wb"), 0); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + nsRefPtr<FileOutputStream> outputStream = FileOutputStream::Create( + mObjectStore->Transaction()->Database()->Origin(), nativeFile); + NS_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = CopyData(inputStream, outputStream); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); cloneFile.mFile->AddFileInfo(fileInfo); } if (index) {
--- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -347,17 +347,18 @@ IDBTransaction::GetOrCreateConnection(mo NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); if (mDatabase->IsInvalidated()) { return NS_ERROR_NOT_AVAILABLE; } if (!mConnection) { nsCOMPtr<mozIStorageConnection> connection = - IDBFactory::GetConnection(mDatabase->FilePath()); + IDBFactory::GetConnection(mDatabase->FilePath(), + mDatabase->Origin()); NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE); nsresult rv; nsRefPtr<UpdateRefcountFunction> function; nsCString beginTransaction; if (mMode != IDBTransaction::READ_ONLY) { function = new UpdateRefcountFunction(Database()->Manager());
--- a/dom/indexedDB/IndexedDatabaseInlines.h +++ b/dom/indexedDB/IndexedDatabaseInlines.h @@ -74,9 +74,22 @@ AppendConditionClause(const nsACString& if (aEquals) { aResult.AppendLiteral("="); } aResult += NS_LITERAL_CSTRING(" :") + aArgName; } +inline void +IncrementUsage(uint64_t* aUsage, uint64_t aDelta) +{ + // Watch for overflow! + if ((UINT64_MAX - *aUsage) < aDelta) { + NS_WARNING("Usage exceeds the maximum!"); + *aUsage = UINT64_MAX; + } + else { + *aUsage += aDelta; + } +} + END_INDEXEDDB_NAMESPACE
--- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -17,42 +17,44 @@ #include "nsIScriptError.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsISHEntry.h" #include "nsISimpleEnumerator.h" #include "nsITimer.h" #include "mozilla/dom/file/FileService.h" +#include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/TabContext.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/storage.h" #include "nsAppDirectoryServiceDefs.h" #include "nsContentUtils.h" #include "nsCRTGlue.h" #include "nsDirectoryServiceUtils.h" #include "nsEventDispatcher.h" #include "nsScriptSecurityManager.h" #include "nsThreadUtils.h" #include "nsXPCOM.h" #include "nsXPCOMPrivate.h" -#include "test_quota.h" #include "xpcpublic.h" #include "AsyncConnectionHelper.h" #include "CheckQuotaHelper.h" #include "IDBDatabase.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "IDBKeyRange.h" #include "OpenDatabaseHelper.h" #include "TransactionThreadPool.h" +#include "IndexedDatabaseInlines.h" + // The amount of time, in milliseconds, that our IO thread will stay alive // after the last event it processes. #define DEFAULT_THREAD_TIMEOUT_MS 30000 // The amount of time, in milliseconds, that we will wait for active database // transactions on shutdown before aborting them. #define DEFAULT_SHUTDOWN_TIMER_MS 30000 @@ -65,16 +67,17 @@ // profile-before-change, when we need to shut down IDB #define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change" USING_INDEXEDDB_NAMESPACE using namespace mozilla::services; using namespace mozilla::dom; using mozilla::Preferences; using mozilla::dom::file::FileService; +using mozilla::dom::quota::QuotaManager; static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); namespace { int32_t gShutdown = 0; int32_t gClosed = 0; @@ -98,39 +101,16 @@ GetDatabaseBaseFilename(const nsAString& return false; } aDatabaseBaseFilename = Substring(aFilename, 0, filenameLen - sqliteLen); return true; } -class QuotaCallback MOZ_FINAL : public mozIStorageQuotaCallback -{ -public: - NS_DECL_ISUPPORTS - - NS_IMETHOD - QuotaExceeded(const nsACString& aFilename, - int64_t aCurrentSizeLimit, - int64_t aCurrentTotalSize, - nsISupports* aUserData, - int64_t* _retval) - { - if (IndexedDatabaseManager::QuotaIsLifted()) { - *_retval = 0; - return NS_OK; - } - - return NS_ERROR_FAILURE; - } -}; - -NS_IMPL_THREADSAFE_ISUPPORTS1(QuotaCallback, mozIStorageQuotaCallback) - // Adds all databases in the hash to the given array. template <class T> PLDHashOperator EnumerateToTArray(const nsACString& aKey, nsTArray<IDBDatabase*>* aValue, void* aUserArg) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -435,18 +415,18 @@ IndexedDatabaseManager::GetOrCreate() // Make a lazy thread for any IO we need (like clearing or enumerating the // contents of indexedDB database directories). instance->mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, NS_LITERAL_CSTRING("IndexedDB I/O"), LazyIdleThread::ManualShutdown); - // We need one quota callback object to hand to SQLite. - instance->mQuotaCallbackSingleton = new QuotaCallback(); + // Make sure that the quota manager is up. + NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), nullptr); // Make a timer here to avoid potential failures later. We don't actually // initialize the timer until shutdown. instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID); NS_ENSURE_TRUE(instance->mShutdownTimer, nullptr); } nsCOMPtr<nsIObserverService> obs = GetObserverService(); @@ -991,47 +971,25 @@ IndexedDatabaseManager::EnsureOriginIsIn NS_ENSURE_SUCCESS(rv, rv); } if (mInitializedOrigins.Contains(aOrigin)) { NS_ADDREF(*aDirectory = directory); return NS_OK; } - // First figure out the filename pattern we'll use. - nsCOMPtr<nsIFile> patternFile; - rv = directory->Clone(getter_AddRefs(patternFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = patternFile->Append(NS_LITERAL_STRING("*")); - NS_ENSURE_SUCCESS(rv, rv); - - nsString pattern; - rv = patternFile->GetPath(pattern); - NS_ENSURE_SUCCESS(rv, rv); - - // Now tell SQLite to start tracking this pattern for content. - nsCOMPtr<mozIStorageServiceQuotaManagement> ss = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE); - - if (aPrivilege != Chrome) { - rv = ss->SetQuotaForFilenamePattern(NS_ConvertUTF16toUTF8(pattern), - GetIndexedDBQuotaMB() * 1024 * 1024, - mQuotaCallbackSingleton, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - } - // We need to see if there are any files in the directory already. If they // are database files then we need to cleanup stored files (if it's needed) - // and also tell SQLite about all of them. + // and also initialize the quota. nsAutoTArray<nsString, 20> subdirsToProcess; nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles; + uint64_t usage = 0; + nsTHashtable<nsStringHashKey> validSubdirs; validSubdirs.Init(20); nsCOMPtr<nsISimpleEnumerator> entries; rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); NS_ENSURE_SUCCESS(rv, rv); bool hasMore; @@ -1063,30 +1021,38 @@ IndexedDatabaseManager::EnsureOriginIsIn } nsString dbBaseFilename; if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) { unknownFiles.AppendElement(file); continue; } - nsCOMPtr<nsIFile> fileManagerDirectory; - rv = directory->Clone(getter_AddRefs(fileManagerDirectory)); + nsCOMPtr<nsIFile> fmDirectory; + rv = directory->Clone(getter_AddRefs(fmDirectory)); NS_ENSURE_SUCCESS(rv, rv); - rv = fileManagerDirectory->Append(dbBaseFilename); + rv = fmDirectory->Append(dbBaseFilename); NS_ENSURE_SUCCESS(rv, rv); - rv = FileManager::InitDirectory(ss, fileManagerDirectory, file, - aPrivilege); + rv = FileManager::InitDirectory(fmDirectory, file, aOrigin); NS_ENSURE_SUCCESS(rv, rv); if (aPrivilege != Chrome) { - rv = ss->UpdateQuotaInformationForFile(file); + uint64_t fileUsage; + rv = FileManager::GetUsage(fmDirectory, &fileUsage); NS_ENSURE_SUCCESS(rv, rv); + + IncrementUsage(&usage, fileUsage); + + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + IncrementUsage(&usage, uint64_t(fileSize)); } validSubdirs.PutEntry(dbBaseFilename); } NS_ENSURE_SUCCESS(rv, rv); for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) { const nsString& subdir = subdirsToProcess[i]; @@ -1112,22 +1078,49 @@ IndexedDatabaseManager::EnsureOriginIsIn // The journal file may exists even after db has been correctly opened. if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { NS_WARNING("Unknown file found!"); return NS_ERROR_UNEXPECTED; } } } + if (aPrivilege != Chrome) { + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->InitQuotaForOrigin(aOrigin, GetIndexedDBQuotaMB(), usage); + } + mInitializedOrigins.AppendElement(aOrigin); NS_ADDREF(*aDirectory = directory); return NS_OK; } +void +IndexedDatabaseManager::UninitializeOriginsByPattern( + const nsACString& aPattern) +{ +#ifdef DEBUG + { + bool correctThread; + NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) && + correctThread, + "Running on the wrong thread!"); + } +#endif + + for (int32_t i = mInitializedOrigins.Length() - 1; i >= 0; i--) { + if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) { + mInitializedOrigins.RemoveElementAt(i); + } + } +} + bool IndexedDatabaseManager::QuotaIsLiftedInternal() { nsPIDOMWindow* window = nullptr; nsRefPtr<CheckQuotaHelper> helper = nullptr; bool createdHelper = false; window = @@ -1245,26 +1238,24 @@ IndexedDatabaseManager::GetFileManager(c return result.forget(); } } return nullptr; } void -IndexedDatabaseManager::AddFileManager(const nsACString& aOrigin, - const nsAString& aDatabaseName, - FileManager* aFileManager) +IndexedDatabaseManager::AddFileManager(FileManager* aFileManager) { NS_ASSERTION(aFileManager, "Null file manager!"); nsTArray<nsRefPtr<FileManager> >* array; - if (!mFileManagers.Get(aOrigin, &array)) { + if (!mFileManagers.Get(aFileManager->Origin(), &array)) { array = new nsTArray<nsRefPtr<FileManager> >(); - mFileManagers.Put(aOrigin, array); + mFileManagers.Put(aFileManager->Origin(), array); } array->AppendElement(aFileManager); } void IndexedDatabaseManager::InvalidateFileManagersForPattern( const nsACString& aPattern) @@ -1778,16 +1769,23 @@ OriginClearRunnable::DeleteFiles(Indexed continue; } if (NS_FAILED(file->Remove(true))) { // This should never fail if we've closed all database connections // correctly... NS_ERROR("Failed to remove directory!"); } + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->RemoveQuotaForPattern(mOriginOrPattern); + + aManager->UninitializeOriginsByPattern(mOriginOrPattern); } } NS_IMETHODIMP IndexedDatabaseManager::OriginClearRunnable::Run() { IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); NS_ASSERTION(mgr, "This should never fail!"); @@ -1875,29 +1873,16 @@ IndexedDatabaseManager::AsyncUsageRunnab void IndexedDatabaseManager::AsyncUsageRunnable::Cancel() { if (PR_ATOMIC_SET(&mCanceled, 1)) { NS_ERROR("Canceled more than once?!"); } } -inline void -IncrementUsage(uint64_t* aUsage, uint64_t aDelta) -{ - // Watch for overflow! - if ((INT64_MAX - *aUsage) <= aDelta) { - NS_WARNING("Database sizes exceed max we can report!"); - *aUsage = INT64_MAX; - } - else { - *aUsage += aDelta; - } -} - nsresult IndexedDatabaseManager::AsyncUsageRunnable::TakeShortcut() { NS_ASSERTION(mCallbackState == Pending, "Huh?"); nsresult rv = NS_DispatchToCurrentThread(this); NS_ENSURE_SUCCESS(rv, rv); @@ -2290,35 +2275,32 @@ IndexedDatabaseManager::AsyncDeleteFileR NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); nsCOMPtr<nsIFile> directory = mFileManager->GetDirectory(); NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(directory, mFileId); NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); - nsString filePath; - nsresult rv = file->GetPath(filePath); - NS_ENSURE_SUCCESS(rv, rv); - - int rc = sqlite3_quota_remove(NS_ConvertUTF16toUTF8(filePath).get()); - if (rc != SQLITE_OK) { - NS_WARNING("Failed to delete stored file!"); - return NS_ERROR_FAILURE; + nsresult rv; + int64_t fileSize; + + if (mFileManager->Privilege() != Chrome) { + rv = file->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); } - // sqlite3_quota_remove won't actually remove anything if we're not tracking - // the quota here. Manually remove the file if it exists. - bool exists; - rv = file->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - rv = file->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); + rv = file->Remove(false); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + if (mFileManager->Privilege() != Chrome) { + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->DecreaseUsageForOrigin(mFileManager->Origin(), fileSize); } directory = mFileManager->GetJournalDirectory(); NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); file = mFileManager->GetFileForId(directory, mFileId); NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
--- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -18,17 +18,16 @@ #include "nsIURI.h" #include "nsClassHashtable.h" #include "nsRefPtrHashtable.h" #include "nsHashKeys.h" #define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1" -class mozIStorageQuotaCallback; class nsIAtom; class nsIFile; class nsITimer; class nsPIDOMWindow; class nsEventChainPostVisitor; namespace mozilla { namespace dom { @@ -129,16 +128,18 @@ public: static uint32_t GetIndexedDBQuotaMB(); nsresult EnsureOriginIsInitialized(const nsACString& aOrigin, FactoryPrivilege aPrivilege, nsIFile** aDirectory); + void UninitializeOriginsByPattern(const nsACString& aPattern); + // Determine if the quota is lifted for the Window the current thread is // using. static inline bool QuotaIsLifted() { IndexedDatabaseManager* mgr = Get(); NS_ASSERTION(mgr, "Must have a manager here!"); @@ -167,19 +168,17 @@ public: } #endif already_AddRefed<FileManager> GetFileManager(const nsACString& aOrigin, const nsAString& aDatabaseName); void - AddFileManager(const nsACString& aOrigin, - const nsAString& aDatabaseName, - FileManager* aFileManager); + AddFileManager(FileManager* aFileManager); void InvalidateFileManagersForPattern(const nsACString& aPattern); void InvalidateFileManager(const nsACString& aOrigin, const nsAString& aDatabaseName); nsresult AsyncDeleteFile(FileManager* aFileManager, int64_t aFileId); @@ -497,20 +496,16 @@ private: nsAutoTArray<nsAutoPtr<SynchronizedOp>, 5> mSynchronizedOps; // Thread on which IO is performed. nsCOMPtr<nsIThread> mIOThread; // A timer that gets activated at shutdown to ensure we close all databases. nsCOMPtr<nsITimer> mShutdownTimer; - // A single threadsafe instance of our quota callback. Created on the main - // thread during GetOrCreate(). - nsCOMPtr<mozIStorageQuotaCallback> mQuotaCallbackSingleton; - // A list of all successfully initialized origins. This list isn't protected // by any mutex but it is only ever touched on the IO thread. nsTArray<nsCString> mInitializedOrigins; // Lock protecting FileManager.mFileInfos and nsDOMFileBase.mFileInfos // It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt // and FileInfo.mSliceRefCnt mozilla::Mutex mFileMutex;
--- a/dom/indexedDB/Makefile.in +++ b/dom/indexedDB/Makefile.in @@ -20,17 +20,16 @@ EXPORTS_NAMESPACES = mozilla/dom/indexed CPPSRCS = \ AsyncConnectionHelper.cpp \ CheckPermissionsHelper.cpp \ CheckQuotaHelper.cpp \ DatabaseInfo.cpp \ FileInfo.cpp \ FileManager.cpp \ - FileStream.cpp \ IDBCursor.cpp \ IDBDatabase.cpp \ IDBEvents.cpp \ IDBFactory.cpp \ IDBFileHandle.cpp \ IDBIndex.cpp \ IDBKeyRange.cpp \ IDBObjectStore.cpp \ @@ -88,17 +87,16 @@ XPIDLSRCS = \ nsIIDBIndex.idl \ nsIIDBKeyRange.idl \ nsIIDBObjectStore.idl \ nsIIDBRequest.idl \ nsIIDBTransaction.idl \ nsIIDBVersionChangeEvent.idl \ nsIIDBOpenDBRequest.idl \ nsIIndexedDatabaseManager.idl \ - nsIStandardFileStream.idl \ $(NULL) DIRS += ipc TEST_DIRS += test include $(topsrcdir)/config/config.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk
--- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -3,29 +3,31 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/DebugOnly.h" #include "OpenDatabaseHelper.h" #include "nsIFile.h" +#include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/storage.h" #include "nsEscape.h" +#include "nsNetUtil.h" #include "nsThreadUtils.h" #include "snappy/snappy.h" -#include "test_quota.h" #include "nsIBFCacheEntry.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "IndexedDatabaseManager.h" using namespace mozilla; USING_INDEXEDDB_NAMESPACE +USING_QUOTA_NAMESPACE namespace { // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major // schema version. MOZ_STATIC_ASSERT(JS_STRUCTURED_CLONE_VERSION == 1, "Need to update the major schema version."); @@ -1627,25 +1629,25 @@ OpenDatabaseHelper::DoDatabaseWork() NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = dbFile->GetPath(mDatabaseFilePath); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsCOMPtr<nsIFile> fileManagerDirectory; - rv = dbDirectory->Clone(getter_AddRefs(fileManagerDirectory)); + nsCOMPtr<nsIFile> fmDirectory; + rv = dbDirectory->Clone(getter_AddRefs(fmDirectory)); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - rv = fileManagerDirectory->Append(filename); + rv = fmDirectory->Append(filename); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsCOMPtr<mozIStorageConnection> connection; - rv = CreateDatabaseConnection(mName, dbFile, fileManagerDirectory, + rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mASCIIOrigin, getter_AddRefs(connection)); if (NS_FAILED(rv) && NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } NS_ENSURE_SUCCESS(rv, rv); rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId, @@ -1686,77 +1688,79 @@ OpenDatabaseHelper::DoDatabaseWork() } if (mCurrentVersion != mRequestedVersion) { mState = eSetVersionPending; } nsRefPtr<FileManager> fileManager = mgr->GetFileManager(mASCIIOrigin, mName); if (!fileManager) { - fileManager = new FileManager(mASCIIOrigin, mName); - - rv = fileManager->Init(fileManagerDirectory, connection); + fileManager = new FileManager(mASCIIOrigin, mPrivilege, mName); + + rv = fileManager->Init(fmDirectory, connection); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - mgr->AddFileManager(mASCIIOrigin, mName, fileManager); + mgr->AddFileManager(fileManager); } mFileManager = fileManager.forget(); return NS_OK; } // static nsresult OpenDatabaseHelper::CreateDatabaseConnection( + nsIFile* aDBFile, + nsIFile* aFMDirectory, const nsAString& aName, - nsIFile* aDBFile, - nsIFile* aFileManagerDirectory, + const nsACString& aOrigin, mozIStorageConnection** aConnection) { NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_NAMED_LITERAL_CSTRING(quotaVFSName, "quota"); - - nsCOMPtr<mozIStorageServiceQuotaManagement> ss = + nsCOMPtr<nsIFileURL> dbFileUrl = + IDBFactory::GetDatabaseFileURL(aDBFile, aOrigin); + NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE); + + nsCOMPtr<mozIStorageService> ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE); nsCOMPtr<mozIStorageConnection> connection; - nsresult rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, - getter_AddRefs(connection)); + nsresult rv = + ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); if (rv == NS_ERROR_FILE_CORRUPTED) { // If we're just opening the database during origin initialization, then // we don't want to erase any files. The failure here will fail origin // initialization too. if (aName.IsVoid()) { return rv; } // Nuke the database file. The web services can recreate their data. rv = aDBFile->Remove(false); NS_ENSURE_SUCCESS(rv, rv); bool exists; - rv = aFileManagerDirectory->Exists(&exists); + rv = aFMDirectory->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); if (exists) { bool isDirectory; - rv = aFileManagerDirectory->IsDirectory(&isDirectory); + rv = aFMDirectory->IsDirectory(&isDirectory); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - rv = aFileManagerDirectory->Remove(true); + rv = aFMDirectory->Remove(true); NS_ENSURE_SUCCESS(rv, rv); } - rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName, - getter_AddRefs(connection)); + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); } NS_ENSURE_SUCCESS(rv, rv); rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem")); NS_ENSURE_SUCCESS(rv, rv); // Check to make sure that the database schema is correct. int32_t schemaVersion; @@ -2342,16 +2346,18 @@ SetVersionHelper::NotifyTransactionPostC return rv; } nsresult DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { NS_ASSERTION(!aConnection, "How did we get a connection here?"); + const FactoryPrivilege& privilege = mOpenHelper->Privilege(); + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); NS_ASSERTION(mgr, "This should never fail!"); nsCOMPtr<nsIFile> directory; nsresult rv = mgr->GetDirectoryForOrigin(mASCIIOrigin, getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); @@ -2367,69 +2373,67 @@ DeleteDatabaseHelper::DoDatabaseWork(moz rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); bool exists = false; rv = dbFile->Exists(&exists); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - int rc; - if (exists) { - nsString dbFilePath; - rv = dbFile->GetPath(dbFilePath); + int64_t fileSize; + + if (privilege != Chrome) { + rv = dbFile->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + rv = dbFile->Remove(false); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - rc = sqlite3_quota_remove(NS_ConvertUTF16toUTF8(dbFilePath).get()); - if (rc != SQLITE_OK) { - NS_WARNING("Failed to delete db file!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - // sqlite3_quota_remove won't actually remove anything if we're not tracking - // the quota here. Manually remove the file if it exists. - rv = dbFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (exists) { - rv = dbFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); + if (privilege != Chrome) { + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, fileSize); } } - nsCOMPtr<nsIFile> fileManagerDirectory; - rv = directory->Clone(getter_AddRefs(fileManagerDirectory)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = fileManagerDirectory->Append(filename); + nsCOMPtr<nsIFile> fmDirectory; + rv = directory->Clone(getter_AddRefs(fmDirectory)); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - rv = fileManagerDirectory->Exists(&exists); + rv = fmDirectory->Append(filename); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = fmDirectory->Exists(&exists); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (exists) { bool isDirectory; - rv = fileManagerDirectory->IsDirectory(&isDirectory); + rv = fmDirectory->IsDirectory(&isDirectory); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsString fileManagerDirectoryPath; - rv = fileManagerDirectory->GetPath(fileManagerDirectoryPath); + uint64_t usage = 0; + + if (privilege != Chrome) { + rv = FileManager::GetUsage(fmDirectory, &usage); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + rv = fmDirectory->Remove(true); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - rc = sqlite3_quota_remove( - NS_ConvertUTF16toUTF8(fileManagerDirectoryPath).get()); - if (rc != SQLITE_OK) { - NS_WARNING("Failed to delete file directory!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + if (privilege != Chrome) { + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, usage); } - - rv = fileManagerDirectory->Remove(true); - NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } nsresult DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, jsval* aVal) {
--- a/dom/indexedDB/OpenDatabaseHelper.h +++ b/dom/indexedDB/OpenDatabaseHelper.h @@ -72,20 +72,26 @@ public: } IDBDatabase* Database() const { NS_ASSERTION(mDatabase, "Calling at the wrong time!"); return mDatabase; } + const FactoryPrivilege& Privilege() const + { + return mPrivilege; + } + static - nsresult CreateDatabaseConnection(const nsAString& aName, - nsIFile* aDBFile, - nsIFile* aFileManagerDirectory, + nsresult CreateDatabaseConnection(nsIFile* aDBFile, + nsIFile* aFMDirectory, + const nsAString& aName, + const nsACString& aOrigin, mozIStorageConnection** aConnection); protected: // Methods only called on the main thread nsresult EnsureSuccessResult(); nsresult StartSetVersion(); nsresult StartDelete(); virtual nsresult GetSuccessResult(JSContext* aCx,
deleted file mode 100644 --- a/dom/indexedDB/nsIStandardFileStream.idl +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIFile; - -/** - * A stream that allows you to read from a file or stream to a file - * using standard file APIs. - */ -[scriptable, uuid(ebbbb779-92a3-4b2a-b7cf-6efbe904c453)] -interface nsIStandardFileStream : nsISupports -{ - /** - * If this is set, the file will be opened (i.e., a call to - * fopen done) only when we do an actual operation on the stream, - * or more specifically, when one of the following is called: - * - Seek - * - Tell - * - SetEOF - * - Available - * - Read - * - Write - * - Flush - * - GetSize - * - GetLastModified - * - Sync - * - * FLAGS_DEFER_OPEN is useful if we use the stream on a background - * thread, so that the opening and possible |stat|ing of the file - * happens there as well. - * - * @note Using this flag results in the file not being opened - * during the call to Init. This means that any errors that might - * happen when this flag is not set would happen during the - * first read. Also, the file is not locked when Init is called, - * so it might be deleted before we try to read from it. - */ - const long FLAGS_DEFER_OPEN = 1 << 0; - - /** - * @param file file to read from or stream to - * @param mode file open mode (see fopen documentation) - * @param flags flags specifying various behaviors of the class - * (see enumerations in the class) - */ - void init(in nsIFile file, - in AString mode, - in long flags); - - /** - * Flush all written content held in memory buffers out to disk. - * This is the equivalent of fflush() - */ - void flushBuffers(); -};
--- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -49,21 +49,23 @@ MOCHITEST_FILES = \ test_event_source.html \ test_exceptions_in_events.html \ test_file_array.html \ test_file_cross_database_copying.html \ test_file_delete.html \ test_file_os_delete.html \ test_file_put_get_object.html \ test_file_put_get_values.html \ + test_file_quota.html \ test_file_replace.html \ test_file_resurrection_delete.html \ test_file_resurrection_transaction_abort.html \ test_file_sharing.html \ test_file_transaction_abort.html \ + test_filehandle_quota.html \ test_filehandle_serialization.html \ test_filehandle_store_snapshot.html \ test_getAll.html \ test_get_filehandle.html \ test_global_data.html \ test_index_empty_keyPath.html \ test_index_getAll.html \ test_index_getAllObjects.html \
--- a/dom/indexedDB/test/file.js +++ b/dom/indexedDB/test/file.js @@ -1,13 +1,15 @@ /** * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +const DEFAULT_QUOTA = 50 * 1024 * 1024; + var bufferCache = []; var utils = SpecialPowers.getDOMWindowUtils(window); if (!SpecialPowers.isMainProcess()) { window.runTest = function() { todo(false, "Test disabled in child processes, for now"); finishTest(); } @@ -179,35 +181,16 @@ function getUsage(usageHandler) onUsageResult: function(uri, usage, fileUsage) { usageHandler(usage, fileUsage); } }; idbManager.getUsageForURI(uri, callback); } -function getUsageSync() -{ - let usage; - - getUsage(function(aUsage, aFileUsage) { - usage = aUsage; - }); - - let comp = SpecialPowers.wrap(Components); - let thread = comp.classes["@mozilla.org/thread-manager;1"] - .getService(comp.interfaces.nsIThreadManager) - .currentThread; - while (!usage) { - thread.processNextEvent(true); - } - - return usage; -} - function scheduleGC() { SpecialPowers.exactGC(window, continueToNextStep); } function getFileId(file) { return utils.getFileId(file);
--- a/dom/indexedDB/test/test_file_quota.html +++ b/dom/indexedDB/test/test_file_quota.html @@ -8,50 +8,44 @@ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="text/javascript;version=1.7"> function testSteps() { const READ_WRITE = IDBTransaction.READ_WRITE; - const DEFAULT_QUOTA_MB = 50; const name = window.location.pathname; const objectStoreName = "Blobs"; - const testData = { key: 0, value: {} }; - const fileData = { key: 1, file: null }; + const fileData = { key: 1, file: getNullFile("random.bin", DEFAULT_QUOTA) }; let request = indexedDB.open(name, 1); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; request.onsuccess = grabEventAndContinueHandler; let event = yield; is(event.type, "upgradeneeded", "Got correct event type"); let db = event.target.result; - let objectStore = db.createObjectStore(objectStoreName, { }); - objectStore.add(testData.value, testData.key); - - let size = (DEFAULT_QUOTA_MB + 1) * 1024 * 1024 - getUsageSync(); - fileData.file = getNullFile("random.bin", size); + db.createObjectStore(objectStoreName, { }); event = yield; is(event.type, "success", "Got correct event type"); trans = db.transaction([objectStoreName], READ_WRITE); - objectStore = trans.objectStore(objectStoreName); + let objectStore = trans.objectStore(objectStoreName); request = objectStore.add(fileData.file, fileData.key); - request.addEventListener("error", new ExpectError("UnknownError")); + request.addEventListener("error", new ExpectError("UnknownError", true)); request.onsuccess = unexpectedSuccessHandler; event = yield; trans.oncomplete = grabEventAndContinueHandler; event = yield; is(event.type, "complete", "Got correct event type");
--- a/dom/indexedDB/test/test_filehandle_quota.html +++ b/dom/indexedDB/test/test_filehandle_quota.html @@ -8,17 +8,16 @@ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="text/javascript;version=1.7"> function testSteps() { const READ_WRITE = IDBTransaction.READ_WRITE; - const DEFAULT_QUOTA_MB = 50; const name = window.location.pathname; let request = indexedDB.open(name, 1); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; request.onsuccess = grabEventAndContinueHandler; let event = yield; @@ -34,20 +33,20 @@ request.onsuccess = grabEventAndContinueHandler; event = yield; let fileHandle = event.target.result; fileHandle.onerror = errorHandler; let lockedFile = fileHandle.open("readwrite"); - let blob = getNullBlob((50 + 1) * 1024 * 1024 - getUsageSync()); + let blob = getNullBlob(DEFAULT_QUOTA); request = lockedFile.write(blob); - request.addEventListener("error", new ExpectError("UnknownError")); + request.addEventListener("error", new ExpectError("UnknownError", true)); request.onsuccess = unexpectedSuccessHandler; event = yield; lockedFile.oncomplete = grabEventAndContinueHandler; event = yield; is(event.type, "complete", "Got correct event type");
new file mode 100644 --- /dev/null +++ b/dom/quota/FileStreams.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileStreams.h" + +USING_QUOTA_NAMESPACE + +template <class FileStreamBase> +NS_IMETHODIMP +FileQuotaStream<FileStreamBase>::SetEOF() +{ + nsresult rv = FileStreamBase::SetEOF(); + NS_ENSURE_SUCCESS(rv, rv); + + if (mQuotaObject) { + int64_t offset; + nsresult rv = FileStreamBase::Tell(&offset); + NS_ENSURE_SUCCESS(rv, rv); + + mQuotaObject->UpdateSize(offset); + } + + return NS_OK; +} + +template <class FileStreamBase> +NS_IMETHODIMP +FileQuotaStream<FileStreamBase>::Close() +{ + nsresult rv = FileStreamBase::Close(); + NS_ENSURE_SUCCESS(rv, rv); + + mQuotaObject = nullptr; + + return NS_OK; +} + +template <class FileStreamBase> +nsresult +FileQuotaStream<FileStreamBase>::DoOpen() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + NS_ASSERTION(!mQuotaObject, "Creating quota object more than once?"); + mQuotaObject = quotaManager->GetQuotaObject(mOrigin, + FileStreamBase::mOpenParams.localFile); + + nsresult rv = FileStreamBase::DoOpen(); + NS_ENSURE_SUCCESS(rv, rv); + + if (mQuotaObject && (FileStreamBase::mOpenParams.ioFlags & PR_TRUNCATE)) { + mQuotaObject->UpdateSize(0); + } + + return NS_OK; +} + +template <class FileStreamBase> +NS_IMETHODIMP +FileQuotaStreamWithWrite<FileStreamBase>::Write(const char* aBuf, + uint32_t aCount, + uint32_t* _retval) +{ + nsresult rv; + + if (FileQuotaStreamWithWrite::mQuotaObject) { + int64_t offset; + rv = FileStreamBase::Tell(&offset); + NS_ENSURE_SUCCESS(rv, rv); + + if (!FileQuotaStreamWithWrite:: + mQuotaObject->MaybeAllocateMoreSpace(offset, aCount)) { + return NS_ERROR_FAILURE; + } + } + + rv = FileStreamBase::Write(aBuf, aCount, _retval); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS_INHERITED0(FileInputStream, nsFileInputStream) + +already_AddRefed<FileInputStream> +FileInputStream::Create(const nsACString& aOrigin, nsIFile* aFile, + int32_t aIOFlags, int32_t aPerm, + int32_t aBehaviorFlags) +{ + nsRefPtr<FileInputStream> stream = new FileInputStream(aOrigin); + nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags); + NS_ENSURE_SUCCESS(rv, nullptr); + return stream.forget(); +} + +NS_IMPL_ISUPPORTS_INHERITED0(FileOutputStream, nsFileOutputStream) + +already_AddRefed<FileOutputStream> +FileOutputStream::Create(const nsACString& aOrigin, nsIFile* aFile, + int32_t aIOFlags, int32_t aPerm, + int32_t aBehaviorFlags) +{ + nsRefPtr<FileOutputStream> stream = new FileOutputStream(aOrigin); + nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags); + NS_ENSURE_SUCCESS(rv, nullptr); + return stream.forget(); +} + +NS_IMPL_ISUPPORTS_INHERITED0(FileStream, nsFileStream) + +already_AddRefed<FileStream> +FileStream::Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags, + int32_t aPerm, int32_t aBehaviorFlags) +{ + nsRefPtr<FileStream> stream = new FileStream(aOrigin); + nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags); + NS_ENSURE_SUCCESS(rv, nullptr); + return stream.forget(); +}
new file mode 100644 --- /dev/null +++ b/dom/quota/FileStreams.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_quota_filestreams_h__ +#define mozilla_dom_quota_filestreams_h__ + +#include "QuotaCommon.h" + +#include "nsFileStreams.h" + +#include "QuotaManager.h" + +BEGIN_QUOTA_NAMESPACE + +template <class FileStreamBase> +class FileQuotaStream : public FileStreamBase +{ +public: + // nsFileStreamBase override + NS_IMETHOD + SetEOF() MOZ_OVERRIDE; + + NS_IMETHOD + Close() MOZ_OVERRIDE; + +protected: + FileQuotaStream(const nsACString& aOrigin) + : mOrigin(aOrigin) + { } + + // nsFileStreamBase override + virtual nsresult + DoOpen() MOZ_OVERRIDE; + + nsCString mOrigin; + nsRefPtr<QuotaObject> mQuotaObject; +}; + +template <class FileStreamBase> +class FileQuotaStreamWithWrite : public FileQuotaStream<FileStreamBase> +{ +public: + // nsFileStreamBase override + NS_IMETHOD + Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) MOZ_OVERRIDE; + +protected: + FileQuotaStreamWithWrite(const nsACString& aOrigin) + : FileQuotaStream<FileStreamBase>(aOrigin) + { } +}; + +class FileInputStream : public FileQuotaStream<nsFileInputStream> +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + static already_AddRefed<FileInputStream> + Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1, + int32_t aPerm = -1, int32_t aBehaviorFlags = 0); + +private: + FileInputStream(const nsACString& aOrigin) + : FileQuotaStream<nsFileInputStream>(aOrigin) + { } + + virtual ~FileInputStream() { + Close(); + } +}; + +class FileOutputStream : public FileQuotaStreamWithWrite<nsFileOutputStream> +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + static already_AddRefed<FileOutputStream> + Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1, + int32_t aPerm = -1, int32_t aBehaviorFlags = 0); + +private: + FileOutputStream(const nsACString& aOrigin) + : FileQuotaStreamWithWrite<nsFileOutputStream>(aOrigin) + { } + + virtual ~FileOutputStream() { + Close(); + } +}; + +class FileStream : public FileQuotaStreamWithWrite<nsFileStream> +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + static already_AddRefed<FileStream> + Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1, + int32_t aPerm = -1, int32_t aBehaviorFlags = 0); + +private: + FileStream(const nsACString& aOrigin) + : FileQuotaStreamWithWrite<nsFileStream>(aOrigin) + { } + + virtual ~FileStream() { + Close(); + } +}; + +END_QUOTA_NAMESPACE + +#endif /* mozilla_dom_quota_filestreams_h__ */
new file mode 100644 --- /dev/null +++ b/dom/quota/Makefile.in @@ -0,0 +1,33 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = dom +LIBRARY_NAME = domquota_s +XPIDL_MODULE = dom_quota +LIBXUL_LIBRARY = 1 +FORCE_STATIC_LIB = 1 + +include $(topsrcdir)/dom/dom-config.mk + +EXPORTS_NAMESPACES = mozilla/dom/quota + +CPPSRCS = \ + FileStreams.cpp \ + QuotaManager.cpp \ + $(NULL) + +EXPORTS_mozilla/dom/quota = \ + FileStreams.h \ + QuotaCommon.h \ + QuotaManager.h \ + $(NULL) + +include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /dev/null +++ b/dom/quota/QuotaCommon.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_quota_quotacommon_h__ +#define mozilla_dom_quota_quotacommon_h__ + +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsStringGlue.h" +#include "nsTArray.h" + +#define BEGIN_QUOTA_NAMESPACE \ + namespace mozilla { namespace dom { namespace quota { +#define END_QUOTA_NAMESPACE \ + } /* namespace quota */ } /* namespace dom */ } /* namespace mozilla */ +#define USING_QUOTA_NAMESPACE \ + using namespace mozilla::dom::quota; + +#endif // mozilla_dom_quota_quotacommon_h__
new file mode 100644 --- /dev/null +++ b/dom/quota/QuotaManager.cpp @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "QuotaManager.h" + +#include "nsIFile.h" + +#include "mozilla/ClearOnShutdown.h" +#include "nsComponentManagerUtils.h" + +#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" + +USING_QUOTA_NAMESPACE + +namespace { + +nsAutoPtr<QuotaManager> gInstance; + +PLDHashOperator +RemoveQuotaForPatternCallback(const nsACString& aKey, + nsRefPtr<OriginInfo>& aValue, + void* aUserArg) +{ + NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); + NS_ASSERTION(aValue, "Null pointer!"); + NS_ASSERTION(aUserArg, "Null pointer!"); + + const nsACString* pattern = + static_cast<const nsACString*>(aUserArg); + + if (StringBeginsWith(aKey, *pattern)) { + return PL_DHASH_REMOVE; + } + + return PL_DHASH_NEXT; +} + +} // anonymous namespace + +void +QuotaObject::AddRef() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + if (!quotaManager) { + NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); + + NS_AtomicIncrementRefcnt(mRefCnt); + + return; + } + + MutexAutoLock lock(quotaManager->mQuotaMutex); + + ++mRefCnt; +} + +void +QuotaObject::Release() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + if (!quotaManager) { + NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); + + nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt); + if (count == 0) { + mRefCnt = 1; + delete this; + } + + return; + } + + { + MutexAutoLock lock(quotaManager->mQuotaMutex); + + --mRefCnt; + + if (mRefCnt > 0) { + return; + } + + if (mOriginInfo) { + mOriginInfo->mQuotaObjects.Remove(mPath); + } + } + + delete this; +} + +void +QuotaObject::UpdateSize(int64_t aSize) +{ + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + MutexAutoLock lock(quotaManager->mQuotaMutex); + + if (mOriginInfo) { + mOriginInfo->mUsage -= mSize; + mSize = aSize; + mOriginInfo->mUsage += mSize; + } +} + +bool +QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount) +{ + int64_t end = aOffset + aCount; + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + MutexAutoLock lock(quotaManager->mQuotaMutex); + + if (mSize >= end || !mOriginInfo) { + return true; + } + + int64_t newUsage = mOriginInfo->mUsage - mSize + end; + if (newUsage > mOriginInfo->mLimit) { + if (!indexedDB::IndexedDatabaseManager::QuotaIsLifted()) { + return false; + } + + nsCString origin = mOriginInfo->mOrigin; + + mOriginInfo->LockedClearOriginInfos(); + NS_ASSERTION(!mOriginInfo, + "Should have cleared in LockedClearOriginInfos!"); + + quotaManager->mOriginInfos.Remove(origin); + + mSize = end; + + return true; + } + + mOriginInfo->mUsage = newUsage; + mSize = end; + + return true; +} + +#ifdef DEBUG +void +OriginInfo::LockedClearOriginInfos() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->mQuotaMutex.AssertCurrentThreadOwns(); + + mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr); +} +#endif + +// static +PLDHashOperator +OriginInfo::ClearOriginInfoCallback(const nsAString& aKey, + QuotaObject* aValue, + void* aUserArg) +{ + NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); + NS_ASSERTION(aValue, "Null pointer!"); + + aValue->mOriginInfo = nullptr; + + return PL_DHASH_NEXT; +} + +// static +QuotaManager* +QuotaManager::GetOrCreate() +{ + if (!gInstance) { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + gInstance = new QuotaManager(); + + ClearOnShutdown(&gInstance); + } + + return gInstance; +} + +// static +QuotaManager* +QuotaManager::Get() +{ + // Does not return an owning reference. + return gInstance; +} + +void +QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin, + int64_t aLimit, + int64_t aUsage) +{ + OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage); + + MutexAutoLock lock(mQuotaMutex); + + NS_ASSERTION(!mOriginInfos.GetWeak(aOrigin), "Replacing an existing entry!"); + mOriginInfos.Put(aOrigin, info); +} + +void +QuotaManager::DecreaseUsageForOrigin(const nsACString& aOrigin, + int64_t aSize) +{ + MutexAutoLock lock(mQuotaMutex); + + nsRefPtr<OriginInfo> originInfo; + mOriginInfos.Get(aOrigin, getter_AddRefs(originInfo)); + + if (originInfo) { + originInfo->mUsage -= aSize; + } +} + +void +QuotaManager::RemoveQuotaForPattern(const nsACString& aPattern) +{ + NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!"); + + MutexAutoLock lock(mQuotaMutex); + + mOriginInfos.Enumerate(RemoveQuotaForPatternCallback, + const_cast<nsACString*>(&aPattern)); +} + +already_AddRefed<QuotaObject> +QuotaManager::GetQuotaObject(const nsACString& aOrigin, + nsIFile* aFile) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsString path; + nsresult rv = aFile->GetPath(path); + NS_ENSURE_SUCCESS(rv, nullptr); + + int64_t fileSize; + + bool exists; + rv = aFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, nullptr); + + if (exists) { + rv = aFile->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, nullptr); + } + else { + fileSize = 0; + } + + QuotaObject* info = nullptr; + { + MutexAutoLock lock(mQuotaMutex); + + nsRefPtr<OriginInfo> originInfo; + mOriginInfos.Get(aOrigin, getter_AddRefs(originInfo)); + + if (!originInfo) { + return nullptr; + } + + originInfo->mQuotaObjects.Get(path, &info); + + if (!info) { + info = new QuotaObject(originInfo, path, fileSize); + originInfo->mQuotaObjects.Put(path, info); + } + } + + nsRefPtr<QuotaObject> result = info; + return result.forget(); +} + +already_AddRefed<QuotaObject> +QuotaManager::GetQuotaObject(const nsACString& aOrigin, + const nsAString& aPath) +{ + nsresult rv; + nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = file->InitWithPath(aPath); + NS_ENSURE_SUCCESS(rv, nullptr); + + return GetQuotaObject(aOrigin, file); +}
new file mode 100644 --- /dev/null +++ b/dom/quota/QuotaManager.h @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_quota_quotamanager_h__ +#define mozilla_dom_quota_quotamanager_h__ + +#include "QuotaCommon.h" + +#include "mozilla/Mutex.h" +#include "nsDataHashtable.h" +#include "nsRefPtrHashtable.h" +#include "nsThreadUtils.h" + +BEGIN_QUOTA_NAMESPACE + +class OriginInfo; +class QuotaManager; + +class QuotaObject +{ + friend class OriginInfo; + friend class QuotaManager; + +public: + void + AddRef(); + + void + Release(); + + void + UpdateSize(int64_t aSize); + + bool + MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount); + +private: + QuotaObject(OriginInfo* aOriginInfo, const nsAString& aPath, int64_t aSize) + : mOriginInfo(aOriginInfo), mPath(aPath), mSize(aSize) + { } + + virtual ~QuotaObject() + { } + + nsAutoRefCnt mRefCnt; + + OriginInfo* mOriginInfo; + nsString mPath; + int64_t mSize; +}; + +class OriginInfo +{ + friend class QuotaManager; + friend class QuotaObject; + +public: + OriginInfo(const nsACString& aOrigin, int64_t aLimit, int64_t aUsage) + : mOrigin(aOrigin), mLimit(aLimit), mUsage(aUsage) + { + mQuotaObjects.Init(); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo) + +private: + void +#ifdef DEBUG + LockedClearOriginInfos(); +#else + LockedClearOriginInfos() + { + mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr); + } +#endif + + static PLDHashOperator + ClearOriginInfoCallback(const nsAString& aKey, + QuotaObject* aValue, void* aUserArg); + + nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects; + + nsCString mOrigin; + int64_t mLimit; + int64_t mUsage; +}; + +class QuotaManager +{ + friend class nsAutoPtr<QuotaManager>; + friend class OriginInfo; + friend class QuotaObject; + +public: + // Returns a non-owning reference. + static QuotaManager* + GetOrCreate(); + + // Returns a non-owning reference. + static QuotaManager* + Get(); + + void + InitQuotaForOrigin(const nsACString& aOrigin, + int64_t aLimit, + int64_t aUsage); + + void + DecreaseUsageForOrigin(const nsACString& aOrigin, + int64_t aSize); + + void + RemoveQuotaForPattern(const nsACString& aPattern); + + already_AddRefed<QuotaObject> + GetQuotaObject(const nsACString& aOrigin, + nsIFile* aFile); + + already_AddRefed<QuotaObject> + GetQuotaObject(const nsACString& aOrigin, + const nsAString& aPath); + +private: + QuotaManager() + : mQuotaMutex("QuotaManager.mQuotaMutex") + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + mOriginInfos.Init(); + } + + virtual ~QuotaManager() + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + } + + mozilla::Mutex mQuotaMutex; + + nsRefPtrHashtable<nsCStringHashKey, OriginInfo> mOriginInfos; +}; + +END_QUOTA_NAMESPACE + +#endif /* mozilla_dom_quota_quotamanager_h__ */
--- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -64,16 +64,17 @@ SHARED_LIBRARY_LIBS = \ $(DEPTH)/dom/base/$(LIB_PREFIX)jsdombase_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/battery/$(LIB_PREFIX)dom_battery_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/contacts/$(LIB_PREFIX)jsdomcontacts_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/alarm/$(LIB_PREFIX)domalarm_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/devicestorage/$(LIB_PREFIX)domdevicestorage_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/encoding/$(LIB_PREFIX)domencoding_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/file/$(LIB_PREFIX)domfile_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/power/$(LIB_PREFIX)dom_power_s.$(LIB_SUFFIX) \ + $(DEPTH)/dom/quota/$(LIB_PREFIX)domquota_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/settings/$(LIB_PREFIX)jsdomsettings_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/permission/$(LIB_PREFIX)jsdompermissionsettings_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/network/src/$(LIB_PREFIX)dom_network_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/media/$(LIB_PREFIX)dom_media_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/sms/src/$(LIB_PREFIX)dom_sms_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/events/$(LIB_PREFIX)jsdomevents_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/json/$(LIB_PREFIX)json_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/jsurl/$(LIB_PREFIX)jsurl_s.$(LIB_SUFFIX) \
--- a/netwerk/base/src/Makefile.in +++ b/netwerk/base/src/Makefile.in @@ -14,16 +14,17 @@ include $(DEPTH)/config/autoconf.mk MODULE = necko LIBRARY_NAME = neckobase_s LIBXUL_LIBRARY = 1 EXPORTS = \ nsMIMEInputStream.h \ nsURLHelper.h \ + nsFileStreams.h \ $(NULL) EXPORTS_NAMESPACES = mozilla/net EXPORTS_mozilla/net = \ Dashboard.h \ DashboardTypes.h \ $(NULL)
--- a/netwerk/base/src/nsFileStreams.cpp +++ b/netwerk/base/src/nsFileStreams.cpp @@ -46,17 +46,19 @@ nsFileStreamBase::nsFileStreamBase() { } nsFileStreamBase::~nsFileStreamBase() { Close(); } -NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStreamBase, nsISeekableStream) +NS_IMPL_THREADSAFE_ISUPPORTS2(nsFileStreamBase, + nsISeekableStream, + nsIFileMetadata) NS_IMETHODIMP nsFileStreamBase::Seek(int32_t whence, int64_t offset) { nsresult rv = DoPendingOpen(); NS_ENSURE_SUCCESS(rv, rv); if (mFD == nullptr) @@ -119,16 +121,62 @@ nsFileStreamBase::SetEOF() } #else // XXX not implemented #endif return NS_OK; } +NS_IMETHODIMP +nsFileStreamBase::GetSize(int64_t* _retval) +{ + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mFD) { + return NS_BASE_STREAM_CLOSED; + } + + PRFileInfo64 info; + if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) { + return NS_BASE_STREAM_OSERROR; + } + + *_retval = int64_t(info.size); + + return NS_OK; +} + +NS_IMETHODIMP +nsFileStreamBase::GetLastModified(int64_t* _retval) +{ + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mFD) { + return NS_BASE_STREAM_CLOSED; + } + + PRFileInfo64 info; + if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) { + return NS_BASE_STREAM_OSERROR; + } + + int64_t modTime = int64_t(info.modifyTime); + if (modTime == 0) { + *_retval = 0; + } + else { + *_retval = modTime / int64_t(PR_USEC_PER_MSEC); + } + + return NS_OK; +} + nsresult nsFileStreamBase::Close() { CleanUpOpen(); nsresult rv = NS_OK; if (mFD) { if (PR_Close(mFD) == PR_FAILURE) @@ -929,23 +977,22 @@ nsSafeFileOutputStream::Write(const char NS_WARNING("writing to output stream failed! data may be lost"); } return rv; } //////////////////////////////////////////////////////////////////////////////// // nsFileStream -NS_IMPL_ISUPPORTS_INHERITED4(nsFileStream, +NS_IMPL_ISUPPORTS_INHERITED3(nsFileStream, nsFileStreamBase, nsIInputStream, nsIOutputStream, - nsIFileStream, - nsIFileMetadata) - + nsIFileStream) + NS_IMETHODIMP nsFileStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm, int32_t behaviorFlags) { NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED); mBehaviorFlags = behaviorFlags; @@ -954,55 +1001,9 @@ nsFileStream::Init(nsIFile* file, int32_ ioFlags = PR_RDWR; if (perm <= 0) perm = 0; return MaybeOpen(file, ioFlags, perm, mBehaviorFlags & nsIFileStream::DEFER_OPEN); } -NS_IMETHODIMP -nsFileStream::GetSize(int64_t* _retval) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mFD) { - return NS_BASE_STREAM_CLOSED; - } - - PRFileInfo64 info; - if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) { - return NS_BASE_STREAM_OSERROR; - } - - *_retval = int64_t(info.size); - - return NS_OK; -} - -NS_IMETHODIMP -nsFileStream::GetLastModified(int64_t* _retval) -{ - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mFD) { - return NS_BASE_STREAM_CLOSED; - } - - PRFileInfo64 info; - if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) { - return NS_BASE_STREAM_OSERROR; - } - - int64_t modTime = int64_t(info.modifyTime); - if (modTime == 0) { - *_retval = 0; - } - else { - *_retval = modTime / int64_t(PR_USEC_PER_MSEC); - } - - return NS_OK; -} - ////////////////////////////////////////////////////////////////////////////////
--- a/netwerk/base/src/nsFileStreams.h +++ b/netwerk/base/src/nsFileStreams.h @@ -19,21 +19,23 @@ #include "prlog.h" #include "prio.h" #include "nsIIPCSerializableInputStream.h" #include "nsReadLine.h" //////////////////////////////////////////////////////////////////////////////// -class nsFileStreamBase : public nsISeekableStream +class nsFileStreamBase : public nsISeekableStream, + public nsIFileMetadata { public: NS_DECL_ISUPPORTS NS_DECL_NSISEEKABLESTREAM + NS_DECL_NSIFILEMETADATA nsFileStreamBase(); virtual ~nsFileStreamBase(); protected: nsresult Close(); nsresult Available(uint64_t* _retval); nsresult Read(char* aBuf, uint32_t aCount, uint32_t* _retval); @@ -119,18 +121,18 @@ public: uint32_t aCount, uint32_t* _retval) { return nsFileStreamBase::ReadSegments(aWriter, aClosure, aCount, _retval); } NS_IMETHOD IsNonBlocking(bool* _retval) { return nsFileStreamBase::IsNonBlocking(_retval); - } - + } + // Overrided from nsFileStreamBase NS_IMETHOD Seek(int32_t aWhence, int64_t aOffset); nsFileInputStream() : mLineBuffer(nullptr), mIOFlags(0), mPerm(0), mCachedPosition(0) {} virtual ~nsFileInputStream() @@ -255,23 +257,21 @@ protected: nsresult mWriteResult; // Internally set in Write() }; //////////////////////////////////////////////////////////////////////////////// class nsFileStream : public nsFileStreamBase, public nsIInputStream, public nsIOutputStream, - public nsIFileStream, - public nsIFileMetadata + public nsIFileStream { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIFILESTREAM - NS_DECL_NSIFILEMETADATA NS_FORWARD_NSIINPUTSTREAM(nsFileStreamBase::) // Can't use NS_FORWARD_NSIOUTPUTSTREAM due to overlapping methods // Close() and IsNonBlocking() NS_IMETHOD Flush() { return nsFileStreamBase::Flush(); }
--- a/storage/public/Makefile.in +++ b/storage/public/Makefile.in @@ -31,17 +31,16 @@ XPIDLSRCS = \ mozIStorageStatementRow.idl \ mozIStorageStatementCallback.idl \ mozIStoragePendingStatement.idl \ mozIStorageBindingParamsArray.idl \ mozIStorageBindingParams.idl \ mozIStorageCompletionCallback.idl \ mozIStorageBaseStatement.idl \ mozIStorageAsyncStatement.idl \ - mozIStorageServiceQuotaManagement.idl \ mozIStorageVacuumParticipant.idl \ $(NULL) # SEE ABOVE NOTE! EXPORTS_NAMESPACES = mozilla mozilla/storage EXPORTS = \ mozStorageHelper.h \
--- a/storage/public/mozIStorageService.idl +++ b/storage/public/mozIStorageService.idl @@ -2,25 +2,26 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" interface mozIStorageConnection; interface nsIFile; +interface nsIFileURL; /** * The mozIStorageService interface is intended to be implemented by * a service that can create storage connections (mozIStorageConnection) * to either a well-known profile database or to a specific database file. * * This is the only way to open a database connection. */ -[scriptable, uuid(fe8e95cb-b377-4c8d-bccb-d9198c67542b)] +[scriptable, uuid(12bfad34-cca3-40fb-8736-d8bf9db61a27)] interface mozIStorageService : nsISupports { /** * Get a connection to a named special database storage. * * @param aStorageKey a string key identifying the type of storage * requested. Valid values include: "profile", "memory". * * @see openDatabase for restrictions on how database connections may be @@ -101,16 +102,26 @@ interface mozIStorageService : nsISuppor * * @throws NS_ERROR_OUT_OF_MEMORY * If allocating a new storage object fails. * @throws NS_ERROR_FILE_CORRUPTED * If the database file is corrupted. */ mozIStorageConnection openUnsharedDatabase(in nsIFile aDatabaseFile); + /** + * See openDatabase(). Exactly the same only initialized with a file URL. + * Custom parameters can be passed to SQLite and VFS implementations through + * the query part of the URL. + * + * @param aURL + * A nsIFileURL that represents the database that is to be opened. + */ + mozIStorageConnection openDatabaseWithFileURL(in nsIFileURL aFileURL); + /* * Utilities */ /** * Copies the specified database file to the specified parent directory with * the specified file name. If the parent directory is not specified, it * places the backup in the same directory as the current file. This function
deleted file mode 100644 --- a/storage/public/mozIStorageServiceQuotaManagement.idl +++ /dev/null @@ -1,99 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface mozIStorageConnection; -interface nsIFile; - -[scriptable, function, uuid(ae94f0a5-ebdf-48f4-9959-085e13235d8d)] -interface mozIStorageQuotaCallback : nsISupports -{ - /** - * Called when the file size quota for a group of databases is exceeded. - * - * @param aFilename - * The filename of the database that has exceeded the quota. - * - * @param aCurrentSizeLimit - * The current size (in bytes) of the quota. - * - * @param aCurrentTotalSize - * The current size of all databases in the quota group. - * - * @param aUserData - * Any additional data that was provided to the - * setQuotaForFilenamePattern function. - * - * @returns A new quota size. A new quota of 0 will disable the quota callback - * and any quota value less than aCurrentTotalSize will cause the - * database operation to fail with NS_ERROR_FILE_NO_DEVICE_SPACE. - */ - long long quotaExceeded(in ACString aFilename, - in long long aCurrentSizeLimit, - in long long aCurrentTotalSize, - in nsISupports aUserData); -}; - -/** - * This is a temporary interface that should eventually merge with - * mozIStorageService. - */ -[scriptable, uuid(4d81faf5-fe01-428b-99b8-c94cba12fd72)] -interface mozIStorageServiceQuotaManagement : nsISupports -{ - /** - * See mozIStorageService.openDatabase. Exactly the same only with a custom - * SQLite VFS. - */ - mozIStorageConnection openDatabaseWithVFS(in nsIFile aDatabaseFile, - in ACString aVFSName); - - /** - * Set a file size quota for a group of databases matching the given filename - * pattern, optionally specifying a callback when the quota is exceeded. - * - * @param aPattern - * A pattern to match filenames for inclusion in the quota system. May - * contain the following special characters: - * '*' 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. - * - * @param aSizeLimit - * The size limit (in bytes) for the quota group. - * - * @param aCallback - * A callback that will be used when the quota is exceeded. - * - * @param aUserData - * Additional information to be passed to the callback. - */ - void setQuotaForFilenamePattern(in ACString aPattern, - in long long aSizeLimit, - in mozIStorageQuotaCallback aCallback, - in nsISupports aUserData); - - /** - * Adds, removes, or updates the file size information maintained by the quota - * system for files not opened through openDatabaseWithVFS(). - * - * Use this function when you want files to be included in quota calculations - * that are either a) not SQLite databases, or b) SQLite databases that have - * not been opened. - * - * This function will have no effect on files that do not match an existing - * quota pattern (set previously by setQuotaForFilenamePattern()). - * - * @param aFile - * The file for which quota information should be updated. If the file - * exists then its size information will be added or refreshed. If the - * file does not exist then the file will be removed from tracking - * under the quota system. - */ - void updateQuotaInformationForFile(in nsIFile aFile); -};
--- a/storage/public/storage.h +++ b/storage/public/storage.h @@ -19,17 +19,16 @@ #include "mozIStorageProgressHandler.h" #include "mozIStorageResultSet.h" #include "mozIStorageRow.h" #include "mozIStorageService.h" #include "mozIStorageStatement.h" #include "mozIStorageStatementCallback.h" #include "mozIStorageBindingParamsArray.h" #include "mozIStorageBindingParams.h" -#include "mozIStorageServiceQuotaManagement.h" #include "mozIStorageVacuumParticipant.h" #include "mozIStorageCompletionCallback.h" #include "mozIStorageAsyncStatement.h" //////////////////////////////////////////////////////////////////////////////// //// Native Language Helpers #include "mozStorageHelper.h"
--- a/storage/src/TelemetryVFS.cpp +++ b/storage/src/TelemetryVFS.cpp @@ -5,30 +5,32 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <string.h> #include "mozilla/Telemetry.h" #include "mozilla/Preferences.h" #include "sqlite3.h" #include "nsThreadUtils.h" #include "mozilla/Util.h" +#include "mozilla/dom/quota/QuotaManager.h" /** * This preference is a workaround to allow users/sysadmins to identify * that the profile exists on an NFS share whose implementation * is incompatible with SQLite's default locking implementation. * Bug 433129 attempted to automatically identify such file-systems, * but a reliable way was not found and it was determined that the fallback * locking is slower than POSIX locking, so we do not want to do it by default. */ #define PREF_NFS_FILESYSTEM "storage.nfs_filesystem" namespace { using namespace mozilla; +using namespace mozilla::dom::quota; struct Histograms { const char *name; const Telemetry::ID readB; const Telemetry::ID writeB; const Telemetry::ID readMS; const Telemetry::ID writeMS; const Telemetry::ID syncMS; @@ -77,33 +79,42 @@ public: } private: const TimeStamp start; const Telemetry::ID id; }; struct telemetry_file { - sqlite3_file base; // Base class. Must be first - Histograms *histograms; // histograms pertaining to this file - sqlite3_file pReal[1]; // This contains the vfs that actually does work + // Base class. Must be first + sqlite3_file base; + + // histograms pertaining to this file + Histograms *histograms; + + // quota object for this file + nsRefPtr<QuotaObject> quotaObject; + + // This contains the vfs that actually does work + sqlite3_file pReal[1]; }; /* ** Close a telemetry_file. */ int xClose(sqlite3_file *pFile) { telemetry_file *p = (telemetry_file *)pFile; int rc; rc = p->pReal->pMethods->xClose(p->pReal); if( rc==SQLITE_OK ){ delete p->base.pMethods; p->base.pMethods = NULL; + p->quotaObject = nullptr; } return rc; } /* ** Read data from a telemetry_file. */ int @@ -121,16 +132,19 @@ xRead(sqlite3_file *pFile, void *zBuf, i /* ** Write data to a telemetry_file. */ int xWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst) { telemetry_file *p = (telemetry_file *)pFile; + if (p->quotaObject && !p->quotaObject->MaybeAllocateMoreSpace(iOfst, iAmt)) { + return SQLITE_FULL; + } IOThreadAutoTimer ioTimer(p->histograms->writeMS); int rc; rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); Telemetry::Accumulate(p->histograms->writeB, rc == SQLITE_OK ? iAmt : 0); return rc; } /* @@ -139,16 +153,19 @@ xWrite(sqlite3_file *pFile, const void * int xTruncate(sqlite3_file *pFile, sqlite_int64 size) { IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS); telemetry_file *p = (telemetry_file *)pFile; int rc; Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_TRUNCATE_MS> timer; rc = p->pReal->pMethods->xTruncate(p->pReal, size); + if (rc == SQLITE_OK && p->quotaObject) { + p->quotaObject->UpdateSize(size); + } return rc; } /* ** Sync a telemetry_file. */ int xSync(sqlite3_file *pFile, int flags) @@ -295,16 +312,28 @@ xOpen(sqlite3_vfs* vfs, const char *zNam if (!match) continue; char c = match[strlen(h->name)]; // include -wal/-journal too if (!c || c == '-') break; } p->histograms = h; + + const char* origin; + if ((flags & SQLITE_OPEN_URI) && + (origin = sqlite3_uri_parameter(zName, "origin"))) { + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + p->quotaObject = quotaManager->GetQuotaObject(nsDependentCString(origin), + NS_ConvertUTF8toUTF16(zName)); + + } + rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags); if( rc != SQLITE_OK ) return rc; if( p->pReal->pMethods ){ sqlite3_io_methods *pNew = new sqlite3_io_methods; const sqlite3_io_methods *pSub = p->pReal->pMethods; memset(pNew, 0, sizeof(*pNew)); pNew->iVersion = pSub->iVersion;
--- a/storage/src/mozStorageConnection.cpp +++ b/storage/src/mozStorageConnection.cpp @@ -7,16 +7,17 @@ #include <stdio.h> #include "nsError.h" #include "nsIMutableArray.h" #include "nsAutoPtr.h" #include "nsIMemoryReporter.h" #include "nsThreadUtils.h" #include "nsIFile.h" +#include "nsIFileURL.h" #include "mozilla/Telemetry.h" #include "mozilla/Mutex.h" #include "mozilla/CondVar.h" #include "mozilla/Attributes.h" #include "mozIStorageAggregateFunction.h" #include "mozIStorageCompletionCallback.h" #include "mozIStorageFunction.h" @@ -466,44 +467,93 @@ Connection::getAsyncExecutionTarget() naming.SetThreadPoolName(NS_LITERAL_CSTRING("mozStorage"), mAsyncExecutionThread); } return mAsyncExecutionThread; } nsresult -Connection::initialize(nsIFile *aDatabaseFile, - const char* aVFSName) +Connection::initialize() { NS_ASSERTION (!mDBConn, "Initialize called on already opened database!"); SAMPLE_LABEL("storage", "Connection::initialize"); - int srv; - nsresult rv; + // in memory database requested, sqlite uses a magic file name + int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, NULL); + if (srv != SQLITE_OK) { + mDBConn = nullptr; + return convertResultCode(srv); + } + + return initializeInternal(nullptr); +} + +nsresult +Connection::initialize(nsIFile *aDatabaseFile) +{ + NS_ASSERTION (aDatabaseFile, "Passed null file!"); + NS_ASSERTION (!mDBConn, "Initialize called on already opened database!"); + SAMPLE_LABEL("storage", "Connection::initialize"); mDatabaseFile = aDatabaseFile; - if (aDatabaseFile) { - nsAutoString path; - rv = aDatabaseFile->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); + nsAutoString path; + nsresult rv = aDatabaseFile->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); - srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags, - aVFSName); - } - else { - // in memory database requested, sqlite uses a magic file name - srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, aVFSName); - } + int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, + mFlags, NULL); if (srv != SQLITE_OK) { mDBConn = nullptr; return convertResultCode(srv); } + rv = initializeInternal(aDatabaseFile); + NS_ENSURE_SUCCESS(rv, rv); + + mDatabaseFile = aDatabaseFile; + + return NS_OK; +} + +nsresult +Connection::initialize(nsIFileURL *aFileURL) +{ + NS_ASSERTION (aFileURL, "Passed null file URL!"); + NS_ASSERTION (!mDBConn, "Initialize called on already opened database!"); + SAMPLE_LABEL("storage", "Connection::initialize"); + + nsCOMPtr<nsIFile> databaseFile; + nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString spec; + rv = aFileURL->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + + int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, NULL); + if (srv != SQLITE_OK) { + mDBConn = nullptr; + return convertResultCode(srv); + } + + rv = initializeInternal(databaseFile); + NS_ENSURE_SUCCESS(rv, rv); + + mFileURL = aFileURL; + mDatabaseFile = databaseFile; + + return NS_OK; +} + + +nsresult +Connection::initializeInternal(nsIFile* aDatabaseFile) +{ // Properly wrap the database handle's mutex. sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn)); #ifdef PR_LOGGING if (!gStorageLog) gStorageLog = ::PR_NewLogModule("mozStorage"); ::sqlite3_trace(mDBConn, tracefunc, this); @@ -517,24 +567,24 @@ Connection::initialize(nsIFile *aDatabas // Set page_size to the preferred default value. This is effective only if // the database has just been created, otherwise, if the database does not // use WAL journal mode, a VACUUM operation will updated its page_size. int64_t pageSize = DEFAULT_PAGE_SIZE; nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size = "); pageSizeQuery.AppendInt(pageSize); - rv = ExecuteSimpleSQL(pageSizeQuery); + nsresult rv = ExecuteSimpleSQL(pageSizeQuery); NS_ENSURE_SUCCESS(rv, rv); // Get the current page_size, since it may differ from the specified value. sqlite3_stmt *stmt; NS_NAMED_LITERAL_CSTRING(pragma_page_size, MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"); - srv = prepareStatement(pragma_page_size, &stmt); + int srv = prepareStatement(pragma_page_size, &stmt); if (srv == SQLITE_OK) { if (SQLITE_ROW == stepStatement(stmt)) { pageSize = ::sqlite3_column_int64(stmt, 0); } (void)::sqlite3_finalize(stmt); } // Setting the cache_size forces the database open, verifying if it is valid @@ -957,17 +1007,18 @@ Connection::Clone(bool aReadOnly, // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY. flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY; // Turn off SQLITE_OPEN_CREATE. flags = (~SQLITE_OPEN_CREATE & flags); } nsRefPtr<Connection> clone = new Connection(mStorageService, flags); NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY); - nsresult rv = clone->initialize(mDatabaseFile); + nsresult rv = mFileURL ? clone->initialize(mFileURL) + : clone->initialize(mDatabaseFile); NS_ENSURE_SUCCESS(rv, rv); // Copy over pragmas from the original connection. static const char * pragmas[] = { "cache_size", "temp_store", "foreign_keys", "journal_size_limit",
--- a/storage/src/mozStorageConnection.h +++ b/storage/src/mozStorageConnection.h @@ -20,16 +20,17 @@ #include "nsIMutableArray.h" #include "mozilla/Attributes.h" #include "sqlite3.h" struct PRLock; class nsIFile; +class nsIFileURL; class nsIEventTarget; class nsIThread; namespace mozilla { namespace storage { class Connection MOZ_FINAL : public mozIStorageConnection , public nsIInterfaceRequestor @@ -58,28 +59,37 @@ public: * Pointer to the storage service. Held onto for the lifetime of the * connection. * @param aFlags * The flags to pass to sqlite3_open_v2. */ Connection(Service *aService, int aFlags); /** + * Creates the connection to an in-memory database. + */ + nsresult initialize(); + + /** * Creates the connection to the database. * * @param aDatabaseFile * The nsIFile of the location of the database to open, or create if it - * does not exist. Passing in nullptr here creates an in-memory - * database. - * @param aVFSName - * The VFS that SQLite will use when opening this database. NULL means - * "default". + * does not exist. */ - nsresult initialize(nsIFile *aDatabaseFile, - const char* aVFSName = NULL); + nsresult initialize(nsIFile *aDatabaseFile); + + /** + * Creates the connection to the database. + * + * @param aFileURL + * The nsIFileURL of the location of the database to open, or create if it + * does not exist. + */ + nsresult initialize(nsIFileURL *aFileURL); // fetch the native handle sqlite3 *GetNativeConnection() { return mDBConn; } operator sqlite3 *() const { return mDBConn; } /** * Lazily creates and returns a background execution thread. In the future, * the thread may be re-claimed if left idle, so you should call this @@ -150,16 +160,18 @@ public: * True if this is an async connection, it is shutting down and it is not * closed yet. */ bool isAsyncClosing(); private: ~Connection(); + nsresult initializeInternal(nsIFile *aDatabaseFile); + /** * Sets the database into a closed state so no further actions can be * performed. * * @note mDBConn is set to NULL in this method. */ nsresult setClosedState(); @@ -201,16 +213,17 @@ private: static int sProgressHelper(void *aArg); // Generic progress handler // Dispatch call to registered progress handler, // if there is one. Do nothing in other cases. int progressHandler(); sqlite3 *mDBConn; + nsCOMPtr<nsIFileURL> mFileURL; nsCOMPtr<nsIFile> mDatabaseFile; /** * Lazily created thread for asynchronous statement execution. Consumers * should use getAsyncExecutionTarget rather than directly accessing this * field. */ nsCOMPtr<nsIThread> mAsyncExecutionThread;
--- a/storage/src/mozStorageService.cpp +++ b/storage/src/mozStorageService.cpp @@ -19,82 +19,25 @@ #include "nsILocale.h" #include "nsILocaleService.h" #include "nsIXPConnect.h" #include "nsIObserverService.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" #include "sqlite3.h" -#include "test_quota.h" -#include "test_quota.c" #ifdef SQLITE_OS_WIN // "windows.h" was included and it can #define lots of things we care about... #undef CompareString #endif #include "nsIPromptService.h" #include "nsIMemoryReporter.h" -namespace { - -class QuotaCallbackData -{ -public: - QuotaCallbackData(mozIStorageQuotaCallback *aCallback, - nsISupports *aUserData) - : callback(aCallback), userData(aUserData) - { - MOZ_COUNT_CTOR(QuotaCallbackData); - } - - ~QuotaCallbackData() - { - MOZ_COUNT_DTOR(QuotaCallbackData); - } - - static void Callback(const char *zFilename, - sqlite3_int64 *piLimit, - sqlite3_int64 iSize, - void *pArg) - { - NS_ASSERTION(zFilename && strlen(zFilename), "Null or empty filename!"); - NS_ASSERTION(piLimit, "Null pointer!"); - - QuotaCallbackData *data = static_cast<QuotaCallbackData*>(pArg); - if (!data) { - // No callback specified, return immediately. - return; - } - - NS_ASSERTION(data->callback, "Should never have a null callback!"); - - nsDependentCString filename(zFilename); - - int64_t newLimit; - if (NS_SUCCEEDED(data->callback->QuotaExceeded(filename, *piLimit, - iSize, data->userData, - &newLimit))) { - *piLimit = newLimit; - } - } - - static void Destroy(void *aUserData) - { - delete static_cast<QuotaCallbackData*>(aUserData); - } - -private: - nsCOMPtr<mozIStorageQuotaCallback> callback; - nsCOMPtr<nsISupports> userData; -}; - -} // anonymous namespace - //////////////////////////////////////////////////////////////////////////////// //// Defines #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous" #define PREF_TS_SYNCHRONOUS_DEFAULT 1 namespace mozilla { namespace storage { @@ -340,21 +283,20 @@ private: nsIObserver *mObserver; nsIXPConnect **mXPConnectPtr; int32_t *mSynchronousPrefValPtr; }; //////////////////////////////////////////////////////////////////////////////// //// Service -NS_IMPL_THREADSAFE_ISUPPORTS3( +NS_IMPL_THREADSAFE_ISUPPORTS2( Service, mozIStorageService, - nsIObserver, - mozIStorageServiceQuotaManagement + nsIObserver ) Service *Service::gService = nullptr; Service * Service::getSingleton() { if (gService) { @@ -433,20 +375,16 @@ Service::~Service() (void)::NS_UnregisterMemoryMultiReporter(mStorageSQLiteMultiReporter); int rc = sqlite3_vfs_unregister(mSqliteVFS); if (rc != SQLITE_OK) NS_WARNING("Failed to unregister sqlite vfs wrapper."); // Shutdown the sqlite3 API. Warn if shutdown did not turn out okay, but // there is nothing actionable we can do in that case. - rc = ::sqlite3_quota_shutdown(); - if (rc != SQLITE_OK) - NS_WARNING("sqlite3 did not shutdown cleanly."); - rc = ::sqlite3_shutdown(); if (rc != SQLITE_OK) NS_WARNING("sqlite3 did not shutdown cleanly."); DebugOnly<bool> shutdownObserved = !sXPConnect; NS_ASSERTION(shutdownObserved, "Shutdown was not observed!"); gService = nullptr; @@ -631,19 +569,16 @@ Service::initialize() mSqliteVFS = ConstructTelemetryVFS(); if (mSqliteVFS) { rc = sqlite3_vfs_register(mSqliteVFS, 1); if (rc != SQLITE_OK) return convertResultCode(rc); } else { NS_WARNING("Failed to register telemetry VFS"); } - rc = ::sqlite3_quota_initialize("telemetry-vfs", 0); - if (rc != SQLITE_OK) - return convertResultCode(rc); // Set the default value for the toolkit.storage.synchronous pref. It will be // updated with the user preference on the main thread. sSynchronousPref = PREF_TS_SYNCHRONOUS_DEFAULT; // Run the things that need to run on the main thread there. nsCOMPtr<nsIRunnable> event = new ServiceMainThreadInitializer(this, this, &sXPConnect, &sSynchronousPref); @@ -734,77 +669,90 @@ Service::OpenSpecialDatabase(const char nsresult rv; nsCOMPtr<nsIFile> storageFile; if (::strcmp(aStorageKey, "memory") == 0) { // just fall through with NULL storageFile, this will cause the storage // connection to use a memory DB. } else if (::strcmp(aStorageKey, "profile") == 0) { - rv = NS_GetSpecialDirectory(NS_APP_STORAGE_50_FILE, getter_AddRefs(storageFile)); NS_ENSURE_SUCCESS(rv, rv); - nsString filename; - storageFile->GetPath(filename); - nsCString filename8 = NS_ConvertUTF16toUTF8(filename.get()); // fall through to DB initialization } else { return NS_ERROR_INVALID_ARG; } - Connection *msc = new Connection(this, SQLITE_OPEN_READWRITE); - NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY); + nsRefPtr<Connection> msc = new Connection(this, SQLITE_OPEN_READWRITE); - rv = msc->initialize(storageFile); + rv = storageFile ? msc->initialize(storageFile) : msc->initialize(); NS_ENSURE_SUCCESS(rv, rv); - NS_ADDREF(*_connection = msc); + msc.forget(_connection); return NS_OK; + } NS_IMETHODIMP Service::OpenDatabase(nsIFile *aDatabaseFile, mozIStorageConnection **_connection) { NS_ENSURE_ARG(aDatabaseFile); // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility // reasons. int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_CREATE; nsRefPtr<Connection> msc = new Connection(this, flags); - NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY); nsresult rv = msc->initialize(aDatabaseFile); NS_ENSURE_SUCCESS(rv, rv); - NS_ADDREF(*_connection = msc); + msc.forget(_connection); return NS_OK; } NS_IMETHODIMP Service::OpenUnsharedDatabase(nsIFile *aDatabaseFile, mozIStorageConnection **_connection) { NS_ENSURE_ARG(aDatabaseFile); // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility // reasons. int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_PRIVATECACHE | SQLITE_OPEN_CREATE; nsRefPtr<Connection> msc = new Connection(this, flags); - NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY); nsresult rv = msc->initialize(aDatabaseFile); NS_ENSURE_SUCCESS(rv, rv); - NS_ADDREF(*_connection = msc); + msc.forget(_connection); + return NS_OK; +} + +NS_IMETHODIMP +Service::OpenDatabaseWithFileURL(nsIFileURL *aFileURL, + mozIStorageConnection **_connection) +{ + NS_ENSURE_ARG(aFileURL); + + // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility + // reasons. + int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | + SQLITE_OPEN_CREATE | SQLITE_OPEN_URI; + nsRefPtr<Connection> msc = new Connection(this, flags); + + nsresult rv = msc->initialize(aFileURL); + NS_ENSURE_SUCCESS(rv, rv); + + msc.forget(_connection); return NS_OK; } NS_IMETHODIMP Service::BackupDatabaseFile(nsIFile *aDBFile, const nsAString &aBackupFileName, nsIFile *aBackupParentDirectory, nsIFile **backup) @@ -880,72 +828,10 @@ Service::Observe(nsISupports *, const ch MOZ_ASSERT(!connections[i]->ConnectionReady()); } #endif } return NS_OK; } -//////////////////////////////////////////////////////////////////////////////// -//// mozIStorageServiceQuotaManagement - -NS_IMETHODIMP -Service::OpenDatabaseWithVFS(nsIFile *aDatabaseFile, - const nsACString &aVFSName, - mozIStorageConnection **_connection) -{ - NS_ENSURE_ARG(aDatabaseFile); - - // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility - // reasons. - int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | - SQLITE_OPEN_CREATE; - nsRefPtr<Connection> msc = new Connection(this, flags); - NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY); - - nsresult rv = msc->initialize(aDatabaseFile, - PromiseFlatCString(aVFSName).get()); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ADDREF(*_connection = msc); - return NS_OK; -} - -NS_IMETHODIMP -Service::SetQuotaForFilenamePattern(const nsACString &aPattern, - int64_t aSizeLimit, - mozIStorageQuotaCallback *aCallback, - nsISupports *aUserData) -{ - NS_ENSURE_FALSE(aPattern.IsEmpty(), NS_ERROR_INVALID_ARG); - - nsAutoPtr<QuotaCallbackData> data; - if (aSizeLimit && aCallback) { - data = new QuotaCallbackData(aCallback, aUserData); - } - - int rc = ::sqlite3_quota_set(PromiseFlatCString(aPattern).get(), - aSizeLimit, QuotaCallbackData::Callback, - data, QuotaCallbackData::Destroy); - NS_ENSURE_TRUE(rc == SQLITE_OK, convertResultCode(rc)); - - data.forget(); - return NS_OK; -} - -NS_IMETHODIMP -Service::UpdateQuotaInformationForFile(nsIFile *aFile) -{ - NS_ENSURE_ARG_POINTER(aFile); - - nsString path; - nsresult rv = aFile->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); - - int rc = ::sqlite3_quota_file(NS_ConvertUTF16toUTF8(path).get()); - NS_ENSURE_TRUE(rc == SQLITE_OK, convertResultCode(rc)); - - return NS_OK; -} - } // namespace storage } // namespace mozilla
--- a/storage/src/mozStorageService.h +++ b/storage/src/mozStorageService.h @@ -10,30 +10,28 @@ #include "nsCOMPtr.h" #include "nsICollation.h" #include "nsIFile.h" #include "nsIObserver.h" #include "nsTArray.h" #include "mozilla/Mutex.h" #include "mozIStorageService.h" -#include "mozIStorageServiceQuotaManagement.h" class nsIMemoryReporter; class nsIMemoryMultiReporter; class nsIXPConnect; struct sqlite3_vfs; namespace mozilla { namespace storage { class Connection; class Service : public mozIStorageService , public nsIObserver - , public mozIStorageServiceQuotaManagement { public: /** * Initializes the service. This must be called before any other function! */ nsresult initialize(); /** @@ -53,17 +51,16 @@ public: const nsAString &aStr2, int32_t aComparisonStrength); static Service *getSingleton(); NS_DECL_ISUPPORTS NS_DECL_MOZISTORAGESERVICE NS_DECL_NSIOBSERVER - NS_DECL_MOZISTORAGESERVICEQUOTAMANAGEMENT /** * Obtains an already AddRefed pointer to XPConnect. This is used by * language helpers. */ static already_AddRefed<nsIXPConnect> getXPConnect(); /**
--- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -63,16 +63,17 @@ MAKEFILES_dom=" dom/mms/interfaces/Makefile dom/mms/src/Makefile dom/network/Makefile dom/network/interfaces/Makefile dom/network/src/Makefile dom/plugins/base/Makefile dom/plugins/ipc/Makefile dom/power/Makefile + dom/quota/Makefile dom/settings/Makefile dom/sms/Makefile dom/sms/interfaces/Makefile dom/sms/src/Makefile dom/src/Makefile dom/src/events/Makefile dom/src/jsurl/Makefile dom/src/geolocation/Makefile