/ Check-in [2241788b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Change the unix VFS to always allocate shared-memory using a file in the same directory as the database. Otherwise, a chroot might cause different processes to use different shared memory files resulting in database corruption.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2241788bc85fbc48e9cfecb95fe0a858338e37cb
User & Date: drh 2010-06-19 15:10:10
Context
2010-06-19
23:53
Fix an uninitialized variable in os_unix.c. check-in: 822a0283 user: drh tags: trunk
15:10
Change the unix VFS to always allocate shared-memory using a file in the same directory as the database. Otherwise, a chroot might cause different processes to use different shared memory files resulting in database corruption. check-in: 2241788b user: drh tags: trunk
2010-06-17
17:05
Apply [b9b11855e8] (the alternate fix to [fc62af4523]) to the trunk. check-in: 9a949a3a user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289


3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
....
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
....
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
....
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
....
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
    sqlite3_free(p->apRegion);
    if( p->h>=0 ) close(p->h);
    p->pInode->pShmNode = 0;
    sqlite3_free(p);
  }
}

/* Forward reference */
static const char *unixTempFileDir(int);

/*
** Open a shared-memory area.  This particular implementation uses
** mmapped files.
**
** zName is a filename used to identify the shared-memory area.  The
** implementation does not (and perhaps should not) use this name
** directly, but rather use it as a template for finding an appropriate
** name for the shared-memory storage.  In this implementation, the
** string "-index" is appended to zName and used as the name of the
** mmapped file.


**
** When opening a new shared-memory file, if no other instances of that
** file are currently open, in this process or in other processes, then
** the file must be truncated to zero length or have its header cleared.
*/
static int unixShmOpen(
  sqlite3_file *fd      /* The file descriptor of the associated database */
){
  struct unixShm *p = 0;             /* The connection to be opened */
  struct unixShmNode *pShmNode = 0;  /* The underlying mmapped file */
  int rc;                            /* Result code */
  struct unixFile *pDbFd;            /* Underlying database file */
  unixInodeInfo *pInode;             /* The inode of fd */
  const char *zTempDir;              /* Directory for temporary files */
  int nTempDir;                      /* Size of the zTempDir string */

  /* Allocate space for the new sqlite3_shm object.
  */
  p = sqlite3_malloc( sizeof(*p) );
  if( p==0 ) return SQLITE_NOMEM;
  memset(p, 0, sizeof(*p));
  pDbFd = (struct unixFile*)fd;
................................................................................
  /* Check to see if a unixShmNode object already exists.  Reuse an existing
  ** one if present.  Create a new one if necessary.
  */
  unixEnterMutex();
  pInode = pDbFd->pInode;
  pShmNode = pInode->pShmNode;
  if( pShmNode==0 ){
    zTempDir = unixTempFileDir(1);
    if( zTempDir==0 ){
      unixLeaveMutex();
      sqlite3_free(p);
      return SQLITE_CANTOPEN_NOTEMPDIR;
    }
    nTempDir = strlen(zTempDir);
    pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nTempDir + 50 );
    if( pShmNode==0 ){
      rc = SQLITE_NOMEM;
      goto shm_open_err;
    }
    memset(pShmNode, 0, sizeof(*pShmNode));
    pShmNode->zFilename = (char*)&pShmNode[1];
    sqlite3_snprintf(nTempDir+50, pShmNode->zFilename,
                     "%s/sqlite-wi-%x-%x", zTempDir,
                     (u32)pInode->fileId.dev, (u32)pInode->fileId.ino);
    pShmNode->h = -1;
    pDbFd->pInode->pShmNode = pShmNode;
    pShmNode->pInode = pDbFd->pInode;
    pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
    if( pShmNode->mutex==0 ){
      rc = SQLITE_NOMEM;
      goto shm_open_err;
    }

    pShmNode->h = open(pShmNode->zFilename, O_RDWR|O_CREAT, 0664);
    if( pShmNode->h<0 ){
      rc = SQLITE_CANTOPEN_BKPT;
      goto shm_open_err;
    }

    /* Check to see if another process is holding the dead-man switch.
    ** If not, truncate the file to zero length. 
................................................................................
  return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT);
}

/*
** Return the name of a directory in which to put temporary files.
** If no suitable temporary file directory can be found, return NULL.
*/
static const char *unixTempFileDir(int allowShm){
  static const char *azDirs[] = {
     0,
     0,
     "/var/tmp",
     "/usr/tmp",
     "/tmp",
     0        /* List terminator */
................................................................................
  };
  unsigned int i;
  struct stat buf;
  const char *zDir = 0;

  azDirs[0] = sqlite3_temp_directory;
  if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
  
  if( allowShm ){
    zDir = "/dev/shm";
    i = 2;  /* Skip the app-defined temp locations for shared-memory */
  }else{
    zDir = azDirs[0];
    i = 1;
  }
  for(; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
    if( zDir==0 ) continue;
    if( stat(zDir, &buf) ) continue;
    if( !S_ISDIR(buf.st_mode) ) continue;
    if( !allowShm && access(zDir, 07) ) continue;
    break;
  }
  return zDir;
}

/*
** Create a temporary file name in zBuf.  zBuf must be allocated
................................................................................

  /* It's odd to simulate an io-error here, but really this is just
  ** using the io-error infrastructure to test that SQLite handles this
  ** function failing. 
  */
  SimulateIOError( return SQLITE_IOERR );

  zDir = unixTempFileDir(0);
  if( zDir==0 ) zDir = ".";

  /* Check that the output buffer is large enough for the temporary file 
  ** name. If it is not, return SQLITE_ERROR.
  */
  if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){
    return SQLITE_ERROR;







|
|
|
|
|
|
|
|
|
|
|
|
|
>
>













|
|







 







|
<
<
<
<
<
<
|





|
|
<
<









|







 







|







 







<
<
<
<
<
<
<
<
|



|







 







|







3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
....
3316
3317
3318
3319
3320
3321
3322
3323






3324
3325
3326
3327
3328
3329
3330
3331


3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
....
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
....
4162
4163
4164
4165
4166
4167
4168








4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
....
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
    sqlite3_free(p->apRegion);
    if( p->h>=0 ) close(p->h);
    p->pInode->pShmNode = 0;
    sqlite3_free(p);
  }
}

/*
** Open a shared-memory area associated with open database file fd.  
** This particular implementation uses mmapped files.
**
** The file used to implement shared-memory is in the same directory
** as the open database file and has the same name as the open database
** file with the "-shm" suffix added.  For example, if the database file
** is "/home/user1/config.db" then the file that is created and mmapped
** for shared memory will be called "/home/user1/config.db-shm".  We
** experimented with using files in /dev/tmp or an some other tmpfs mount.
** But if a file in a different directory from the database file is used,
** then differing access permissions or a chroot() might cause two different 
** processes on the same database to end up using different files for 
** shared memory - meaning that their memory would not really be shared - 
** resulting in database corruption.
**
** When opening a new shared-memory file, if no other instances of that
** file are currently open, in this process or in other processes, then
** the file must be truncated to zero length or have its header cleared.
*/
static int unixShmOpen(
  sqlite3_file *fd      /* The file descriptor of the associated database */
){
  struct unixShm *p = 0;             /* The connection to be opened */
  struct unixShmNode *pShmNode = 0;  /* The underlying mmapped file */
  int rc;                            /* Result code */
  struct unixFile *pDbFd;            /* Underlying database file */
  unixInodeInfo *pInode;             /* The inode of fd */
  char *zShmFilename;                /* Name of the file used for SHM */
  int nShmFilename;                  /* Size of the SHM filename in bytes */

  /* Allocate space for the new sqlite3_shm object.
  */
  p = sqlite3_malloc( sizeof(*p) );
  if( p==0 ) return SQLITE_NOMEM;
  memset(p, 0, sizeof(*p));
  pDbFd = (struct unixFile*)fd;
................................................................................
  /* Check to see if a unixShmNode object already exists.  Reuse an existing
  ** one if present.  Create a new one if necessary.
  */
  unixEnterMutex();
  pInode = pDbFd->pInode;
  pShmNode = pInode->pShmNode;
  if( pShmNode==0 ){
    nShmFilename = 5 + (int)strlen(pDbFd->zPath);






    pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename );
    if( pShmNode==0 ){
      rc = SQLITE_NOMEM;
      goto shm_open_err;
    }
    memset(pShmNode, 0, sizeof(*pShmNode));
    zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
    sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath);


    pShmNode->h = -1;
    pDbFd->pInode->pShmNode = pShmNode;
    pShmNode->pInode = pDbFd->pInode;
    pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
    if( pShmNode->mutex==0 ){
      rc = SQLITE_NOMEM;
      goto shm_open_err;
    }

    pShmNode->h = open(zShmFilename, O_RDWR|O_CREAT, 0664);
    if( pShmNode->h<0 ){
      rc = SQLITE_CANTOPEN_BKPT;
      goto shm_open_err;
    }

    /* Check to see if another process is holding the dead-man switch.
    ** If not, truncate the file to zero length. 
................................................................................
  return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT);
}

/*
** Return the name of a directory in which to put temporary files.
** If no suitable temporary file directory can be found, return NULL.
*/
static const char *unixTempFileDir(void){
  static const char *azDirs[] = {
     0,
     0,
     "/var/tmp",
     "/usr/tmp",
     "/tmp",
     0        /* List terminator */
................................................................................
  };
  unsigned int i;
  struct stat buf;
  const char *zDir = 0;

  azDirs[0] = sqlite3_temp_directory;
  if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");








  for(i==0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
    if( zDir==0 ) continue;
    if( stat(zDir, &buf) ) continue;
    if( !S_ISDIR(buf.st_mode) ) continue;
    if( access(zDir, 07) ) continue;
    break;
  }
  return zDir;
}

/*
** Create a temporary file name in zBuf.  zBuf must be allocated
................................................................................

  /* It's odd to simulate an io-error here, but really this is just
  ** using the io-error infrastructure to test that SQLite handles this
  ** function failing. 
  */
  SimulateIOError( return SQLITE_IOERR );

  zDir = unixTempFileDir();
  if( zDir==0 ) zDir = ".";

  /* Check that the output buffer is large enough for the temporary file 
  ** name. If it is not, return SQLITE_ERROR.
  */
  if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){
    return SQLITE_ERROR;