mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2025-12-05 15:15:29 +00:00
2719 lines
89 KiB
C
2719 lines
89 KiB
C
/*
|
|
** 2004 May 22
|
|
**
|
|
** The author disclaims copyright to this source code. In place of
|
|
** a legal notice, here is a blessing:
|
|
**
|
|
** May you do good and not evil.
|
|
** May you find forgiveness for yourself and forgive others.
|
|
** May you share freely, never taking more than you give.
|
|
**
|
|
******************************************************************************
|
|
**
|
|
** This file contains code that is specific to Windows.
|
|
*/
|
|
#include "sqliteInt.h"
|
|
#if SQLITE_OS_RTTHREAD /* This file is used for rt-thread only */
|
|
|
|
#include <rtthread.h>
|
|
#include <dfs_posix.h>
|
|
|
|
/*
|
|
** Include code that is common to all os_*.c files
|
|
*/
|
|
#include "os_common.h"
|
|
|
|
#ifndef RT_USING_NEWLIB
|
|
|
|
#ifndef EINTR
|
|
#define EINTR 4 /* Interrupted system call */
|
|
#endif
|
|
|
|
#ifndef ENOLCK
|
|
#define ENOLCK 46 /* No record locks available */
|
|
#endif
|
|
|
|
#ifndef EACCES
|
|
#define EACCES 13 /* Permission denied */
|
|
#endif
|
|
|
|
#ifndef EPERM
|
|
#define EPERM 1 /* Operation not permitted */
|
|
#endif
|
|
|
|
#ifndef ETIMEDOUT
|
|
#define ETIMEDOUT 145 /* Connection timed out */
|
|
#endif
|
|
|
|
#ifndef ENOTCONN
|
|
#define ENOTCONN 134 /* Transport endpoint is not connected */
|
|
#endif
|
|
|
|
#if defined(__GNUC__) || defined(__ADSPBLACKFIN__)
|
|
int _gettimeofday(struct timeval *tp, void *ignore) __attribute__((weak));
|
|
int _gettimeofday(struct timeval *tp, void *ignore)
|
|
#elif defined(__CC_ARM)
|
|
__weak int _gettimeofday(struct timeval *tp, void *ignore)
|
|
#elif defined(__IAR_SYSTEMS_ICC__)
|
|
#if __VER__ > 540
|
|
__weak
|
|
#endif
|
|
int _gettimeofday(struct timeval *tp, void *ignore)
|
|
#else
|
|
int _gettimeofday(struct timeval *tp, void *ignore)
|
|
#endif
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif /* RT_USING_NEWLIB */
|
|
|
|
/*
|
|
** Compiling and using WAL mode requires several APIs that are not
|
|
** available in rt-thread.
|
|
*/
|
|
#if !defined(SQLITE_OMIT_WAL)
|
|
# error "WAL mode requires not support from the rt-thread, compile\
|
|
with SQLITE_OMIT_WAL."
|
|
#endif
|
|
|
|
/*
|
|
** Are most of the rtthread ANSI APIs available (i.e. with certain exceptions
|
|
** based on the sub-platform)?
|
|
*/
|
|
#if !defined(SQLITE_RTTHREAD_NO_ANSI)
|
|
# warning "please ensure rtthread ANSI APIs is available, otherwise compile with\
|
|
SQLITE_RTTHREAD_NO_ANSI"
|
|
# define SQLITE_RTTHREAD_HAS_ANSI
|
|
#endif
|
|
|
|
/*
|
|
** Are most of the rtthread Unicode APIs available (i.e. with certain exceptions
|
|
** based on the sub-platform)?
|
|
*/
|
|
#if !defined(SQLITE_RTTHREAD_NO_WIDE)
|
|
# error "rtthread not support Unicode APIs"
|
|
# define SQLITE_RTTHREAD_HAS_WIDE
|
|
#endif
|
|
|
|
/*
|
|
** Make sure at least one set of rtthread APIs is available.
|
|
*/
|
|
#if !defined(SQLITE_RTTHREAD_HAS_ANSI) && !defined(SQLITE_RTTHREAD_HAS_WIDE)
|
|
# error "At least one of SQLITE_RTTHREAD_HAS_ANSI and SQLITE_RTTHREAD_HAS_WIDE\
|
|
must be defined."
|
|
#endif
|
|
|
|
/*
|
|
** Maximum pathname length (in chars) for rtthread. This should normally be
|
|
** MAX_PATH.
|
|
*/
|
|
#ifndef SQLITE_RTTHREAD_MAX_PATH_CHARS
|
|
# warning "default Maximum pathname length be 255, otherwise compile with\
|
|
SQLITE_RTTHREAD_MAX_PATH_CHARS=?"
|
|
# define SQLITE_RTTHREAD_MAX_PATH_CHARS (255)
|
|
#endif
|
|
|
|
/*
|
|
** Maximum supported path-length.
|
|
*/
|
|
#define MAX_PATHNAME 512
|
|
|
|
/*
|
|
** Returns non-zero if the character should be treated as a directory
|
|
** separator.
|
|
*/
|
|
#ifndef rtthreadIsDirSep
|
|
# define rtthreadIsDirSep(a) ((a) == '/')
|
|
#endif
|
|
|
|
/*
|
|
** This macro is used when a local variable is set to a value that is
|
|
** [sometimes] not used by the code (e.g. via conditional compilation).
|
|
*/
|
|
#ifndef UNUSED_VARIABLE_VALUE
|
|
# define UNUSED_VARIABLE_VALUE(x) (void)(x)
|
|
#endif
|
|
|
|
/*
|
|
** Returns the string that should be used as the directory separator.
|
|
*/
|
|
#ifndef rtthreadGetDirDep
|
|
# define rtthreadGetDirDep() "/"
|
|
#endif
|
|
|
|
/*
|
|
** The winFile structure is a subclass of sqlite3_file* specific to the win32
|
|
** portability layer.
|
|
*/
|
|
typedef struct rtthreadFile rtthreadFile;
|
|
struct rtthreadFile {
|
|
sqlite3_io_methods const *pMethod; /* Always the first entry */
|
|
sqlite3_vfs *pVfs; /* The VFS that created this rtthreadFile */
|
|
int h; /* The file descriptor */
|
|
unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
|
|
unsigned char eFileLock; /* The type of lock held on this fd */
|
|
int lastErrno; /* The unix errno from last I/O error */
|
|
void *lockingContext; /* Locking style specific state */
|
|
const char *zPath; /* Name of the file */
|
|
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
int openFlags; /* The flags specified at open() */
|
|
#endif
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
unsigned fsFlags; /* cached details from statfs() */
|
|
#endif
|
|
#ifdef SQLITE_DEBUG
|
|
/* The next group of variables are used to track whether or not the
|
|
** transaction counter in bytes 24-27 of database files are updated
|
|
** whenever any part of the database changes. An assertion fault will
|
|
** occur if a file is updated without also updating the transaction
|
|
** counter. This test is made to avoid new problems similar to the
|
|
** one described by ticket #3584.
|
|
*/
|
|
unsigned char transCntrChng; /* True if the transaction counter changed */
|
|
unsigned char dbUpdate; /* True if any part of database file changed */
|
|
unsigned char inNormalWrite; /* True if in a normal write operation */
|
|
|
|
#endif
|
|
|
|
#ifdef SQLITE_TEST
|
|
/* In test mode, increase the size of this structure a bit so that
|
|
** it is larger than the struct CrashFile defined in test6.c.
|
|
*/
|
|
char aPadding[32];
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
** Allowed values for the rtthreadFile.ctrlFlags bitmask:
|
|
*/
|
|
#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
|
|
#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
|
|
#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
|
|
#ifndef SQLITE_DISABLE_DIRSYNC
|
|
# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */
|
|
#else
|
|
# define UNIXFILE_DIRSYNC 0x00
|
|
#endif
|
|
#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
|
|
#define UNIXFILE_DELETE 0x20 /* Delete on close */
|
|
#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
|
|
#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
|
|
#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */
|
|
|
|
/*
|
|
** The following variable is (normally) set once and never changes
|
|
** thereafter. It records whether the operating system is Win9x
|
|
** or WinNT.
|
|
**
|
|
** 0: Operating system unknown.
|
|
** 1: Operating system is rtthread.
|
|
**
|
|
** In order to facilitate testing on a rtthread system, the test fixture
|
|
** can manually set this value to 1 to emulate Win98 behavior.
|
|
*/
|
|
#ifdef SQLITE_TEST
|
|
int sqlite3_os_type = 0;
|
|
#elif !SQLITE_OS_RTTHREAD && \
|
|
defined(SQLITE_RTTHREAD_HAS_ANSI) && defined(SQLITE_RTTHREAD_HAS_WIDE)
|
|
static int sqlite3_os_type = 0;
|
|
#endif
|
|
|
|
#ifndef SYSCALL
|
|
# define SYSCALL sqlite3_syscall_ptr
|
|
#endif
|
|
|
|
#include <dfs_posix.h>
|
|
|
|
static int _Access(const char *pathname, int mode)
|
|
{
|
|
int fd;
|
|
|
|
fd = open(pathname, O_RDONLY, mode);
|
|
|
|
if (fd >= 0)
|
|
{
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
** Invoke open(). Do so multiple times, until it either succeeds or
|
|
** fails for some reason other than EINTR.
|
|
**
|
|
** If the file creation mode "m" is 0 then set it to the default for
|
|
** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally
|
|
** 0644) as modified by the system umask. If m is not 0, then
|
|
** make the file creation mode be exactly m ignoring the umask.
|
|
**
|
|
** The m parameter will be non-zero only when creating -wal, -journal,
|
|
** and -shm files. We want those files to have *exactly* the same
|
|
** permissions as their original database, unadulterated by the umask.
|
|
** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a
|
|
** transaction crashes and leaves behind hot journals, then any
|
|
** process that is able to write to the database will also be able to
|
|
** recover the hot journals.
|
|
*/
|
|
static int robust_open(const char *z, int f, mode_t m);
|
|
|
|
/*
|
|
** Open a file descriptor to the directory containing file zFilename.
|
|
** If successful, *pFd is set to the opened file descriptor and
|
|
** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
|
|
** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
|
|
** value.
|
|
**
|
|
** The directory file descriptor is used for only one thing - to
|
|
** fsync() a directory to make sure file creation and deletion events
|
|
** are flushed to disk. Such fsyncs are not needed on newer
|
|
** journaling filesystems, but are required on older filesystems.
|
|
**
|
|
** This routine can be overridden using the xSetSysCall interface.
|
|
** The ability to override this routine was added in support of the
|
|
** chromium sandbox. Opening a directory is a security risk (we are
|
|
** told) so making it overrideable allows the chromium sandbox to
|
|
** replace this routine with a harmless no-op. To make this routine
|
|
** a no-op, replace it with a stub that returns SQLITE_OK but leaves
|
|
** *pFd set to a negative number.
|
|
**
|
|
** If SQLITE_OK is returned, the caller is responsible for closing
|
|
** the file descriptor *pFd using close().
|
|
*/
|
|
static int openDirectory(const char *zFilename, int *pFd);
|
|
|
|
/*
|
|
** Many system calls are accessed through pointer-to-functions so that
|
|
** they may be overridden at runtime to facilitate fault injection during
|
|
** testing and sandboxing. The following array holds the names and pointers
|
|
** to all overrideable system calls.
|
|
*/
|
|
static struct rtthread_syscall {
|
|
const char *zName; /* Name of the system call */
|
|
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
|
|
sqlite3_syscall_ptr pDefault; /* Default value */
|
|
} aSyscall[] = {
|
|
{"sleep", (sqlite3_syscall_ptr)rt_thread_delay, 0},
|
|
#define osSleep ((rt_err_t(*)(rt_tick_t))aSyscall[0].pCurrent)
|
|
|
|
{ "open", (sqlite3_syscall_ptr)open, 0 },
|
|
#define osOpen ((int(*)(const char*,int,int))aSyscall[1].pCurrent)
|
|
|
|
{ "close", (sqlite3_syscall_ptr)close, 0 },
|
|
#define osClose ((int(*)(int))aSyscall[2].pCurrent)
|
|
|
|
{ "getcwd", (sqlite3_syscall_ptr)getcwd, 0 },
|
|
#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent)
|
|
|
|
{ "stat", (sqlite3_syscall_ptr)stat, 0 },
|
|
#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent)
|
|
|
|
{ "fstat", (sqlite3_syscall_ptr)fstat, 0 },
|
|
#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent)
|
|
|
|
{ "read", (sqlite3_syscall_ptr)read, 0 },
|
|
#define osRead ((int(*)(int,void*,size_t))aSyscall[6].pCurrent)
|
|
|
|
{ "write", (sqlite3_syscall_ptr)write, 0 },
|
|
#define osWrite ((int(*)(int,const void*,size_t))aSyscall[7].pCurrent)
|
|
|
|
{ "unlink", (sqlite3_syscall_ptr)unlink, 0 },
|
|
#define osUnlink ((int(*)(const char*))aSyscall[8].pCurrent)
|
|
|
|
{ "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 },
|
|
#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[9].pCurrent)
|
|
|
|
{ "mkdir", (sqlite3_syscall_ptr)mkdir, 0 },
|
|
#define osMkdir ((int(*)(const char*,mode_t))aSyscall[10].pCurrent)
|
|
|
|
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
|
|
#define osRmdir ((int(*)(const char*))aSyscall[11].pCurrent)
|
|
|
|
{"access", (sqlite3_syscall_ptr)_Access, 0 },
|
|
#define osAccess ((int(*)(const char*, int))aSyscall[12].pCurrent)
|
|
}; /* End of the overrideable system calls */
|
|
|
|
/*
|
|
**
|
|
** This function - unixLogError_x(), is only ever called via the macro
|
|
** unixLogError().
|
|
**
|
|
** It is invoked after an error occurs in an OS function and errno has been
|
|
** set. It logs a message using sqlite3_log() containing the current value of
|
|
** errno and, if possible, the human-readable equivalent from strerror() or
|
|
** strerror_r().
|
|
**
|
|
** The first argument passed to the macro should be the error code that
|
|
** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
|
|
** The two subsequent arguments should be the name of the OS function that
|
|
** failed (e.g. "unlink", "open") and the associated file-system path,
|
|
** if any.
|
|
*/
|
|
#define rtthreadLogError(a,b,c) rtthreadLogErrorAtLine(a,b,c,__LINE__)
|
|
static int rtthreadLogErrorAtLine(
|
|
int errcode, /* SQLite error code */
|
|
const char *zFunc, /* Name of OS function that failed */
|
|
const char *zPath, /* File path associated with error */
|
|
int iLine /* Source line number where error occurred */
|
|
){
|
|
char *zErr; /* Message from strerror() or equivalent */
|
|
int iErrno = errno; /* Saved syscall error number */
|
|
|
|
/* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use
|
|
** the strerror() function to obtain the human-readable error message
|
|
** equivalent to errno. Otherwise, use strerror_r().
|
|
*/
|
|
#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R)
|
|
char aErr[80];
|
|
memset(aErr, 0, sizeof(aErr));
|
|
zErr = aErr;
|
|
|
|
/* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined,
|
|
** assume that the system provides the GNU version of strerror_r() that
|
|
** returns a pointer to a buffer containing the error message. That pointer
|
|
** may point to aErr[], or it may point to some static storage somewhere.
|
|
** Otherwise, assume that the system provides the POSIX version of
|
|
** strerror_r(), which always writes an error message into aErr[].
|
|
**
|
|
** If the code incorrectly assumes that it is the POSIX version that is
|
|
** available, the error message will often be an empty string. Not a
|
|
** huge problem. Incorrectly concluding that the GNU version is available
|
|
** could lead to a segfault though.
|
|
*/
|
|
#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)
|
|
zErr =
|
|
# endif
|
|
strerror_r(iErrno, aErr, sizeof(aErr)-1);
|
|
|
|
#elif SQLITE_THREADSAFE
|
|
/* This is a threadsafe build, but strerror_r() is not available. */
|
|
zErr = "";
|
|
#else
|
|
/* Non-threadsafe build, use strerror(). */
|
|
zErr = strerror(iErrno);
|
|
#endif
|
|
|
|
if( zPath==0 ) zPath = "";
|
|
sqlite3_log(errcode,
|
|
"os_rtthread.c:%d: (%d) %s(%s) - %s",
|
|
iLine, iErrno, zFunc, zPath, zErr
|
|
);
|
|
|
|
return errcode;
|
|
}
|
|
|
|
|
|
/*
|
|
** Do not accept any file descriptor less than this value, in order to avoid
|
|
** opening database file using file descriptors that are commonly used for
|
|
** standard input, output, and error.
|
|
*/
|
|
#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
|
|
# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3
|
|
#endif
|
|
|
|
/*
|
|
** Invoke open(). Do so multiple times, until it either succeeds or
|
|
** fails for some reason other than EINTR.
|
|
**
|
|
** If the file creation mode "m" is 0 then set it to the default for
|
|
** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally
|
|
** 0644) as modified by the system umask. If m is not 0, then
|
|
** make the file creation mode be exactly m ignoring the umask.
|
|
**
|
|
** The m parameter will be non-zero only when creating -wal, -journal,
|
|
** and -shm files. We want those files to have *exactly* the same
|
|
** permissions as their original database, unadulterated by the umask.
|
|
** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a
|
|
** transaction crashes and leaves behind hot journals, then any
|
|
** process that is able to write to the database will also be able to
|
|
** recover the hot journals.
|
|
*/
|
|
static int robust_open(const char *z, int f, mode_t m){
|
|
int fd;
|
|
mode_t m2 = m ;
|
|
while(1){
|
|
#if defined(O_CLOEXEC)
|
|
fd = osOpen(z,f|O_CLOEXEC,m2);
|
|
#else
|
|
fd = osOpen(z,f,m2);
|
|
#endif
|
|
if( fd<0 ){
|
|
if( errno==EINTR ) continue;
|
|
break;
|
|
}
|
|
if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
|
|
osClose(fd);
|
|
sqlite3_log(SQLITE_WARNING,
|
|
"attempt to open \"%s\" as file descriptor %d", z, fd);
|
|
fd = -1;
|
|
if( osOpen("/000111sql.test111000", f, m)<0 ) break;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
** Open a file descriptor to the directory containing file zFilename.
|
|
** If successful, *pFd is set to the opened file descriptor and
|
|
** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
|
|
** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
|
|
** value.
|
|
**
|
|
** The directory file descriptor is used for only one thing - to
|
|
** fsync() a directory to make sure file creation and deletion events
|
|
** are flushed to disk. Such fsyncs are not needed on newer
|
|
** journaling filesystems, but are required on older filesystems.
|
|
**
|
|
** This routine can be overridden using the xSetSysCall interface.
|
|
** The ability to override this routine was added in support of the
|
|
** chromium sandbox. Opening a directory is a security risk (we are
|
|
** told) so making it overrideable allows the chromium sandbox to
|
|
** replace this routine with a harmless no-op. To make this routine
|
|
** a no-op, replace it with a stub that returns SQLITE_OK but leaves
|
|
** *pFd set to a negative number.
|
|
**
|
|
** If SQLITE_OK is returned, the caller is responsible for closing
|
|
** the file descriptor *pFd using close().
|
|
*/
|
|
static int openDirectory(const char *zFilename, int *pFd){
|
|
int ii;
|
|
int fd = -1;
|
|
char zDirname[MAX_PATHNAME+1];
|
|
|
|
sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
|
|
for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
|
|
if( ii>0 ){
|
|
zDirname[ii] = '\0';
|
|
fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
|
|
if( fd>=0 ){
|
|
OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
|
|
}
|
|
}
|
|
*pFd = fd;
|
|
return (fd>=0?SQLITE_OK:rtthreadLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
|
|
** "win32" VFSes. Return SQLITE_OK opon successfully updating the
|
|
** system call pointer, or SQLITE_NOTFOUND if there is no configurable
|
|
** system call named zName.
|
|
*/
|
|
static int rtthreadSetSystemCall(
|
|
sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */
|
|
const char *zName, /* Name of system call to override */
|
|
sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */
|
|
){
|
|
unsigned int i;
|
|
int rc = SQLITE_NOTFOUND;
|
|
|
|
UNUSED_PARAMETER(pNotUsed);
|
|
if( zName==0 ){
|
|
/* If no zName is given, restore all system calls to their default
|
|
** settings and return NULL
|
|
*/
|
|
rc = SQLITE_OK;
|
|
for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
|
if( aSyscall[i].pDefault ){
|
|
aSyscall[i].pCurrent = aSyscall[i].pDefault;
|
|
}
|
|
}
|
|
}else{
|
|
/* If zName is specified, operate on only the one system call
|
|
** specified.
|
|
*/
|
|
for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
|
if( strcmp(zName, aSyscall[i].zName)==0 ){
|
|
if( aSyscall[i].pDefault==0 ){
|
|
aSyscall[i].pDefault = aSyscall[i].pCurrent;
|
|
}
|
|
rc = SQLITE_OK;
|
|
if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault;
|
|
aSyscall[i].pCurrent = pNewFunc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Return the value of a system call. Return NULL if zName is not a
|
|
** recognized system call name. NULL is also returned if the system call
|
|
** is currently undefined.
|
|
*/
|
|
static sqlite3_syscall_ptr rtthreadGetSystemCall(
|
|
sqlite3_vfs *pNotUsed,
|
|
const char *zName
|
|
){
|
|
unsigned int i;
|
|
|
|
UNUSED_PARAMETER(pNotUsed);
|
|
for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
|
if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Return the name of the first system call after zName. If zName==NULL
|
|
** then return the name of the first system call. Return NULL if zName
|
|
** is the last system call or if zName is not the name of a valid
|
|
** system call.
|
|
*/
|
|
static const char* rtthreadNextSystemCall(sqlite3_vfs *p, const char *zName){
|
|
int i = -1;
|
|
|
|
UNUSED_PARAMETER(p);
|
|
if( zName ){
|
|
for(i=0; i<ArraySize(aSyscall)-1; i++){
|
|
if( strcmp(zName, aSyscall[i].zName)==0 ) break;
|
|
}
|
|
}
|
|
for(i++; i<ArraySize(aSyscall); i++){
|
|
if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** The following routine suspends the current thread for at least ms
|
|
** milliseconds. This is equivalent to the Win32 Sleep() interface.
|
|
*/
|
|
void sqlite3_rtthread_sleep(int milliseconds){
|
|
rt_tick_t sleep_tick;
|
|
|
|
if (milliseconds <= 0)
|
|
return;
|
|
|
|
sleep_tick = rt_tick_from_millisecond(milliseconds);
|
|
|
|
osSleep(sleep_tick);
|
|
}
|
|
|
|
/*
|
|
** Helper functions to obtain and relinquish the global mutex. The
|
|
** global mutex is used to protect the unixInodeInfo and
|
|
** vxworksFileId objects used by this file, all of which may be
|
|
** shared by multiple threads.
|
|
**
|
|
** Function unixMutexHeld() is used to assert() that the global mutex
|
|
** is held when required. This function is only used as part of assert()
|
|
** statements. e.g.
|
|
**
|
|
** unixEnterMutex()
|
|
** assert( unixMutexHeld() );
|
|
** unixEnterLeave()
|
|
*/
|
|
static void rtthreadEnterMutex(void){
|
|
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
}
|
|
static void rtthreadLeaveMutex(void){
|
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
}
|
|
#ifdef SQLITE_DEBUG
|
|
static int rtthreadMutexHeld(void) {
|
|
return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
|
/*
|
|
** Helper function for printing out trace information from debugging
|
|
** binaries. This returns the string represetation of the supplied
|
|
** integer lock-type.
|
|
*/
|
|
static const char *azFileLock(int eFileLock){
|
|
switch( eFileLock ){
|
|
case NO_LOCK: return "NONE";
|
|
case SHARED_LOCK: return "SHARED";
|
|
case RESERVED_LOCK: return "RESERVED";
|
|
case PENDING_LOCK: return "PENDING";
|
|
case EXCLUSIVE_LOCK: return "EXCLUSIVE";
|
|
}
|
|
return "ERROR";
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** This routine translates a standard POSIX errno code into something
|
|
** useful to the clients of the sqlite3 functions. Specifically, it is
|
|
** intended to translate a variety of "try again" errors into SQLITE_BUSY
|
|
** and a variety of "please close the file descriptor NOW" errors into
|
|
** SQLITE_IOERR
|
|
**
|
|
** Errors during initialization of locks, or file system support for locks,
|
|
** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
|
|
*/
|
|
static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
|
|
switch (posixError) {
|
|
#if 0
|
|
/* At one point this code was not commented out. In theory, this branch
|
|
** should never be hit, as this function should only be called after
|
|
** a locking-related function (i.e. fcntl()) has returned non-zero with
|
|
** the value of errno as the first argument. Since a system call has failed,
|
|
** errno should be non-zero.
|
|
**
|
|
** Despite this, if errno really is zero, we still don't want to return
|
|
** SQLITE_OK. The system call failed, and *some* SQLite error should be
|
|
** propagated back to the caller. Commenting this branch out means errno==0
|
|
** will be handled by the "default:" case below.
|
|
*/
|
|
case 0:
|
|
return SQLITE_OK;
|
|
#endif
|
|
|
|
case DFS_STATUS_EAGAIN:
|
|
case ETIMEDOUT:
|
|
case DFS_STATUS_EBUSY:
|
|
case EINTR:
|
|
case ENOLCK:
|
|
/* random NFS retry error, unless during file system support
|
|
* introspection, in which it actually means what it says */
|
|
return SQLITE_BUSY;
|
|
|
|
case EACCES:
|
|
/* EACCES is like EAGAIN during locking operations, but not any other time*/
|
|
if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
|
|
(sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
|
|
(sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
|
|
(sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
|
|
return SQLITE_BUSY;
|
|
}
|
|
/* else fall through */
|
|
case EPERM:
|
|
return SQLITE_PERM;
|
|
|
|
/* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
|
|
** this module never makes such a call. And the code in SQLite itself
|
|
** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
|
|
** this case is also commented out. If the system does set errno to EDEADLK,
|
|
** the default SQLITE_IOERR_XXX code will be returned. */
|
|
#if 0
|
|
case EDEADLK:
|
|
return SQLITE_IOERR_BLOCKED;
|
|
#endif
|
|
|
|
#if EOPNOTSUPP!=ENOTSUP
|
|
case EOPNOTSUPP:
|
|
/* something went terribly awry, unless during file system support
|
|
* introspection, in which it actually means what it says */
|
|
#endif
|
|
#ifdef ENOTSUP
|
|
case ENOTSUP:
|
|
/* invalid fd, unless during file system support introspection, in which
|
|
* it actually means what it says */
|
|
#endif
|
|
case DFS_STATUS_EIO:
|
|
case DFS_STATUS_EBADF:
|
|
case DFS_STATUS_EINVAL:
|
|
case ENOTCONN:
|
|
case DFS_STATUS_ENODEV:
|
|
case DFS_STATUS_ENXIO:
|
|
case DFS_STATUS_ENOENT:
|
|
#ifdef ESTALE /* ESTALE is not defined on Interix systems */
|
|
case ESTALE:
|
|
#endif
|
|
case DFS_STATUS_ENOSYS:
|
|
/* these should force the client to close the file and reconnect */
|
|
|
|
default:
|
|
return sqliteIOErr;
|
|
}
|
|
}
|
|
|
|
static int robust_ftruncate(int h, sqlite3_int64 sz){
|
|
int rc;
|
|
rc = -1;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Close a file descriptor.
|
|
**
|
|
** We assume that close() almost always works, since it is only in a
|
|
** very sick application or on a very sick platform that it might fail.
|
|
** If it does fail, simply leak the file descriptor, but do log the
|
|
** error.
|
|
**
|
|
** Note that it is not safe to retry close() after EINTR since the
|
|
** file descriptor might have already been reused by another thread.
|
|
** So we don't even try to recover from an EINTR. Just log the error
|
|
** and move on.
|
|
*/
|
|
static void robust_close(rtthreadFile *pFile, int h, int lineno){
|
|
if( osClose(h) ){
|
|
rtthreadLogErrorAtLine(SQLITE_IOERR_CLOSE, "close",
|
|
pFile ? pFile->zPath : 0, lineno);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Check a rtthreadFile that is a database. Verify the following:
|
|
**
|
|
** (1) There is exactly one hard link on the file
|
|
** (2) The file is not a symbolic link
|
|
** (3) The file has not been renamed or unlinked
|
|
**
|
|
** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right.
|
|
*/
|
|
static void verifyDbFile(rtthreadFile *pFile){
|
|
struct stat buf;
|
|
int rc;
|
|
if( pFile->ctrlFlags & UNIXFILE_WARNED ){
|
|
/* One or more of the following warnings have already been issued. Do not
|
|
** repeat them so as not to clutter the error log */
|
|
return;
|
|
}
|
|
rc = osFstat(pFile->h, &buf);
|
|
if( rc!=0 ){
|
|
sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath);
|
|
pFile->ctrlFlags |= UNIXFILE_WARNED;
|
|
return;
|
|
}
|
|
#warning " struct \"stat\" has no field \"st_nlink\""
|
|
#ifndef RT_USING_SQLITE
|
|
if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){
|
|
sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath);
|
|
pFile->ctrlFlags |= UNIXFILE_WARNED;
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
** This function performs the parts of the "close file" operation
|
|
** common to all locking schemes. It closes the directory and file
|
|
** handles, if they are valid, and sets all fields of the rtthreadFile
|
|
** structure to 0.
|
|
**
|
|
** It is *not* necessary to hold the mutex when this routine is called,
|
|
** even on VxWorks. A mutex will be acquired on VxWorks by the
|
|
** vxworksReleaseFileId() routine.
|
|
*/
|
|
static int closeRtthreadFile(sqlite3_file *id){
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
if( pFile->h>=0 ){
|
|
robust_close(pFile, pFile->h, __LINE__);
|
|
pFile->h = -1;
|
|
}
|
|
OSTRACE(("CLOSE %-3d\n", pFile->h));
|
|
OpenCounter(-1);
|
|
memset(pFile, 0, sizeof(rtthreadFile));
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/************** End of the posix advisory lock implementation *****************
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
****************************** No-op Locking **********************************
|
|
**
|
|
** Of the various locking implementations available, this is by far the
|
|
** simplest: locking is ignored. No attempt is made to lock the database
|
|
** file for reading or writing.
|
|
**
|
|
** This locking mode is appropriate for use on read-only databases
|
|
** (ex: databases that are burned into CD-ROM, for example.) It can
|
|
** also be used if the application employs some external mechanism to
|
|
** prevent simultaneous access of the same database by two or more
|
|
** database connections. But there is a serious risk of database
|
|
** corruption if this locking mode is used in situations where multiple
|
|
** database connections are accessing the same database file at the same
|
|
** time and one or more of those connections are writing.
|
|
*/
|
|
|
|
static int nolockCheckReservedLock(sqlite3_file *NotUsed, int *pResOut){
|
|
UNUSED_PARAMETER(NotUsed);
|
|
*pResOut = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
static int nolockLock(sqlite3_file *NotUsed, int NotUsed2){
|
|
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
return SQLITE_OK;
|
|
}
|
|
static int nolockUnlock(sqlite3_file *NotUsed, int NotUsed2){
|
|
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Close the file.
|
|
*/
|
|
static int nolockClose(sqlite3_file *id) {
|
|
return closeRtthreadFile(id);
|
|
}
|
|
|
|
/******************* End of the no-op lock implementation *********************
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
************************* Begin dot-file Locking ******************************
|
|
**
|
|
** The dotfile locking implementation uses the existence of separate lock
|
|
** files (really a directory) to control access to the database. This works
|
|
** on just about every filesystem imaginable. But there are serious downsides:
|
|
**
|
|
** (1) There is zero concurrency. A single reader blocks all other
|
|
** connections from reading or writing the database.
|
|
**
|
|
** (2) An application crash or power loss can leave stale lock files
|
|
** sitting around that need to be cleared manually.
|
|
**
|
|
** Nevertheless, a dotlock is an appropriate locking mode for use if no
|
|
** other locking strategy is available.
|
|
**
|
|
** Dotfile locking works by creating a subdirectory in the same directory as
|
|
** the database and with the same name but with a ".lock" extension added.
|
|
** The existence of a lock directory implies an EXCLUSIVE lock. All other
|
|
** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
|
|
*/
|
|
|
|
/*
|
|
** The file suffix added to the data base filename in order to create the
|
|
** lock directory.
|
|
*/
|
|
#define DOTLOCK_SUFFIX ".lock"
|
|
|
|
/*
|
|
** Only set the lastErrno if the error code is a real error and not
|
|
** a normal expected return code of SQLITE_BUSY or SQLITE_OK
|
|
*/
|
|
#define IS_LOCK_ERROR(x) (((x) != SQLITE_OK) && ((x) != SQLITE_BUSY))
|
|
|
|
/*
|
|
** This routine checks if there is a RESERVED lock held on the specified
|
|
** file by this or any other process. If such a lock is held, set *pResOut
|
|
** to a non-zero value otherwise *pResOut is set to zero. The return value
|
|
** is set to SQLITE_OK unless an I/O error occurs during lock checking.
|
|
**
|
|
** In dotfile locking, either a lock exists or it does not. So in this
|
|
** variation of CheckReservedLock(), *pResOut is set to true if any lock
|
|
** is held on the file and false if the file is unlocked.
|
|
*/
|
|
static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
|
int rc = SQLITE_OK;
|
|
int reserved = 0;
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
|
|
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
|
|
|
assert( pFile );
|
|
|
|
/* Check if a thread in this process holds such a lock */
|
|
if( pFile->eFileLock>SHARED_LOCK ){
|
|
/* Either this connection or some other connection in the same process
|
|
** holds a lock on the file. No need to check further. */
|
|
reserved = 1;
|
|
}else{
|
|
/* The lock is held if and only if the lockfile exists */
|
|
const char *zLockFile = (const char*)pFile->lockingContext;
|
|
reserved = 0;
|
|
}
|
|
OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved));
|
|
*pResOut = reserved;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Lock the file with the lock specified by parameter eFileLock - one
|
|
** of the following:
|
|
**
|
|
** (1) SHARED_LOCK
|
|
** (2) RESERVED_LOCK
|
|
** (3) PENDING_LOCK
|
|
** (4) EXCLUSIVE_LOCK
|
|
**
|
|
** Sometimes when requesting one lock state, additional lock states
|
|
** are inserted in between. The locking might fail on one of the later
|
|
** transitions leaving the lock state different from what it started but
|
|
** still short of its goal. The following chart shows the allowed
|
|
** transitions and the inserted intermediate states:
|
|
**
|
|
** UNLOCKED -> SHARED
|
|
** SHARED -> RESERVED
|
|
** SHARED -> (PENDING) -> EXCLUSIVE
|
|
** RESERVED -> (PENDING) -> EXCLUSIVE
|
|
** PENDING -> EXCLUSIVE
|
|
**
|
|
** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
|
** routine to lower a locking level.
|
|
**
|
|
** With dotfile locking, we really only support state (4): EXCLUSIVE.
|
|
** But we track the other locking levels internally.
|
|
*/
|
|
static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
char *zLockFile = (char *)pFile->lockingContext;
|
|
int rc = SQLITE_OK;
|
|
|
|
|
|
/* If we have any lock, then the lock file already exists. All we have
|
|
** to do is adjust our internal record of the lock level.
|
|
*/
|
|
if( pFile->eFileLock > NO_LOCK ){
|
|
pFile->eFileLock = eFileLock;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* grab an exclusive lock */
|
|
rc = osMkdir(zLockFile, 0777);
|
|
if( rc<0 ){
|
|
/* failed to open/create the lock directory */
|
|
int tErrno = errno;
|
|
if( DFS_STATUS_EEXIST == tErrno ){
|
|
rc = SQLITE_BUSY;
|
|
} else {
|
|
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
|
if( IS_LOCK_ERROR(rc) ){
|
|
pFile->lastErrno = tErrno;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* got it, set the type and return ok */
|
|
pFile->eFileLock = eFileLock;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
|
** must be either NO_LOCK or SHARED_LOCK.
|
|
**
|
|
** If the locking level of the file descriptor is already at or below
|
|
** the requested locking level, this routine is a no-op.
|
|
**
|
|
** When the locking level reaches NO_LOCK, delete the lock file.
|
|
*/
|
|
static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
char *zLockFile = (char *)pFile->lockingContext;
|
|
int rc;
|
|
|
|
assert( pFile );
|
|
OSTRACE(("UNLOCK %d %d was %d tnm=%s (dotlock)\n", pFile->h, eFileLock,
|
|
pFile->eFileLock, rt_thread_self()->name ));
|
|
assert( eFileLock<=SHARED_LOCK );
|
|
|
|
/* no-op if possible */
|
|
if( pFile->eFileLock==eFileLock ){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* To downgrade to shared, simply update our internal notion of the
|
|
** lock state. No need to mess with the file on disk.
|
|
*/
|
|
if( eFileLock==SHARED_LOCK ){
|
|
pFile->eFileLock = SHARED_LOCK;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* To fully unlock the database, delete the lock file */
|
|
assert( eFileLock==NO_LOCK );
|
|
rc = osRmdir(zLockFile);
|
|
if( rc<0 && errno==DFS_STATUS_ENOTDIR ) rc = osUnlink(zLockFile);
|
|
if( rc<0 ){
|
|
int tErrno = errno;
|
|
rc = 0;
|
|
if( DFS_STATUS_ENOENT != tErrno ){
|
|
rc = SQLITE_IOERR_UNLOCK;
|
|
}
|
|
if( IS_LOCK_ERROR(rc) ){
|
|
pFile->lastErrno = tErrno;
|
|
}
|
|
return rc;
|
|
}
|
|
pFile->eFileLock = NO_LOCK;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Close a file. Make sure the lock has been released before closing.
|
|
*/
|
|
static int dotlockClose(sqlite3_file *id) {
|
|
int rc = SQLITE_OK;
|
|
if( id ){
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
dotlockUnlock(id, NO_LOCK);
|
|
sqlite3_free(pFile->lockingContext);
|
|
rc = closeRtthreadFile(id);
|
|
}
|
|
return rc;
|
|
}
|
|
/****************** End of the dot-file lock implementation *******************
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
************************** Begin flock Locking ********************************
|
|
**
|
|
** Use the flock() system call to do file locking.
|
|
**
|
|
** flock() locking is like dot-file locking in that the various
|
|
** fine-grain locking levels supported by SQLite are collapsed into
|
|
** a single exclusive lock. In other words, SHARED, RESERVED, and
|
|
** PENDING locks are the same thing as an EXCLUSIVE lock. SQLite
|
|
** still works when you do this, but concurrency is reduced since
|
|
** only a single process can be reading the database at a time.
|
|
**
|
|
** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if
|
|
** compiling for VXWORKS.
|
|
*/
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
#warning "rtthread file lock not available"
|
|
/*
|
|
** Retry flock() calls that fail with EINTR
|
|
*/
|
|
static int robust_flock(int fd, int op){
|
|
int rc = 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** This routine checks if there is a RESERVED lock held on the specified
|
|
** file by this or any other process. If such a lock is held, set *pResOut
|
|
** to a non-zero value otherwise *pResOut is set to zero. The return value
|
|
** is set to SQLITE_OK unless an I/O error occurs during lock checking.
|
|
*/
|
|
static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
|
int rc = SQLITE_OK;
|
|
int reserved = 0;
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
|
|
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
|
|
|
assert( pFile );
|
|
|
|
/* Check if a thread in this process holds such a lock */
|
|
if( pFile->eFileLock>SHARED_LOCK ){
|
|
reserved = 1;
|
|
}
|
|
|
|
/* Otherwise see if some other process holds it. */
|
|
if( !reserved ){
|
|
/* attempt to get the lock */
|
|
int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB);
|
|
if( !lrc ){
|
|
/* got the lock, unlock it */
|
|
lrc = robust_flock(pFile->h, LOCK_UN);
|
|
if ( lrc ) {
|
|
int tErrno = errno;
|
|
/* unlock failed with an error */
|
|
lrc = SQLITE_IOERR_UNLOCK;
|
|
if( IS_LOCK_ERROR(lrc) ){
|
|
pFile->lastErrno = tErrno;
|
|
rc = lrc;
|
|
}
|
|
}
|
|
} else {
|
|
int tErrno = errno;
|
|
reserved = 1;
|
|
/* someone else might have it reserved */
|
|
lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
|
if( IS_LOCK_ERROR(lrc) ){
|
|
pFile->lastErrno = tErrno;
|
|
rc = lrc;
|
|
}
|
|
}
|
|
}
|
|
OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved));
|
|
|
|
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
|
if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
|
rc = SQLITE_OK;
|
|
reserved=1;
|
|
}
|
|
#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
|
*pResOut = reserved;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Lock the file with the lock specified by parameter eFileLock - one
|
|
** of the following:
|
|
**
|
|
** (1) SHARED_LOCK
|
|
** (2) RESERVED_LOCK
|
|
** (3) PENDING_LOCK
|
|
** (4) EXCLUSIVE_LOCK
|
|
**
|
|
** Sometimes when requesting one lock state, additional lock states
|
|
** are inserted in between. The locking might fail on one of the later
|
|
** transitions leaving the lock state different from what it started but
|
|
** still short of its goal. The following chart shows the allowed
|
|
** transitions and the inserted intermediate states:
|
|
**
|
|
** UNLOCKED -> SHARED
|
|
** SHARED -> RESERVED
|
|
** SHARED -> (PENDING) -> EXCLUSIVE
|
|
** RESERVED -> (PENDING) -> EXCLUSIVE
|
|
** PENDING -> EXCLUSIVE
|
|
**
|
|
** flock() only really support EXCLUSIVE locks. We track intermediate
|
|
** lock states in the sqlite3_file structure, but all locks SHARED or
|
|
** above are really EXCLUSIVE locks and exclude all other processes from
|
|
** access the file.
|
|
**
|
|
** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
|
** routine to lower a locking level.
|
|
*/
|
|
static int flockLock(sqlite3_file *id, int eFileLock) {
|
|
int rc = SQLITE_OK;
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
|
|
assert( pFile );
|
|
|
|
/* if we already have a lock, it is exclusive.
|
|
** Just adjust level and punt on outta here. */
|
|
if (pFile->eFileLock > NO_LOCK) {
|
|
pFile->eFileLock = eFileLock;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* grab an exclusive lock */
|
|
|
|
if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) {
|
|
int tErrno = errno;
|
|
/* didn't get, must be busy */
|
|
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
|
if( IS_LOCK_ERROR(rc) ){
|
|
pFile->lastErrno = tErrno;
|
|
}
|
|
} else {
|
|
/* got it, set the type and return ok */
|
|
pFile->eFileLock = eFileLock;
|
|
}
|
|
OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock),
|
|
rc==SQLITE_OK ? "ok" : "failed"));
|
|
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
|
if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
|
rc = SQLITE_BUSY;
|
|
}
|
|
#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
|
** must be either NO_LOCK or SHARED_LOCK.
|
|
**
|
|
** If the locking level of the file descriptor is already at or below
|
|
** the requested locking level, this routine is a no-op.
|
|
*/
|
|
static int flockUnlock(sqlite3_file *id, int eFileLock) {
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
|
|
assert( pFile );
|
|
OSTRACE(("UNLOCK %d %d was %d tnm=%s (flock)\n", pFile->h, eFileLock,
|
|
pFile->eFileLock, rt_thread_self()->name));
|
|
assert( eFileLock<=SHARED_LOCK );
|
|
|
|
/* no-op if possible */
|
|
if( pFile->eFileLock==eFileLock ){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* shared can just be set because we always have an exclusive */
|
|
if (eFileLock==SHARED_LOCK) {
|
|
pFile->eFileLock = eFileLock;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* no, really, unlock. */
|
|
if( robust_flock(pFile->h, LOCK_UN) ){
|
|
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
|
return SQLITE_OK;
|
|
#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
|
return SQLITE_IOERR_UNLOCK;
|
|
}else{
|
|
pFile->eFileLock = NO_LOCK;
|
|
return SQLITE_OK;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Close a file.
|
|
*/
|
|
static int flockClose(sqlite3_file *id) {
|
|
int rc = SQLITE_OK;
|
|
if( id ){
|
|
flockUnlock(id, NO_LOCK);
|
|
rc = closeRtthreadFile(id);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */
|
|
|
|
/******************* End of the flock lock implementation *********************
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
**************** Non-locking sqlite3_file methods *****************************
|
|
**
|
|
** The next division contains implementations for all methods of the
|
|
** sqlite3_file object other than the locking methods. The locking
|
|
** methods were defined in divisions above (one locking method per
|
|
** division). Those methods that are common to all locking modes
|
|
** are gather together into this division.
|
|
*/
|
|
|
|
/*
|
|
** Seek to the offset passed as the second argument, then read cnt
|
|
** bytes into pBuf. Return the number of bytes actually read.
|
|
**
|
|
** NB: If you define USE_PREAD or USE_PREAD64, then it might also
|
|
** be necessary to define _XOPEN_SOURCE to be 500. This varies from
|
|
** one system to another. Since SQLite does not define USE_PREAD
|
|
** any any form by default, we will not attempt to define _XOPEN_SOURCE.
|
|
** See tickets #2741 and #2681.
|
|
**
|
|
** To avoid stomping the errno value on a failed read the lastErrno value
|
|
** is set before returning.
|
|
*/
|
|
static int seekAndRead(rtthreadFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
|
|
int got;
|
|
int prior = 0;
|
|
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
|
|
i64 newOffset;
|
|
#endif
|
|
TIMER_START;
|
|
assert( cnt==(cnt&0x1ffff) );
|
|
assert( id->h>2 );
|
|
cnt &= 0x1ffff;
|
|
do{
|
|
#if defined(USE_PREAD)
|
|
#error "rtthread pread not support"
|
|
got = osPread(id->h, pBuf, cnt, offset);
|
|
SimulateIOError( got = -1 );
|
|
#elif defined(USE_PREAD64)
|
|
#error "rtthread pread64 not support"
|
|
got = osPread64(id->h, pBuf, cnt, offset);
|
|
SimulateIOError( got = -1 );
|
|
#else
|
|
newOffset = lseek(id->h, offset, SEEK_SET);
|
|
SimulateIOError( newOffset-- );
|
|
if( newOffset!=offset ){
|
|
if( newOffset == -1 ){
|
|
((rtthreadFile*)id)->lastErrno = errno;
|
|
}else{
|
|
((rtthreadFile*)id)->lastErrno = 0;
|
|
}
|
|
return -1;
|
|
}
|
|
got = osRead(id->h, pBuf, cnt);
|
|
#endif
|
|
if( got==cnt ) break;
|
|
if( got<0 ){
|
|
if( errno==EINTR ){ got = 1; continue; }
|
|
prior = 0;
|
|
((rtthreadFile*)id)->lastErrno = errno;
|
|
break;
|
|
}else if( got>0 ){
|
|
cnt -= got;
|
|
offset += got;
|
|
prior += got;
|
|
pBuf = (void*)(got + (char*)pBuf);
|
|
}
|
|
}while( got>0 );
|
|
TIMER_END;
|
|
OSTRACE(("READ %-3d %5d %7lld %llu\n",
|
|
id->h, got+prior, offset-prior, TIMER_ELAPSED));
|
|
return got+prior;
|
|
}
|
|
|
|
/*
|
|
** 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 rtthreadRead(
|
|
sqlite3_file *id,
|
|
void *pBuf,
|
|
int amt,
|
|
sqlite3_int64 offset
|
|
){
|
|
rtthreadFile *pFile = (rtthreadFile *)id;
|
|
int got;
|
|
assert( id );
|
|
assert( offset>=0 );
|
|
assert( amt>0 );
|
|
|
|
got = seekAndRead(pFile, offset, pBuf, amt);
|
|
if( got==amt ){
|
|
return SQLITE_OK;
|
|
}else if( got<0 ){
|
|
/* lastErrno set by seekAndRead */
|
|
return SQLITE_IOERR_READ;
|
|
}else{
|
|
pFile->lastErrno = 0; /* not a system error */
|
|
/* Unread parts of the buffer must be zero-filled */
|
|
memset(&((char*)pBuf)[got], 0, amt-got);
|
|
return SQLITE_IOERR_SHORT_READ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Attempt to seek the file-descriptor passed as the first argument to
|
|
** absolute offset iOff, then attempt to write nBuf bytes of data from
|
|
** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise,
|
|
** return the actual number of bytes written (which may be less than
|
|
** nBuf).
|
|
*/
|
|
static int seekAndWriteFd(
|
|
int fd, /* File descriptor to write to */
|
|
i64 iOff, /* File offset to begin writing at */
|
|
const void *pBuf, /* Copy data from this buffer to the file */
|
|
int nBuf, /* Size of buffer pBuf in bytes */
|
|
int *piErrno /* OUT: Error number if error occurs */
|
|
){
|
|
int rc = 0; /* Value returned by system call */
|
|
|
|
assert( nBuf==(nBuf&0x1ffff) );
|
|
assert( fd>2 );
|
|
nBuf &= 0x1ffff;
|
|
TIMER_START;
|
|
|
|
#if defined(USE_PREAD)
|
|
do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR );
|
|
#elif defined(USE_PREAD64)
|
|
do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR);
|
|
#else
|
|
do{
|
|
i64 iSeek = lseek(fd, iOff, SEEK_SET);
|
|
SimulateIOError( iSeek-- );
|
|
|
|
if( iSeek!=iOff ){
|
|
if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0);
|
|
return -1;
|
|
}
|
|
rc = osWrite(fd, pBuf, nBuf);
|
|
}while( rc<0 && errno==EINTR );
|
|
#endif
|
|
|
|
TIMER_END;
|
|
OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED));
|
|
|
|
if( rc<0 && piErrno ) *piErrno = errno;
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
** Seek to the offset in id->offset then read cnt bytes into pBuf.
|
|
** Return the number of bytes actually read. Update the offset.
|
|
**
|
|
** To avoid stomping the errno value on a failed write the lastErrno value
|
|
** is set before returning.
|
|
*/
|
|
static int seekAndWrite(rtthreadFile *id, i64 offset, const void *pBuf, int cnt){
|
|
return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno);
|
|
}
|
|
|
|
|
|
/*
|
|
** Write data from a buffer into a file. Return SQLITE_OK on success
|
|
** or some other error code on failure.
|
|
*/
|
|
static int rtthreadWrite(
|
|
sqlite3_file *id,
|
|
const void *pBuf,
|
|
int amt,
|
|
sqlite3_int64 offset
|
|
){
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
int wrote = 0;
|
|
assert( id );
|
|
assert( amt>0 );
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
/* If we are doing a normal write to a database file (as opposed to
|
|
** doing a hot-journal rollback or a write to some file other than a
|
|
** normal database file) then record the fact that the database
|
|
** has changed. If the transaction counter is modified, record that
|
|
** fact too.
|
|
*/
|
|
if( pFile->inNormalWrite ){
|
|
pFile->dbUpdate = 1; /* The database has been modified */
|
|
if( offset<=24 && offset+amt>=27 ){
|
|
int rc;
|
|
char oldCntr[4];
|
|
SimulateIOErrorBenign(1);
|
|
rc = seekAndRead(pFile, 24, oldCntr, 4);
|
|
SimulateIOErrorBenign(0);
|
|
if( rc!=4 || memcmp(oldCntr, &((char*)pBuf)[24-offset], 4)!=0 ){
|
|
pFile->transCntrChng = 1; /* The transaction counter has changed */
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
|
|
amt -= wrote;
|
|
offset += wrote;
|
|
pBuf = &((char*)pBuf)[wrote];
|
|
}
|
|
SimulateIOError(( wrote=(-1), amt=1 ));
|
|
SimulateDiskfullError(( wrote=0, amt=1 ));
|
|
|
|
if( amt>0 ){
|
|
if( wrote<0 && pFile->lastErrno!=DFS_STATUS_ENOSPC ){
|
|
/* lastErrno set by seekAndWrite */
|
|
return SQLITE_IOERR_WRITE;
|
|
}else{
|
|
pFile->lastErrno = 0; /* not a system error */
|
|
return SQLITE_FULL;
|
|
}
|
|
}
|
|
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
#ifdef SQLITE_TEST
|
|
/*
|
|
** Count the number of fullsyncs and normal syncs. This is used to test
|
|
** that syncs and fullsyncs are occurring at the right times.
|
|
*/
|
|
int sqlite3_sync_count = 0;
|
|
int sqlite3_fullsync_count = 0;
|
|
#endif
|
|
|
|
/*
|
|
** We do not trust systems to provide a working fdatasync(). Some do.
|
|
** Others do no. To be safe, we will stick with the (slightly slower)
|
|
** fsync(). If you know that your system does support fdatasync() correctly,
|
|
** then simply compile with -Dfdatasync=fdatasync
|
|
*/
|
|
#if !defined(fdatasync)
|
|
#include "dfs.h"
|
|
#include "dfs_file.h"
|
|
int fdatasync(fd)
|
|
{
|
|
struct dfs_fd *dfs_fd;
|
|
dfs_fd = fd_get(fd);
|
|
return dfs_file_flush(dfs_fd);
|
|
}
|
|
#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 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.
|
|
**
|
|
** SQLite sets the dataOnly flag if the size of the file is unchanged.
|
|
** The idea behind dataOnly is that it should only write the file content
|
|
** to disk, not the inode. We only set dataOnly if the file size is
|
|
** unchanged since the file size is part of the inode. However,
|
|
** Ted Ts'o tells us that fdatasync() will also write the inode if the
|
|
** file size has changed. The only real difference between fdatasync()
|
|
** and fsync(), Ted tells us, is that fdatasync() will not flush the
|
|
** inode if the mtime or owner or other inode attributes have changed.
|
|
** We only care about the file size, not the other file attributes, so
|
|
** as far as SQLite is concerned, an fdatasync() is always adequate.
|
|
** So, we always use fdatasync() if it is available, regardless of
|
|
** the value of the dataOnly flag.
|
|
*/
|
|
static int full_fsync(int fd, int fullSync, int dataOnly){
|
|
int rc;
|
|
|
|
/* The following "ifdef/elif/else/" block has the same structure as
|
|
** the one below. It is replicated here solely to avoid cluttering
|
|
** up the real code with the UNUSED_PARAMETER() macros.
|
|
*/
|
|
#ifdef SQLITE_NO_SYNC
|
|
UNUSED_PARAMETER(fd);
|
|
UNUSED_PARAMETER(fullSync);
|
|
UNUSED_PARAMETER(dataOnly);
|
|
#elif HAVE_FULLFSYNC
|
|
UNUSED_PARAMETER(dataOnly);
|
|
#else
|
|
UNUSED_PARAMETER(fullSync);
|
|
UNUSED_PARAMETER(dataOnly);
|
|
#endif
|
|
|
|
/* 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;
|
|
#elif HAVE_FULLFSYNC
|
|
#error "rtthread not support FULLFSYNC"
|
|
#else
|
|
rc = fdatasync(fd);
|
|
#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */
|
|
|
|
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 Rtthread, 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 rtthreadSync(sqlite3_file *id, int flags){
|
|
int rc;
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
|
|
int isDataOnly = (flags&SQLITE_SYNC_DATAONLY);
|
|
int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL;
|
|
|
|
/* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */
|
|
assert((flags&0x0F)==SQLITE_SYNC_NORMAL
|
|
|| (flags&0x0F)==SQLITE_SYNC_FULL
|
|
);
|
|
|
|
/* Rtthread cannot, but some systems may return SQLITE_FULL from here. This
|
|
** line is to test that doing so does not cause any problems.
|
|
*/
|
|
SimulateDiskfullError( return SQLITE_FULL );
|
|
|
|
assert( pFile );
|
|
OSTRACE(("SYNC %-3d\n", pFile->h));
|
|
rc = full_fsync(pFile->h, isFullsync, isDataOnly);
|
|
SimulateIOError( rc=1 );
|
|
if( rc ){
|
|
pFile->lastErrno = errno;
|
|
return rtthreadLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath);
|
|
}
|
|
|
|
/* Also fsync the directory containing the file if the DIRSYNC flag
|
|
** is set. This is a one-time occurrence. Many systems (examples: AIX)
|
|
** are unable to fsync a directory, so ignore errors on the fsync.
|
|
*/
|
|
if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){
|
|
int dirfd;
|
|
OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath,
|
|
HAVE_FULLFSYNC, isFullsync));
|
|
rc = osOpenDirectory(pFile->zPath, &dirfd);
|
|
if( rc==SQLITE_OK && dirfd>=0 ){
|
|
full_fsync(dirfd, 0, 0);
|
|
robust_close(pFile, dirfd, __LINE__);
|
|
}else if( rc==SQLITE_CANTOPEN ){
|
|
rc = SQLITE_OK;
|
|
}
|
|
pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Truncate an open file to a specified size
|
|
*/
|
|
static int rtthreadTruncate(sqlite3_file *id, i64 nByte){
|
|
rtthreadFile *pFile = (rtthreadFile *)id;
|
|
int rc;
|
|
assert( pFile );
|
|
SimulateIOError( return SQLITE_IOERR_TRUNCATE );
|
|
|
|
/* If the user has configured a chunk-size for this file, truncate the
|
|
** file so that it consists of an integer number of chunks (i.e. the
|
|
** actual file size after the operation may be larger than the requested
|
|
** size).
|
|
*/
|
|
if( pFile->szChunk>0 ){
|
|
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
|
|
}
|
|
|
|
rc = robust_ftruncate(pFile->h, (off_t)nByte);
|
|
if( rc ){
|
|
pFile->lastErrno = errno;
|
|
return rtthreadLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
|
}else{
|
|
#ifdef SQLITE_DEBUG
|
|
/* If we are doing a normal write to a database file (as opposed to
|
|
** doing a hot-journal rollback or a write to some file other than a
|
|
** normal database file) and we truncate the file to zero length,
|
|
** that effectively updates the change counter. This might happen
|
|
** when restoring a database using the backup API from a zero-length
|
|
** source.
|
|
*/
|
|
if( pFile->inNormalWrite && nByte==0 ){
|
|
pFile->transCntrChng = 1;
|
|
}
|
|
#endif
|
|
|
|
return SQLITE_OK;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Determine the current size of a file in bytes
|
|
*/
|
|
static int rtthreadFileSize(sqlite3_file *id, i64 *pSize){
|
|
int rc;
|
|
struct stat buf;
|
|
assert( id );
|
|
rc = osFstat(((rtthreadFile*)id)->h, &buf);
|
|
SimulateIOError( rc=1 );
|
|
if( rc!=0 ){
|
|
((rtthreadFile*)id)->lastErrno = errno;
|
|
return SQLITE_IOERR_FSTAT;
|
|
}
|
|
*pSize = buf.st_size;
|
|
|
|
/* When opening a zero-size database, the findInodeInfo() procedure
|
|
** writes a single byte into that file in order to work around a bug
|
|
** in the OS-X msdos filesystem. In order to avoid problems with upper
|
|
** layers, we need to report this file size as zero even though it is
|
|
** really 1. Ticket #3260.
|
|
*/
|
|
if( *pSize==1 ) *pSize = 0;
|
|
|
|
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
|
|
** file-control operation. Enlarge the database to nBytes in size
|
|
** (rounded up to the next chunk-size). If the database is already
|
|
** nBytes or larger, this routine is a no-op.
|
|
*/
|
|
static int fcntlSizeHint(rtthreadFile *pFile, i64 nByte){
|
|
if( pFile->szChunk>0 ){
|
|
i64 nSize; /* Required file size */
|
|
struct stat buf; /* Used to hold return values of fstat() */
|
|
|
|
if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT;
|
|
|
|
nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
|
|
if( nSize>(i64)buf.st_size ){
|
|
/* If the OS does not have posix_fallocate(), fake it. First use
|
|
** ftruncate() to set the file size, then write a single byte to
|
|
** the last byte in each block within the extended region. This
|
|
** is the same technique used by glibc to implement posix_fallocate()
|
|
** on systems that do not have a real fallocate() system call.
|
|
*/
|
|
int nBlk = 4096; // no blksize in RT-Thread, use 4096. /* File-system block size */
|
|
i64 iWrite; /* Next offset to write to */
|
|
|
|
if( robust_ftruncate(pFile->h, nSize) ){
|
|
pFile->lastErrno = errno;
|
|
return rtthreadLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
|
}
|
|
iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
|
|
while( iWrite<nSize ){
|
|
int nWrite = seekAndWrite(pFile, iWrite, "", 1);
|
|
if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
|
|
iWrite += nBlk;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** If *pArg is inititially negative then this is a query. Set *pArg to
|
|
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
|
|
**
|
|
** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
|
|
*/
|
|
static void rtthreadModeBit(rtthreadFile *pFile, unsigned char mask, int *pArg){
|
|
if( *pArg<0 ){
|
|
*pArg = (pFile->ctrlFlags & mask)!=0;
|
|
}else if( (*pArg)==0 ){
|
|
pFile->ctrlFlags &= ~mask;
|
|
}else{
|
|
pFile->ctrlFlags |= mask;
|
|
}
|
|
}
|
|
|
|
/* Forward declaration */
|
|
static int rtthreadGetTempname(int nBuf, char *zBuf);
|
|
|
|
/*
|
|
** Information and control of an open file handle.
|
|
*/
|
|
static int rtthreadFileControl(sqlite3_file *id, int op, void *pArg){
|
|
rtthreadFile *pFile = (rtthreadFile*)id;
|
|
switch( op ){
|
|
case SQLITE_FCNTL_LOCKSTATE: {
|
|
*(int*)pArg = pFile->eFileLock;
|
|
return SQLITE_OK;
|
|
}
|
|
case SQLITE_LAST_ERRNO: {
|
|
*(int*)pArg = pFile->lastErrno;
|
|
return SQLITE_OK;
|
|
}
|
|
case SQLITE_FCNTL_CHUNK_SIZE: {
|
|
pFile->szChunk = *(int *)pArg;
|
|
return SQLITE_OK;
|
|
}
|
|
case SQLITE_FCNTL_SIZE_HINT: {
|
|
int rc;
|
|
SimulateIOErrorBenign(1);
|
|
rc = fcntlSizeHint(pFile, *(i64 *)pArg);
|
|
SimulateIOErrorBenign(0);
|
|
return rc;
|
|
}
|
|
case SQLITE_FCNTL_PERSIST_WAL: {
|
|
rtthreadModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg);
|
|
return SQLITE_OK;
|
|
}
|
|
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
|
|
rtthreadModeBit(pFile, UNIXFILE_PSOW, (int*)pArg);
|
|
return SQLITE_OK;
|
|
}
|
|
case SQLITE_FCNTL_VFSNAME: {
|
|
*(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
|
|
return SQLITE_OK;
|
|
}
|
|
case SQLITE_FCNTL_TEMPFILENAME: {
|
|
char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname );
|
|
if( zTFile ){
|
|
rtthreadGetTempname(pFile->pVfs->mxPathname, zTFile);
|
|
*(char**)pArg = zTFile;
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
#ifdef SQLITE_DEBUG
|
|
/* The pager calls this method to signal that it has done
|
|
** a rollback and that the database is therefore unchanged and
|
|
** it hence it is OK for the transaction change counter to be
|
|
** unchanged.
|
|
*/
|
|
case SQLITE_FCNTL_DB_UNCHANGED: {
|
|
((rtthreadFile*)id)->dbUpdate = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
#endif
|
|
}
|
|
return SQLITE_NOTFOUND;
|
|
}
|
|
|
|
/*
|
|
** Return the sector size in bytes of the underlying block device for
|
|
** the specified file. This is almost always 512 bytes, but may be
|
|
** larger for some devices.
|
|
**
|
|
** SQLite code assumes this function cannot fail. It also assumes that
|
|
** if two files are created in the same file-system directory (i.e.
|
|
** a database and its journal file) that the sector size will be the
|
|
** same for both.
|
|
*/
|
|
static int rtthreadSectorSize(sqlite3_file *NotUsed){
|
|
UNUSED_PARAMETER(NotUsed);
|
|
return SQLITE_DEFAULT_SECTOR_SIZE;
|
|
}
|
|
|
|
|
|
/*
|
|
** Return the device characteristics for the file.
|
|
**
|
|
** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
|
|
** However, that choice is contraversial since technically the underlying
|
|
** file system does not always provide powersafe overwrites. (In other
|
|
** words, after a power-loss event, parts of the file that were never
|
|
** written might end up being altered.) However, non-PSOW behavior is very,
|
|
** very rare. And asserting PSOW makes a large reduction in the amount
|
|
** of required I/O for journaling, since a lot of padding is eliminated.
|
|
** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control
|
|
** available to turn it off and URI query parameter available to turn it off.
|
|
*/
|
|
static int rtthreadDeviceCharacteristics(sqlite3_file *id){
|
|
rtthreadFile *p = (rtthreadFile*)id;
|
|
int rc = 0;
|
|
|
|
if( p->ctrlFlags & UNIXFILE_PSOW ){
|
|
rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
#ifndef SQLITE_OMIT_WAL
|
|
# error "WAL mode requires not support from the rt-thread, compile\
|
|
with SQLITE_OMIT_WAL."
|
|
#else
|
|
# define rtthreadShmMap 0
|
|
# define rtthreadShmLock 0
|
|
# define rtthreadShmBarrier 0
|
|
# define rtthreadShmUnmap 0
|
|
#endif /* #ifndef SQLITE_OMIT_WAL */
|
|
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
#error "rtthread not spportt mmap"
|
|
#endif /* SQLITE_MAX_MMAP_SIZE>0 */
|
|
|
|
/*
|
|
** If possible, return a pointer to a mapping of file fd starting at offset
|
|
** iOff. The mapping must be valid for at least nAmt bytes.
|
|
**
|
|
** If such a pointer can be obtained, store it in *pp and return SQLITE_OK.
|
|
** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK.
|
|
** Finally, if an error does occur, return an SQLite error code. The final
|
|
** value of *pp is undefined in this case.
|
|
**
|
|
** If this function does return a pointer, the caller must eventually
|
|
** release the reference by calling unixUnfetch().
|
|
*/
|
|
static int rtthreadFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
|
|
|
*pp = 0;
|
|
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** If the third argument is non-NULL, then this function releases a
|
|
** reference obtained by an earlier call to unixFetch(). The second
|
|
** argument passed to this function must be the same as the corresponding
|
|
** argument that was passed to the unixFetch() invocation.
|
|
**
|
|
** Or, if the third argument is NULL, then this function is being called
|
|
** to inform the VFS layer that, according to POSIX, any existing mapping
|
|
** may now be invalid and should be unmapped.
|
|
*/
|
|
static int rtthreadUnfetch(sqlite3_file *fd, i64 iOff, void *p){
|
|
rtthreadFile *pFd = (rtthreadFile *)fd; /* The underlying database file */
|
|
UNUSED_PARAMETER(iOff);
|
|
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Here ends the implementation of all sqlite3_file methods.
|
|
**
|
|
********************** End sqlite3_file Methods *******************************
|
|
******************************************************************************/
|
|
|
|
/*
|
|
** This division contains definitions of sqlite3_io_methods objects that
|
|
** implement various file locking strategies. It also contains definitions
|
|
** of "finder" functions. A finder-function is used to locate the appropriate
|
|
** sqlite3_io_methods object for a particular database file. The pAppData
|
|
** field of the sqlite3_vfs VFS objects are initialized to be pointers to
|
|
** the correct finder-function for that VFS.
|
|
**
|
|
** Most finder functions return a pointer to a fixed sqlite3_io_methods
|
|
** object. The only interesting finder-function is autolockIoFinder, which
|
|
** looks at the filesystem type and tries to guess the best locking
|
|
** strategy from that.
|
|
**
|
|
** For finder-funtion F, two objects are created:
|
|
**
|
|
** (1) The real finder-function named "FImpt()".
|
|
**
|
|
** (2) A constant pointer to this function named just "F".
|
|
**
|
|
**
|
|
** A pointer to the F pointer is used as the pAppData value for VFS
|
|
** objects. We have to do this instead of letting pAppData point
|
|
** directly at the finder-function since C90 rules prevent a void*
|
|
** from be cast into a function pointer.
|
|
**
|
|
**
|
|
** Each instance of this macro generates two objects:
|
|
**
|
|
** * A constant sqlite3_io_methods object call METHOD that has locking
|
|
** methods CLOSE, LOCK, UNLOCK, CKRESLOCK.
|
|
**
|
|
** * An I/O method finder function called FINDER that returns a pointer
|
|
** to the METHOD object in the previous bullet.
|
|
*/
|
|
#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \
|
|
static const sqlite3_io_methods METHOD = { \
|
|
VERSION, /* iVersion */ \
|
|
CLOSE, /* xClose */ \
|
|
rtthreadRead, /* xRead */ \
|
|
rtthreadWrite, /* xWrite */ \
|
|
rtthreadTruncate, /* xTruncate */ \
|
|
rtthreadSync, /* xSync */ \
|
|
rtthreadFileSize, /* xFileSize */ \
|
|
LOCK, /* xLock */ \
|
|
UNLOCK, /* xUnlock */ \
|
|
CKLOCK, /* xCheckReservedLock */ \
|
|
rtthreadFileControl, /* xFileControl */ \
|
|
rtthreadSectorSize, /* xSectorSize */ \
|
|
rtthreadDeviceCharacteristics, /* xDeviceCapabilities */ \
|
|
rtthreadShmMap, /* xShmMap */ \
|
|
rtthreadShmLock, /* xShmLock */ \
|
|
rtthreadShmBarrier, /* xShmBarrier */ \
|
|
rtthreadShmUnmap, /* xShmUnmap */ \
|
|
rtthreadFetch, /* xFetch */ \
|
|
rtthreadUnfetch, /* xUnfetch */ \
|
|
}; \
|
|
static const sqlite3_io_methods *FINDER##Impl(const char *z, rtthreadFile *p){ \
|
|
UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \
|
|
return &METHOD; \
|
|
} \
|
|
static const sqlite3_io_methods *(*const FINDER)(const char*,rtthreadFile *p) \
|
|
= FINDER##Impl;
|
|
|
|
/*
|
|
** Here are all of the sqlite3_io_methods objects for each of the
|
|
** locking strategies. Functions that return pointers to these methods
|
|
** are also created.
|
|
*/
|
|
IOMETHODS(
|
|
nolockIoFinder, /* Finder function name */
|
|
nolockIoMethods, /* sqlite3_io_methods object name */
|
|
1, /* shared memory is disabled */
|
|
nolockClose, /* xClose method */
|
|
nolockLock, /* xLock method */
|
|
nolockUnlock, /* xUnlock method */
|
|
nolockCheckReservedLock /* xCheckReservedLock method */
|
|
)
|
|
IOMETHODS(
|
|
dotlockIoFinder, /* Finder function name */
|
|
dotlockIoMethods, /* sqlite3_io_methods object name */
|
|
1, /* shared memory is disabled */
|
|
dotlockClose, /* xClose method */
|
|
dotlockLock, /* xLock method */
|
|
dotlockUnlock, /* xUnlock method */
|
|
dotlockCheckReservedLock /* xCheckReservedLock method */
|
|
)
|
|
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
IOMETHODS(
|
|
flockIoFinder, /* Finder function name */
|
|
flockIoMethods, /* sqlite3_io_methods object name */
|
|
1, /* shared memory is disabled */
|
|
flockClose, /* xClose method */
|
|
flockLock, /* xLock method */
|
|
flockUnlock, /* xUnlock method */
|
|
flockCheckReservedLock /* xCheckReservedLock method */
|
|
)
|
|
#endif
|
|
|
|
/*
|
|
** An abstract type for a pointer to a IO method finder function:
|
|
*/
|
|
typedef const sqlite3_io_methods *(*finder_type)(const char*,rtthreadFile*);
|
|
|
|
|
|
/****************************************************************************
|
|
**************************** sqlite3_vfs methods ****************************
|
|
**
|
|
** This division contains the implementation of methods on the
|
|
** sqlite3_vfs object.
|
|
*/
|
|
|
|
/*
|
|
** Initialize the contents of the rtthreadFile structure pointed to by pId.
|
|
*/
|
|
static int fillInRtthreadFile(
|
|
sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
|
int h, /* Open file descriptor of file being opened */
|
|
sqlite3_file *pId, /* Write to the rtthreadFile structure here */
|
|
const char *zFilename, /* Name of the file being opened */
|
|
int ctrlFlags /* Zero or more UNIXFILE_* values */
|
|
){
|
|
const sqlite3_io_methods *pLockingStyle;
|
|
rtthreadFile *pNew = (rtthreadFile *)pId;
|
|
int rc = SQLITE_OK;
|
|
|
|
assert( pNew->pInode==NULL );
|
|
|
|
/* Usually the path zFilename should not be a relative pathname. The
|
|
** exception is when opening the proxy "conch" file in builds that
|
|
** include the special Apple locking styles.
|
|
*/
|
|
assert( zFilename==0 || zFilename[0]=='/' );
|
|
|
|
/* No locking occurs in temporary files */
|
|
assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 );
|
|
|
|
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
|
|
pNew->h = h;
|
|
pNew->pVfs = pVfs;
|
|
pNew->zPath = zFilename;
|
|
pNew->ctrlFlags = (u8)ctrlFlags;
|
|
if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
|
|
"psow", SQLITE_POWERSAFE_OVERWRITE) ){
|
|
pNew->ctrlFlags |= UNIXFILE_PSOW;
|
|
}
|
|
if( strcmp(pVfs->zName,"unix-excl")==0 ){
|
|
pNew->ctrlFlags |= UNIXFILE_EXCL;
|
|
}
|
|
|
|
if( ctrlFlags & UNIXFILE_NOLOCK ){
|
|
pLockingStyle = &nolockIoMethods;
|
|
}else{
|
|
pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew);
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
/* Cache zFilename in the locking context (AFP and dotlock override) for
|
|
** proxyLock activation is possible (remote proxy is based on db name)
|
|
** zFilename remains valid until file is closed, to support */
|
|
pNew->lockingContext = (void*)zFilename;
|
|
#endif
|
|
}
|
|
|
|
if( pLockingStyle == &dotlockIoMethods ){
|
|
/* Dotfile locking uses the file path so it needs to be included in
|
|
** the dotlockLockingContext
|
|
*/
|
|
char *zLockFile;
|
|
int nFilename;
|
|
assert( zFilename!=0 );
|
|
nFilename = (int)strlen(zFilename) + 6;
|
|
zLockFile = (char *)sqlite3_malloc(nFilename);
|
|
if( zLockFile==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename);
|
|
}
|
|
pNew->lockingContext = zLockFile;
|
|
}
|
|
|
|
pNew->lastErrno = 0;
|
|
if( rc!=SQLITE_OK ){
|
|
if( h>=0 ) robust_close(pNew, h, __LINE__);
|
|
}else{
|
|
pNew->pMethod = pLockingStyle;
|
|
OpenCounter(+1);
|
|
verifyDbFile(pNew);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** 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* rtthreadTempFileDir(void){
|
|
static const char *azDirs[] = {
|
|
0,
|
|
"/sql",
|
|
"/sql/tmp"
|
|
"/tmp",
|
|
0 /* List terminator */
|
|
};
|
|
unsigned int i;
|
|
struct stat buf;
|
|
const char *zDir = 0;
|
|
|
|
azDirs[0] = sqlite3_temp_directory;
|
|
|
|
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
|
if( zDir==0 ) continue;
|
|
if( osStat(zDir, &buf) ) continue;
|
|
if( !S_ISDIR(buf.st_mode) ) continue;
|
|
break;
|
|
}
|
|
return zDir;
|
|
}
|
|
|
|
/*
|
|
** Create a temporary file name in zBuf. zBuf must be allocated
|
|
** by the calling process and must be big enough to hold at least
|
|
** pVfs->mxPathname bytes.
|
|
*/
|
|
static int rtthreadGetTempname(int nBuf, char *zBuf){
|
|
static const unsigned char zChars[] =
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"0123456789";
|
|
unsigned int i, j;
|
|
const char *zDir;
|
|
|
|
/* 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 = rtthreadTempFileDir();
|
|
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) + 18) >= (size_t)nBuf ){
|
|
return SQLITE_ERROR;
|
|
}
|
|
|
|
do{
|
|
sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
|
|
j = (int)strlen(zBuf);
|
|
sqlite3_randomness(15, &zBuf[j]);
|
|
for(i=0; i<15; i++, j++){
|
|
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
|
}
|
|
zBuf[j] = 0;
|
|
zBuf[j+1] = 0;
|
|
}while( osAccess(zBuf,0)==0 );
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Open the file zPath.
|
|
**
|
|
** Previously, the SQLite OS layer used three functions in place of this
|
|
** one:
|
|
**
|
|
** sqlite3OsOpenReadWrite();
|
|
** sqlite3OsOpenReadOnly();
|
|
** sqlite3OsOpenExclusive();
|
|
**
|
|
** These calls correspond to the following combinations of flags:
|
|
**
|
|
** ReadWrite() -> (READWRITE | CREATE)
|
|
** ReadOnly() -> (READONLY)
|
|
** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
|
|
**
|
|
** The old OpenExclusive() accepted a boolean argument - "delFlag". If
|
|
** true, the file was configured to be automatically deleted when the
|
|
** file handle closed. To achieve the same effect using this new
|
|
** interface, add the DELETEONCLOSE flag to those specified above for
|
|
** OpenExclusive().
|
|
*/
|
|
static int rtthreadOpen(
|
|
sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */
|
|
const char *zPath, /* Pathname of file to be opened */
|
|
sqlite3_file *pFile, /* The file descriptor to be filled in */
|
|
int flags, /* Input flags to control the opening */
|
|
int *pOutFlags /* Output flags returned to SQLite core */
|
|
){
|
|
rtthreadFile *p = (rtthreadFile *)pFile;
|
|
int fd = -1; /* File descriptor returned by open() */
|
|
int openFlags = 0; /* Flags to pass to open() */
|
|
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
|
int noLock; /* True to omit locking primitives */
|
|
int rc = SQLITE_OK; /* Function Return Code */
|
|
int ctrlFlags = 0; /* UNIXFILE_* flags */
|
|
|
|
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
|
|
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
|
|
int isCreate = (flags & SQLITE_OPEN_CREATE);
|
|
int isReadonly = (flags & SQLITE_OPEN_READONLY);
|
|
int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY);
|
|
#endif
|
|
|
|
/* If creating a master or main-file journal, this function will open
|
|
** a file-descriptor on the directory too. The first time unixSync()
|
|
** is called the directory file descriptor will be fsync()ed and close()d.
|
|
*/
|
|
int syncDir = (isCreate && (
|
|
eType==SQLITE_OPEN_MASTER_JOURNAL
|
|
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|
|
|| eType==SQLITE_OPEN_WAL
|
|
));
|
|
|
|
/* If argument zPath is a NULL pointer, this function is required to open
|
|
** a temporary file. Use this buffer to store the file name in.
|
|
*/
|
|
char zTmpname[MAX_PATHNAME+2];
|
|
const char *zName = zPath;
|
|
|
|
/* Check the following statements are true:
|
|
**
|
|
** (a) Exactly one of the READWRITE and READONLY flags must be set, and
|
|
** (b) if CREATE is set, then READWRITE must also be set, and
|
|
** (c) if EXCLUSIVE is set, then CREATE must also be set.
|
|
** (d) if DELETEONCLOSE is set, then CREATE must also be set.
|
|
*/
|
|
assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
|
|
assert(isCreate==0 || isReadWrite);
|
|
assert(isExclusive==0 || isCreate);
|
|
assert(isDelete==0 || isCreate);
|
|
|
|
/* The main DB, main journal, WAL file and master journal are never
|
|
** automatically deleted. Nor are they ever temporary files. */
|
|
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
|
|
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
|
|
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
|
|
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
|
|
|
|
/* Assert that the upper layer has set one of the "file-type" flags. */
|
|
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|
|
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
|
|
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
|
|
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
|
|
);
|
|
|
|
memset(p, 0, sizeof(rtthreadFile));
|
|
if( !zName ){
|
|
/* If zName is NULL, the upper layer is requesting a temp file. */
|
|
assert(isDelete && !syncDir);
|
|
rc = rtthreadGetTempname(MAX_PATHNAME+2, zTmpname);
|
|
if( rc!=SQLITE_OK ){
|
|
return rc;
|
|
}
|
|
zName = zTmpname;
|
|
|
|
/* Generated temporary filenames are always double-zero terminated
|
|
** for use by sqlite3_uri_parameter(). */
|
|
assert( zName[strlen(zName)+1]==0 );
|
|
}
|
|
|
|
/* Determine the value of the flags parameter passed to POSIX function
|
|
** open(). These must be calculated even if open() is not called, as
|
|
** they may be stored as part of the file handle and used by the
|
|
** 'conch file' locking functions later on. */
|
|
if( isReadonly ) openFlags |= O_RDONLY;
|
|
if( isReadWrite ) openFlags |= O_RDWR;
|
|
if( isCreate ) openFlags |= O_CREAT;
|
|
if( isExclusive ) openFlags |= (O_EXCL|0/*O_NOFOLLOW8*/);
|
|
openFlags |= (0/*O_LARGEFILE*/|O_BINARY);
|
|
|
|
if( fd<0 ){
|
|
mode_t openMode = 0; /* Permissions to create file with */
|
|
|
|
fd = robust_open(zName, openFlags, openMode);
|
|
OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags));
|
|
if( fd<0 && errno!=DFS_STATUS_EISDIR && isReadWrite && !isExclusive ){
|
|
/* Failed to open the file for read/write access. Try read-only. */
|
|
flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
|
openFlags &= ~(O_RDWR|O_CREAT);
|
|
flags |= SQLITE_OPEN_READONLY;
|
|
openFlags |= O_RDONLY;
|
|
isReadonly = 1;
|
|
fd = robust_open(zName, openFlags, openMode);
|
|
}
|
|
if( fd<0 ){
|
|
rc = rtthreadLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
|
|
goto open_finished;
|
|
}
|
|
}
|
|
assert( fd>=0 );
|
|
if( pOutFlags ){
|
|
*pOutFlags = flags;
|
|
}
|
|
|
|
if( isDelete ){
|
|
osUnlink(zName);
|
|
}
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
else{
|
|
p->openFlags = openFlags;
|
|
}
|
|
#endif
|
|
|
|
noLock = eType!=SQLITE_OPEN_MAIN_DB;
|
|
|
|
/* Set up appropriate ctrlFlags */
|
|
if( isDelete ) ctrlFlags |= UNIXFILE_DELETE;
|
|
if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
|
|
if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
|
|
if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
|
|
if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
|
|
|
|
rc = fillInRtthreadFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
|
|
|
open_finished:
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
** Delete the file at zPath. If the dirSync argument is true, fsync()
|
|
** the directory after deleting the file.
|
|
*/
|
|
static int rtthreadDelete(
|
|
sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */
|
|
const char *zPath, /* Name of file to be deleted */
|
|
int dirSync /* If true, fsync() directory after deleting file */
|
|
){
|
|
int rc = SQLITE_OK;
|
|
UNUSED_PARAMETER(NotUsed);
|
|
SimulateIOError(return SQLITE_IOERR_DELETE);
|
|
if( osUnlink(zPath)==(-1) ){
|
|
if( errno==DFS_STATUS_ENOENT ){
|
|
rc = SQLITE_IOERR_DELETE_NOENT;
|
|
}else{
|
|
rc = rtthreadLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
|
|
}
|
|
return rc;
|
|
}
|
|
#ifndef SQLITE_DISABLE_DIRSYNC
|
|
if( (dirSync & 1)!=0 ){
|
|
int fd;
|
|
rc = osOpenDirectory(zPath, &fd);
|
|
if( rc==SQLITE_OK ){
|
|
robust_close(0, fd, __LINE__);
|
|
}else if( rc==SQLITE_CANTOPEN ){
|
|
rc = SQLITE_OK;
|
|
}
|
|
}
|
|
#endif
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Test the existence of or access permissions of file zPath. The
|
|
** test performed depends on the value of flags:
|
|
**
|
|
** SQLITE_ACCESS_EXISTS: Return 1 if the file exists
|
|
** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable.
|
|
** SQLITE_ACCESS_READONLY: Return 1 if the file is readable.
|
|
**
|
|
** Otherwise return 0.
|
|
*/
|
|
|
|
#ifndef F_OK
|
|
# define F_OK 0
|
|
#endif
|
|
#ifndef R_OK
|
|
# define R_OK 4
|
|
#endif
|
|
#ifndef W_OK
|
|
# define W_OK 2
|
|
#endif
|
|
|
|
static int rtthreadAccess(
|
|
sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */
|
|
const char *zPath, /* Path of the file to examine */
|
|
int flags, /* What do we want to learn about the zPath file? */
|
|
int *pResOut /* Write result boolean here */
|
|
){
|
|
int amode = 0;
|
|
UNUSED_PARAMETER(NotUsed);
|
|
SimulateIOError( return SQLITE_IOERR_ACCESS; );
|
|
switch( flags ){
|
|
case SQLITE_ACCESS_EXISTS:
|
|
amode = F_OK;
|
|
break;
|
|
case SQLITE_ACCESS_READWRITE:
|
|
amode = W_OK|R_OK;
|
|
break;
|
|
case SQLITE_ACCESS_READ:
|
|
amode = R_OK;
|
|
break;
|
|
|
|
default:
|
|
assert(!"Invalid flags argument");
|
|
}
|
|
*pResOut = (osAccess(zPath, amode)==0);
|
|
if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
|
|
struct stat buf;
|
|
if( 0==osStat(zPath, &buf) && buf.st_size==0 ){
|
|
*pResOut = 0;
|
|
}
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
** Turn a relative pathname into a full pathname. The relative path
|
|
** is stored as a nul-terminated string in the buffer pointed to by
|
|
** zPath.
|
|
**
|
|
** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes
|
|
** (in this case, MAX_PATHNAME bytes). The full-path is written to
|
|
** this buffer before returning.
|
|
*/
|
|
static int rtthreadFullPathname(
|
|
sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
|
const char *zPath, /* Possibly relative input path */
|
|
int nOut, /* Size of output buffer in bytes */
|
|
char *zOut /* Output buffer */
|
|
){
|
|
|
|
/* 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. This function could fail if, for example, the
|
|
** current working directory has been unlinked.
|
|
*/
|
|
SimulateIOError( return SQLITE_ERROR );
|
|
|
|
assert( pVfs->mxPathname==MAX_PATHNAME );
|
|
UNUSED_PARAMETER(pVfs);
|
|
|
|
zOut[nOut-1] = '\0';
|
|
if( zPath[0]=='/' ){
|
|
sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
|
}else{
|
|
int nCwd;
|
|
if( osGetcwd(zOut, nOut-1)==0 ){
|
|
return rtthreadLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
|
|
}
|
|
nCwd = (int)strlen(zOut);
|
|
sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath);
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
|
|
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
# error "rtthread not support load extension, compile with SQLITE_OMIT_WAL."
|
|
#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
|
|
#define rtthreadDlOpen 0
|
|
#define rtthreadDlError 0
|
|
#define rtthreadDlSym 0
|
|
#define rtthreadDlClose 0
|
|
#endif
|
|
|
|
/*
|
|
** Write nBuf bytes of random data to the supplied buffer zBuf.
|
|
*/
|
|
static int rtthreadRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
|
|
UNUSED_PARAMETER(NotUsed);
|
|
assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int)));
|
|
|
|
/* We have to initialize zBuf to prevent valgrind from reporting
|
|
** errors. The reports issued by valgrind are incorrect - we would
|
|
** prefer that the randomness be increased by making use of the
|
|
** uninitialized space in zBuf - but valgrind errors tend to worry
|
|
** some users. Rather than argue, it seems easier just to initialize
|
|
** the whole array and silence valgrind, even if that means less randomness
|
|
** in the random seed.
|
|
**
|
|
** When testing, initializing zBuf[] to zero is all we do. That means
|
|
** that we always use the same random number sequence. This makes the
|
|
** tests repeatable.
|
|
*/
|
|
memset(zBuf, 0, nBuf);
|
|
{
|
|
int i;
|
|
char tick8, tick16;
|
|
tick8 = (char)rt_tick_get();
|
|
tick16 = (char)(rt_tick_get() >> 8);
|
|
|
|
for (i=0; i<nBuf; i++)
|
|
{
|
|
zBuf[i] = (char)(i ^ tick8 ^ tick16);
|
|
tick8 = zBuf[i];
|
|
tick16 = ~(tick8 ^ tick16);
|
|
}
|
|
}
|
|
return nBuf;
|
|
}
|
|
|
|
|
|
/*
|
|
** Sleep for a little while. Return the amount of time slept.
|
|
** The argument is the number of microseconds we want to sleep.
|
|
** The return value is the number of microseconds of sleep actually
|
|
** requested from the underlying operating system, a number which
|
|
** might be greater than or equal to the argument, but not less
|
|
** than the argument.
|
|
*/
|
|
static int rtthreadSleep(sqlite3_vfs *NotUsed, int microseconds){
|
|
|
|
int seconds = (microseconds+999999)/1000000;
|
|
osSleep(seconds * 1000);
|
|
UNUSED_PARAMETER(NotUsed);
|
|
return seconds*1000000;
|
|
}
|
|
|
|
/*
|
|
** The following variable, if set to a non-zero value, is interpreted as
|
|
** the number of seconds since 1970 and is used to set the result of
|
|
** sqlite3OsCurrentTime() during testing.
|
|
*/
|
|
#ifdef SQLITE_TEST
|
|
int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
|
#endif
|
|
|
|
#ifndef NO_GETTOD
|
|
#define NO_GETTOD 1
|
|
#endif
|
|
|
|
/*
|
|
** Find the current time (in Universal Coordinated Time). Write into *piNow
|
|
** the current time and date as a Julian Day number times 86_400_000. In
|
|
** other words, write into *piNow the number of milliseconds since the Julian
|
|
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
|
|
** proleptic Gregorian calendar.
|
|
**
|
|
** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
|
|
** cannot be found.
|
|
*/
|
|
static int rtthreadCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
|
|
static const sqlite3_int64 rtthreadEpoch = 24405875*(sqlite3_int64)8640000;
|
|
int rc = SQLITE_OK;
|
|
#if defined(NO_GETTOD)
|
|
time_t t;
|
|
time(&t);
|
|
*piNow = ((sqlite3_int64)t)*1000 + rtthreadEpoch;
|
|
#else
|
|
struct timeval sNow;
|
|
if( gettimeofday(&sNow, 0)==0 ){
|
|
*piNow = rtthreadEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
|
|
}else{
|
|
rc = SQLITE_ERROR;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SQLITE_TEST
|
|
if( sqlite3_current_time ){
|
|
*piNow = 1000*(sqlite3_int64)sqlite3_current_time + rtthreadEpoch;
|
|
}
|
|
#endif
|
|
UNUSED_PARAMETER(NotUsed);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Find the current time (in Universal Coordinated Time). Write the
|
|
** current time and date as a Julian Day number into *prNow and
|
|
** return 0. Return 1 if the time and date cannot be found.
|
|
*/
|
|
static int rtthreadCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
|
|
sqlite3_int64 i = 0;
|
|
int rc;
|
|
UNUSED_PARAMETER(NotUsed);
|
|
rc = rtthreadCurrentTimeInt64(0, &i);
|
|
*prNow = i/86400000.0;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** We added the xGetLastError() method with the intention of providing
|
|
** better low-level error messages when operating-system problems come up
|
|
** during SQLite operation. But so far, none of that has been implemented
|
|
** in the core. So this routine is never called. For now, it is merely
|
|
** a place-holder.
|
|
*/
|
|
static int rtthreadGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
|
|
UNUSED_PARAMETER(NotUsed);
|
|
UNUSED_PARAMETER(NotUsed2);
|
|
UNUSED_PARAMETER(NotUsed3);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
************************ End of sqlite3_vfs methods ***************************
|
|
******************************************************************************/
|
|
|
|
/*
|
|
** Initialize the operating system interface.
|
|
**
|
|
** This routine registers all VFS implementations for unix-like operating
|
|
** systems. This routine, and the sqlite3_os_end() routine that follows,
|
|
** should be the only routines in this file that are visible from other
|
|
** files.
|
|
**
|
|
** This routine is called once during SQLite initialization and by a
|
|
** single thread. The memory allocation and mutex subsystems have not
|
|
** necessarily been initialized when this routine is called, and so they
|
|
** should not be used.
|
|
*/
|
|
/*
|
|
** The following macro defines an initializer for an sqlite3_vfs object.
|
|
** The name of the VFS is NAME. The pAppData is a pointer to a pointer
|
|
** to the "finder" function. (pAppData is a pointer to a pointer because
|
|
** silly C90 rules prohibit a void* from being cast to a function pointer
|
|
** and so we have to go through the intermediate pointer to avoid problems
|
|
** when compiling with -pedantic-errors on GCC.)
|
|
**
|
|
** The FINDER parameter to this macro is the name of the pointer to the
|
|
** finder-function. The finder-function returns a pointer to the
|
|
** sqlite_io_methods object that implements the desired locking
|
|
** behaviors. See the division above that contains the IOMETHODS
|
|
** macro for addition information on finder-functions.
|
|
**
|
|
** Most finders simply return a pointer to a fixed sqlite3_io_methods
|
|
** object. But the "autolockIoFinder" available on MacOSX does a little
|
|
** more than that; it looks at the filesystem type that hosts the
|
|
** database file and tries to choose an locking method appropriate for
|
|
** that filesystem time.
|
|
*/
|
|
#define UNIXVFS(VFSNAME, FINDER) { \
|
|
3, /* iVersion */ \
|
|
sizeof(rtthreadFile), /* szOsFile */ \
|
|
MAX_PATHNAME, /* mxPathname */ \
|
|
0, /* pNext */ \
|
|
VFSNAME, /* zName */ \
|
|
(void*)&FINDER, /* pAppData */ \
|
|
rtthreadOpen, /* xOpen */ \
|
|
rtthreadDelete, /* xDelete */ \
|
|
rtthreadAccess, /* xAccess */ \
|
|
rtthreadFullPathname, /* xFullPathname */ \
|
|
rtthreadDlOpen, /* xDlOpen */ \
|
|
rtthreadDlError, /* xDlError */ \
|
|
rtthreadDlSym, /* xDlSym */ \
|
|
rtthreadDlClose, /* xDlClose */ \
|
|
rtthreadRandomness, /* xRandomness */ \
|
|
rtthreadSleep, /* xSleep */ \
|
|
rtthreadCurrentTime, /* xCurrentTime */ \
|
|
rtthreadGetLastError, /* xGetLastError */ \
|
|
rtthreadCurrentTimeInt64, /* xCurrentTimeInt64 */ \
|
|
rtthreadSetSystemCall, /* xSetSystemCall */ \
|
|
rtthreadGetSystemCall, /* xGetSystemCall */ \
|
|
rtthreadNextSystemCall, /* xNextSystemCall */ \
|
|
}
|
|
|
|
int sqlite3_os_init(void){
|
|
/*
|
|
** All default VFSes for unix are contained in the following array.
|
|
**
|
|
** Note that the sqlite3_vfs.pNext field of the VFS object is modified
|
|
** by the SQLite core when the VFS is registered. So the following
|
|
** array cannot be const.
|
|
*/
|
|
static sqlite3_vfs aVfs[] = {
|
|
UNIXVFS("unix-none", nolockIoFinder ),
|
|
UNIXVFS("unix-dotfile", dotlockIoFinder ),
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
UNIXVFS("unix-flock", flockIoFinder ),
|
|
#endif
|
|
};
|
|
unsigned int i; /* Loop counter */
|
|
|
|
/* Double-check that the aSyscall[] array has been constructed
|
|
** correctly. See ticket [bb3a86e890c8e96ab] */
|
|
assert( ArraySize(aSyscall)==24 );
|
|
|
|
/* Register all VFSes defined in the aVfs[] array */
|
|
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
|
sqlite3_vfs_register(&aVfs[i], i==0);
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Shutdown the operating system interface.
|
|
**
|
|
** Some operating systems might need to do some cleanup in this routine,
|
|
** to release dynamically allocated objects. But not on unix.
|
|
** This routine is a no-op for unix.
|
|
*/
|
|
int sqlite3_os_end(void){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
|
|
#endif /* SQLITE_OS_RTTHREAD */
|
|
|