From ce201b3a6be487bb3eb48e1d102ba246332aba66 Mon Sep 17 00:00:00 2001
From: MartinChoo <214582617@qq.com>
Date: Wed, 23 Jul 2025 17:43:40 +0800
Subject: [PATCH 08/12] Rekey multi process

---
 src/sqlite3.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 90 insertions(+), 3 deletions(-)

diff --git a/src/sqlite3.c b/src/sqlite3.c
index 3f1195a..4c538a1 100644
--- a/src/sqlite3.c
+++ b/src/sqlite3.c
@@ -15689,6 +15689,7 @@ typedef int VList;
 #define RESERVED_BYTE     (PENDING_BYTE+1)
 #define SHARED_FIRST      (PENDING_BYTE+2)
 #define SHARED_SIZE       510
+#define SHARED_CODEC_BYTE (PENDING_BYTE+2097)
 
 /*
 ** Wrapper around OS specific sqlite3_os_init() function.
@@ -38682,6 +38683,10 @@ struct unixFile {
 #endif
 };
 
+#ifdef SQLITE_HAS_CODEC
+static int CodecFileLock(Pager *pPager, short lockType);
+#endif
+
 /* This variable holds the process id (pid) from when the xRandomness()
 ** method was called.  If xOpen() is called from a different process id,
 ** indicating that a fork() has occurred, the PRNG will be reset.
@@ -39627,6 +39632,9 @@ struct unixInodeInfo {
   sqlite3_mutex *pLockMutex;      /* Hold this mutex for... */
   int nShared;                      /* Number of SHARED locks held */
   int nLock;                        /* Number of outstanding file locks */
+#if SQLITE_HAS_CODEC
+  void *codecPager;
+#endif
   unsigned char eFileLock;          /* One of SHARED_LOCK, RESERVED_LOCK etc. */
   unsigned char bProcessLock;       /* An exclusive process lock is held */
   UnixUnusedFd *pUnused;            /* Unused file descriptors to close */
@@ -61697,6 +61705,9 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
   PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
   IOTRACE(("CLOSE %p\n", pPager))
   sqlite3OsClose(pPager->jfd);
+#if SQLITE_OS_UNIX && defined(SQLITE_HAS_CODEC)
+  if( pPager->xCodec ) CodecFileLock(pPager, F_UNLCK);
+#endif
   sqlite3OsClose(pPager->fd);
   sqlite3PageFree(pTmp);
   sqlite3PcacheClose(pPager->pPCache);
@@ -255500,6 +255511,54 @@ void sqlite3CodecDetach(void *ctx){
   return;
 }
 
+#if SQLITE_OS_UNIX
+static int CodecFileLock(Pager *pPager, short lockType)
+{
+  sqlite3_file *fd = pPager->fd;
+  unixFile *pFile = (unixFile *)fd;
+  unixInodeInfo *pInode = pFile->pInode;
+  if (pInode == NULL) {
+    sqlite3_log(SQLITE_IOERR_RDLOCK, "Codec file lock %d go wrong", lockType);
+    return SQLITE_IOERR_RDLOCK;
+  }
+  sqlite3_mutex_enter(pInode->pLockMutex);
+  // last conntection release the lock
+  if (lockType == F_UNLCK && pInode->nRef > 1) {
+    sqlite3_mutex_leave(pInode->pLockMutex);
+    return SQLITE_OK;
+  }
+  if (lockType == F_WRLCK && pInode->nRef > 1) {
+    sqlite3_mutex_leave(pInode->pLockMutex);
+    sqlite3_log(SQLITE_BUSY, "Codec file lock wrlock busy ref %d", pInode->nRef);
+    return SQLITE_BUSY;
+  }
+  if (lockType == F_RDLCK && pInode->codecPager != 0 && pInode->codecPager != pPager) {
+    sqlite3_mutex_leave(pInode->pLockMutex);
+    sqlite3_log(SQLITE_BUSY, "Codec exists file lock wrlock");
+    return SQLITE_BUSY;
+  }
+  struct flock lock;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = SHARED_CODEC_BYTE;
+  lock.l_len = 1;
+  // F_RDLCK, F_WRLCK, F_UNLCK
+  lock.l_type = lockType;
+  int rc = osSetPosixAdvisoryLock(pFile->h, &lock, pFile);
+  if (rc < 0) {
+    int tErrno = errno;
+    rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
+    if (IS_LOCK_ERROR(rc)) {
+      storeLastErrno(pFile, tErrno);
+    }
+    sqlite3_log(rc, "Codec file lock %d: errno = %d", lockType, tErrno);
+  } else {
+    pInode->codecPager = (lockType == F_WRLCK ? pPager : 0);
+  }
+  sqlite3_mutex_leave(pInode->pLockMutex);
+  return rc;
+}
+#endif
+
 int sqlite3CodecAttach(sqlite3* db, int nDb, const void *pKey, int nKey){
   if(db == NULL){
     return SQLITE_ERROR;
@@ -255519,6 +255578,18 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *pKey, int nKey){
     return SQLITE_ERROR;
   }
   sqlite3_mutex_enter(db->mutex);
+  Pager *pPager = p->pBt->pPager;
+#if SQLITE_OS_UNIX
+  if (pPager->fd != NULL && isOpen(pPager->fd)) {
+    int ret = CodecFileLock(pPager, F_RDLCK);
+    if(ret != SQLITE_OK){
+      sqlite3_free(ctx);
+      sqlite3_mutex_leave(db->mutex);
+      sqlite3_log(ret, "sqlite3CodecAttach: codec lock error");
+      return ret;
+    }
+  }
+#endif
 #ifdef SQLITE_CODEC_ATTACH_CHANGED
   int rc = sqlite3CodecInitContext(ctx, p, pKey, nKey, nDb);
 #else
@@ -255526,6 +255597,7 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *pKey, int nKey){
 #endif /* SQLITE_CODEC_ATTACH_CHANGED */
   if(rc != SQLITE_OK){
     sqlite3_free(ctx);
+    sqlite3_mutex_leave(db->mutex);
     return rc;
   }
   sqlite3PagerSetCodec(sqlite3BtreePager(p), sqlite3Codec, NULL, sqlite3CodecDetach, (void *)ctx);
@@ -255534,7 +255606,7 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *pKey, int nKey){
   p->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
   sqlite3BtreeSetPageSize(p, ctx->readCtx->codecConst.cipherPageSize, ctx->readCtx->codecConst.reserveSize, 0);
   sqlite3BtreeSecureDelete(p, 1);
-  if(isOpen(p->pBt->pPager->fd)){
+  if(isOpen(pPager->fd)){
     sqlite3BtreeSetAutoVacuum(p, SQLITE_DEFAULT_AUTOVACUUM);
   }
 
@@ -255605,7 +255677,20 @@ int sqlite3_rekey_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey){
     return rc;
   }
   sqlite3_mutex_enter(db->mutex);
-  (void)sqlite3BtreeBeginTrans(p, 1, 0);
+  rc = sqlite3BtreeBeginTrans(p, 1, 0);
+  if(rc != SQLITE_OK){
+    sqlite3_mutex_leave(db->mutex);
+    sqlite3_log(rc, "sqlite3 rekey: error when begin trans %s.", sqlite3PagerFilename(pPager, 1));
+    return rc;
+  }
+#if SQLITE_OS_UNIX
+  if((rc = CodecFileLock(pPager, F_WRLCK)) != SQLITE_OK){
+    (void)sqlite3BtreeRollback(p, SQLITE_ABORT_ROLLBACK, 0);
+    sqlite3_mutex_leave(db->mutex);
+    sqlite3_log(rc, "sqlite3_rekey_v2: error when lock %s.", sqlite3PagerFilename(pPager, 1));
+    return rc;
+  }
+#endif
   sqlite3PagerPagecount(pPager, &pageCount);
   // support hmac algo changed by using rekey operation
   int oldHmacAlgo = ctx->writeCtx->codecConst.hmacAlgo;
@@ -255640,8 +255725,10 @@ int sqlite3_rekey_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey){
     }
     (void)sqlite3BtreeRollback(p, SQLITE_ABORT_ROLLBACK, 0);
   }
+#if SQLITE_OS_UNIX
+  (void)CodecFileLock(pPager, F_RDLCK);
+#endif
   sqlite3_mutex_leave(db->mutex);
-
   return rc;
 }
 
-- 
2.47.0.windows.2