diff options
author | drh <drh@noemail.net> | 2003-07-27 18:59:42 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2003-07-27 18:59:42 +0000 |
commit | a76c82eb0de11673f9b810acdbdbb25ae5cd7d74 (patch) | |
tree | 762fe938495a4075b5a63122eb5a80b3f9fa17be /src | |
parent | 98e3e600122e7da2371f732e726a96d26ab4554f (diff) | |
download | sqlite-a76c82eb0de11673f9b810acdbdbb25ae5cd7d74.tar.gz sqlite-a76c82eb0de11673f9b810acdbdbb25ae5cd7d74.zip |
When creating a new journal file, open a (read-only) file descriptor on the
directory containing the journal and sync that directory once to make sure
that the journal filename entry gets into the directory. Ticket #410. (CVS 1066)
FossilOrigin-Name: 09c10fe3c99cffc64ed02c2929f206d99c8e3309
Diffstat (limited to 'src')
-rw-r--r-- | src/os.c | 55 | ||||
-rw-r--r-- | src/os.h | 2 | ||||
-rw-r--r-- | src/pager.c | 16 |
3 files changed, 69 insertions, 4 deletions
@@ -326,6 +326,7 @@ int sqliteOsOpenReadWrite( int *pReadonly ){ #if OS_UNIX + id->dirfd = -1; id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644); if( id->fd<0 ){ id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); @@ -450,6 +451,7 @@ int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ if( access(zFilename, 0)==0 ){ return SQLITE_CANTOPEN; } + id->dirfd = -1; id->fd = open(zFilename, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600); if( id->fd<0 ){ @@ -536,6 +538,7 @@ int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ */ int sqliteOsOpenReadOnly(const char *zFilename, OsFile *id){ #if OS_UNIX + id->dirfd = -1; id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); if( id->fd<0 ){ return SQLITE_CANTOPEN; @@ -598,6 +601,42 @@ int sqliteOsOpenReadOnly(const char *zFilename, OsFile *id){ } /* +** Attempt to open a file descriptor for the directory that contains a +** file. This file descriptor can be used to fsync() the directory +** in order to make sure the creation of a new file is actually written +** to disk. +** +** This routine is only meaningful for Unix. It is a no-op under +** windows since windows does not support hard links. +** +** On success, a handle for a previously open file is at *id is +** updated with the new directory file descriptor and SQLITE_OK is +** returned. +** +** On failure, the function returns SQLITE_CANTOPEN and leaves +** *id unchanged. +*/ +int sqliteOsOpenDirectory( + const char *zDirname, + OsFile *id +){ +#if OS_UNIX + if( id->fd<0 ){ + /* Do not open the directory if the corresponding file is not already + ** open. */ + return SQLITE_CANTOPEN; + } + assert( id->dirfd<0 ); + id->dirfd = open(zDirname, O_RDONLY|O_BINARY, 0644); + if( id->dirfd<0 ){ + return SQLITE_CANTOPEN; + } + TRACE3("OPENDIR %-3d %s\n", id->dirfd, zDirname); +#endif + return SQLITE_OK; +} + +/* ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ @@ -706,6 +745,8 @@ int sqliteOsTempFileName(char *zBuf){ int sqliteOsClose(OsFile *id){ #if OS_UNIX close(id->fd); + if( id->dirfd>=0 ) close(id->dirfd); + id->dirfd = -1; sqliteOsEnterMutex(); releaseLockInfo(id->pLock); sqliteOsLeaveMutex(); @@ -892,6 +933,14 @@ int sqliteOsSeek(OsFile *id, off_t offset){ /* ** Make sure all writes to a particular file are committed to disk. +** +** Under Unix, also make sure that the directory entry for the file +** has been created by fsync-ing the directory that contains the file. +** If we do not do this and we encounter a power failure, the directory +** entry for the journal might not exist after we reboot. The next +** SQLite to access the file will not know that the journal exists (because +** the directory entry for the journal was never created) and the transaction +** will not roll back - possibly leading to database corruption. */ int sqliteOsSync(OsFile *id){ #if OS_UNIX @@ -900,6 +949,12 @@ int sqliteOsSync(OsFile *id){ if( fsync(id->fd) ){ return SQLITE_IOERR; }else{ + if( id->dirfd>=0 ){ + TRACE2("DIRSYNC %-3d\n", id->dirfd); + fsync(id->dirfd); + close(id->dirfd); /* Only need to sync once, so close the directory */ + id->dirfd = -1; /* when we are done. */ + } return SQLITE_OK; } #endif @@ -104,6 +104,7 @@ struct lockInfo *pLock; /* Information about locks on this inode */ int fd; /* The file descriptor */ int locked; /* True if this user holds the lock */ + int dirfd; /* File descriptor for the directory */ }; # define SQLITE_TEMPNAME_SIZE 200 # if defined(HAVE_USLEEP) && HAVE_USLEEP @@ -156,6 +157,7 @@ int sqliteOsFileRename(const char*, const char*); int sqliteOsOpenReadWrite(const char*, OsFile*, int*); int sqliteOsOpenExclusive(const char*, OsFile*, int); int sqliteOsOpenReadOnly(const char*, OsFile*); +int sqliteOsOpenDirectory(const char*, OsFile*); int sqliteOsTempFileName(char*); int sqliteOsClose(OsFile*); int sqliteOsRead(OsFile*, void*, int amt); diff --git a/src/pager.c b/src/pager.c index 66c361597..f470c0dd6 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.86 2003/07/07 10:47:10 drh Exp $ +** @(#) $Id: pager.c,v 1.87 2003/07/27 18:59:43 drh Exp $ */ #include "os.h" /* Must be first to enable large file support */ #include "sqliteInt.h" @@ -129,6 +129,7 @@ struct PgHdr { struct Pager { char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ + char *zDirectory; /* Directory hold database and journal files */ OsFile fd, jfd; /* File descriptors for database and journal */ OsFile cpfd; /* File descriptor for the checkpoint journal */ int dbSize; /* Number of pages in the file */ @@ -828,7 +829,7 @@ int sqlitepager_open( char *zFullPathname; int nameLen; OsFile fd; - int rc; + int rc, i; int tempFile; int readOnly = 0; char zTemp[SQLITE_TEMPNAME_SIZE]; @@ -855,7 +856,7 @@ int sqlitepager_open( return SQLITE_CANTOPEN; } nameLen = strlen(zFullPathname); - pPager = sqliteMalloc( sizeof(*pPager) + nameLen*2 + 30 ); + pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 ); if( pPager==0 ){ sqliteOsClose(&fd); sqliteFree(zFullPathname); @@ -863,8 +864,12 @@ int sqlitepager_open( } SET_PAGER(pPager); pPager->zFilename = (char*)&pPager[1]; - pPager->zJournal = &pPager->zFilename[nameLen+1]; + pPager->zDirectory = &pPager->zFilename[nameLen+1]; + pPager->zJournal = &pPager->zDirectory[nameLen+1]; strcpy(pPager->zFilename, zFullPathname); + strcpy(pPager->zDirectory, zFullPathname); + for(i=nameLen; i>0 && pPager->zDirectory[i-1]!='/'; i--){} + if( i>0 ) pPager->zDirectory[i-1] = 0; strcpy(pPager->zJournal, zFullPathname); sqliteFree(zFullPathname); strcpy(&pPager->zJournal[nameLen], "-journal"); @@ -995,8 +1000,10 @@ int sqlitepager_close(Pager *pPager){ */ CLR_PAGER(pPager); if( pPager->zFilename!=(char*)&pPager[1] ){ + assert( 0 ); /* Cannot happen */ sqliteFree(pPager->zFilename); sqliteFree(pPager->zJournal); + sqliteFree(pPager->zDirectory); } sqliteFree(pPager); return SQLITE_OK; @@ -1535,6 +1542,7 @@ static int pager_open_journal(Pager *pPager){ pPager->state = SQLITE_READLOCK; return SQLITE_CANTOPEN; } + sqliteOsOpenDirectory(pPager->zDirectory, &pPager->jfd); pPager->journalOpen = 1; pPager->journalStarted = 0; pPager->needSync = 0; |