📄 os_unix.c
字号:
** On success, a handle for the open file is written to *id
** and *pReadonly is set to 0 if the file was opened for reading and
** writing or 1 if the file was opened read-only. The function returns
** SQLITE_OK.
**
** On failure, the function returns SQLITE_CANTOPEN and leaves
** *id and *pReadonly unchanged.
*/
int sqlite3UnixOpenReadWrite(
const char *zFilename,
OsFile **pId,
int *pReadonly
){
int h;
CRASH_TEST_OVERRIDE(sqlite3CrashOpenReadWrite, zFilename, pId, pReadonly);
assert( 0==*pId );
h = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY,
SQLITE_DEFAULT_FILE_PERMISSIONS);
if( h<0 ){
#ifdef EISDIR
if( errno==EISDIR ){
return SQLITE_CANTOPEN;
}
#endif
h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
if( h<0 ){
return SQLITE_CANTOPEN;
}
*pReadonly = 1;
}else{
*pReadonly = 0;
}
return allocateUnixFile(h, pId, zFilename, 0);
}
/*
** Attempt to open a new file for exclusive access by this process.
** The file will be opened for both reading and writing. To avoid
** a potential security problem, we do not allow the file to have
** previously existed. Nor do we allow the file to be a symbolic
** link.
**
** If delFlag is true, then make arrangements to automatically delete
** the file when it is closed.
**
** On success, write the file handle into *id and return SQLITE_OK.
**
** On failure, return SQLITE_CANTOPEN.
*/
int sqlite3UnixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
int h;
CRASH_TEST_OVERRIDE(sqlite3CrashOpenExclusive, zFilename, pId, delFlag);
assert( 0==*pId );
h = open(zFilename,
O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY,
SQLITE_DEFAULT_FILE_PERMISSIONS);
if( h<0 ){
return SQLITE_CANTOPEN;
}
return allocateUnixFile(h, pId, zFilename, delFlag);
}
/*
** Attempt to open a new file for read-only access.
**
** On success, write the file handle into *id and return SQLITE_OK.
**
** On failure, return SQLITE_CANTOPEN.
*/
int sqlite3UnixOpenReadOnly(const char *zFilename, OsFile **pId){
int h;
CRASH_TEST_OVERRIDE(sqlite3CrashOpenReadOnly, zFilename, pId, 0);
assert( 0==*pId );
h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
if( h<0 ){
return SQLITE_CANTOPEN;
}
return allocateUnixFile(h, pId, zFilename, 0);
}
/*
** 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.
**
** If FULL_FSYNC is enabled, this function is not longer useful,
** a FULL_FSYNC sync applies to all pending disk operations.
**
** On success, a handle for a previously open file 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.
*/
static int unixOpenDirectory(
OsFile *id,
const char *zDirname
){
unixFile *pFile = (unixFile*)id;
if( pFile==0 ){
/* Do not open the directory if the corresponding file is not already
** open. */
return SQLITE_CANTOPEN;
}
SET_THREADID(pFile);
assert( pFile->dirfd<0 );
pFile->dirfd = open(zDirname, O_RDONLY|O_BINARY, 0);
if( pFile->dirfd<0 ){
return SQLITE_CANTOPEN;
}
TRACE3("OPENDIR %-3d %s\n", pFile->dirfd, zDirname);
return SQLITE_OK;
}
/*
** If the following global variable points to a string which is the
** name of a directory, then that directory will be used to store
** temporary files.
**
** See also the "PRAGMA temp_store_directory" SQL command.
*/
char *sqlite3_temp_directory = 0;
/*
** Create a temporary file name in zBuf. zBuf must be big enough to
** hold at least SQLITE_TEMPNAME_SIZE characters.
*/
int sqlite3UnixTempFileName(char *zBuf){
static const char *azDirs[] = {
0,
"/var/tmp",
"/usr/tmp",
"/tmp",
".",
};
static const unsigned char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
int i, j;
struct stat buf;
const char *zDir = ".";
azDirs[0] = sqlite3_temp_directory;
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
if( azDirs[i]==0 ) continue;
if( stat(azDirs[i], &buf) ) continue;
if( !S_ISDIR(buf.st_mode) ) continue;
if( access(azDirs[i], 07) ) continue;
zDir = azDirs[i];
break;
}
do{
sprintf(zBuf, "%s/"TEMP_FILE_PREFIX, zDir);
j = strlen(zBuf);
sqlite3Randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
zBuf[j] = 0;
}while( access(zBuf,0)==0 );
return SQLITE_OK;
}
/*
** Check that a given pathname is a directory and is writable
**
*/
int sqlite3UnixIsDirWritable(char *zBuf){
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
struct stat buf;
if( zBuf==0 ) return 0;
if( zBuf[0]==0 ) return 0;
if( stat(zBuf, &buf) ) return 0;
if( !S_ISDIR(buf.st_mode) ) return 0;
if( access(zBuf, 07) ) return 0;
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
return 1;
}
/*
** Seek to the offset in id->offset then read cnt bytes into pBuf.
** Return the number of bytes actually read. Update the offset.
*/
static int seekAndRead(unixFile *id, void *pBuf, int cnt){
int got;
#ifdef USE_PREAD
got = pread(id->h, pBuf, cnt, id->offset);
#else
lseek(id->h, id->offset, SEEK_SET);
got = read(id->h, pBuf, cnt);
#endif
if( got>0 ){
id->offset += got;
}
return got;
}
/*
** Read data from a file into a buffer. Return SQLITE_OK if all
** bytes were read successfully and SQLITE_IOERR if anything goes
** wrong.
*/
static int unixRead(OsFile *id, void *pBuf, int amt){
int got;
assert( id );
TIMER_START;
got = seekAndRead((unixFile*)id, pBuf, amt);
TIMER_END;
TRACE5("READ %-3d %5d %7d %d\n", ((unixFile*)id)->h, got,
last_page, TIMER_ELAPSED);
SEEK(0);
SimulateIOError( got=0 );
if( got==amt ){
return SQLITE_OK;
}else if( got<0 ){
return SQLITE_IOERR_READ;
}else{
return SQLITE_IOERR_SHORT_READ;
}
}
/*
** Seek to the offset in id->offset then read cnt bytes into pBuf.
** Return the number of bytes actually read. Update the offset.
*/
static int seekAndWrite(unixFile *id, const void *pBuf, int cnt){
int got;
#ifdef USE_PREAD
got = pwrite(id->h, pBuf, cnt, id->offset);
#else
lseek(id->h, id->offset, SEEK_SET);
got = write(id->h, pBuf, cnt);
#endif
if( got>0 ){
id->offset += got;
}
return got;
}
/*
** Write data from a buffer into a file. Return SQLITE_OK on success
** or some other error code on failure.
*/
static int unixWrite(OsFile *id, const void *pBuf, int amt){
int wrote = 0;
assert( id );
assert( amt>0 );
TIMER_START;
while( amt>0 && (wrote = seekAndWrite((unixFile*)id, pBuf, amt))>0 ){
amt -= wrote;
pBuf = &((char*)pBuf)[wrote];
}
TIMER_END;
TRACE5("WRITE %-3d %5d %7d %d\n", ((unixFile*)id)->h, wrote,
last_page, TIMER_ELAPSED);
SEEK(0);
SimulateIOError(( wrote=(-1), amt=1 ));
SimulateDiskfullError(( wrote=0, amt=1 ));
if( amt>0 ){
if( wrote<0 ){
return SQLITE_IOERR_WRITE;
}else{
return SQLITE_FULL;
}
}
return SQLITE_OK;
}
/*
** Move the read/write pointer in a file.
*/
static int unixSeek(OsFile *id, i64 offset){
assert( id );
SEEK(offset/1024 + 1);
#ifdef SQLITE_TEST
if( offset ) SimulateDiskfullError(return SQLITE_FULL);
#endif
((unixFile*)id)->offset = offset;
return SQLITE_OK;
}
#ifdef SQLITE_TEST
/*
** Count the number of fullsyncs and normal syncs. This is used to test
** that syncs and fullsyncs are occuring at the right times.
*/
int sqlite3_sync_count = 0;
int sqlite3_fullsync_count = 0;
#endif
/*
** Use the fdatasync() API only if the HAVE_FDATASYNC macro is defined.
** Otherwise use fsync() in its place.
*/
#ifndef HAVE_FDATASYNC
# define fdatasync fsync
#endif
/*
** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not
** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently
** only available on Mac OS X. But that could change.
*/
#ifdef F_FULLFSYNC
# define HAVE_FULLFSYNC 1
#else
# define HAVE_FULLFSYNC 0
#endif
/*
** The fsync() system call does not work as advertised on many
** unix systems. The following procedure is an attempt to make
** it work better.
**
** The SQLITE_NO_SYNC macro disables all fsync()s. This is useful
** for testing when we want to run through the test suite quickly.
** You are strongly advised *not* to deploy with SQLITE_NO_SYNC
** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash
** or power failure will likely corrupt the database file.
*/
static int full_fsync(int fd, int fullSync, int dataOnly){
int rc;
/* Record the number of times that we do a normal fsync() and
** FULLSYNC. This is used during testing to verify that this procedure
** gets called with the correct arguments.
*/
#ifdef SQLITE_TEST
if( fullSync ) sqlite3_fullsync_count++;
sqlite3_sync_count++;
#endif
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
** no-op
*/
#ifdef SQLITE_NO_SYNC
rc = SQLITE_OK;
#else
#if HAVE_FULLFSYNC
if( fullSync ){
rc = fcntl(fd, F_FULLFSYNC, 0);
}else
#endif /* HAVE_FULLFSYNC */
if( dataOnly ){
rc = fdatasync(fd);
}else{
rc = fsync(fd);
}
#endif /* defined(SQLITE_NO_SYNC) */
return rc;
}
/*
** Make sure all writes to a particular file are committed to disk.
**
** If dataOnly==0 then both the file itself and its metadata (file
** size, access time, etc) are synced. If dataOnly!=0 then only the
** file data is synced.
**
** 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.
*/
static int unixSync(OsFile *id, int dataOnly){
int rc;
unixFile *pFile = (unixFile*)id;
assert( pFile );
TRACE2("SYNC %-3d\n", pFile->h);
rc = full_fsync(pFile->h, pFile->fullSync, dataOnly);
SimulateIOError( rc=1 );
if( rc ){
return SQLITE_IOERR_FSYNC;
}
if( pFile->dirfd>=0 ){
TRACE4("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd,
HAVE_FULLFSYNC, pFile->fullSync);
#ifndef SQLITE_DISABLE_DIRSYNC
/* The directory sync is only attempted if full_fsync is
** turned off or unavailable. If a full_fsync occurred above,
** then the directory sync is superfluous.
*/
if( (!HAVE_FULLFSYNC || !pFile->fullSync) && full_fsync(pFile->dirfd,0,0) ){
/*
** We have received multiple reports of fsync() returning
** errors when applied to directories on certain file systems.
** A failed directory sync is not a big deal. So it seems
** better to ignore the error. Ticket #1657
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -