[almanah] Decryption from file to non-pageable memory



commit e265b5a29e3628eedf2a3bf913eb16e1dc7f1b1f
Author: Álvaro Peña <alvaropg gmail com>
Date:   Fri Nov 21 20:54:03 2014 +0100

    Decryption from file to non-pageable memory
    
    Another big step to complete our own SQLite VFS: now the encrypted file
    decrypts to a non-pageable memory zone and never touch the disk again
    until encryptio again when the SQLiteVFS closes the file.
    
    This require the GCR package, but I have no clue what exactly version is
    required, because the gcr_secure_memory development documentation not
    mention when this API was introduced.
    
    See Bug #659500 - Prevent the unencrypted database from hitting the disk
    https://bugzilla.gnome.org/show_bug.cgi?id=659500

 configure.ac |    2 +-
 src/vfs.c    |  535 ++++++++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 392 insertions(+), 145 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index cf156e2..8df1b74 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,7 +106,7 @@ fi
 
 dnl Encryption support
 if test $encryption = "true"; then
-       PKG_CHECK_MODULES(ENCRYPTION, cryptui-0.0)
+       PKG_CHECK_MODULES(ENCRYPTION, cryptui-0.0 gcr-base-3)
 
        dnl Check for GPGME, which doesn't provide a pkgconfig file
        dnl This code courtesy of seahorse
diff --git a/src/vfs.c b/src/vfs.c
index 4c52e65..fd7a7bf 100644
--- a/src/vfs.c
+++ b/src/vfs.c
@@ -44,6 +44,8 @@
 #include <config.h>
 
 #ifdef ENABLE_ENCRYPTION
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr-base.h>
 #include <gpgme.h>
 #define ENCRYPTED_SUFFIX ".encrypted"
 #endif /* ENABLE_ENCRYPTION */
@@ -64,8 +66,10 @@
 ** When using this VFS, the sqlite3_file* handles that SQLite uses are
 ** actually pointers to instances of type DemoFile.
 */
-typedef struct AlmanahSQLiteVFS AlmanahSQLiteVFS;
-struct AlmanahSQLiteVFS
+
+typedef struct _AlmanahSQLiteVFS AlmanahSQLiteVFS;
+
+struct _AlmanahSQLiteVFS
 {
        sqlite3_file base;              /* Base class. Must be first. */
        int fd;                         /* File descriptor */
@@ -78,19 +82,144 @@ struct AlmanahSQLiteVFS
 #ifdef ENABLE_ENCRYPTION
        gchar *encrypted_filename;
        gboolean decrypted;
+
+       gpointer plain_buffer;
+       gsize    plain_buffer_size;     /* Reserved memory size */
+       goffset  plain_offset;
+       gsize    plain_size;            /* Data size (plain_size <= plain_buffer_size) */
 #endif /* ENABLE_ENCRYPTION */
 };
 
 #ifdef ENABLE_ENCRYPTION
 
-typedef struct {
+typedef struct _CipherOperation CipherOperation;
+typedef struct _GpgmeNpmClosure GpgmeNpmClosure;
+
+struct _GpgmeNpmClosure {
+       gpointer buffer;
+       gsize    buffer_size;
+       goffset  offset;
+       gsize    size;
+};
+
+struct _CipherOperation {
        GIOChannel *cipher_io_channel;
        GIOChannel *plain_io_channel;
        gpgme_data_t gpgme_cipher;
        gpgme_data_t gpgme_plain;
        gpgme_ctx_t context;
        AlmanahSQLiteVFS *vfs;
-} CipherOperation;
+       struct gpgme_data_cbs *gpgme_cbs;
+       GpgmeNpmClosure *npm_closure;
+};
+
+enum {
+       ALMANAH_VFS_ERROR_DECRYPT = 1
+};
+
+#define ALMANAH_VFS_ERROR almanah_vfs_error_quark ()
+
+GQuark
+almanah_vfs_error_quark (void)
+{
+  return g_quark_from_static_string ("almanah-vfs-error-quark");
+}
+
+/* Callback based data buffer functions for GPGME */
+ssize_t _gpgme_read_cb    (void *handle, void *buffer, size_t size);
+ssize_t _gpgme_write_cb   (void *handle, const void *buffer, size_t size);
+off_t   _gpgme_seek_cb    (void *handle, off_t offset, int whence);
+
+ssize_t
+_gpgme_read_cb (void *handle, void *buffer, size_t size)
+{
+        GpgmeNpmClosure *npm_closure = (GpgmeNpmClosure *) handle;
+       gsize read_size;
+
+       read_size = npm_closure->size - npm_closure->offset;
+       if (!read_size)
+               return 0;
+       if (size < read_size)
+               read_size = size;
+
+       memcpy (buffer, npm_closure->buffer + npm_closure->offset, read_size);
+       npm_closure->offset += read_size;
+
+       return (ssize_t) read_size;
+}
+
+ssize_t
+_gpgme_write_cb (void *handle, const void *buffer, size_t size)
+{
+       GpgmeNpmClosure *npm_closure = (GpgmeNpmClosure *) handle;
+       gsize unused;
+
+       unused = npm_closure->buffer_size - npm_closure->offset;
+       if (unused < size) {
+               gsize new_size;
+               gsize exponential_size;
+               gsize required_size;
+               gpointer new_buffer;
+
+               exponential_size = npm_closure->size ? (2 * npm_closure->size) : 512;
+               required_size = npm_closure->offset + size;
+
+               new_size = MAX (exponential_size, required_size);
+               new_buffer = gcr_secure_memory_try_realloc (npm_closure->buffer, new_size);
+               if (!new_buffer && (new_size > required_size)) {
+                       new_size = required_size;
+                       new_buffer = gcr_secure_memory_realloc (npm_closure->buffer, new_size);
+               }
+
+               if (!new_buffer)
+                       return -1;
+
+               npm_closure->buffer = new_buffer;
+               npm_closure->buffer_size = new_size;
+       }
+
+       memcpy (npm_closure->buffer + npm_closure->offset, buffer, size);
+       npm_closure->offset += (gsize) size;
+       npm_closure->size = MAX(npm_closure->size, (gsize) npm_closure->offset);
+
+       return size;
+}
+
+off_t
+_gpgme_seek_cb (void *handle, off_t offset, int whence)
+{
+       GpgmeNpmClosure *npm_closure = (GpgmeNpmClosure *) handle;
+
+       switch (whence) {
+       case SEEK_SET:
+               if (offset < 0 || (gsize) offset > npm_closure->size) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               npm_closure->offset = (goffset) offset;
+               break;
+       case SEEK_CUR:
+               if ((offset > 0 && (npm_closure->size - npm_closure->offset) < (gsize) offset)
+                   || (offset < 0 && npm_closure->offset < -offset)) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               npm_closure->offset += offset;
+               break;
+       case SEEK_END:
+               if (offset > 0 || -offset > npm_closure->size) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               npm_closure->offset = npm_closure->size + offset;
+               break;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+
+       return (off_t) npm_closure->offset;
+}
 
 static gboolean
 prepare_gpgme (CipherOperation *operation)
@@ -145,21 +274,19 @@ open_db_files (AlmanahSQLiteVFS *self, gboolean encrypting, CipherOperation *ope
                return FALSE;
        }
 
-       /* Open/Create the plain file */
-       operation->plain_io_channel = g_io_channel_new_file (self->plain_filename, encrypting ? "r" : "w", 
&io_error);
-       if (operation->plain_io_channel == NULL) {
-               g_critical (_("Can't create a new GIOChannel for the plain database: %s"), io_error->message);
-               g_propagate_error (error, io_error);
-               return FALSE;
-       }
-
-       /* Ensure the permissions are restricted to only the current user */
-       fchmod (g_io_channel_unix_get_fd (operation->plain_io_channel), S_IRWXU);
-
-       /* Pass it to GPGME */
-       error_gpgme = gpgme_data_new_from_fd (&(operation->gpgme_plain), g_io_channel_unix_get_fd 
(operation->plain_io_channel));
+       /* Pass the non-pageable memory to GPGME as a Callback Base Data Buffer, 
+        * see: http://www.gnupg.org/documentation/manuals/gpgme/Callback-Based-Data-Buffers.html
+        */
+       operation->npm_closure = g_new0 (GpgmeNpmClosure, 1);
+       operation->gpgme_cbs = g_new0 (struct gpgme_data_cbs, 1);
+       operation->gpgme_cbs->read =_gpgme_read_cb;
+       operation->gpgme_cbs->write =_gpgme_write_cb;
+       operation->gpgme_cbs->seek =_gpgme_seek_cb;
+       error_gpgme = gpgme_data_new_from_cbs (&(operation->gpgme_plain), operation->gpgme_cbs, 
operation->npm_closure);
        if (error_gpgme != GPG_ERR_NO_ERROR) {
-               g_critical (_("Error opening plain database file \"%s\": %s"), self->plain_filename, 
gpgme_strerror (error_gpgme));
+               g_set_error (error, 0, 0,
+                            _("Error creating Callback base data buffer: %s"),
+                            gpgme_strerror (error_gpgme));
                return FALSE;
        }
 
@@ -188,6 +315,9 @@ cipher_operation_free (CipherOperation *operation)
                gpgme_release (operation->context);
        }
 
+       if (operation->npm_closure != NULL)
+               g_free (operation->npm_closure);
+
        g_free (operation);
 }
 
@@ -211,10 +341,18 @@ decrypt_database (AlmanahSQLiteVFS *self, GError **error)
        error_gpgme = gpgme_op_decrypt_verify (operation->context, operation->gpgme_cipher, 
operation->gpgme_plain);
        if (error_gpgme != GPG_ERR_NO_ERROR) {
                cipher_operation_free (operation);
-               g_critical (_("Error decrypting database: %s"), gpgme_strerror (error_gpgme));
+               g_set_error (error,
+                            ALMANAH_VFS_ERROR,
+                            ALMANAH_VFS_ERROR_DECRYPT,
+                            "%s: %s", gpgme_strsource (error_gpgme), gpgme_strerror (error_gpgme));
                return FALSE;
        }
 
+       /* Setup the database content in memory */
+       self->plain_buffer = operation->npm_closure->buffer;
+       self->plain_offset = 0;
+       self->plain_size = operation->npm_closure->size;
+
        /* Do this one synchronously */
        cipher_operation_free (operation);
 
@@ -255,6 +393,10 @@ encrypt_database (AlmanahSQLiteVFS *self,  const gchar *encryption_key, GError *
                return FALSE;
        }
 
+       operation->npm_closure->buffer = self->plain_buffer;
+       operation->npm_closure->offset = 0;
+       operation->npm_closure->size = self->plain_size;
+
        /* Encrypt and sign! */
        error_gpgme = gpgme_op_encrypt_sign_start (operation->context, gpgme_keys, 0, operation->gpgme_plain, 
operation->gpgme_cipher);
        gpgme_key_unref (gpgme_keys[0]);
@@ -291,8 +433,6 @@ encrypt_database (AlmanahSQLiteVFS *self,  const gchar *encryption_key, GError *
        }
 
        return TRUE;
-
-       return TRUE;
 }
 
 static gchar *
@@ -364,22 +504,54 @@ back_up_file (const gchar *filename)
 ** file has a write-buffer (AlmanahSQLiteVFS.aBuffer), ignore it.
 */
 static int
-demoDirectWrite (AlmanahSQLiteVFS *p,            /* File handle */
-                const void *zBuf,               /* Buffer containing data to write */
-                int iAmt,                       /* Size of data to write in bytes */
-                sqlite_int64 iOfst              /* File offset to write to */
+demoDirectWrite (AlmanahSQLiteVFS *self,            /* File handle */
+                const void *buffer,               /* Buffer containing data to write */
+                int len,                       /* Size of data to write in bytes */
+                sqlite_int64 offset              /* File offset to write to */
                 )
 {
-       off_t ofst;                     /* Return value from lseek() */
-       size_t nWrite;                  /* Return value from write() */
+       off_t ofst;
+       size_t nWrite;
+
+#ifdef ENABLE_ENCRYPTION
+       if (self->decrypted) {
+               if ((gsize) (offset + len) > self->plain_buffer_size) {
+                       gsize new_size;
+                       gsize exponential_size;
+                       gsize required_size;
+                       gpointer new_buffer = NULL;
+
+                       exponential_size = self->plain_size ? (2 * self->plain_size) : 512;
+                       required_size = offset + len;
+
+                       new_size = MAX (exponential_size, required_size);
+                       new_buffer = gcr_secure_memory_try_realloc (self->plain_buffer, new_size);
+                       if (new_buffer == NULL && (new_size > required_size)) {
+                               new_size = required_size;
+                               new_buffer = gcr_secure_memory_realloc (self->plain_buffer, new_size);
+                       }
 
-       ofst = lseek (p->fd, iOfst, SEEK_SET);
-       if (ofst!=iOfst) {
+                       if (new_buffer == NULL)
+                               return SQLITE_NOMEM;
+
+                       self->plain_buffer = new_buffer;
+                       self->plain_buffer_size = new_size;
+               }
+
+               memcpy (self->plain_buffer + offset, buffer, len);
+               self->plain_size = MAX(self->plain_size, (gsize) (offset + len));
+
+               return SQLITE_OK;
+       }
+#endif /* ENABLE_ENCRYPTION */
+
+       ofst = lseek (self->fd, offset, SEEK_SET);
+       if (ofst != offset) {
                return SQLITE_IOERR_WRITE;
        }
 
-       nWrite = write (p->fd, zBuf, iAmt);
-       if (nWrite != iAmt){
+       nWrite = write (self->fd, buffer, len);
+       if (nWrite != (size_t) len){
                return SQLITE_IOERR_WRITE;
        }
 
@@ -391,9 +563,16 @@ demoDirectWrite (AlmanahSQLiteVFS *p,            /* File handle */
 ** no-op if this particular file does not have a buffer (i.e. it is not
 ** a journal file) or if the buffer is currently empty.
 */
-static int demoFlushBuffer(AlmanahSQLiteVFS *p){
+static int
+demoFlushBuffer (AlmanahSQLiteVFS *p)
+{
+#ifdef ENABLE_ENCRYPTION
+       if (p->decrypted)
+               return SQLITE_OK;
+#endif /* ENABLE_ENCRYPTION */
        int rc = SQLITE_OK;
-       if( p->nBuffer ){
+
+       if (p->nBuffer) {
                rc = demoDirectWrite(p, p->aBuffer, p->nBuffer, p->iBufferOfst);
                p->nBuffer = 0;
        }
@@ -406,37 +585,45 @@ static int demoFlushBuffer(AlmanahSQLiteVFS *p){
 static int
 demoClose (sqlite3_file *pFile)
 {
-       int rc;
+       int rc = SQLITE_OK;
        AlmanahSQLiteVFS *self = (AlmanahSQLiteVFS*) pFile;
 #ifdef ENABLE_ENCRYPTION
        gchar *encryption_key;
        GError *child_error = NULL;
-#endif /* ENABLE_ENCRYPTION */
 
-       rc = demoFlushBuffer (self);
-       sqlite3_free (self->aBuffer);
-       close (self->fd);
+       /* TODO: This code is not completed */
 
-#ifdef ENABLE_ENCRYPTION
-       /* If the database wasn't encrypted before we opened it, we won't encrypt it when closing. In fact, 
we'll go so far as to delete the old
-        * encrypted database file. */
        if (self->decrypted == FALSE)
-               goto delete_encrypted_db;
+               goto close_db;
 
        /* Get the encryption key */
        encryption_key = get_encryption_key ();
-       if (encryption_key == NULL)
+       if (encryption_key == NULL) {
+               /* TODO: No encryption key, so save in plain file from DB in memory,
+                  this happends when the user has changed from encrypted to no encrypted */
+               if (self->plain_buffer)
+                       gcr_secure_memory_free (self->plain_buffer);
                goto delete_encrypted_db;
-
-       /* Encrypt the plain DB file */
-       if (encrypt_database (self, encryption_key, &child_error) != TRUE) {
-               rc = SQLITE_IOERR;
        }
 
-       g_free (encryption_key);
+       /* Encrypt the plain in memory DB to a file */
+       if (encrypt_database (self, encryption_key, &child_error) != TRUE)
+               return SQLITE_IOERR;
+
+       if (self->plain_buffer)
+               gcr_secure_memory_free (self->plain_buffer);
+
+       if (encryption_key)
+               g_free (encryption_key);
+
+ close_db:
 #endif /* ENABLE_ENCRYPTION */
 
-       return rc;
+       rc = demoFlushBuffer (self);
+       sqlite3_free (self->aBuffer);
+       close (self->fd);
+
+       return SQLITE_OK;
 
 #ifdef ENABLE_ENCRYPTION
  delete_encrypted_db:
@@ -450,12 +637,23 @@ demoClose (sqlite3_file *pFile)
 ** Read data from a file.
 */
 static int
-demoRead (sqlite3_file *pFile,  void *zBuf,  int iAmt,  sqlite_int64 iOfst)
+demoRead (sqlite3_file *pFile,  void *buffer,  int len,  sqlite_int64 offset)
 {
-       AlmanahSQLiteVFS *p = (AlmanahSQLiteVFS*)pFile;
-       off_t ofst;                     /* Return value from lseek() */
-       int nRead;                      /* Return value from read() */
-       int rc;                         /* Return code from demoFlushBuffer() */
+       AlmanahSQLiteVFS *self = (AlmanahSQLiteVFS*) pFile;
+       off_t ofst;
+       int nRead;
+       int rc;
+
+#ifdef ENABLE_ENCRYPTION
+       if (self->decrypted) {
+               if ((gsize) (offset + len) > self->plain_size)
+                       return SQLITE_IOERR_SHORT_READ;
+
+               memcpy (buffer, self->plain_buffer + offset, len);
+
+               return SQLITE_OK;
+       }
+#endif /* ENABLE_ENCRYPTION */
 
        /* Flush any data in the write buffer to disk in case this operation
        ** is trying to read data the file-region currently cached in the buffer.
@@ -463,20 +661,20 @@ demoRead (sqlite3_file *pFile,  void *zBuf,  int iAmt,  sqlite_int64 iOfst)
        ** unnecessary write here, but in practice SQLite will rarely read from
        ** a journal file when there is data cached in the write-buffer.
        */
-       rc = demoFlushBuffer (p);
-       if (rc!=SQLITE_OK) {
+       rc = demoFlushBuffer (self);
+       if (rc != SQLITE_OK) {
                return rc;
        }
 
-       ofst = lseek (p->fd, iOfst, SEEK_SET);
-       if (ofst!=iOfst) {
+       ofst = lseek (self->fd, offset, SEEK_SET);
+       if (ofst != offset) {
                return SQLITE_IOERR_READ;
        }
-       nRead = read (p->fd, zBuf, iAmt);
+       nRead = read (self->fd, buffer, len);
 
-       if (nRead==iAmt) {
+       if (nRead == len) {
                return SQLITE_OK;
-       } else if (nRead>=0) {
+       } else if (nRead >= 0) {
                return SQLITE_IOERR_SHORT_READ;
        }
 
@@ -487,14 +685,19 @@ demoRead (sqlite3_file *pFile,  void *zBuf,  int iAmt,  sqlite_int64 iOfst)
 ** Write data to a crash-file.
 */
 static int
-demoWrite (sqlite3_file *pFile,  const void *zBuf, int iAmt, sqlite_int64 iOfst)
+demoWrite (sqlite3_file *pFile,  const void *buffer, int len, sqlite_int64 offset)
 {
-       AlmanahSQLiteVFS *p = (AlmanahSQLiteVFS*)pFile;
-  
-       if (p->aBuffer) {
-               char *z = (char *)zBuf;       /* Pointer to remaining data to write */
-               int n = iAmt;                 /* Number of bytes at z */
-               sqlite3_int64 i = iOfst;      /* File offset to write to */
+       AlmanahSQLiteVFS *self = (AlmanahSQLiteVFS*)pFile;
+
+#ifdef ENABLE_ENCRYPTION
+       if (self->decrypted)
+               return demoDirectWrite (self, buffer, len, offset);
+#endif /* ENABLE_ENCRYPTION */
+
+       if (self->aBuffer) {
+               char *z = (char *)buffer;       /* Pointer to remaining data to write */
+               int n = len;                 /* Number of bytes at z */
+               sqlite3_int64 i = offset;      /* File offset to write to */
 
                while (n > 0) {
                        int nCopy;                  /* Number of bytes to copy into buffer */
@@ -503,29 +706,29 @@ demoWrite (sqlite3_file *pFile,  const void *zBuf, int iAmt, sqlite_int64 iOfst)
                        ** following the data already buffered, flush the buffer. Flushing
                        ** the buffer is a no-op if it is empty.  
                        */
-                       if (p->nBuffer==SQLITE_DEMOVFS_BUFFERSZ || p->iBufferOfst+p->nBuffer!=i) {
-                               int rc = demoFlushBuffer (p);
+                       if (self->nBuffer==SQLITE_DEMOVFS_BUFFERSZ || self->iBufferOfst+self->nBuffer!=i) {
+                               int rc = demoFlushBuffer (self);
                                if (rc!=SQLITE_OK) {
                                        return rc;
                                }
                        }
-                       assert (p->nBuffer==0 || p->iBufferOfst+p->nBuffer==i);
-                       p->iBufferOfst = i - p->nBuffer;
+                       assert (self->nBuffer==0 || self->iBufferOfst+self->nBuffer==i);
+                       self->iBufferOfst = i - self->nBuffer;
 
                        /* Copy as much data as possible into the buffer. */
-                       nCopy = SQLITE_DEMOVFS_BUFFERSZ - p->nBuffer;
+                       nCopy = SQLITE_DEMOVFS_BUFFERSZ - self->nBuffer;
                        if (nCopy>n) {
                                nCopy = n;
                        }
-                       memcpy (&p->aBuffer[p->nBuffer], z, nCopy);
-                       p->nBuffer += nCopy;
+                       memcpy (&self->aBuffer[self->nBuffer], z, nCopy);
+                       self->nBuffer += nCopy;
 
                        n -= nCopy;
                        i += nCopy;
                        z += nCopy;
                }
        } else {
-               return demoDirectWrite (p, zBuf, iAmt, iOfst);
+               return demoDirectWrite (self, buffer, len, offset);
        }
 
        return SQLITE_OK;
@@ -549,17 +752,22 @@ demoTruncate(sqlite3_file *pFile, sqlite_int64 size)
 ** Sync the contents of the file to the persistent media.
 */
 static int
-demoSync (sqlite3_file *pFile, int flags)
+demoSync (sqlite3_file *pFile, __attribute__ ((unused)) int flags)
 {
-       AlmanahSQLiteVFS *p = (AlmanahSQLiteVFS*) pFile;
+       AlmanahSQLiteVFS *self = (AlmanahSQLiteVFS*) pFile;
+
+#ifdef ENABLE_ENCRYPTION
+       if (self->decrypted)
+               return SQLITE_OK;
+#endif
        int rc;
 
-       rc = demoFlushBuffer (p);
+       rc = demoFlushBuffer (self);
        if (rc!=SQLITE_OK) {
                return rc;
        }
 
-       rc = fsync (p->fd);
+       rc = fsync (self->fd);
        return (rc==0 ? SQLITE_OK : SQLITE_IOERR_FSYNC);
 }
 
@@ -569,23 +777,31 @@ demoSync (sqlite3_file *pFile, int flags)
 static int
 demoFileSize (sqlite3_file *pFile, sqlite_int64 *pSize)
 {
-       AlmanahSQLiteVFS *p = (AlmanahSQLiteVFS*)pFile;
+       AlmanahSQLiteVFS *self = (AlmanahSQLiteVFS*)pFile;
        int rc;                         /* Return code from fstat() call */
        struct stat sStat;              /* Output of fstat() call */
 
+#ifdef ENABLE_ENCRYPTION
+       if (self->decrypted) {
+               *pSize = self->plain_size;
+               return SQLITE_OK;
+       }
+#endif
+
        /* Flush the contents of the buffer to disk. As with the flush in the
        ** demoRead() method, it would be possible to avoid this and save a write
        ** here and there. But in practice this comes up so infrequently it is
        ** not worth the trouble.
        */
-       rc = demoFlushBuffer(p);
+       rc = demoFlushBuffer(self);
        if (rc != SQLITE_OK) {
                return rc;
        }
 
-       rc = fstat (p->fd, &sStat);
-       if (rc!=0)
+       rc = fstat (self->fd, &sStat);
+       if (rc != 0)
                return SQLITE_IOERR_FSTAT;
+
        *pSize = sStat.st_size;
        return SQLITE_OK;
 }
@@ -655,13 +871,17 @@ demoOpen (__attribute__ ((unused)) sqlite3_vfs *pVfs, /* VFS */
 
        AlmanahSQLiteVFS *self = (AlmanahSQLiteVFS*) pFile; /* Populate this structure */
        int oflags = 0;                 /* flags to pass to open() call */
-       char *aBuf = 0;
+       char *aBuf = NULL;
+#ifdef ENABLE_ENCRYPTION
+       struct stat encrypted_db_stat, plaintext_db_stat;
+       GError *child_error = NULL;
+#endif
 
        if (zName == 0) {
                return SQLITE_IOERR;
        }
 
-       if (flags & SQLITE_OPEN_MAIN_JOURNAL){
+       if (flags & SQLITE_OPEN_MAIN_JOURNAL) {
                aBuf = (char *) sqlite3_malloc(SQLITE_DEMOVFS_BUFFERSZ);
                if(!aBuf) {
                        return SQLITE_NOMEM;
@@ -671,59 +891,69 @@ demoOpen (__attribute__ ((unused)) sqlite3_vfs *pVfs, /* VFS */
        memset(self, 0, sizeof(AlmanahSQLiteVFS));
 
        self->plain_filename = g_strdup (zName);
+       self->decrypted = FALSE;
 
 #ifdef ENABLE_ENCRYPTION
-       struct stat encrypted_db_stat, plaintext_db_stat;
-       GError *child_error = NULL;
-
-       self->encrypted_filename = g_strdup_printf ("%s%s", self->plain_filename, ENCRYPTED_SUFFIX);
+       if (flags & SQLITE_OPEN_MAIN_DB) {
+               self->encrypted_filename = g_strdup_printf ("%s%s", self->plain_filename, ENCRYPTED_SUFFIX);
 
-       if (g_chmod (self->encrypted_filename, 0600) != 0 && errno != ENOENT) {
-               return SQLITE_IOERR;
-       }
-
-       g_stat (self->encrypted_filename, &encrypted_db_stat);
-
-       /* If we're decrypting, don't bother if the cipher file doesn't exist (i.e. the database hasn't yet 
been created), or is empty
-        * (i.e. corrupt). */
-       if (g_file_test (self->encrypted_filename, G_FILE_TEST_IS_REGULAR) == TRUE && 
encrypted_db_stat.st_size > 0) {
-               /* Make a backup of the encrypted database file */
-               if (back_up_file (self->encrypted_filename) != FALSE) {
-                       /* Translators: the first parameter is a filename, the second is an error message. */
-                       g_warning (_("Error backing up file ‘%s’"), self->encrypted_filename);
-                       g_clear_error (&child_error);
+               if (g_chmod (self->encrypted_filename, 0600) != 0 && errno != ENOENT) {
+                       return SQLITE_IOERR;
                }
 
-               g_stat (self->plain_filename, &plaintext_db_stat);
-
-               /* Only decrypt the database if the plaintext database doesn't exist or is empty. If the 
plaintext database exists and is non-empty,
-                * don't decrypt — just use that database. */
-               if (g_file_test (self->plain_filename, G_FILE_TEST_IS_REGULAR) != TRUE || 
plaintext_db_stat.st_size == 0) {
-                       /* Decrypt the database, or display an error if that fails (but not if it fails due 
to a missing encrypted DB file — just
-                        * fall through and try to open the plain DB file in that case). */
-                       if (decrypt_database (self, &child_error) != TRUE) {
-                               if (child_error->code != G_FILE_ERROR_NOENT) {
-                                       g_free (self->plain_filename);
-                                       g_free (self->encrypted_filename);
-                                       return SQLITE_IOERR;
-                               }
+               g_stat (self->encrypted_filename, &encrypted_db_stat);
 
-                               g_error_free (child_error);
+               /* If we're decrypting, don't bother if the cipher file doesn't exist (i.e. the database 
hasn't yet been created), or is empty
+                * (i.e. corrupt). */
+               if (g_file_test (self->encrypted_filename, G_FILE_TEST_IS_REGULAR) == TRUE && 
encrypted_db_stat.st_size > 0) {
+                       /* Make a backup of the encrypted database file */
+                       if (back_up_file (self->encrypted_filename) == FALSE) {
+                               /* Translators: the first parameter is a filename, the second is an error 
message. */
+                               g_warning (_("Error backing up file ‘%s’"), self->encrypted_filename);
+                               g_clear_error (&child_error);
+                       }
+
+                       g_stat (self->plain_filename, &plaintext_db_stat);
+
+                       /* Only decrypt the database if the plaintext database doesn't exist or is empty. If 
the plaintext database exists and is non-empty,
+                        * don't decrypt — just use that database. */
+                       if (g_file_test (self->plain_filename, G_FILE_TEST_IS_REGULAR) != TRUE || 
plaintext_db_stat.st_size == 0) {
+                               /* Decrypt the database, or display an error if that fails (but not if it 
fails due to a missing encrypted DB file — just
+                                * fall through and try to open the plain DB file in that case). */
+                               if (decrypt_database (self, &child_error) != TRUE) {
+                                       if (child_error != NULL && child_error->code != G_FILE_ERROR_NOENT) {
+                                               g_warning (_("Error decrypting database: %s"), 
child_error->message);
+                                               g_free (self->plain_filename);
+                                               g_free (self->encrypted_filename);
+                                               return SQLITE_IOERR;
+                                       }
+
+                                       g_error_free (child_error);
+                               } else
+                                       self->decrypted = TRUE;
                        }
                }
        }
 
-       self->decrypted = TRUE;
 #else
-       /* Make a backup of the plaintext database file */
-       if (back_up_file (self->plain_filename) != TRUE) {
-               /* Translators: the first parameter is a filename. */
-               g_warning (_("Error backing up file ‘%s’"), self->priv->plain_filename);
-               g_clear_error (&child_error);
+       if (flags & SQLITE_OPEN_MAIN_DB) {
+               /* Make a backup of the plaintext database file */
+               if (back_up_file (self->plain_filename) != TRUE) {
+                       /* Translators: the first parameter is a filename. */
+                       g_warning (_("Error backing up file ‘%s’"), self->priv->plain_filename);
+                       g_clear_error (&child_error);
+               }
        }
        self->decrypted = FALSE;
 #endif /* ENABLE_ENCRYPTION */
 
+#ifdef ENABLE_ENCRYPTION
+       if (self->decrypted) {
+               sqlite3_free (aBuf);
+               *pOutFlags = 0;
+       } else {
+#endif /* ENABLE_ENCRYPTION */
+
        if (flags & SQLITE_OPEN_EXCLUSIVE) oflags |= O_EXCL;
        if (flags & SQLITE_OPEN_CREATE)    oflags |= O_CREAT;
        if (flags & SQLITE_OPEN_READONLY)  oflags |= O_RDONLY;
@@ -750,6 +980,10 @@ demoOpen (__attribute__ ((unused)) sqlite3_vfs *pVfs, /* VFS */
                *pOutFlags = flags;
        }
 
+#ifdef ENABLE_ENCRYPTION
+       }
+#endif /* ENABLE_ENCRYPTION */
+
        self->base.pMethods = &demoio;
 
        return SQLITE_OK;
@@ -760,7 +994,9 @@ demoOpen (__attribute__ ((unused)) sqlite3_vfs *pVfs, /* VFS */
 ** is non-zero, then ensure the file-system modification to delete the
 ** file has been synced to disk before returning.
 */
-static int demoDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+static int
+demoDelete (__attribute__ ((unused)) sqlite3_vfs *pVfs, const char *zPath, int dirSync)
+{
        int rc;                         /* Return code */
 
        rc = unlink(zPath);
@@ -803,12 +1039,9 @@ static int demoDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
 ** Query the file-system to see if the named file exists, is readable or
 ** is both readable and writable.
 */
-static int demoAccess(
-                     sqlite3_vfs *pVfs, 
-                     const char *zPath, 
-                     int flags, 
-                     int *pResOut
-                     ){
+static int
+demoAccess (__attribute__ ((unused)) sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut)
+{
        int rc;                         /* access() return code */
        int eAccess = F_OK;             /* Second argument to access() */
 
@@ -836,12 +1069,9 @@ static int demoAccess(
 **   1. Path components are separated by a '/'. and 
 **   2. Full paths begin with a '/' character.
 */
-static int demoFullPathname(
-                           sqlite3_vfs *pVfs,              /* VFS */
-                           const char *zPath,              /* Input path (possibly a relative path) */
-                           int nPathOut,                   /* Size of output buffer in bytes */
-                           char *zPathOut                  /* Pointer to output buffer */
-                           ){
+static int
+demoFullPathname (__attribute__ ((unused)) sqlite3_vfs *pVfs, const char *zPath, int nPathOut, char 
*zPathOut)
+{
        char zDir[MAXPATHNAME+1];
        if( zPath[0]=='/' ){
                zDir[0] = '\0';
@@ -868,17 +1098,28 @@ static int demoFullPathname(
 ** extensions compiled as shared objects. This simple VFS does not support
 ** this functionality, so the following functions are no-ops.
 */
-static void *demoDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+static void*
+demoDlOpen (__attribute__ ((unused)) sqlite3_vfs *pVfs, __attribute__ ((unused)) const char *zPath)
+{
        return 0;
 }
-static void demoDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+
+static void
+demoDlError(__attribute__ ((unused)) sqlite3_vfs *pVfs, int nByte, char *zErrMsg)
+{
        sqlite3_snprintf(nByte, zErrMsg, "Loadable extensions are not supported");
        zErrMsg[nByte-1] = '\0';
 }
-static void (*demoDlSym(sqlite3_vfs *pVfs, void *pH, const char *z))(void){
+
+static void
+(*demoDlSym(__attribute__ ((unused)) sqlite3_vfs *pVfs, __attribute__ ((unused)) void *pH, __attribute__ 
((unused)) const char *z)) (void)
+{
        return 0;
 }
-static void demoDlClose(sqlite3_vfs *pVfs, void *pHandle){
+
+static void
+demoDlClose (__attribute__ ((unused)) sqlite3_vfs *pVfs, __attribute__ ((unused)) void *pHandle)
+{
        return;
 }
 
@@ -886,7 +1127,9 @@ static void demoDlClose(sqlite3_vfs *pVfs, void *pHandle){
 ** Parameter zByte points to a buffer nByte bytes in size. Populate this
 ** buffer with pseudo-random data.
 */
-static int demoRandomness(sqlite3_vfs *pVfs, int nByte, char *zByte){
+static int
+demoRandomness (__attribute__ ((unused)) sqlite3_vfs *pVfs, __attribute__ ((unused)) int nByte, 
__attribute__ ((unused)) char *zByte)
+{
        return SQLITE_OK;
 }
 
@@ -894,7 +1137,9 @@ static int demoRandomness(sqlite3_vfs *pVfs, int nByte, char *zByte){
 ** Sleep for at least nMicro microseconds. Return the (approximate) number 
 ** of microseconds slept for.
 */
-static int demoSleep(sqlite3_vfs *pVfs, int nMicro){
+static int
+demoSleep (__attribute__ ((unused)) sqlite3_vfs *pVfs, int nMicro)
+{
        sleep(nMicro / 1000000);
        usleep(nMicro % 1000000);
        return nMicro;
@@ -911,7 +1156,9 @@ static int demoSleep(sqlite3_vfs *pVfs, int nMicro){
 ** value, it will stop working some time in the year 2038 AD (the so-called
 ** "year 2038" problem that afflicts systems that store time this way). 
 */
-static int demoCurrentTime(sqlite3_vfs *pVfs, double *pTime){
+static int
+demoCurrentTime (__attribute__ ((unused)) sqlite3_vfs *pVfs, double *pTime)
+{
        time_t t = time(0);
        *pTime = t/86400.0 + 2440587.5; 
        return SQLITE_OK;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]