diff options
Diffstat (limited to 'tqtinterface/qt4/src/3rdparty/sqlite/os.c')
| -rw-r--r-- | tqtinterface/qt4/src/3rdparty/sqlite/os.c | 1818 | 
1 files changed, 1818 insertions, 0 deletions
| diff --git a/tqtinterface/qt4/src/3rdparty/sqlite/os.c b/tqtinterface/qt4/src/3rdparty/sqlite/os.c new file mode 100644 index 0000000..1a1b583 --- /dev/null +++ b/tqtinterface/qt4/src/3rdparty/sqlite/os.c @@ -0,0 +1,1818 @@ +/* +** 2001 September 16 +** +** 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 tqfind forgiveness for yourself and forgive others. +**    May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file tqcontains code that is specific to particular operating +** systems.  The purpose of this file is to provide a uniform abstraction +** on which the rest of STQLite can operate. +*/ +#include "os.h"          /* Must be first to enable large file support */ +#include "sqliteInt.h" + +#if OS_UNIX +# include <time.h> +# include <errno.h> +# include <unistd.h> +# ifndef O_LARGEFILE +#  define O_LARGEFILE 0 +# endif +# ifdef STQLITE_DISABLE_LFS +#  undef O_LARGEFILE +#  define O_LARGEFILE 0 +# endif +# ifndef O_NOFOLLOW +#  define O_NOFOLLOW 0 +# endif +# ifndef O_BINARY +#  define O_BINARY 0 +# endif +#endif + + +#if OS_WIN +# include <winbase.h> +#endif + +#if OS_MAC +# include <extras.h> +# include <path2fss.h> +# include <TextUtils.h> +# include <FinderRegistry.h> +# include <Folders.h> +# include <Timer.h> +# include <OSUtils.h> +#endif + +/* +** The DJGPP compiler environment looks mostly like Unix, but it +** lacks the fcntl() system call.  So redefine fcntl() to be something +** that always succeeds.  This means that locking does not occur under +** DJGPP.  But its DOS - what did you expect? +*/ +#ifdef __DJGPP__ +# define fcntl(A,B,C) 0 +#endif + +/* +** Macros used to determine whether or not to use threads.  The +** STQLITE_UNIX_THREADS macro is defined if we are synchronizing for +** Posix threads and STQLITE_W32_THREADS is defined if we are +** synchronizing using Win32 threads. +*/ +#if OS_UNIX && defined(THREADSAFE) && THREADSAFE +# include <pthread.h> +# define STQLITE_UNIX_THREADS 1 +#endif +#if OS_WIN && defined(THREADSAFE) && THREADSAFE +# define STQLITE_W32_THREADS 1 +#endif +#if OS_MAC && defined(THREADSAFE) && THREADSAFE +# include <Multiprocessing.h> +# define STQLITE_MACOS_MULTITASKING 1 +#endif + +/* +** Macros for performance tracing.  Normally turned off +*/ +#if 0 +static int last_page = 0; +__inline__ unsigned long long int hwtime(void){ +  unsigned long long int x; +  __asm__("rdtsc\n\t" +          "mov %%edx, %%ecx\n\t" +          :"=A" (x)); +  return x; +} +static unsigned long long int g_start; +static unsigned int elapse; +#define TIMER_START       g_start=hwtime() +#define TIMER_END         elapse=hwtime()-g_start +#define SEEK(X)           last_page=(X) +#define TRACE1(X)         fprintf(stderr,X) +#define TRACE2(X,Y)       fprintf(stderr,X,Y) +#define TRACE3(X,Y,Z)     fprintf(stderr,X,Y,Z) +#define TRACE4(X,Y,Z,A)   fprintf(stderr,X,Y,Z,A) +#define TRACE5(X,Y,Z,A,B) fprintf(stderr,X,Y,Z,A,B) +#else +#define TIMER_START +#define TIMER_END +#define SEEK(X) +#define TRACE1(X) +#define TRACE2(X,Y) +#define TRACE3(X,Y,Z) +#define TRACE4(X,Y,Z,A) +#define TRACE5(X,Y,Z,A,B) +#endif + + +#if OS_UNIX +/* +** Here is the dirt on POSIX advisory locks:  ANSI STD 1003.1 (1996) +** section 6.5.2.2 lines 483 through 490 specify that when a process +** sets or clears a lock, that operation overrides any prior locks set +** by the same process.  It does not explicitly say so, but this implies +** that it overrides locks set by the same process using a different +** file descriptor.  Consider this test case: +** +**       int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); +**       int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); +** +** Suppose ./file1 and ./file2 are really the same file (because +** one is a hard or symbolic link to the other) then if you set +** an exclusive lock on fd1, then try to get an exclusive lock +** on fd2, it works.  I would have expected the second lock to +** fail since there was already a lock on the file due to fd1. +** But not so.  Since both locks came from the same process, the +** second overrides the first, even though they were on different +** file descriptors opened on different file names. +** +** Bummer.  If you ask me, this is broken.  Badly broken.  It means +** that we cannot use POSIX locks to synchronize file access among +** competing threads of the same process.  POSIX locks will work fine +** to synchronize access for threads in separate processes, but not +** threads within the same process. +** +** To work around the problem, STQLite has to manage file locks internally +** on its own.  Whenever a new database is opened, we have to tqfind the +** specific inode of the database file (the inode is determined by the +** st_dev and st_ino fields of the stat structure that fstat() fills in) +** and check for locks already existing on that inode.  When locks are +** created or removed, we have to look at our own internal record of the +** locks to see if another thread has previously set a lock on that same +** inode. +** +** The OsFile structure for POSIX is no longer just an integer file +** descriptor.  It is now a structure that holds the integer file +** descriptor and a pointer to a structure that describes the internal +** locks on the corresponding inode.  There is one locking structure +** per inode, so if the same inode is opened twice, both OsFile structures +** point to the same locking structure.  The locking structure keeps +** a reference count (so we will know when to delete it) and a "cnt" +** field that tells us its internal lock status.  cnt==0 means the +** file is unlocked.  cnt==-1 means the file has an exclusive lock. +** cnt>0 means there are cnt shared locks on the file. +** +** Any attempt to lock or unlock a file first checks the locking +** structure.  The fcntl() system call is only invoked to set a  +** POSIX lock if the internal lock structure transitions between +** a locked and an unlocked state. +** +** 2004-Jan-11: +** More recent discoveries about POSIX advisory locks.  (The more +** I discover, the more I realize the a POSIX advisory locks are +** an abomination.) +** +** If you close a file descriptor that points to a file that has locks, +** all locks on that file that are owned by the current process are +** released.  To work around this problem, each OsFile structure tqcontains +** a pointer to an openCnt structure.  There is one openCnt structure +** per open inode, which means that multiple OsFiles can point to a single +** openCnt.  When an attempt is made to close an OsFile, if there are +** other OsFiles open on the same inode that are holding locks, the call +** to close() the file descriptor is deferred until all of the locks clear. +** The openCnt structure keeps a list of file descriptors that need to +** be closed and that list is walked (and cleared) when the last lock +** clears. +** +** First, under Linux threads, because each thread has a separate +** process ID, lock operations in one thread do not override locks +** to the same file in other threads.  Linux threads behave like +** separate processes in this respect.  But, if you close a file +** descriptor in linux threads, all locks are cleared, even locks +** on other threads and even though the other threads have different +** process IDs.  Linux threads is inconsistent in this respect. +** (I'm beginning to think that linux threads is an abomination too.) +** The consequence of this all is that the hash table for the lockInfo +** structure has to include the process id as part of its key because +** locks in different threads are treated as distinct.  But the  +** openCnt structure should not include the process id in its +** key because close() clears lock on all threads, not just the current +** thread.  Were it not for this goofiness in linux threads, we could +** combine the lockInfo and openCnt structures into a single structure. +*/ + +/* +** An instance of the following structure serves as the key used +** to locate a particular lockInfo structure given its inode.  Note +** that we have to include the process ID as part of the key.  On some +** threading implementations (ex: linux), each thread has a separate +** process ID. +*/ +struct lockKey { +  dev_t dev;   /* Device number */ +  ino_t ino;   /* Inode number */ +  pid_t pid;   /* Process ID */ +}; + +/* +** An instance of the following structure is allocated for each open +** inode on each thread with a different process ID.  (Threads have +** different process IDs on linux, but not on most other unixes.) +** +** A single inode can have multiple file descriptors, so each OsFile +** structure tqcontains a pointer to an instance of this object and this +** object keeps a count of the number of OsFiles pointing to it. +*/ +struct lockInfo { +  struct lockKey key;  /* The lookup key */ +  int cnt;             /* 0: unlocked.  -1: write lock.  1...: read lock. */ +  int nRef;            /* Number of pointers to this structure */ +}; + +/* +** An instance of the following structure serves as the key used +** to locate a particular openCnt structure given its inode.  This +** is the same as the lockKey except that the process ID is omitted. +*/ +struct openKey { +  dev_t dev;   /* Device number */ +  ino_t ino;   /* Inode number */ +}; + +/* +** An instance of the following structure is allocated for each open +** inode.  This structure keeps track of the number of locks on that +** inode.  If a close is attempted against an inode that is holding +** locks, the close is deferred until all locks clear by adding the +** file descriptor to be closed to the pending list. +*/ +struct openCnt { +  struct openKey key;   /* The lookup key */ +  int nRef;             /* Number of pointers to this structure */ +  int nLock;            /* Number of outstanding locks */ +  int nPending;         /* Number of pending close() operations */ +  int *aPending;        /* Malloced space holding fd's awaiting a close() */ +}; + +/*  +** These hash table maps inodes and process IDs into lockInfo and openCnt +** structures.  Access to these hash tables must be protected by a mutex. +*/ +static Hash lockHash = { STQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; +static Hash openHash = { STQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; + +/* +** Release a lockInfo structure previously allocated by tqfindLockInfo(). +*/ +static void releaseLockInfo(struct lockInfo *pLock){ +  pLock->nRef--; +  if( pLock->nRef==0 ){ +    sqliteHashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0); +    sqliteFree(pLock); +  } +} + +/* +** Release a openCnt structure previously allocated by tqfindLockInfo(). +*/ +static void releaseOpenCnt(struct openCnt *pOpen){ +  pOpen->nRef--; +  if( pOpen->nRef==0 ){ +    sqliteHashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0); +    sqliteFree(pOpen->aPending); +    sqliteFree(pOpen); +  } +} + +/* +** Given a file descriptor, locate lockInfo and openCnt structures that +** describes that file descriptor.  Create a new ones if necessary.  The +** return values might be unset if an error occurs. +** +** Return the number of errors. +*/ +int tqfindLockInfo( +  int fd,                      /* The file descriptor used in the key */ +  struct lockInfo **ppLock,    /* Return the lockInfo structure here */ +  struct openCnt **ppOpen   /* Return the openCnt structure here */ +){ +  int rc; +  struct lockKey key1; +  struct openKey key2; +  struct stat statbuf; +  struct lockInfo *pLock; +  struct openCnt *pOpen; +  rc = fstat(fd, &statbuf); +  if( rc!=0 ) return 1; +  memset(&key1, 0, sizeof(key1)); +  key1.dev = statbuf.st_dev; +  key1.ino = statbuf.st_ino; +  key1.pid = getpid(); +  memset(&key2, 0, sizeof(key2)); +  key2.dev = statbuf.st_dev; +  key2.ino = statbuf.st_ino; +  pLock = (struct lockInfo*)sqliteHashFind(&lockHash, &key1, sizeof(key1)); +  if( pLock==0 ){ +    struct lockInfo *pOld; +    pLock = sqliteMallocRaw( sizeof(*pLock) ); +    if( pLock==0 ) return 1; +    pLock->key = key1; +    pLock->nRef = 1; +    pLock->cnt = 0; +    pOld = sqliteHashInsert(&lockHash, &pLock->key, sizeof(key1), pLock); +    if( pOld!=0 ){ +      assert( pOld==pLock ); +      sqliteFree(pLock); +      return 1; +    } +  }else{ +    pLock->nRef++; +  } +  *ppLock = pLock; +  pOpen = (struct openCnt*)sqliteHashFind(&openHash, &key2, sizeof(key2)); +  if( pOpen==0 ){ +    struct openCnt *pOld; +    pOpen = sqliteMallocRaw( sizeof(*pOpen) ); +    if( pOpen==0 ){ +      releaseLockInfo(pLock); +      return 1; +    } +    pOpen->key = key2; +    pOpen->nRef = 1; +    pOpen->nLock = 0; +    pOpen->nPending = 0; +    pOpen->aPending = 0; +    pOld = sqliteHashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen); +    if( pOld!=0 ){ +      assert( pOld==pOpen ); +      sqliteFree(pOpen); +      releaseLockInfo(pLock); +      return 1; +    } +  }else{ +    pOpen->nRef++; +  } +  *ppOpen = pOpen; +  return 0; +} + +#endif  /** POSIX advisory lock work-around **/ + +/* +** If we compile with the STQLITE_TEST macro set, then the following block +** of code will give us the ability to simulate a disk I/O error.  This +** is used for testing the I/O recovery logic. +*/ +#ifdef STQLITE_TEST +int sqlite_io_error_pending = 0; +#define SimulateIOError(A)  \ +   if( sqlite_io_error_pending ) \ +     if( sqlite_io_error_pending-- == 1 ){ local_ioerr(); return A; } +static void local_ioerr(){ +  sqlite_io_error_pending = 0;  /* Really just a place to set a breakpoint */ +} +#else +#define SimulateIOError(A) +#endif + +/* +** When testing, keep a count of the number of open files. +*/ +#ifdef STQLITE_TEST +int sqlite_open_file_count = 0; +#define OpenCounter(X)  sqlite_open_file_count+=(X) +#else +#define OpenCounter(X) +#endif + + +/* +** Delete the named file +*/ +int sqliteOsDelete(const char *zFilename){ +#if OS_UNIX +  unlink(zFilename); +#endif +#if OS_WIN +  DeleteFile(zFilename); +#endif +#if OS_MAC +  unlink(zFilename); +#endif +  return STQLITE_OK; +} + +/* +** Return TRUE if the named file exists. +*/ +int sqliteOsFileExists(const char *zFilename){ +#if OS_UNIX +  return access(zFilename, 0)==0; +#endif +#if OS_WIN +  return GetFileAttributes(zFilename) != 0xffffffff; +#endif +#if OS_MAC +  return access(zFilename, 0)==0; +#endif +} + + +#if 0 /* NOT USED */ +/* +** Change the name of an existing file. +*/ +int sqliteOsFileRename(const char *zOldName, const char *zNewName){ +#if OS_UNIX +  if( link(zOldName, zNewName) ){ +    return STQLITE_ERROR; +  } +  unlink(zOldName); +  return STQLITE_OK; +#endif +#if OS_WIN +  if( !MoveFile(zOldName, zNewName) ){ +    return STQLITE_ERROR; +  } +  return STQLITE_OK; +#endif +#if OS_MAC +  /**** FIX ME ***/ +  return STQLITE_ERROR; +#endif +} +#endif /* NOT USED */ + +/* +** Attempt to open a file for both reading and writing.  If that +** fails, try opening it read-only.  If the file does not exist, +** try to create it. +** +** On success, a handle for the open file is written to *id +** and *pReadonly is set to 0 if the file was opened for reading and +** writing or 1 if the file was opened read-only.  The function returns +** STQLITE_OK. +** +** On failure, the function returns STQLITE_CANTOPEN and leaves +** *id and *pReadonly unchanged. +*/ +int sqliteOsOpenReadWrite( +  const char *zFilename, +  OsFile *id, +  int *pReadonly +){ +#if OS_UNIX +  int rc; +  id->dirfd = -1; +  id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644); +  if( id->fd<0 ){ +    id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); +    if( id->fd<0 ){ +      return STQLITE_CANTOPEN;  +    } +    *pReadonly = 1; +  }else{ +    *pReadonly = 0; +  } +  sqliteOsEnterMutex(); +  rc = tqfindLockInfo(id->fd, &id->pLock, &id->pOpen); +  sqliteOsLeaveMutex(); +  if( rc ){ +    close(id->fd); +    return STQLITE_NOMEM; +  } +  id->locked = 0; +  TRACE3("OPEN    %-3d %s\n", id->fd, zFilename); +  OpenCounter(+1); +  return STQLITE_OK; +#endif +#if OS_WIN +  HANDLE h = CreateFile(zFilename, +     GENERIC_READ | GENERIC_WRITE, +     FILE_SHARE_READ | FILE_SHARE_WRITE, +     NULL, +     OPEN_ALWAYS, +     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, +     NULL +  ); +  if( h==INVALID_HANDLE_VALUE ){ +    h = CreateFile(zFilename, +       GENERIC_READ, +       FILE_SHARE_READ, +       NULL, +       OPEN_ALWAYS, +       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, +       NULL +    ); +    if( h==INVALID_HANDLE_VALUE ){ +      return STQLITE_CANTOPEN; +    } +    *pReadonly = 1; +  }else{ +    *pReadonly = 0; +  } +  id->h = h; +  id->locked = 0; +  OpenCounter(+1); +  return STQLITE_OK; +#endif +#if OS_MAC +  FSSpec fsSpec; +# ifdef _LARGE_FILE +  HFSUniStr255 dfName; +  FSRef fsRef; +  if( __path2fss(zFilename, &fsSpec) != noErr ){ +    if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'STQLI', cDocumentFile) != noErr ) +      return STQLITE_CANTOPEN; +  } +  if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) +    return STQLITE_CANTOPEN; +  FSGetDataForkName(&dfName); +  if( FSOpenFork(&fsRef, dfName.length, dfName.tqunicode, +                 fsRdWrShPerm, &(id->refNum)) != noErr ){ +    if( FSOpenFork(&fsRef, dfName.length, dfName.tqunicode, +                   fsRdWrPerm, &(id->refNum)) != noErr ){ +      if (FSOpenFork(&fsRef, dfName.length, dfName.tqunicode, +                   fsRdPerm, &(id->refNum)) != noErr ) +        return STQLITE_CANTOPEN; +      else +        *pReadonly = 1; +    } else +      *pReadonly = 0; +  } else +    *pReadonly = 0; +# else +  __path2fss(zFilename, &fsSpec); +  if( !sqliteOsFileExists(zFilename) ){ +    if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'STQLI', cDocumentFile) != noErr ) +      return STQLITE_CANTOPEN; +  } +  if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNum)) != noErr ){ +    if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ){ +      if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr ) +        return STQLITE_CANTOPEN; +      else +        *pReadonly = 1; +    } else +      *pReadonly = 0; +  } else +    *pReadonly = 0; +# endif +  if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){ +    id->refNumRF = -1; +  } +  id->locked = 0; +  id->delOnClose = 0; +  OpenCounter(+1); +  return STQLITE_OK; +#endif +} + + +/* +** Attempt to open a new file for exclusive access by this process. +** The file will be opened for both reading and writing.  To avoid +** a potential security problem, we do not allow the file to have +** previously existed.  Nor do we allow the file to be a symbolic +** link. +** +** If delFlag is true, then make arrangements to automatically delete +** the file when it is closed. +** +** On success, write the file handle into *id and return STQLITE_OK. +** +** On failure, return STQLITE_CANTOPEN. +*/ +int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ +#if OS_UNIX +  int rc; +  if( access(zFilename, 0)==0 ){ +    return STQLITE_CANTOPEN; +  } +  id->dirfd = -1; +  id->fd = open(zFilename, +                O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600); +  if( id->fd<0 ){ +    return STQLITE_CANTOPEN; +  } +  sqliteOsEnterMutex(); +  rc = tqfindLockInfo(id->fd, &id->pLock, &id->pOpen); +  sqliteOsLeaveMutex(); +  if( rc ){ +    close(id->fd); +    unlink(zFilename); +    return STQLITE_NOMEM; +  } +  id->locked = 0; +  if( delFlag ){ +    unlink(zFilename); +  } +  TRACE3("OPEN-EX %-3d %s\n", id->fd, zFilename); +  OpenCounter(+1); +  return STQLITE_OK; +#endif +#if OS_WIN +  HANDLE h; +  int fileflags; +  if( delFlag ){ +    fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS  +                     | FILE_FLAG_DELETE_ON_CLOSE; +  }else{ +    fileflags = FILE_FLAG_RANDOM_ACCESS; +  } +  h = CreateFile(zFilename, +     GENERIC_READ | GENERIC_WRITE, +     0, +     NULL, +     CREATE_ALWAYS, +     fileflags, +     NULL +  ); +  if( h==INVALID_HANDLE_VALUE ){ +    return STQLITE_CANTOPEN; +  } +  id->h = h; +  id->locked = 0; +  OpenCounter(+1); +  return STQLITE_OK; +#endif +#if OS_MAC +  FSSpec fsSpec; +# ifdef _LARGE_FILE +  HFSUniStr255 dfName; +  FSRef fsRef; +  __path2fss(zFilename, &fsSpec); +  if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'STQLI', cDocumentFile) != noErr ) +    return STQLITE_CANTOPEN; +  if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) +    return STQLITE_CANTOPEN; +  FSGetDataForkName(&dfName); +  if( FSOpenFork(&fsRef, dfName.length, dfName.tqunicode, +                 fsRdWrPerm, &(id->refNum)) != noErr ) +    return STQLITE_CANTOPEN; +# else +  __path2fss(zFilename, &fsSpec); +  if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'STQLI', cDocumentFile) != noErr ) +    return STQLITE_CANTOPEN; +  if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ) +    return STQLITE_CANTOPEN; +# endif +  id->refNumRF = -1; +  id->locked = 0; +  id->delOnClose = delFlag; +  if (delFlag) +    id->pathToDel = sqliteOsFullPathname(zFilename); +  OpenCounter(+1); +  return STQLITE_OK; +#endif +} + +/* +** Attempt to open a new file for read-only access. +** +** On success, write the file handle into *id and return STQLITE_OK. +** +** On failure, return STQLITE_CANTOPEN. +*/ +int sqliteOsOpenReadOnly(const char *zFilename, OsFile *id){ +#if OS_UNIX +  int rc; +  id->dirfd = -1; +  id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); +  if( id->fd<0 ){ +    return STQLITE_CANTOPEN; +  } +  sqliteOsEnterMutex(); +  rc = tqfindLockInfo(id->fd, &id->pLock, &id->pOpen); +  sqliteOsLeaveMutex(); +  if( rc ){ +    close(id->fd); +    return STQLITE_NOMEM; +  } +  id->locked = 0; +  TRACE3("OPEN-RO %-3d %s\n", id->fd, zFilename); +  OpenCounter(+1); +  return STQLITE_OK; +#endif +#if OS_WIN +  HANDLE h = CreateFile(zFilename, +     GENERIC_READ, +     0, +     NULL, +     OPEN_EXISTING, +     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, +     NULL +  ); +  if( h==INVALID_HANDLE_VALUE ){ +    return STQLITE_CANTOPEN; +  } +  id->h = h; +  id->locked = 0; +  OpenCounter(+1); +  return STQLITE_OK; +#endif +#if OS_MAC +  FSSpec fsSpec; +# ifdef _LARGE_FILE +  HFSUniStr255 dfName; +  FSRef fsRef; +  if( __path2fss(zFilename, &fsSpec) != noErr ) +    return STQLITE_CANTOPEN; +  if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) +    return STQLITE_CANTOPEN; +  FSGetDataForkName(&dfName); +  if( FSOpenFork(&fsRef, dfName.length, dfName.tqunicode, +                 fsRdPerm, &(id->refNum)) != noErr ) +    return STQLITE_CANTOPEN; +# else +  __path2fss(zFilename, &fsSpec); +  if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr ) +    return STQLITE_CANTOPEN; +# endif +  if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){ +    id->refNumRF = -1; +  } +  id->locked = 0; +  id->delOnClose = 0; +  OpenCounter(+1); +  return STQLITE_OK; +#endif +} + +/* +** Attempt to open a file descriptor for the directory that tqcontains a +** file.  This file descriptor can be used to fsync() the directory +** in order to make sure the creation of a new file is actually written +** to disk. +** +** This routine is only meaningful for Unix.  It is a no-op under +** windows since windows does not support hard links. +** +** On success, a handle for a previously open file is at *id is +** updated with the new directory file descriptor and STQLITE_OK is +** returned. +** +** On failure, the function returns STQLITE_CANTOPEN and leaves +** *id unchanged. +*/ +int sqliteOsOpenDirectory( +  const char *zDirname, +  OsFile *id +){ +#if OS_UNIX +  if( id->fd<0 ){ +    /* Do not open the directory if the corresponding file is not already +    ** open. */ +    return STQLITE_CANTOPEN; +  } +  assert( id->dirfd<0 ); +  id->dirfd = open(zDirname, O_RDONLY|O_BINARY, 0644); +  if( id->dirfd<0 ){ +    return STQLITE_CANTOPEN;  +  } +  TRACE3("OPENDIR %-3d %s\n", id->dirfd, zDirname); +#endif +  return STQLITE_OK; +} + +/* +** Create a temporary file name in zBuf.  zBuf must be big enough to +** hold at least STQLITE_TEMPNAME_SIZE characters. +*/ +int sqliteOsTempFileName(char *zBuf){ +#if OS_UNIX +  static const char *azDirs[] = { +     "/var/tmp", +     "/usr/tmp", +     "/tmp", +     ".", +  }; +  static unsigned char zChars[] = +    "abcdefghijklmnopqrstuvwxyz" +    "ABCDEFGHIJKLMNOPTQRSTUVWXYZ" +    "0123456789"; +  int i, j; +  struct stat buf; +  const char *zDir = "."; +  for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){ +    if( stat(azDirs[i], &buf) ) continue; +    if( !S_ISDIR(buf.st_mode) ) continue; +    if( access(azDirs[i], 07) ) continue; +    zDir = azDirs[i]; +    break; +  } +  do{ +    sprintf(zBuf, "%s/"TEMP_FILE_PREFIX, zDir); +    j = strlen(zBuf); +    sqliteRandomness(15, &zBuf[j]); +    for(i=0; i<15; i++, j++){ +      zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; +    } +    zBuf[j] = 0; +  }while( access(zBuf,0)==0 ); +#endif +#if OS_WIN +  static char zChars[] = +    "abcdefghijklmnopqrstuvwxyz" +    "ABCDEFGHIJKLMNOPTQRSTUVWXYZ" +    "0123456789"; +  int i, j; +  char zTempPath[STQLITE_TEMPNAME_SIZE]; +  GetTempPath(STQLITE_TEMPNAME_SIZE-30, zTempPath); +  for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} +  zTempPath[i] = 0; +  for(;;){ +    sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath); +    j = strlen(zBuf); +    sqliteRandomness(15, &zBuf[j]); +    for(i=0; i<15; i++, j++){ +      zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; +    } +    zBuf[j] = 0; +    if( !sqliteOsFileExists(zBuf) ) break; +  } +#endif +#if OS_MAC +  static char zChars[] = +    "abcdefghijklmnopqrstuvwxyz" +    "ABCDEFGHIJKLMNOPTQRSTUVWXYZ" +    "0123456789"; +  int i, j; +  char zTempPath[STQLITE_TEMPNAME_SIZE]; +  char zdirName[32]; +  CInfoPBRec infoRec; +  Str31 dirName; +  memset(&infoRec, 0, sizeof(infoRec)); +  memset(zTempPath, 0, STQLITE_TEMPNAME_SIZE); +  if( FindFolder(kOnSystemDisk, kTemporaryFolderType,  kCreateFolder, +       &(infoRec.dirInfo.ioVRefNum), &(infoRec.dirInfo.ioDrParID)) == noErr ){ +    infoRec.dirInfo.ioNamePtr = dirName; +    do{ +      infoRec.dirInfo.ioFDirIndex = -1; +      infoRec.dirInfo.ioDrDirID = infoRec.dirInfo.ioDrParID; +      if( PBGetCatInfoSync(&infoRec) == noErr ){ +        CopyPascalStringToC(dirName, zdirName); +        i = strlen(zdirName); +        memmove(&(zTempPath[i+1]), zTempPath, strlen(zTempPath)); +        strcpy(zTempPath, zdirName); +        zTempPath[i] = ':'; +      }else{ +        *zTempPath = 0; +        break; +      } +    } while( infoRec.dirInfo.ioDrDirID != fsRtDirID ); +  } +  if( *zTempPath == 0 ) +    getcwd(zTempPath, STQLITE_TEMPNAME_SIZE-24); +  for(;;){ +    sprintf(zBuf, "%s"TEMP_FILE_PREFIX, zTempPath); +    j = strlen(zBuf); +    sqliteRandomness(15, &zBuf[j]); +    for(i=0; i<15; i++, j++){ +      zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; +    } +    zBuf[j] = 0; +    if( !sqliteOsFileExists(zBuf) ) break; +  } +#endif +  return STQLITE_OK;  +} + +/* +** Close a file. +*/ +int sqliteOsClose(OsFile *id){ +#if OS_UNIX +  sqliteOsUnlock(id); +  if( id->dirfd>=0 ) close(id->dirfd); +  id->dirfd = -1; +  sqliteOsEnterMutex(); +  if( id->pOpen->nLock ){ +    /* If there are outstanding locks, do not actually close the file just +    ** yet because that would clear those locks.  Instead, add the file +    ** descriptor to pOpen->aPending.  It will be automatically closed when +    ** the last lock is cleared. +    */ +    int *aNew; +    struct openCnt *pOpen = id->pOpen; +    pOpen->nPending++; +    aNew = sqliteRealloc( pOpen->aPending, pOpen->nPending*sizeof(int) ); +    if( aNew==0 ){ +      /* If a malloc fails, just leak the file descriptor */ +    }else{ +      pOpen->aPending = aNew; +      pOpen->aPending[pOpen->nPending-1] = id->fd; +    } +  }else{ +    /* There are no outstanding locks so we can close the file immediately */ +    close(id->fd); +  } +  releaseLockInfo(id->pLock); +  releaseOpenCnt(id->pOpen); +  sqliteOsLeaveMutex(); +  TRACE2("CLOSE   %-3d\n", id->fd); +  OpenCounter(-1); +  return STQLITE_OK; +#endif +#if OS_WIN +  CloseHandle(id->h); +  OpenCounter(-1); +  return STQLITE_OK; +#endif +#if OS_MAC +  if( id->refNumRF!=-1 ) +    FSClose(id->refNumRF); +# ifdef _LARGE_FILE +  FSCloseFork(id->refNum); +# else +  FSClose(id->refNum); +# endif +  if( id->delOnClose ){ +    unlink(id->pathToDel); +    sqliteFree(id->pathToDel); +  } +  OpenCounter(-1); +  return STQLITE_OK; +#endif +} + +/* +** Read data from a file into a buffer.  Return STQLITE_OK if all +** bytes were read successfully and STQLITE_IOERR if anything goes +** wrong. +*/ +int sqliteOsRead(OsFile *id, void *pBuf, int amt){ +#if OS_UNIX +  int got; +  SimulateIOError(STQLITE_IOERR); +  TIMER_START; +  got = read(id->fd, pBuf, amt); +  TIMER_END; +  TRACE4("READ    %-3d %7d %d\n", id->fd, last_page, elapse); +  SEEK(0); +  /* if( got<0 ) got = 0; */ +  if( got==amt ){ +    return STQLITE_OK; +  }else{ +    return STQLITE_IOERR; +  } +#endif +#if OS_WIN +  DWORD got; +  SimulateIOError(STQLITE_IOERR); +  TRACE2("READ %d\n", last_page); +  if( !ReadFile(id->h, pBuf, amt, &got, 0) ){ +    got = 0; +  } +  if( got==(DWORD)amt ){ +    return STQLITE_OK; +  }else{ +    return STQLITE_IOERR; +  } +#endif +#if OS_MAC +  int got; +  SimulateIOError(STQLITE_IOERR); +  TRACE2("READ %d\n", last_page); +# ifdef _LARGE_FILE +  FSReadFork(id->refNum, fsAtMark, 0, (ByteCount)amt, pBuf, (ByteCount*)&got); +# else +  got = amt; +  FSRead(id->refNum, &got, pBuf); +# endif +  if( got==amt ){ +    return STQLITE_OK; +  }else{ +    return STQLITE_IOERR; +  } +#endif +} + +/* +** Write data from a buffer into a file.  Return STQLITE_OK on success +** or some other error code on failure. +*/ +int sqliteOsWrite(OsFile *id, const void *pBuf, int amt){ +#if OS_UNIX +  int wrote = 0; +  SimulateIOError(STQLITE_IOERR); +  TIMER_START; +  while( amt>0 && (wrote = write(id->fd, pBuf, amt))>0 ){ +    amt -= wrote; +    pBuf = &((char*)pBuf)[wrote]; +  } +  TIMER_END; +  TRACE4("WRITE   %-3d %7d %d\n", id->fd, last_page, elapse); +  SEEK(0); +  if( amt>0 ){ +    return STQLITE_FULL; +  } +  return STQLITE_OK; +#endif +#if OS_WIN +  int rc; +  DWORD wrote; +  SimulateIOError(STQLITE_IOERR); +  TRACE2("WRITE %d\n", last_page); +  while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){ +    amt -= wrote; +    pBuf = &((char*)pBuf)[wrote]; +  } +  if( !rc || amt>(int)wrote ){ +    return STQLITE_FULL; +  } +  return STQLITE_OK; +#endif +#if OS_MAC +  OSErr oserr; +  int wrote = 0; +  SimulateIOError(STQLITE_IOERR); +  TRACE2("WRITE %d\n", last_page); +  while( amt>0 ){ +# ifdef _LARGE_FILE +    oserr = FSWriteFork(id->refNum, fsAtMark, 0, +                        (ByteCount)amt, pBuf, (ByteCount*)&wrote); +# else +    wrote = amt; +    oserr = FSWrite(id->refNum, &wrote, pBuf); +# endif +    if( wrote == 0 || oserr != noErr) +      break; +    amt -= wrote; +    pBuf = &((char*)pBuf)[wrote]; +  } +  if( oserr != noErr || amt>wrote ){ +    return STQLITE_FULL; +  } +  return STQLITE_OK; +#endif +} + +/* +** Move the read/write pointer in a file. +*/ +int sqliteOsSeek(OsFile *id, off_t offset){ +  SEEK(offset/1024 + 1); +#if OS_UNIX +  lseek(id->fd, offset, SEEK_SET); +  return STQLITE_OK; +#endif +#if OS_WIN +  { +    LONG upperBits = offset>>32; +    LONG lowerBits = offset & 0xffffffff; +    DWORD rc; +    rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN); +    /* TRACE3("SEEK rc=0x%x upper=0x%x\n", rc, upperBits); */ +  } +  return STQLITE_OK; +#endif +#if OS_MAC +  { +    off_t curSize; +    if( sqliteOsFileSize(id, &curSize) != STQLITE_OK ){ +      return STQLITE_IOERR; +    } +    if( offset >= curSize ){ +      if( sqliteOsTruncate(id, offset+1) != STQLITE_OK ){ +        return STQLITE_IOERR; +      } +    } +# ifdef _LARGE_FILE +    if( FSSetForkPosition(id->refNum, fsFromStart, offset) != noErr ){ +# else +    if( SetFPos(id->refNum, fsFromStart, offset) != noErr ){ +# endif +      return STQLITE_IOERR; +    }else{ +      return STQLITE_OK; +    } +  } +#endif +} + +/* +** Make sure all writes to a particular file are committed to disk. +** +** Under Unix, also make sure that the directory entry for the file +** has been created by fsync-ing the directory that tqcontains 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 +** STQLite to access the file will not know that the journal exists (because +** the directory entry for the journal was never created) and the transaction +** will not roll back - possibly leading to database corruption. +*/ +int sqliteOsSync(OsFile *id){ +#if OS_UNIX +  SimulateIOError(STQLITE_IOERR); +  TRACE2("SYNC    %-3d\n", id->fd); +  if( fsync(id->fd) ){ +    return STQLITE_IOERR; +  }else{ +    if( id->dirfd>=0 ){ +      TRACE2("DIRSYNC %-3d\n", id->dirfd); +      fsync(id->dirfd); +      close(id->dirfd);  /* Only need to sync once, so close the directory */ +      id->dirfd = -1;    /* when we are done. */ +    } +    return STQLITE_OK; +  } +#endif +#if OS_WIN +  if( FlushFileBuffers(id->h) ){ +    return STQLITE_OK; +  }else{ +    return STQLITE_IOERR; +  } +#endif +#if OS_MAC +# ifdef _LARGE_FILE +  if( FSFlushFork(id->refNum) != noErr ){ +# else +  ParamBlockRec params; +  memset(¶ms, 0, sizeof(ParamBlockRec)); +  params.ioParam.ioRefNum = id->refNum; +  if( PBFlushFileSync(¶ms) != noErr ){ +# endif +    return STQLITE_IOERR; +  }else{ +    return STQLITE_OK; +  } +#endif +} + +/* +** Truncate an open file to a specified size +*/ +int sqliteOsTruncate(OsFile *id, off_t nByte){ +  SimulateIOError(STQLITE_IOERR); +#if OS_UNIX +  return ftruncate(id->fd, nByte)==0 ? STQLITE_OK : STQLITE_IOERR; +#endif +#if OS_WIN +  { +    LONG upperBits = nByte>>32; +    SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN); +    SetEndOfFile(id->h); +  } +  return STQLITE_OK; +#endif +#if OS_MAC +# ifdef _LARGE_FILE +  if( FSSetForkSize(id->refNum, fsFromStart, nByte) != noErr){ +# else +  if( SetEOF(id->refNum, nByte) != noErr ){ +# endif +    return STQLITE_IOERR; +  }else{ +    return STQLITE_OK; +  } +#endif +} + +/* +** Determine the current size of a file in bytes +*/ +int sqliteOsFileSize(OsFile *id, off_t *pSize){ +#if OS_UNIX +  struct stat buf; +  SimulateIOError(STQLITE_IOERR); +  if( fstat(id->fd, &buf)!=0 ){ +    return STQLITE_IOERR; +  } +  *pSize = buf.st_size; +  return STQLITE_OK; +#endif +#if OS_WIN +  DWORD upperBits, lowerBits; +  SimulateIOError(STQLITE_IOERR); +  lowerBits = GetFileSize(id->h, &upperBits); +  *pSize = (((off_t)upperBits)<<32) + lowerBits; +  return STQLITE_OK; +#endif +#if OS_MAC +# ifdef _LARGE_FILE +  if( FSGetForkSize(id->refNum, pSize) != noErr){ +# else +  if( GetEOF(id->refNum, pSize) != noErr ){ +# endif +    return STQLITE_IOERR; +  }else{ +    return STQLITE_OK; +  } +#endif +} + +#if OS_WIN +/* +** Return true (non-zero) if we are running under WinNT, Win2K or WinXP. +** Return false (zero) for Win95, Win98, or WinME. +** +** Here is an interesting observation:  Win95, Win98, and WinME lack +** the LockFileEx() API.  But we can still statically link against that +** API as long as we don't call it win running Win95/98/ME.  A call to +** this routine is used to determine if the host is Win95/98/ME or +** WinNT/2K/XP so that we will know whether or not we can safely call +** the LockFileEx() API. +*/ +int isNT(void){ +  static int osType = 0;   /* 0=unknown 1=win95 2=winNT */ +  if( osType==0 ){ +    OSVERSIONINFO sInfo; +    sInfo.dwOSVersionInfoSize = sizeof(sInfo); +    GetVersionEx(&sInfo); +    osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; +  } +  return osType==2; +} +#endif + +/* +** Windows file locking notes:  [similar issues apply to MacOS] +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available.  So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** (This is a design error on the part of Windows, but there is nothing +** we can do about that.)  So the region used for locking is at the +** end of the file where it is unlikely to ever interfere with an +** actual read attempt. +** +** A database read lock is obtained by locking a single randomly-chosen  +** byte out of a specific range of bytes. The lock byte is obtained at  +** random so two separate readers can probably access the file at the  +** same time, unless they are unlucky and choose the same lock byte. +** A database write lock is obtained by locking all bytes in the range. +** There can only be one writer. +** +** A lock is obtained on the first byte of the lock range before acquiring +** either a read lock or a write lock.  This prevents two processes from +** attempting to get a lock at a same time.  The semantics of  +** sqliteOsReadLock() require that if there is already a write lock, that +** lock is converted into a read lock atomically.  The lock on the first +** byte allows us to drop the old write lock and get the read lock without +** another process jumping into the middle and messing us up.  The same +** argument applies to sqliteOsWriteLock(). +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks.  When reader writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME.  Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** Note: On MacOS we use the resource fork for locking. +** +** The following #defines specify the range of bytes used for locking. +** N_LOCKBYTE is the number of bytes available for doing the locking. +** The first byte used to hold the lock while the lock is changing does +** not count toward this number.  FIRST_LOCKBYTE is the address of +** the first byte in the range of bytes used for locking. +*/ +#define N_LOCKBYTE       10239 +#if OS_MAC +# define FIRST_LOCKBYTE   (0x000fffff - N_LOCKBYTE) +#else +# define FIRST_LOCKBYTE   (0xffffffff - N_LOCKBYTE) +#endif + +/* +** Change the status of the lock on the file "id" to be a readlock. +** If the file was write locked, then this reduces the lock to a read. +** If the file was read locked, then this acquires a new read lock. +** +** Return STQLITE_OK on success and STQLITE_BUSY on failure.  If this +** library was compiled with large file support (LFS) but LFS is not +** available on the host, then an STQLITE_NOLFS is returned. +*/ +int sqliteOsReadLock(OsFile *id){ +#if OS_UNIX +  int rc; +  sqliteOsEnterMutex(); +  if( id->pLock->cnt>0 ){ +    if( !id->locked ){ +      id->pLock->cnt++; +      id->locked = 1; +      id->pOpen->nLock++; +    } +    rc = STQLITE_OK; +  }else if( id->locked || id->pLock->cnt==0 ){ +    struct flock lock; +    int s; +    lock.l_type = F_RDLCK; +    lock.l_whence = SEEK_SET; +    lock.l_start = lock.l_len = 0L; +    s = fcntl(id->fd, F_SETLK, &lock); +    if( s!=0 ){ +      rc = (errno==EINVAL) ? STQLITE_NOLFS : STQLITE_BUSY; +    }else{ +      rc = STQLITE_OK; +      if( !id->locked ){ +        id->pOpen->nLock++; +        id->locked = 1; +      } +      id->pLock->cnt = 1; +    } +  }else{ +    rc = STQLITE_BUSY; +  } +  sqliteOsLeaveMutex(); +  return rc; +#endif +#if OS_WIN +  int rc; +  if( id->locked>0 ){ +    rc = STQLITE_OK; +  }else{ +    int lk; +    int res; +    int cnt = 100; +    sqliteRandomness(sizeof(lk), &lk); +    lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1; +    while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){ +      Sleep(1); +    } +    if( res ){ +      UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); +      if( isNT() ){ +        OVERLAPPED ovlp; +        ovlp.Offset = FIRST_LOCKBYTE+1; +        ovlp.OffsetHigh = 0; +        ovlp.hEvent = 0; +        res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY,  +                          0, N_LOCKBYTE, 0, &ovlp); +      }else{ +        res = LockFile(id->h, FIRST_LOCKBYTE+lk, 0, 1, 0); +      } +      UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0); +    } +    if( res ){ +      id->locked = lk; +      rc = STQLITE_OK; +    }else{ +      rc = STQLITE_BUSY; +    } +  } +  return rc; +#endif +#if OS_MAC +  int rc; +  if( id->locked>0 || id->refNumRF == -1 ){ +    rc = STQLITE_OK; +  }else{ +    int lk; +    OSErr res; +    int cnt = 5; +    ParamBlockRec params; +    sqliteRandomness(sizeof(lk), &lk); +    lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1; +    memset(¶ms, 0, sizeof(params)); +    params.ioParam.ioRefNum = id->refNumRF; +    params.ioParam.ioPosMode = fsFromStart; +    params.ioParam.ioPosOffset = FIRST_LOCKBYTE; +    params.ioParam.ioRetqCount = 1; +    while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ +      UInt32 finalTicks; +      Delay(1, &finalTicks); /* 1/60 sec */ +    } +    if( res == noErr ){ +      params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; +      params.ioParam.ioRetqCount = N_LOCKBYTE; +      PBUnlockRangeSync(¶ms); +      params.ioParam.ioPosOffset = FIRST_LOCKBYTE+lk; +      params.ioParam.ioRetqCount = 1; +      res = PBLockRangeSync(¶ms); +      params.ioParam.ioPosOffset = FIRST_LOCKBYTE; +      params.ioParam.ioRetqCount = 1; +      PBUnlockRangeSync(¶ms); +    } +    if( res == noErr ){ +      id->locked = lk; +      rc = STQLITE_OK; +    }else{ +      rc = STQLITE_BUSY; +    } +  } +  return rc; +#endif +} + +/* +** Change the lock status to be an exclusive or write lock.  Return +** STQLITE_OK on success and STQLITE_BUSY on a failure.  If this +** library was compiled with large file support (LFS) but LFS is not +** available on the host, then an STQLITE_NOLFS is returned. +*/ +int sqliteOsWriteLock(OsFile *id){ +#if OS_UNIX +  int rc; +  sqliteOsEnterMutex(); +  if( id->pLock->cnt==0 || (id->pLock->cnt==1 && id->locked==1) ){ +    struct flock lock; +    int s; +    lock.l_type = F_WRLCK; +    lock.l_whence = SEEK_SET; +    lock.l_start = lock.l_len = 0L; +    s = fcntl(id->fd, F_SETLK, &lock); +    if( s!=0 ){ +      rc = (errno==EINVAL) ? STQLITE_NOLFS : STQLITE_BUSY; +    }else{ +      rc = STQLITE_OK; +      if( !id->locked ){ +        id->pOpen->nLock++; +        id->locked = 1; +      } +      id->pLock->cnt = -1; +    } +  }else{ +    rc = STQLITE_BUSY; +  } +  sqliteOsLeaveMutex(); +  return rc; +#endif +#if OS_WIN +  int rc; +  if( id->locked<0 ){ +    rc = STQLITE_OK; +  }else{ +    int res; +    int cnt = 100; +    while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){ +      Sleep(1); +    } +    if( res ){ +      if( id->locked>0 ){ +        if( isNT() ){ +          UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); +        }else{ +          res = UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, 0, 1, 0); +        } +      } +      if( res ){ +        res = LockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); +      }else{ +        res = 0; +      } +      UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0); +    } +    if( res ){ +      id->locked = -1; +      rc = STQLITE_OK; +    }else{ +      rc = STQLITE_BUSY; +    } +  } +  return rc; +#endif +#if OS_MAC +  int rc; +  if( id->locked<0 || id->refNumRF == -1 ){ +    rc = STQLITE_OK; +  }else{ +    OSErr res; +    int cnt = 5; +    ParamBlockRec params; +    memset(¶ms, 0, sizeof(params)); +    params.ioParam.ioRefNum = id->refNumRF; +    params.ioParam.ioPosMode = fsFromStart; +    params.ioParam.ioPosOffset = FIRST_LOCKBYTE; +    params.ioParam.ioRetqCount = 1; +    while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ +      UInt32 finalTicks; +      Delay(1, &finalTicks); /* 1/60 sec */ +    } +    if( res == noErr ){ +      params.ioParam.ioPosOffset = FIRST_LOCKBYTE + id->locked; +      params.ioParam.ioRetqCount = 1; +      if( id->locked==0  +            || PBUnlockRangeSync(¶ms)==noErr ){ +        params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; +        params.ioParam.ioRetqCount = N_LOCKBYTE; +        res = PBLockRangeSync(¶ms); +      }else{ +        res = afpRangeNotLocked; +      } +      params.ioParam.ioPosOffset = FIRST_LOCKBYTE; +      params.ioParam.ioRetqCount = 1; +      PBUnlockRangeSync(¶ms); +    } +    if( res == noErr ){ +      id->locked = -1; +      rc = STQLITE_OK; +    }else{ +      rc = STQLITE_BUSY; +    } +  } +  return rc; +#endif +} + +/* +** Unlock the given file descriptor.  If the file descriptor was +** not previously locked, then this routine is a no-op.  If this +** library was compiled with large file support (LFS) but LFS is not +** available on the host, then an STQLITE_NOLFS is returned. +*/ +int sqliteOsUnlock(OsFile *id){ +#if OS_UNIX +  int rc; +  if( !id->locked ) return STQLITE_OK; +  sqliteOsEnterMutex(); +  assert( id->pLock->cnt!=0 ); +  if( id->pLock->cnt>1 ){ +    id->pLock->cnt--; +    rc = STQLITE_OK; +  }else{ +    struct flock lock; +    int s; +    lock.l_type = F_UNLCK; +    lock.l_whence = SEEK_SET; +    lock.l_start = lock.l_len = 0L; +    s = fcntl(id->fd, F_SETLK, &lock); +    if( s!=0 ){ +      rc = (errno==EINVAL) ? STQLITE_NOLFS : STQLITE_BUSY; +    }else{ +      rc = STQLITE_OK; +      id->pLock->cnt = 0; +    } +  } +  if( rc==STQLITE_OK ){ +    /* Decrement the count of locks against this same file.  When the +    ** count reaches zero, close any other file descriptors whose close +    ** was deferred because of outstanding locks. +    */ +    struct openCnt *pOpen = id->pOpen; +    pOpen->nLock--; +    assert( pOpen->nLock>=0 ); +    if( pOpen->nLock==0 && pOpen->nPending>0 ){ +      int i; +      for(i=0; i<pOpen->nPending; i++){ +        close(pOpen->aPending[i]); +      } +      sqliteFree(pOpen->aPending); +      pOpen->nPending = 0; +      pOpen->aPending = 0; +    } +  } +  sqliteOsLeaveMutex(); +  id->locked = 0; +  return rc; +#endif +#if OS_WIN +  int rc; +  if( id->locked==0 ){ +    rc = STQLITE_OK; +  }else if( isNT() || id->locked<0 ){ +    UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); +    rc = STQLITE_OK; +    id->locked = 0; +  }else{ +    UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, 0, 1, 0); +    rc = STQLITE_OK; +    id->locked = 0; +  } +  return rc; +#endif +#if OS_MAC +  int rc; +  ParamBlockRec params; +  memset(¶ms, 0, sizeof(params)); +  params.ioParam.ioRefNum = id->refNumRF; +  params.ioParam.ioPosMode = fsFromStart; +  if( id->locked==0 || id->refNumRF == -1 ){ +    rc = STQLITE_OK; +  }else if( id->locked<0 ){ +    params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; +    params.ioParam.ioRetqCount = N_LOCKBYTE; +    PBUnlockRangeSync(¶ms); +    rc = STQLITE_OK; +    id->locked = 0; +  }else{ +    params.ioParam.ioPosOffset = FIRST_LOCKBYTE+id->locked; +    params.ioParam.ioRetqCount = 1; +    PBUnlockRangeSync(¶ms); +    rc = STQLITE_OK; +    id->locked = 0; +  } +  return rc; +#endif +} + +/* +** Get information to seed the random number generator.  The seed +** is written into the buffer zBuf[256].  The calling function must +** supply a sufficiently large buffer. +*/ +int sqliteOsRandomSeed(char *zBuf){ +  /* 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, 256); +#if OS_UNIX && !defined(STQLITE_TEST) +  { +    int pid; +    time((time_t*)zBuf); +    pid = getpid(); +    memcpy(&zBuf[sizeof(time_t)], &pid, sizeof(pid)); +  } +#endif +#if OS_WIN && !defined(STQLITE_TEST) +  GetSystemTime((LPSYSTEMTIME)zBuf); +#endif +#if OS_MAC +  { +    int pid; +    Microseconds((UnsignedWide*)zBuf); +    pid = getpid(); +    memcpy(&zBuf[sizeof(UnsignedWide)], &pid, sizeof(pid)); +  } +#endif +  return STQLITE_OK; +} + +/* +** Sleep for a little while.  Return the amount of time slept. +*/ +int sqliteOsSleep(int ms){ +#if OS_UNIX +#if defined(HAVE_USLEEP) && HAVE_USLEEP +  usleep(ms*1000); +  return ms; +#else +  sleep((ms+999)/1000); +  return 1000*((ms+999)/1000); +#endif +#endif +#if OS_WIN +  Sleep(ms); +  return ms; +#endif +#if OS_MAC +  UInt32 finalTicks; +  UInt32 ticks = (((UInt32)ms+16)*3)/50;  /* 1/60 sec per tick */ +  Delay(ticks, &finalTicks); +  return (int)((ticks*50)/3); +#endif +} + +/* +** Static variables used for thread synchronization +*/ +static int inMutex = 0; +#ifdef STQLITE_UNIX_THREADS +  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +#ifdef STQLITE_W32_THREADS +  static CRITICAL_SECTION cs; +#endif +#ifdef STQLITE_MACOS_MULTITASKING +  static MPCriticalRegionID criticalRegion; +#endif + +/* +** The following pair of routine implement mutual exclusion for +** multi-threaded processes.  Only a single thread is allowed to +** executed code that is surrounded by EnterMutex() and LeaveMutex(). +** +** STQLite uses only a single Mutex.  There is not much critical +** code and what little there is executes quickly and without blocking. +*/ +void sqliteOsEnterMutex(){ +#ifdef STQLITE_UNIX_THREADS +  pthread_mutex_lock(&mutex); +#endif +#ifdef STQLITE_W32_THREADS +  static int isInit = 0; +  while( !isInit ){ +    static long lock = 0; +    if( InterlockedIncrement(&lock)==1 ){ +      InitializeCriticalSection(&cs); +      isInit = 1; +    }else{ +      Sleep(1); +    } +  } +  EnterCriticalSection(&cs); +#endif +#ifdef STQLITE_MACOS_MULTITASKING +  static volatile int notInit = 1; +  if( notInit ){ +    if( notInit == 2 ) /* as close as you can get to thread safe init */ +      MPYield(); +    else{ +      notInit = 2; +      MPCreateCriticalRegion(&criticalRegion); +      notInit = 0; +    } +  } +  MPEnterCriticalRegion(criticalRegion, kDurationForever); +#endif +  assert( !inMutex ); +  inMutex = 1; +} +void sqliteOsLeaveMutex(){ +  assert( inMutex ); +  inMutex = 0; +#ifdef STQLITE_UNIX_THREADS +  pthread_mutex_unlock(&mutex); +#endif +#ifdef STQLITE_W32_THREADS +  LeaveCriticalSection(&cs); +#endif +#ifdef STQLITE_MACOS_MULTITASKING +  MPExitCriticalRegion(criticalRegion); +#endif +} + +/* +** Turn a relative pathname into a full pathname.  Return a pointer +** to the full pathname stored in space obtained from sqliteMalloc(). +** The calling function is responsible for freeing this space once it +** is no longer needed. +*/ +char *sqliteOsFullPathname(const char *zRelative){ +#if OS_UNIX +  char *zFull = 0; +  if( zRelative[0]=='/' ){ +    sqliteSetString(&zFull, zRelative, (char*)0); +  }else{ +    char zBuf[5000]; +    sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), "/", zRelative, +                    (char*)0); +  } +  return zFull; +#endif +#if OS_WIN +  char *zNotUsed; +  char *zFull; +  int nByte; +  nByte = GetFullPathName(zRelative, 0, 0, &zNotUsed) + 1; +  zFull = sqliteMalloc( nByte ); +  if( zFull==0 ) return 0; +  GetFullPathName(zRelative, nByte, zFull, &zNotUsed); +  return zFull; +#endif +#if OS_MAC +  char *zFull = 0; +  if( zRelative[0]==':' ){ +    char zBuf[_MAX_PATH+1]; +    sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), &(zRelative[1]), +                    (char*)0); +  }else{ +    if( strchr(zRelative, ':') ){ +      sqliteSetString(&zFull, zRelative, (char*)0); +    }else{ +    char zBuf[_MAX_PATH+1]; +      sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, (char*)0); +    } +  } +  return zFull; +#endif +} + +/* +** The following variable, if set to a now-zero value, become the result +** returned from sqliteOsCurrentTime().  This is used for testing. +*/ +#ifdef STQLITE_TEST +int sqlite_current_time = 0; +#endif + +/* +** 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. +*/ +int sqliteOsCurrentTime(double *prNow){ +#if OS_UNIX +  time_t t; +  time(&t); +  *prNow = t/86400.0 + 2440587.5; +#endif +#if OS_WIN +  FILETIME ft; +  /* FILETIME structure is a 64-bit value representing the number of  +     100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).  +  */ +  double now; +  GetSystemTimeAsFileTime( &ft ); +  now = ((double)ft.dwHighDateTime) * 4294967296.0;  +  *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5; +#endif +#ifdef STQLITE_TEST +  if( sqlite_current_time ){ +    *prNow = sqlite_current_time/86400.0 + 2440587.5; +  } +#endif +  return 0; +} | 
