xref: /illumos-gate/usr/src/lib/libsqlite/src/os.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate ** 2001 September 16
37c478bd9Sstevel@tonic-gate **
47c478bd9Sstevel@tonic-gate ** The author disclaims copyright to this source code.  In place of
57c478bd9Sstevel@tonic-gate ** a legal notice, here is a blessing:
67c478bd9Sstevel@tonic-gate **
77c478bd9Sstevel@tonic-gate **    May you do good and not evil.
87c478bd9Sstevel@tonic-gate **    May you find forgiveness for yourself and forgive others.
97c478bd9Sstevel@tonic-gate **    May you share freely, never taking more than you give.
107c478bd9Sstevel@tonic-gate **
117c478bd9Sstevel@tonic-gate ******************************************************************************
127c478bd9Sstevel@tonic-gate **
137c478bd9Sstevel@tonic-gate ** This file contains code that is specific to particular operating
147c478bd9Sstevel@tonic-gate ** systems.  The purpose of this file is to provide a uniform abstraction
157c478bd9Sstevel@tonic-gate ** on which the rest of SQLite can operate.
167c478bd9Sstevel@tonic-gate */
177c478bd9Sstevel@tonic-gate #include "os.h"          /* Must be first to enable large file support */
187c478bd9Sstevel@tonic-gate #include "sqliteInt.h"
197c478bd9Sstevel@tonic-gate 
207c478bd9Sstevel@tonic-gate #if OS_UNIX
217c478bd9Sstevel@tonic-gate # include <time.h>
227c478bd9Sstevel@tonic-gate # include <errno.h>
237c478bd9Sstevel@tonic-gate # include <unistd.h>
247c478bd9Sstevel@tonic-gate # ifndef O_LARGEFILE
257c478bd9Sstevel@tonic-gate #  define O_LARGEFILE 0
267c478bd9Sstevel@tonic-gate # endif
277c478bd9Sstevel@tonic-gate # ifdef SQLITE_DISABLE_LFS
287c478bd9Sstevel@tonic-gate #  undef O_LARGEFILE
297c478bd9Sstevel@tonic-gate #  define O_LARGEFILE 0
307c478bd9Sstevel@tonic-gate # endif
317c478bd9Sstevel@tonic-gate # ifndef O_NOFOLLOW
327c478bd9Sstevel@tonic-gate #  define O_NOFOLLOW 0
337c478bd9Sstevel@tonic-gate # endif
347c478bd9Sstevel@tonic-gate # ifndef O_BINARY
357c478bd9Sstevel@tonic-gate #  define O_BINARY 0
367c478bd9Sstevel@tonic-gate # endif
377c478bd9Sstevel@tonic-gate #endif
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #if OS_WIN
417c478bd9Sstevel@tonic-gate # include <winbase.h>
427c478bd9Sstevel@tonic-gate #endif
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate #if OS_MAC
457c478bd9Sstevel@tonic-gate # include <extras.h>
467c478bd9Sstevel@tonic-gate # include <path2fss.h>
477c478bd9Sstevel@tonic-gate # include <TextUtils.h>
487c478bd9Sstevel@tonic-gate # include <FinderRegistry.h>
497c478bd9Sstevel@tonic-gate # include <Folders.h>
507c478bd9Sstevel@tonic-gate # include <Timer.h>
517c478bd9Sstevel@tonic-gate # include <OSUtils.h>
527c478bd9Sstevel@tonic-gate #endif
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate /*
557c478bd9Sstevel@tonic-gate ** The DJGPP compiler environment looks mostly like Unix, but it
567c478bd9Sstevel@tonic-gate ** lacks the fcntl() system call.  So redefine fcntl() to be something
577c478bd9Sstevel@tonic-gate ** that always succeeds.  This means that locking does not occur under
587c478bd9Sstevel@tonic-gate ** DJGPP.  But its DOS - what did you expect?
597c478bd9Sstevel@tonic-gate */
607c478bd9Sstevel@tonic-gate #ifdef __DJGPP__
617c478bd9Sstevel@tonic-gate # define fcntl(A,B,C) 0
627c478bd9Sstevel@tonic-gate #endif
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate ** Macros used to determine whether or not to use threads.  The
667c478bd9Sstevel@tonic-gate ** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
677c478bd9Sstevel@tonic-gate ** Posix threads and SQLITE_W32_THREADS is defined if we are
687c478bd9Sstevel@tonic-gate ** synchronizing using Win32 threads.
697c478bd9Sstevel@tonic-gate */
707c478bd9Sstevel@tonic-gate #if OS_UNIX && defined(THREADSAFE) && THREADSAFE
717c478bd9Sstevel@tonic-gate # include <pthread.h>
727c478bd9Sstevel@tonic-gate # define SQLITE_UNIX_THREADS 1
737c478bd9Sstevel@tonic-gate #endif
747c478bd9Sstevel@tonic-gate #if OS_WIN && defined(THREADSAFE) && THREADSAFE
757c478bd9Sstevel@tonic-gate # define SQLITE_W32_THREADS 1
767c478bd9Sstevel@tonic-gate #endif
777c478bd9Sstevel@tonic-gate #if OS_MAC && defined(THREADSAFE) && THREADSAFE
787c478bd9Sstevel@tonic-gate # include <Multiprocessing.h>
797c478bd9Sstevel@tonic-gate # define SQLITE_MACOS_MULTITASKING 1
807c478bd9Sstevel@tonic-gate #endif
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate ** Macros for performance tracing.  Normally turned off
847c478bd9Sstevel@tonic-gate */
857c478bd9Sstevel@tonic-gate #if 0
867c478bd9Sstevel@tonic-gate static int last_page = 0;
877c478bd9Sstevel@tonic-gate __inline__ unsigned long long int hwtime(void){
887c478bd9Sstevel@tonic-gate   unsigned long long int x;
897c478bd9Sstevel@tonic-gate   __asm__("rdtsc\n\t"
907c478bd9Sstevel@tonic-gate           "mov %%edx, %%ecx\n\t"
917c478bd9Sstevel@tonic-gate           :"=A" (x));
927c478bd9Sstevel@tonic-gate   return x;
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate static unsigned long long int g_start;
957c478bd9Sstevel@tonic-gate static unsigned int elapse;
967c478bd9Sstevel@tonic-gate #define TIMER_START       g_start=hwtime()
977c478bd9Sstevel@tonic-gate #define TIMER_END         elapse=hwtime()-g_start
987c478bd9Sstevel@tonic-gate #define SEEK(X)           last_page=(X)
997c478bd9Sstevel@tonic-gate #define TRACE1(X)         fprintf(stderr,X)
1007c478bd9Sstevel@tonic-gate #define TRACE2(X,Y)       fprintf(stderr,X,Y)
1017c478bd9Sstevel@tonic-gate #define TRACE3(X,Y,Z)     fprintf(stderr,X,Y,Z)
1027c478bd9Sstevel@tonic-gate #define TRACE4(X,Y,Z,A)   fprintf(stderr,X,Y,Z,A)
1037c478bd9Sstevel@tonic-gate #define TRACE5(X,Y,Z,A,B) fprintf(stderr,X,Y,Z,A,B)
1047c478bd9Sstevel@tonic-gate #else
1057c478bd9Sstevel@tonic-gate #define TIMER_START
1067c478bd9Sstevel@tonic-gate #define TIMER_END
1077c478bd9Sstevel@tonic-gate #define SEEK(X)
1087c478bd9Sstevel@tonic-gate #define TRACE1(X)
1097c478bd9Sstevel@tonic-gate #define TRACE2(X,Y)
1107c478bd9Sstevel@tonic-gate #define TRACE3(X,Y,Z)
1117c478bd9Sstevel@tonic-gate #define TRACE4(X,Y,Z,A)
1127c478bd9Sstevel@tonic-gate #define TRACE5(X,Y,Z,A,B)
1137c478bd9Sstevel@tonic-gate #endif
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate #if OS_UNIX
1177c478bd9Sstevel@tonic-gate /*
1187c478bd9Sstevel@tonic-gate ** Here is the dirt on POSIX advisory locks:  ANSI STD 1003.1 (1996)
1197c478bd9Sstevel@tonic-gate ** section 6.5.2.2 lines 483 through 490 specify that when a process
1207c478bd9Sstevel@tonic-gate ** sets or clears a lock, that operation overrides any prior locks set
1217c478bd9Sstevel@tonic-gate ** by the same process.  It does not explicitly say so, but this implies
1227c478bd9Sstevel@tonic-gate ** that it overrides locks set by the same process using a different
1237c478bd9Sstevel@tonic-gate ** file descriptor.  Consider this test case:
1247c478bd9Sstevel@tonic-gate **
1257c478bd9Sstevel@tonic-gate **       int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
1267c478bd9Sstevel@tonic-gate **       int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
1277c478bd9Sstevel@tonic-gate **
1287c478bd9Sstevel@tonic-gate ** Suppose ./file1 and ./file2 are really the same file (because
1297c478bd9Sstevel@tonic-gate ** one is a hard or symbolic link to the other) then if you set
1307c478bd9Sstevel@tonic-gate ** an exclusive lock on fd1, then try to get an exclusive lock
1317c478bd9Sstevel@tonic-gate ** on fd2, it works.  I would have expected the second lock to
1327c478bd9Sstevel@tonic-gate ** fail since there was already a lock on the file due to fd1.
1337c478bd9Sstevel@tonic-gate ** But not so.  Since both locks came from the same process, the
1347c478bd9Sstevel@tonic-gate ** second overrides the first, even though they were on different
1357c478bd9Sstevel@tonic-gate ** file descriptors opened on different file names.
1367c478bd9Sstevel@tonic-gate **
1377c478bd9Sstevel@tonic-gate ** Bummer.  If you ask me, this is broken.  Badly broken.  It means
1387c478bd9Sstevel@tonic-gate ** that we cannot use POSIX locks to synchronize file access among
1397c478bd9Sstevel@tonic-gate ** competing threads of the same process.  POSIX locks will work fine
1407c478bd9Sstevel@tonic-gate ** to synchronize access for threads in separate processes, but not
1417c478bd9Sstevel@tonic-gate ** threads within the same process.
1427c478bd9Sstevel@tonic-gate **
1437c478bd9Sstevel@tonic-gate ** To work around the problem, SQLite has to manage file locks internally
1447c478bd9Sstevel@tonic-gate ** on its own.  Whenever a new database is opened, we have to find the
1457c478bd9Sstevel@tonic-gate ** specific inode of the database file (the inode is determined by the
1467c478bd9Sstevel@tonic-gate ** st_dev and st_ino fields of the stat structure that fstat() fills in)
1477c478bd9Sstevel@tonic-gate ** and check for locks already existing on that inode.  When locks are
1487c478bd9Sstevel@tonic-gate ** created or removed, we have to look at our own internal record of the
1497c478bd9Sstevel@tonic-gate ** locks to see if another thread has previously set a lock on that same
1507c478bd9Sstevel@tonic-gate ** inode.
1517c478bd9Sstevel@tonic-gate **
1527c478bd9Sstevel@tonic-gate ** The OsFile structure for POSIX is no longer just an integer file
1537c478bd9Sstevel@tonic-gate ** descriptor.  It is now a structure that holds the integer file
1547c478bd9Sstevel@tonic-gate ** descriptor and a pointer to a structure that describes the internal
1557c478bd9Sstevel@tonic-gate ** locks on the corresponding inode.  There is one locking structure
1567c478bd9Sstevel@tonic-gate ** per inode, so if the same inode is opened twice, both OsFile structures
1577c478bd9Sstevel@tonic-gate ** point to the same locking structure.  The locking structure keeps
1587c478bd9Sstevel@tonic-gate ** a reference count (so we will know when to delete it) and a "cnt"
1597c478bd9Sstevel@tonic-gate ** field that tells us its internal lock status.  cnt==0 means the
1607c478bd9Sstevel@tonic-gate ** file is unlocked.  cnt==-1 means the file has an exclusive lock.
1617c478bd9Sstevel@tonic-gate ** cnt>0 means there are cnt shared locks on the file.
1627c478bd9Sstevel@tonic-gate **
1637c478bd9Sstevel@tonic-gate ** Any attempt to lock or unlock a file first checks the locking
164*1da57d55SToomas Soome ** structure.  The fcntl() system call is only invoked to set a
1657c478bd9Sstevel@tonic-gate ** POSIX lock if the internal lock structure transitions between
1667c478bd9Sstevel@tonic-gate ** a locked and an unlocked state.
1677c478bd9Sstevel@tonic-gate **
1687c478bd9Sstevel@tonic-gate ** 2004-Jan-11:
1697c478bd9Sstevel@tonic-gate ** More recent discoveries about POSIX advisory locks.  (The more
1707c478bd9Sstevel@tonic-gate ** I discover, the more I realize the a POSIX advisory locks are
1717c478bd9Sstevel@tonic-gate ** an abomination.)
1727c478bd9Sstevel@tonic-gate **
1737c478bd9Sstevel@tonic-gate ** If you close a file descriptor that points to a file that has locks,
1747c478bd9Sstevel@tonic-gate ** all locks on that file that are owned by the current process are
1757c478bd9Sstevel@tonic-gate ** released.  To work around this problem, each OsFile structure contains
1767c478bd9Sstevel@tonic-gate ** a pointer to an openCnt structure.  There is one openCnt structure
1777c478bd9Sstevel@tonic-gate ** per open inode, which means that multiple OsFiles can point to a single
1787c478bd9Sstevel@tonic-gate ** openCnt.  When an attempt is made to close an OsFile, if there are
1797c478bd9Sstevel@tonic-gate ** other OsFiles open on the same inode that are holding locks, the call
1807c478bd9Sstevel@tonic-gate ** to close() the file descriptor is deferred until all of the locks clear.
1817c478bd9Sstevel@tonic-gate ** The openCnt structure keeps a list of file descriptors that need to
1827c478bd9Sstevel@tonic-gate ** be closed and that list is walked (and cleared) when the last lock
1837c478bd9Sstevel@tonic-gate ** clears.
1847c478bd9Sstevel@tonic-gate **
1857c478bd9Sstevel@tonic-gate ** First, under Linux threads, because each thread has a separate
1867c478bd9Sstevel@tonic-gate ** process ID, lock operations in one thread do not override locks
1877c478bd9Sstevel@tonic-gate ** to the same file in other threads.  Linux threads behave like
1887c478bd9Sstevel@tonic-gate ** separate processes in this respect.  But, if you close a file
1897c478bd9Sstevel@tonic-gate ** descriptor in linux threads, all locks are cleared, even locks
1907c478bd9Sstevel@tonic-gate ** on other threads and even though the other threads have different
1917c478bd9Sstevel@tonic-gate ** process IDs.  Linux threads is inconsistent in this respect.
1927c478bd9Sstevel@tonic-gate ** (I'm beginning to think that linux threads is an abomination too.)
1937c478bd9Sstevel@tonic-gate ** The consequence of this all is that the hash table for the lockInfo
1947c478bd9Sstevel@tonic-gate ** structure has to include the process id as part of its key because
195*1da57d55SToomas Soome ** locks in different threads are treated as distinct.  But the
1967c478bd9Sstevel@tonic-gate ** openCnt structure should not include the process id in its
1977c478bd9Sstevel@tonic-gate ** key because close() clears lock on all threads, not just the current
1987c478bd9Sstevel@tonic-gate ** thread.  Were it not for this goofiness in linux threads, we could
1997c478bd9Sstevel@tonic-gate ** combine the lockInfo and openCnt structures into a single structure.
2007c478bd9Sstevel@tonic-gate */
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate /*
2037c478bd9Sstevel@tonic-gate ** An instance of the following structure serves as the key used
2047c478bd9Sstevel@tonic-gate ** to locate a particular lockInfo structure given its inode.  Note
2057c478bd9Sstevel@tonic-gate ** that we have to include the process ID as part of the key.  On some
2067c478bd9Sstevel@tonic-gate ** threading implementations (ex: linux), each thread has a separate
2077c478bd9Sstevel@tonic-gate ** process ID.
2087c478bd9Sstevel@tonic-gate */
2097c478bd9Sstevel@tonic-gate struct lockKey {
2107c478bd9Sstevel@tonic-gate   dev_t dev;   /* Device number */
2117c478bd9Sstevel@tonic-gate   ino_t ino;   /* Inode number */
2127c478bd9Sstevel@tonic-gate   pid_t pid;   /* Process ID */
2137c478bd9Sstevel@tonic-gate };
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate /*
2167c478bd9Sstevel@tonic-gate ** An instance of the following structure is allocated for each open
2177c478bd9Sstevel@tonic-gate ** inode on each thread with a different process ID.  (Threads have
2187c478bd9Sstevel@tonic-gate ** different process IDs on linux, but not on most other unixes.)
2197c478bd9Sstevel@tonic-gate **
2207c478bd9Sstevel@tonic-gate ** A single inode can have multiple file descriptors, so each OsFile
2217c478bd9Sstevel@tonic-gate ** structure contains a pointer to an instance of this object and this
2227c478bd9Sstevel@tonic-gate ** object keeps a count of the number of OsFiles pointing to it.
2237c478bd9Sstevel@tonic-gate */
2247c478bd9Sstevel@tonic-gate struct lockInfo {
2257c478bd9Sstevel@tonic-gate   struct lockKey key;  /* The lookup key */
2267c478bd9Sstevel@tonic-gate   int cnt;             /* 0: unlocked.  -1: write lock.  1...: read lock. */
2277c478bd9Sstevel@tonic-gate   int nRef;            /* Number of pointers to this structure */
2287c478bd9Sstevel@tonic-gate };
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate /*
2317c478bd9Sstevel@tonic-gate ** An instance of the following structure serves as the key used
2327c478bd9Sstevel@tonic-gate ** to locate a particular openCnt structure given its inode.  This
2337c478bd9Sstevel@tonic-gate ** is the same as the lockKey except that the process ID is omitted.
2347c478bd9Sstevel@tonic-gate */
2357c478bd9Sstevel@tonic-gate struct openKey {
2367c478bd9Sstevel@tonic-gate   dev_t dev;   /* Device number */
2377c478bd9Sstevel@tonic-gate   ino_t ino;   /* Inode number */
2387c478bd9Sstevel@tonic-gate };
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate /*
2417c478bd9Sstevel@tonic-gate ** An instance of the following structure is allocated for each open
2427c478bd9Sstevel@tonic-gate ** inode.  This structure keeps track of the number of locks on that
2437c478bd9Sstevel@tonic-gate ** inode.  If a close is attempted against an inode that is holding
2447c478bd9Sstevel@tonic-gate ** locks, the close is deferred until all locks clear by adding the
2457c478bd9Sstevel@tonic-gate ** file descriptor to be closed to the pending list.
2467c478bd9Sstevel@tonic-gate */
2477c478bd9Sstevel@tonic-gate struct openCnt {
2487c478bd9Sstevel@tonic-gate   struct openKey key;   /* The lookup key */
2497c478bd9Sstevel@tonic-gate   int nRef;             /* Number of pointers to this structure */
2507c478bd9Sstevel@tonic-gate   int nLock;            /* Number of outstanding locks */
2517c478bd9Sstevel@tonic-gate   int nPending;         /* Number of pending close() operations */
2527c478bd9Sstevel@tonic-gate   int *aPending;        /* Malloced space holding fd's awaiting a close() */
2537c478bd9Sstevel@tonic-gate };
2547c478bd9Sstevel@tonic-gate 
255*1da57d55SToomas Soome /*
2567c478bd9Sstevel@tonic-gate ** These hash table maps inodes and process IDs into lockInfo and openCnt
2577c478bd9Sstevel@tonic-gate ** structures.  Access to these hash tables must be protected by a mutex.
2587c478bd9Sstevel@tonic-gate */
2597c478bd9Sstevel@tonic-gate static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };
2607c478bd9Sstevel@tonic-gate static Hash openHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate /*
2637c478bd9Sstevel@tonic-gate ** Release a lockInfo structure previously allocated by findLockInfo().
2647c478bd9Sstevel@tonic-gate */
releaseLockInfo(struct lockInfo * pLock)2657c478bd9Sstevel@tonic-gate static void releaseLockInfo(struct lockInfo *pLock){
2667c478bd9Sstevel@tonic-gate   pLock->nRef--;
2677c478bd9Sstevel@tonic-gate   if( pLock->nRef==0 ){
2687c478bd9Sstevel@tonic-gate     sqliteHashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0);
2697c478bd9Sstevel@tonic-gate     sqliteFree(pLock);
2707c478bd9Sstevel@tonic-gate   }
2717c478bd9Sstevel@tonic-gate }
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate /*
2747c478bd9Sstevel@tonic-gate ** Release a openCnt structure previously allocated by findLockInfo().
2757c478bd9Sstevel@tonic-gate */
releaseOpenCnt(struct openCnt * pOpen)2767c478bd9Sstevel@tonic-gate static void releaseOpenCnt(struct openCnt *pOpen){
2777c478bd9Sstevel@tonic-gate   pOpen->nRef--;
2787c478bd9Sstevel@tonic-gate   if( pOpen->nRef==0 ){
2797c478bd9Sstevel@tonic-gate     sqliteHashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0);
2807c478bd9Sstevel@tonic-gate     sqliteFree(pOpen->aPending);
2817c478bd9Sstevel@tonic-gate     sqliteFree(pOpen);
2827c478bd9Sstevel@tonic-gate   }
2837c478bd9Sstevel@tonic-gate }
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate /*
2867c478bd9Sstevel@tonic-gate ** Given a file descriptor, locate lockInfo and openCnt structures that
2877c478bd9Sstevel@tonic-gate ** describes that file descriptor.  Create a new ones if necessary.  The
2887c478bd9Sstevel@tonic-gate ** return values might be unset if an error occurs.
2897c478bd9Sstevel@tonic-gate **
2907c478bd9Sstevel@tonic-gate ** Return the number of errors.
2917c478bd9Sstevel@tonic-gate */
findLockInfo(int fd,struct lockInfo ** ppLock,struct openCnt ** ppOpen)2927c478bd9Sstevel@tonic-gate int findLockInfo(
2937c478bd9Sstevel@tonic-gate   int fd,                      /* The file descriptor used in the key */
2947c478bd9Sstevel@tonic-gate   struct lockInfo **ppLock,    /* Return the lockInfo structure here */
2957c478bd9Sstevel@tonic-gate   struct openCnt **ppOpen   /* Return the openCnt structure here */
2967c478bd9Sstevel@tonic-gate ){
2977c478bd9Sstevel@tonic-gate   int rc;
2987c478bd9Sstevel@tonic-gate   struct lockKey key1;
2997c478bd9Sstevel@tonic-gate   struct openKey key2;
3007c478bd9Sstevel@tonic-gate   struct stat statbuf;
3017c478bd9Sstevel@tonic-gate   struct lockInfo *pLock;
3027c478bd9Sstevel@tonic-gate   struct openCnt *pOpen;
3037c478bd9Sstevel@tonic-gate   rc = fstat(fd, &statbuf);
3047c478bd9Sstevel@tonic-gate   if( rc!=0 ) return 1;
3057c478bd9Sstevel@tonic-gate   memset(&key1, 0, sizeof(key1));
3067c478bd9Sstevel@tonic-gate   key1.dev = statbuf.st_dev;
3077c478bd9Sstevel@tonic-gate   key1.ino = statbuf.st_ino;
3087c478bd9Sstevel@tonic-gate   key1.pid = getpid();
3097c478bd9Sstevel@tonic-gate   memset(&key2, 0, sizeof(key2));
3107c478bd9Sstevel@tonic-gate   key2.dev = statbuf.st_dev;
3117c478bd9Sstevel@tonic-gate   key2.ino = statbuf.st_ino;
3127c478bd9Sstevel@tonic-gate   pLock = (struct lockInfo*)sqliteHashFind(&lockHash, &key1, sizeof(key1));
3137c478bd9Sstevel@tonic-gate   if( pLock==0 ){
3147c478bd9Sstevel@tonic-gate     struct lockInfo *pOld;
3157c478bd9Sstevel@tonic-gate     pLock = sqliteMallocRaw( sizeof(*pLock) );
3167c478bd9Sstevel@tonic-gate     if( pLock==0 ) return 1;
3177c478bd9Sstevel@tonic-gate     pLock->key = key1;
3187c478bd9Sstevel@tonic-gate     pLock->nRef = 1;
3197c478bd9Sstevel@tonic-gate     pLock->cnt = 0;
3207c478bd9Sstevel@tonic-gate     pOld = sqliteHashInsert(&lockHash, &pLock->key, sizeof(key1), pLock);
3217c478bd9Sstevel@tonic-gate     if( pOld!=0 ){
3227c478bd9Sstevel@tonic-gate       assert( pOld==pLock );
3237c478bd9Sstevel@tonic-gate       sqliteFree(pLock);
3247c478bd9Sstevel@tonic-gate       return 1;
3257c478bd9Sstevel@tonic-gate     }
3267c478bd9Sstevel@tonic-gate   }else{
3277c478bd9Sstevel@tonic-gate     pLock->nRef++;
3287c478bd9Sstevel@tonic-gate   }
3297c478bd9Sstevel@tonic-gate   *ppLock = pLock;
3307c478bd9Sstevel@tonic-gate   pOpen = (struct openCnt*)sqliteHashFind(&openHash, &key2, sizeof(key2));
3317c478bd9Sstevel@tonic-gate   if( pOpen==0 ){
3327c478bd9Sstevel@tonic-gate     struct openCnt *pOld;
3337c478bd9Sstevel@tonic-gate     pOpen = sqliteMallocRaw( sizeof(*pOpen) );
3347c478bd9Sstevel@tonic-gate     if( pOpen==0 ){
3357c478bd9Sstevel@tonic-gate       releaseLockInfo(pLock);
3367c478bd9Sstevel@tonic-gate       return 1;
3377c478bd9Sstevel@tonic-gate     }
3387c478bd9Sstevel@tonic-gate     pOpen->key = key2;
3397c478bd9Sstevel@tonic-gate     pOpen->nRef = 1;
3407c478bd9Sstevel@tonic-gate     pOpen->nLock = 0;
3417c478bd9Sstevel@tonic-gate     pOpen->nPending = 0;
3427c478bd9Sstevel@tonic-gate     pOpen->aPending = 0;
3437c478bd9Sstevel@tonic-gate     pOld = sqliteHashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen);
3447c478bd9Sstevel@tonic-gate     if( pOld!=0 ){
3457c478bd9Sstevel@tonic-gate       assert( pOld==pOpen );
3467c478bd9Sstevel@tonic-gate       sqliteFree(pOpen);
3477c478bd9Sstevel@tonic-gate       releaseLockInfo(pLock);
3487c478bd9Sstevel@tonic-gate       return 1;
3497c478bd9Sstevel@tonic-gate     }
3507c478bd9Sstevel@tonic-gate   }else{
3517c478bd9Sstevel@tonic-gate     pOpen->nRef++;
3527c478bd9Sstevel@tonic-gate   }
3537c478bd9Sstevel@tonic-gate   *ppOpen = pOpen;
3547c478bd9Sstevel@tonic-gate   return 0;
3557c478bd9Sstevel@tonic-gate }
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate #endif  /** POSIX advisory lock work-around **/
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate /*
3607c478bd9Sstevel@tonic-gate ** If we compile with the SQLITE_TEST macro set, then the following block
3617c478bd9Sstevel@tonic-gate ** of code will give us the ability to simulate a disk I/O error.  This
3627c478bd9Sstevel@tonic-gate ** is used for testing the I/O recovery logic.
3637c478bd9Sstevel@tonic-gate */
3647c478bd9Sstevel@tonic-gate #ifdef SQLITE_TEST
3657c478bd9Sstevel@tonic-gate int sqlite_io_error_pending = 0;
3667c478bd9Sstevel@tonic-gate #define SimulateIOError(A)  \
3677c478bd9Sstevel@tonic-gate    if( sqlite_io_error_pending ) \
3687c478bd9Sstevel@tonic-gate      if( sqlite_io_error_pending-- == 1 ){ local_ioerr(); return A; }
local_ioerr()3697c478bd9Sstevel@tonic-gate static void local_ioerr(){
3707c478bd9Sstevel@tonic-gate   sqlite_io_error_pending = 0;  /* Really just a place to set a breakpoint */
3717c478bd9Sstevel@tonic-gate }
3727c478bd9Sstevel@tonic-gate #else
3737c478bd9Sstevel@tonic-gate #define SimulateIOError(A)
3747c478bd9Sstevel@tonic-gate #endif
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate /*
3777c478bd9Sstevel@tonic-gate ** When testing, keep a count of the number of open files.
3787c478bd9Sstevel@tonic-gate */
3797c478bd9Sstevel@tonic-gate #ifdef SQLITE_TEST
3807c478bd9Sstevel@tonic-gate int sqlite_open_file_count = 0;
3817c478bd9Sstevel@tonic-gate #define OpenCounter(X)  sqlite_open_file_count+=(X)
3827c478bd9Sstevel@tonic-gate #else
3837c478bd9Sstevel@tonic-gate #define OpenCounter(X)
3847c478bd9Sstevel@tonic-gate #endif
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate /*
3887c478bd9Sstevel@tonic-gate ** Delete the named file
3897c478bd9Sstevel@tonic-gate */
sqliteOsDelete(const char * zFilename)3907c478bd9Sstevel@tonic-gate int sqliteOsDelete(const char *zFilename){
3917c478bd9Sstevel@tonic-gate #if OS_UNIX
3927c478bd9Sstevel@tonic-gate   unlink(zFilename);
3937c478bd9Sstevel@tonic-gate #endif
3947c478bd9Sstevel@tonic-gate #if OS_WIN
3957c478bd9Sstevel@tonic-gate   DeleteFile(zFilename);
3967c478bd9Sstevel@tonic-gate #endif
3977c478bd9Sstevel@tonic-gate #if OS_MAC
3987c478bd9Sstevel@tonic-gate   unlink(zFilename);
3997c478bd9Sstevel@tonic-gate #endif
4007c478bd9Sstevel@tonic-gate   return SQLITE_OK;
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate /*
4047c478bd9Sstevel@tonic-gate ** Return TRUE if the named file exists.
4057c478bd9Sstevel@tonic-gate */
sqliteOsFileExists(const char * zFilename)4067c478bd9Sstevel@tonic-gate int sqliteOsFileExists(const char *zFilename){
4077c478bd9Sstevel@tonic-gate #if OS_UNIX
4087c478bd9Sstevel@tonic-gate   return access(zFilename, 0)==0;
4097c478bd9Sstevel@tonic-gate #endif
4107c478bd9Sstevel@tonic-gate #if OS_WIN
4117c478bd9Sstevel@tonic-gate   return GetFileAttributes(zFilename) != 0xffffffff;
4127c478bd9Sstevel@tonic-gate #endif
4137c478bd9Sstevel@tonic-gate #if OS_MAC
4147c478bd9Sstevel@tonic-gate   return access(zFilename, 0)==0;
4157c478bd9Sstevel@tonic-gate #endif
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate #if 0 /* NOT USED */
4207c478bd9Sstevel@tonic-gate /*
4217c478bd9Sstevel@tonic-gate ** Change the name of an existing file.
4227c478bd9Sstevel@tonic-gate */
4237c478bd9Sstevel@tonic-gate int sqliteOsFileRename(const char *zOldName, const char *zNewName){
4247c478bd9Sstevel@tonic-gate #if OS_UNIX
4257c478bd9Sstevel@tonic-gate   if( link(zOldName, zNewName) ){
4267c478bd9Sstevel@tonic-gate     return SQLITE_ERROR;
4277c478bd9Sstevel@tonic-gate   }
4287c478bd9Sstevel@tonic-gate   unlink(zOldName);
4297c478bd9Sstevel@tonic-gate   return SQLITE_OK;
4307c478bd9Sstevel@tonic-gate #endif
4317c478bd9Sstevel@tonic-gate #if OS_WIN
4327c478bd9Sstevel@tonic-gate   if( !MoveFile(zOldName, zNewName) ){
4337c478bd9Sstevel@tonic-gate     return SQLITE_ERROR;
4347c478bd9Sstevel@tonic-gate   }
4357c478bd9Sstevel@tonic-gate   return SQLITE_OK;
4367c478bd9Sstevel@tonic-gate #endif
4377c478bd9Sstevel@tonic-gate #if OS_MAC
4387c478bd9Sstevel@tonic-gate   /**** FIX ME ***/
4397c478bd9Sstevel@tonic-gate   return SQLITE_ERROR;
4407c478bd9Sstevel@tonic-gate #endif
4417c478bd9Sstevel@tonic-gate }
4427c478bd9Sstevel@tonic-gate #endif /* NOT USED */
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate /*
4457c478bd9Sstevel@tonic-gate ** Attempt to open a file for both reading and writing.  If that
4467c478bd9Sstevel@tonic-gate ** fails, try opening it read-only.  If the file does not exist,
4477c478bd9Sstevel@tonic-gate ** try to create it.
4487c478bd9Sstevel@tonic-gate **
4497c478bd9Sstevel@tonic-gate ** On success, a handle for the open file is written to *id
4507c478bd9Sstevel@tonic-gate ** and *pReadonly is set to 0 if the file was opened for reading and
4517c478bd9Sstevel@tonic-gate ** writing or 1 if the file was opened read-only.  The function returns
4527c478bd9Sstevel@tonic-gate ** SQLITE_OK.
4537c478bd9Sstevel@tonic-gate **
4547c478bd9Sstevel@tonic-gate ** On failure, the function returns SQLITE_CANTOPEN and leaves
4557c478bd9Sstevel@tonic-gate ** *id and *pReadonly unchanged.
4567c478bd9Sstevel@tonic-gate */
sqliteOsOpenReadWrite(const char * zFilename,OsFile * id,int * pReadonly)4577c478bd9Sstevel@tonic-gate int sqliteOsOpenReadWrite(
4587c478bd9Sstevel@tonic-gate   const char *zFilename,
4597c478bd9Sstevel@tonic-gate   OsFile *id,
4607c478bd9Sstevel@tonic-gate   int *pReadonly
4617c478bd9Sstevel@tonic-gate ){
4627c478bd9Sstevel@tonic-gate #if OS_UNIX
4637c478bd9Sstevel@tonic-gate   int rc;
4647c478bd9Sstevel@tonic-gate   id->dirfd = -1;
4657c478bd9Sstevel@tonic-gate   id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644);
4667c478bd9Sstevel@tonic-gate   if( id->fd<0 ){
4677c478bd9Sstevel@tonic-gate #ifdef EISDIR
4687c478bd9Sstevel@tonic-gate     if( errno==EISDIR ){
4697c478bd9Sstevel@tonic-gate       return SQLITE_CANTOPEN;
4707c478bd9Sstevel@tonic-gate     }
4717c478bd9Sstevel@tonic-gate #endif
4727c478bd9Sstevel@tonic-gate     id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
4737c478bd9Sstevel@tonic-gate     if( id->fd<0 ){
474*1da57d55SToomas Soome       return SQLITE_CANTOPEN;
4757c478bd9Sstevel@tonic-gate     }
4767c478bd9Sstevel@tonic-gate     *pReadonly = 1;
4777c478bd9Sstevel@tonic-gate   }else{
4787c478bd9Sstevel@tonic-gate     *pReadonly = 0;
4797c478bd9Sstevel@tonic-gate   }
4807c478bd9Sstevel@tonic-gate   sqliteOsEnterMutex();
4817c478bd9Sstevel@tonic-gate   rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
4827c478bd9Sstevel@tonic-gate   sqliteOsLeaveMutex();
4837c478bd9Sstevel@tonic-gate   if( rc ){
4847c478bd9Sstevel@tonic-gate     close(id->fd);
4857c478bd9Sstevel@tonic-gate     return SQLITE_NOMEM;
4867c478bd9Sstevel@tonic-gate   }
4877c478bd9Sstevel@tonic-gate   id->locked = 0;
4887c478bd9Sstevel@tonic-gate   TRACE3("OPEN    %-3d %s\n", id->fd, zFilename);
4897c478bd9Sstevel@tonic-gate   OpenCounter(+1);
4907c478bd9Sstevel@tonic-gate   return SQLITE_OK;
4917c478bd9Sstevel@tonic-gate #endif
4927c478bd9Sstevel@tonic-gate #if OS_WIN
4937c478bd9Sstevel@tonic-gate   HANDLE h = CreateFile(zFilename,
4947c478bd9Sstevel@tonic-gate      GENERIC_READ | GENERIC_WRITE,
4957c478bd9Sstevel@tonic-gate      FILE_SHARE_READ | FILE_SHARE_WRITE,
4967c478bd9Sstevel@tonic-gate      NULL,
4977c478bd9Sstevel@tonic-gate      OPEN_ALWAYS,
4987c478bd9Sstevel@tonic-gate      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
4997c478bd9Sstevel@tonic-gate      NULL
5007c478bd9Sstevel@tonic-gate   );
5017c478bd9Sstevel@tonic-gate   if( h==INVALID_HANDLE_VALUE ){
5027c478bd9Sstevel@tonic-gate     h = CreateFile(zFilename,
5037c478bd9Sstevel@tonic-gate        GENERIC_READ,
5047c478bd9Sstevel@tonic-gate        FILE_SHARE_READ,
5057c478bd9Sstevel@tonic-gate        NULL,
5067c478bd9Sstevel@tonic-gate        OPEN_ALWAYS,
5077c478bd9Sstevel@tonic-gate        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5087c478bd9Sstevel@tonic-gate        NULL
5097c478bd9Sstevel@tonic-gate     );
5107c478bd9Sstevel@tonic-gate     if( h==INVALID_HANDLE_VALUE ){
5117c478bd9Sstevel@tonic-gate       return SQLITE_CANTOPEN;
5127c478bd9Sstevel@tonic-gate     }
5137c478bd9Sstevel@tonic-gate     *pReadonly = 1;
5147c478bd9Sstevel@tonic-gate   }else{
5157c478bd9Sstevel@tonic-gate     *pReadonly = 0;
5167c478bd9Sstevel@tonic-gate   }
5177c478bd9Sstevel@tonic-gate   id->h = h;
5187c478bd9Sstevel@tonic-gate   id->locked = 0;
5197c478bd9Sstevel@tonic-gate   OpenCounter(+1);
5207c478bd9Sstevel@tonic-gate   return SQLITE_OK;
5217c478bd9Sstevel@tonic-gate #endif
5227c478bd9Sstevel@tonic-gate #if OS_MAC
5237c478bd9Sstevel@tonic-gate   FSSpec fsSpec;
5247c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
5257c478bd9Sstevel@tonic-gate   HFSUniStr255 dfName;
5267c478bd9Sstevel@tonic-gate   FSRef fsRef;
5277c478bd9Sstevel@tonic-gate   if( __path2fss(zFilename, &fsSpec) != noErr ){
5287c478bd9Sstevel@tonic-gate     if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
5297c478bd9Sstevel@tonic-gate       return SQLITE_CANTOPEN;
5307c478bd9Sstevel@tonic-gate   }
5317c478bd9Sstevel@tonic-gate   if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr )
5327c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
5337c478bd9Sstevel@tonic-gate   FSGetDataForkName(&dfName);
5347c478bd9Sstevel@tonic-gate   if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
5357c478bd9Sstevel@tonic-gate                  fsRdWrShPerm, &(id->refNum)) != noErr ){
5367c478bd9Sstevel@tonic-gate     if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
5377c478bd9Sstevel@tonic-gate                    fsRdWrPerm, &(id->refNum)) != noErr ){
5387c478bd9Sstevel@tonic-gate       if (FSOpenFork(&fsRef, dfName.length, dfName.unicode,
5397c478bd9Sstevel@tonic-gate                    fsRdPerm, &(id->refNum)) != noErr )
5407c478bd9Sstevel@tonic-gate         return SQLITE_CANTOPEN;
5417c478bd9Sstevel@tonic-gate       else
5427c478bd9Sstevel@tonic-gate         *pReadonly = 1;
5437c478bd9Sstevel@tonic-gate     } else
5447c478bd9Sstevel@tonic-gate       *pReadonly = 0;
5457c478bd9Sstevel@tonic-gate   } else
5467c478bd9Sstevel@tonic-gate     *pReadonly = 0;
5477c478bd9Sstevel@tonic-gate # else
5487c478bd9Sstevel@tonic-gate   __path2fss(zFilename, &fsSpec);
5497c478bd9Sstevel@tonic-gate   if( !sqliteOsFileExists(zFilename) ){
5507c478bd9Sstevel@tonic-gate     if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
5517c478bd9Sstevel@tonic-gate       return SQLITE_CANTOPEN;
5527c478bd9Sstevel@tonic-gate   }
5537c478bd9Sstevel@tonic-gate   if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNum)) != noErr ){
5547c478bd9Sstevel@tonic-gate     if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ){
5557c478bd9Sstevel@tonic-gate       if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr )
5567c478bd9Sstevel@tonic-gate         return SQLITE_CANTOPEN;
5577c478bd9Sstevel@tonic-gate       else
5587c478bd9Sstevel@tonic-gate         *pReadonly = 1;
5597c478bd9Sstevel@tonic-gate     } else
5607c478bd9Sstevel@tonic-gate       *pReadonly = 0;
5617c478bd9Sstevel@tonic-gate   } else
5627c478bd9Sstevel@tonic-gate     *pReadonly = 0;
5637c478bd9Sstevel@tonic-gate # endif
5647c478bd9Sstevel@tonic-gate   if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){
5657c478bd9Sstevel@tonic-gate     id->refNumRF = -1;
5667c478bd9Sstevel@tonic-gate   }
5677c478bd9Sstevel@tonic-gate   id->locked = 0;
5687c478bd9Sstevel@tonic-gate   id->delOnClose = 0;
5697c478bd9Sstevel@tonic-gate   OpenCounter(+1);
5707c478bd9Sstevel@tonic-gate   return SQLITE_OK;
5717c478bd9Sstevel@tonic-gate #endif
5727c478bd9Sstevel@tonic-gate }
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate /*
5767c478bd9Sstevel@tonic-gate ** Attempt to open a new file for exclusive access by this process.
5777c478bd9Sstevel@tonic-gate ** The file will be opened for both reading and writing.  To avoid
5787c478bd9Sstevel@tonic-gate ** a potential security problem, we do not allow the file to have
5797c478bd9Sstevel@tonic-gate ** previously existed.  Nor do we allow the file to be a symbolic
5807c478bd9Sstevel@tonic-gate ** link.
5817c478bd9Sstevel@tonic-gate **
5827c478bd9Sstevel@tonic-gate ** If delFlag is true, then make arrangements to automatically delete
5837c478bd9Sstevel@tonic-gate ** the file when it is closed.
5847c478bd9Sstevel@tonic-gate **
5857c478bd9Sstevel@tonic-gate ** On success, write the file handle into *id and return SQLITE_OK.
5867c478bd9Sstevel@tonic-gate **
5877c478bd9Sstevel@tonic-gate ** On failure, return SQLITE_CANTOPEN.
5887c478bd9Sstevel@tonic-gate */
sqliteOsOpenExclusive(const char * zFilename,OsFile * id,int delFlag)5897c478bd9Sstevel@tonic-gate int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
5907c478bd9Sstevel@tonic-gate #if OS_UNIX
5917c478bd9Sstevel@tonic-gate   int rc;
5927c478bd9Sstevel@tonic-gate   if( access(zFilename, 0)==0 ){
5937c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
5947c478bd9Sstevel@tonic-gate   }
5957c478bd9Sstevel@tonic-gate   id->dirfd = -1;
5967c478bd9Sstevel@tonic-gate   id->fd = open(zFilename,
5977c478bd9Sstevel@tonic-gate                 O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600);
5987c478bd9Sstevel@tonic-gate   if( id->fd<0 ){
5997c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6007c478bd9Sstevel@tonic-gate   }
6017c478bd9Sstevel@tonic-gate   sqliteOsEnterMutex();
6027c478bd9Sstevel@tonic-gate   rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
6037c478bd9Sstevel@tonic-gate   sqliteOsLeaveMutex();
6047c478bd9Sstevel@tonic-gate   if( rc ){
6057c478bd9Sstevel@tonic-gate     close(id->fd);
6067c478bd9Sstevel@tonic-gate     unlink(zFilename);
6077c478bd9Sstevel@tonic-gate     return SQLITE_NOMEM;
6087c478bd9Sstevel@tonic-gate   }
6097c478bd9Sstevel@tonic-gate   id->locked = 0;
6107c478bd9Sstevel@tonic-gate   if( delFlag ){
6117c478bd9Sstevel@tonic-gate     unlink(zFilename);
6127c478bd9Sstevel@tonic-gate   }
6137c478bd9Sstevel@tonic-gate   TRACE3("OPEN-EX %-3d %s\n", id->fd, zFilename);
6147c478bd9Sstevel@tonic-gate   OpenCounter(+1);
6157c478bd9Sstevel@tonic-gate   return SQLITE_OK;
6167c478bd9Sstevel@tonic-gate #endif
6177c478bd9Sstevel@tonic-gate #if OS_WIN
6187c478bd9Sstevel@tonic-gate   HANDLE h;
6197c478bd9Sstevel@tonic-gate   int fileflags;
6207c478bd9Sstevel@tonic-gate   if( delFlag ){
621*1da57d55SToomas Soome     fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS
6227c478bd9Sstevel@tonic-gate                      | FILE_FLAG_DELETE_ON_CLOSE;
6237c478bd9Sstevel@tonic-gate   }else{
6247c478bd9Sstevel@tonic-gate     fileflags = FILE_FLAG_RANDOM_ACCESS;
6257c478bd9Sstevel@tonic-gate   }
6267c478bd9Sstevel@tonic-gate   h = CreateFile(zFilename,
6277c478bd9Sstevel@tonic-gate      GENERIC_READ | GENERIC_WRITE,
6287c478bd9Sstevel@tonic-gate      0,
6297c478bd9Sstevel@tonic-gate      NULL,
6307c478bd9Sstevel@tonic-gate      CREATE_ALWAYS,
6317c478bd9Sstevel@tonic-gate      fileflags,
6327c478bd9Sstevel@tonic-gate      NULL
6337c478bd9Sstevel@tonic-gate   );
6347c478bd9Sstevel@tonic-gate   if( h==INVALID_HANDLE_VALUE ){
6357c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6367c478bd9Sstevel@tonic-gate   }
6377c478bd9Sstevel@tonic-gate   id->h = h;
6387c478bd9Sstevel@tonic-gate   id->locked = 0;
6397c478bd9Sstevel@tonic-gate   OpenCounter(+1);
6407c478bd9Sstevel@tonic-gate   return SQLITE_OK;
6417c478bd9Sstevel@tonic-gate #endif
6427c478bd9Sstevel@tonic-gate #if OS_MAC
6437c478bd9Sstevel@tonic-gate   FSSpec fsSpec;
6447c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
6457c478bd9Sstevel@tonic-gate   HFSUniStr255 dfName;
6467c478bd9Sstevel@tonic-gate   FSRef fsRef;
6477c478bd9Sstevel@tonic-gate   __path2fss(zFilename, &fsSpec);
6487c478bd9Sstevel@tonic-gate   if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
6497c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6507c478bd9Sstevel@tonic-gate   if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr )
6517c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6527c478bd9Sstevel@tonic-gate   FSGetDataForkName(&dfName);
6537c478bd9Sstevel@tonic-gate   if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
6547c478bd9Sstevel@tonic-gate                  fsRdWrPerm, &(id->refNum)) != noErr )
6557c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6567c478bd9Sstevel@tonic-gate # else
6577c478bd9Sstevel@tonic-gate   __path2fss(zFilename, &fsSpec);
6587c478bd9Sstevel@tonic-gate   if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
6597c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6607c478bd9Sstevel@tonic-gate   if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr )
6617c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6627c478bd9Sstevel@tonic-gate # endif
6637c478bd9Sstevel@tonic-gate   id->refNumRF = -1;
6647c478bd9Sstevel@tonic-gate   id->locked = 0;
6657c478bd9Sstevel@tonic-gate   id->delOnClose = delFlag;
6667c478bd9Sstevel@tonic-gate   if (delFlag)
6677c478bd9Sstevel@tonic-gate     id->pathToDel = sqliteOsFullPathname(zFilename);
6687c478bd9Sstevel@tonic-gate   OpenCounter(+1);
6697c478bd9Sstevel@tonic-gate   return SQLITE_OK;
6707c478bd9Sstevel@tonic-gate #endif
6717c478bd9Sstevel@tonic-gate }
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate /*
6747c478bd9Sstevel@tonic-gate ** Attempt to open a new file for read-only access.
6757c478bd9Sstevel@tonic-gate **
6767c478bd9Sstevel@tonic-gate ** On success, write the file handle into *id and return SQLITE_OK.
6777c478bd9Sstevel@tonic-gate **
6787c478bd9Sstevel@tonic-gate ** On failure, return SQLITE_CANTOPEN.
6797c478bd9Sstevel@tonic-gate */
sqliteOsOpenReadOnly(const char * zFilename,OsFile * id)6807c478bd9Sstevel@tonic-gate int sqliteOsOpenReadOnly(const char *zFilename, OsFile *id){
6817c478bd9Sstevel@tonic-gate #if OS_UNIX
6827c478bd9Sstevel@tonic-gate   int rc;
6837c478bd9Sstevel@tonic-gate   id->dirfd = -1;
6847c478bd9Sstevel@tonic-gate   id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
6857c478bd9Sstevel@tonic-gate   if( id->fd<0 ){
6867c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
6877c478bd9Sstevel@tonic-gate   }
6887c478bd9Sstevel@tonic-gate   sqliteOsEnterMutex();
6897c478bd9Sstevel@tonic-gate   rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
6907c478bd9Sstevel@tonic-gate   sqliteOsLeaveMutex();
6917c478bd9Sstevel@tonic-gate   if( rc ){
6927c478bd9Sstevel@tonic-gate     close(id->fd);
6937c478bd9Sstevel@tonic-gate     return SQLITE_NOMEM;
6947c478bd9Sstevel@tonic-gate   }
6957c478bd9Sstevel@tonic-gate   id->locked = 0;
6967c478bd9Sstevel@tonic-gate   TRACE3("OPEN-RO %-3d %s\n", id->fd, zFilename);
6977c478bd9Sstevel@tonic-gate   OpenCounter(+1);
6987c478bd9Sstevel@tonic-gate   return SQLITE_OK;
6997c478bd9Sstevel@tonic-gate #endif
7007c478bd9Sstevel@tonic-gate #if OS_WIN
7017c478bd9Sstevel@tonic-gate   HANDLE h = CreateFile(zFilename,
7027c478bd9Sstevel@tonic-gate      GENERIC_READ,
7037c478bd9Sstevel@tonic-gate      0,
7047c478bd9Sstevel@tonic-gate      NULL,
7057c478bd9Sstevel@tonic-gate      OPEN_EXISTING,
7067c478bd9Sstevel@tonic-gate      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7077c478bd9Sstevel@tonic-gate      NULL
7087c478bd9Sstevel@tonic-gate   );
7097c478bd9Sstevel@tonic-gate   if( h==INVALID_HANDLE_VALUE ){
7107c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
7117c478bd9Sstevel@tonic-gate   }
7127c478bd9Sstevel@tonic-gate   id->h = h;
7137c478bd9Sstevel@tonic-gate   id->locked = 0;
7147c478bd9Sstevel@tonic-gate   OpenCounter(+1);
7157c478bd9Sstevel@tonic-gate   return SQLITE_OK;
7167c478bd9Sstevel@tonic-gate #endif
7177c478bd9Sstevel@tonic-gate #if OS_MAC
7187c478bd9Sstevel@tonic-gate   FSSpec fsSpec;
7197c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
7207c478bd9Sstevel@tonic-gate   HFSUniStr255 dfName;
7217c478bd9Sstevel@tonic-gate   FSRef fsRef;
7227c478bd9Sstevel@tonic-gate   if( __path2fss(zFilename, &fsSpec) != noErr )
7237c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
7247c478bd9Sstevel@tonic-gate   if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr )
7257c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
7267c478bd9Sstevel@tonic-gate   FSGetDataForkName(&dfName);
7277c478bd9Sstevel@tonic-gate   if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
7287c478bd9Sstevel@tonic-gate                  fsRdPerm, &(id->refNum)) != noErr )
7297c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
7307c478bd9Sstevel@tonic-gate # else
7317c478bd9Sstevel@tonic-gate   __path2fss(zFilename, &fsSpec);
7327c478bd9Sstevel@tonic-gate   if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr )
7337c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
7347c478bd9Sstevel@tonic-gate # endif
7357c478bd9Sstevel@tonic-gate   if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){
7367c478bd9Sstevel@tonic-gate     id->refNumRF = -1;
7377c478bd9Sstevel@tonic-gate   }
7387c478bd9Sstevel@tonic-gate   id->locked = 0;
7397c478bd9Sstevel@tonic-gate   id->delOnClose = 0;
7407c478bd9Sstevel@tonic-gate   OpenCounter(+1);
7417c478bd9Sstevel@tonic-gate   return SQLITE_OK;
7427c478bd9Sstevel@tonic-gate #endif
7437c478bd9Sstevel@tonic-gate }
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate /*
7467c478bd9Sstevel@tonic-gate ** Attempt to open a file descriptor for the directory that contains a
7477c478bd9Sstevel@tonic-gate ** file.  This file descriptor can be used to fsync() the directory
7487c478bd9Sstevel@tonic-gate ** in order to make sure the creation of a new file is actually written
7497c478bd9Sstevel@tonic-gate ** to disk.
7507c478bd9Sstevel@tonic-gate **
7517c478bd9Sstevel@tonic-gate ** This routine is only meaningful for Unix.  It is a no-op under
7527c478bd9Sstevel@tonic-gate ** windows since windows does not support hard links.
7537c478bd9Sstevel@tonic-gate **
7547c478bd9Sstevel@tonic-gate ** On success, a handle for a previously open file is at *id is
7557c478bd9Sstevel@tonic-gate ** updated with the new directory file descriptor and SQLITE_OK is
7567c478bd9Sstevel@tonic-gate ** returned.
7577c478bd9Sstevel@tonic-gate **
7587c478bd9Sstevel@tonic-gate ** On failure, the function returns SQLITE_CANTOPEN and leaves
7597c478bd9Sstevel@tonic-gate ** *id unchanged.
7607c478bd9Sstevel@tonic-gate */
sqliteOsOpenDirectory(const char * zDirname,OsFile * id)7617c478bd9Sstevel@tonic-gate int sqliteOsOpenDirectory(
7627c478bd9Sstevel@tonic-gate   const char *zDirname,
7637c478bd9Sstevel@tonic-gate   OsFile *id
7647c478bd9Sstevel@tonic-gate ){
7657c478bd9Sstevel@tonic-gate #if OS_UNIX
7667c478bd9Sstevel@tonic-gate   if( id->fd<0 ){
7677c478bd9Sstevel@tonic-gate     /* Do not open the directory if the corresponding file is not already
7687c478bd9Sstevel@tonic-gate     ** open. */
7697c478bd9Sstevel@tonic-gate     return SQLITE_CANTOPEN;
7707c478bd9Sstevel@tonic-gate   }
7717c478bd9Sstevel@tonic-gate   assert( id->dirfd<0 );
7727c478bd9Sstevel@tonic-gate   id->dirfd = open(zDirname, O_RDONLY|O_BINARY, 0644);
7737c478bd9Sstevel@tonic-gate   if( id->dirfd<0 ){
774*1da57d55SToomas Soome     return SQLITE_CANTOPEN;
7757c478bd9Sstevel@tonic-gate   }
7767c478bd9Sstevel@tonic-gate   TRACE3("OPENDIR %-3d %s\n", id->dirfd, zDirname);
7777c478bd9Sstevel@tonic-gate #endif
7787c478bd9Sstevel@tonic-gate   return SQLITE_OK;
7797c478bd9Sstevel@tonic-gate }
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate /*
7827c478bd9Sstevel@tonic-gate ** If the following global variable points to a string which is the
7837c478bd9Sstevel@tonic-gate ** name of a directory, then that directory will be used to store
7847c478bd9Sstevel@tonic-gate ** temporary files.
7857c478bd9Sstevel@tonic-gate */
7867c478bd9Sstevel@tonic-gate const char *sqlite_temp_directory = 0;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate /*
7897c478bd9Sstevel@tonic-gate ** Create a temporary file name in zBuf.  zBuf must be big enough to
7907c478bd9Sstevel@tonic-gate ** hold at least SQLITE_TEMPNAME_SIZE characters.
7917c478bd9Sstevel@tonic-gate */
sqliteOsTempFileName(char * zBuf)7927c478bd9Sstevel@tonic-gate int sqliteOsTempFileName(char *zBuf){
7937c478bd9Sstevel@tonic-gate #if OS_UNIX
7947c478bd9Sstevel@tonic-gate   static const char *azDirs[] = {
7957c478bd9Sstevel@tonic-gate      0,
7967c478bd9Sstevel@tonic-gate      "/var/tmp",
7977c478bd9Sstevel@tonic-gate      "/usr/tmp",
7987c478bd9Sstevel@tonic-gate      "/tmp",
7997c478bd9Sstevel@tonic-gate      ".",
8007c478bd9Sstevel@tonic-gate   };
8017c478bd9Sstevel@tonic-gate   static unsigned char zChars[] =
8027c478bd9Sstevel@tonic-gate     "abcdefghijklmnopqrstuvwxyz"
8037c478bd9Sstevel@tonic-gate     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
8047c478bd9Sstevel@tonic-gate     "0123456789";
8057c478bd9Sstevel@tonic-gate   int i, j;
8067c478bd9Sstevel@tonic-gate   struct stat buf;
8077c478bd9Sstevel@tonic-gate   const char *zDir = ".";
8087c478bd9Sstevel@tonic-gate   azDirs[0] = sqlite_temp_directory;
8097c478bd9Sstevel@tonic-gate   for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
8107c478bd9Sstevel@tonic-gate     if( azDirs[i]==0 ) continue;
8117c478bd9Sstevel@tonic-gate     if( stat(azDirs[i], &buf) ) continue;
8127c478bd9Sstevel@tonic-gate     if( !S_ISDIR(buf.st_mode) ) continue;
8137c478bd9Sstevel@tonic-gate     if( access(azDirs[i], 07) ) continue;
8147c478bd9Sstevel@tonic-gate     zDir = azDirs[i];
8157c478bd9Sstevel@tonic-gate     break;
8167c478bd9Sstevel@tonic-gate   }
8177c478bd9Sstevel@tonic-gate   do{
8187c478bd9Sstevel@tonic-gate     sprintf(zBuf, "%s/"TEMP_FILE_PREFIX, zDir);
8197c478bd9Sstevel@tonic-gate     j = strlen(zBuf);
8207c478bd9Sstevel@tonic-gate     sqliteRandomness(15, &zBuf[j]);
8217c478bd9Sstevel@tonic-gate     for(i=0; i<15; i++, j++){
8227c478bd9Sstevel@tonic-gate       zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
8237c478bd9Sstevel@tonic-gate     }
8247c478bd9Sstevel@tonic-gate     zBuf[j] = 0;
8257c478bd9Sstevel@tonic-gate   }while( access(zBuf,0)==0 );
8267c478bd9Sstevel@tonic-gate #endif
8277c478bd9Sstevel@tonic-gate #if OS_WIN
8287c478bd9Sstevel@tonic-gate   static char zChars[] =
8297c478bd9Sstevel@tonic-gate     "abcdefghijklmnopqrstuvwxyz"
8307c478bd9Sstevel@tonic-gate     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
8317c478bd9Sstevel@tonic-gate     "0123456789";
8327c478bd9Sstevel@tonic-gate   int i, j;
8337c478bd9Sstevel@tonic-gate   const char *zDir;
8347c478bd9Sstevel@tonic-gate   char zTempPath[SQLITE_TEMPNAME_SIZE];
8357c478bd9Sstevel@tonic-gate   if( sqlite_temp_directory==0 ){
8367c478bd9Sstevel@tonic-gate     GetTempPath(SQLITE_TEMPNAME_SIZE-30, zTempPath);
8377c478bd9Sstevel@tonic-gate     for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
8387c478bd9Sstevel@tonic-gate     zTempPath[i] = 0;
8397c478bd9Sstevel@tonic-gate     zDir = zTempPath;
8407c478bd9Sstevel@tonic-gate   }else{
8417c478bd9Sstevel@tonic-gate     zDir = sqlite_temp_directory;
8427c478bd9Sstevel@tonic-gate   }
8437c478bd9Sstevel@tonic-gate   for(;;){
8447c478bd9Sstevel@tonic-gate     sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zDir);
8457c478bd9Sstevel@tonic-gate     j = strlen(zBuf);
8467c478bd9Sstevel@tonic-gate     sqliteRandomness(15, &zBuf[j]);
8477c478bd9Sstevel@tonic-gate     for(i=0; i<15; i++, j++){
8487c478bd9Sstevel@tonic-gate       zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
8497c478bd9Sstevel@tonic-gate     }
8507c478bd9Sstevel@tonic-gate     zBuf[j] = 0;
8517c478bd9Sstevel@tonic-gate     if( !sqliteOsFileExists(zBuf) ) break;
8527c478bd9Sstevel@tonic-gate   }
8537c478bd9Sstevel@tonic-gate #endif
8547c478bd9Sstevel@tonic-gate #if OS_MAC
8557c478bd9Sstevel@tonic-gate   static char zChars[] =
8567c478bd9Sstevel@tonic-gate     "abcdefghijklmnopqrstuvwxyz"
8577c478bd9Sstevel@tonic-gate     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
8587c478bd9Sstevel@tonic-gate     "0123456789";
8597c478bd9Sstevel@tonic-gate   int i, j;
8607c478bd9Sstevel@tonic-gate   char *zDir;
8617c478bd9Sstevel@tonic-gate   char zTempPath[SQLITE_TEMPNAME_SIZE];
8627c478bd9Sstevel@tonic-gate   char zdirName[32];
8637c478bd9Sstevel@tonic-gate   CInfoPBRec infoRec;
8647c478bd9Sstevel@tonic-gate   Str31 dirName;
8657c478bd9Sstevel@tonic-gate   memset(&infoRec, 0, sizeof(infoRec));
8667c478bd9Sstevel@tonic-gate   memset(zTempPath, 0, SQLITE_TEMPNAME_SIZE);
8677c478bd9Sstevel@tonic-gate   if( sqlite_temp_directory!=0 ){
8687c478bd9Sstevel@tonic-gate     zDir = sqlite_temp_directory;
8697c478bd9Sstevel@tonic-gate   }else if( FindFolder(kOnSystemDisk, kTemporaryFolderType,  kCreateFolder,
8707c478bd9Sstevel@tonic-gate        &(infoRec.dirInfo.ioVRefNum), &(infoRec.dirInfo.ioDrParID)) == noErr ){
8717c478bd9Sstevel@tonic-gate     infoRec.dirInfo.ioNamePtr = dirName;
8727c478bd9Sstevel@tonic-gate     do{
8737c478bd9Sstevel@tonic-gate       infoRec.dirInfo.ioFDirIndex = -1;
8747c478bd9Sstevel@tonic-gate       infoRec.dirInfo.ioDrDirID = infoRec.dirInfo.ioDrParID;
8757c478bd9Sstevel@tonic-gate       if( PBGetCatInfoSync(&infoRec) == noErr ){
8767c478bd9Sstevel@tonic-gate         CopyPascalStringToC(dirName, zdirName);
8777c478bd9Sstevel@tonic-gate         i = strlen(zdirName);
8787c478bd9Sstevel@tonic-gate         memmove(&(zTempPath[i+1]), zTempPath, strlen(zTempPath));
8797c478bd9Sstevel@tonic-gate         strcpy(zTempPath, zdirName);
8807c478bd9Sstevel@tonic-gate         zTempPath[i] = ':';
8817c478bd9Sstevel@tonic-gate       }else{
8827c478bd9Sstevel@tonic-gate         *zTempPath = 0;
8837c478bd9Sstevel@tonic-gate         break;
8847c478bd9Sstevel@tonic-gate       }
8857c478bd9Sstevel@tonic-gate     } while( infoRec.dirInfo.ioDrDirID != fsRtDirID );
8867c478bd9Sstevel@tonic-gate     zDir = zTempPath;
8877c478bd9Sstevel@tonic-gate   }
8887c478bd9Sstevel@tonic-gate   if( zDir[0]==0 ){
8897c478bd9Sstevel@tonic-gate     getcwd(zTempPath, SQLITE_TEMPNAME_SIZE-24);
8907c478bd9Sstevel@tonic-gate     zDir = zTempPath;
8917c478bd9Sstevel@tonic-gate   }
8927c478bd9Sstevel@tonic-gate   for(;;){
8937c478bd9Sstevel@tonic-gate     sprintf(zBuf, "%s"TEMP_FILE_PREFIX, zDir);
8947c478bd9Sstevel@tonic-gate     j = strlen(zBuf);
8957c478bd9Sstevel@tonic-gate     sqliteRandomness(15, &zBuf[j]);
8967c478bd9Sstevel@tonic-gate     for(i=0; i<15; i++, j++){
8977c478bd9Sstevel@tonic-gate       zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
8987c478bd9Sstevel@tonic-gate     }
8997c478bd9Sstevel@tonic-gate     zBuf[j] = 0;
9007c478bd9Sstevel@tonic-gate     if( !sqliteOsFileExists(zBuf) ) break;
9017c478bd9Sstevel@tonic-gate   }
9027c478bd9Sstevel@tonic-gate #endif
903*1da57d55SToomas Soome   return SQLITE_OK;
9047c478bd9Sstevel@tonic-gate }
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate /*
9077c478bd9Sstevel@tonic-gate ** Close a file.
9087c478bd9Sstevel@tonic-gate */
sqliteOsClose(OsFile * id)9097c478bd9Sstevel@tonic-gate int sqliteOsClose(OsFile *id){
9107c478bd9Sstevel@tonic-gate #if OS_UNIX
9117c478bd9Sstevel@tonic-gate   sqliteOsUnlock(id);
9127c478bd9Sstevel@tonic-gate   if( id->dirfd>=0 ) close(id->dirfd);
9137c478bd9Sstevel@tonic-gate   id->dirfd = -1;
9147c478bd9Sstevel@tonic-gate   sqliteOsEnterMutex();
9157c478bd9Sstevel@tonic-gate   if( id->pOpen->nLock ){
9167c478bd9Sstevel@tonic-gate     /* If there are outstanding locks, do not actually close the file just
9177c478bd9Sstevel@tonic-gate     ** yet because that would clear those locks.  Instead, add the file
9187c478bd9Sstevel@tonic-gate     ** descriptor to pOpen->aPending.  It will be automatically closed when
9197c478bd9Sstevel@tonic-gate     ** the last lock is cleared.
9207c478bd9Sstevel@tonic-gate     */
9217c478bd9Sstevel@tonic-gate     int *aNew;
9227c478bd9Sstevel@tonic-gate     struct openCnt *pOpen = id->pOpen;
9237c478bd9Sstevel@tonic-gate     pOpen->nPending++;
9247c478bd9Sstevel@tonic-gate     aNew = sqliteRealloc( pOpen->aPending, pOpen->nPending*sizeof(int) );
9257c478bd9Sstevel@tonic-gate     if( aNew==0 ){
9267c478bd9Sstevel@tonic-gate       /* If a malloc fails, just leak the file descriptor */
9277c478bd9Sstevel@tonic-gate     }else{
9287c478bd9Sstevel@tonic-gate       pOpen->aPending = aNew;
9297c478bd9Sstevel@tonic-gate       pOpen->aPending[pOpen->nPending-1] = id->fd;
9307c478bd9Sstevel@tonic-gate     }
9317c478bd9Sstevel@tonic-gate   }else{
9327c478bd9Sstevel@tonic-gate     /* There are no outstanding locks so we can close the file immediately */
9337c478bd9Sstevel@tonic-gate     close(id->fd);
9347c478bd9Sstevel@tonic-gate   }
9357c478bd9Sstevel@tonic-gate   releaseLockInfo(id->pLock);
9367c478bd9Sstevel@tonic-gate   releaseOpenCnt(id->pOpen);
9377c478bd9Sstevel@tonic-gate   sqliteOsLeaveMutex();
9387c478bd9Sstevel@tonic-gate   TRACE2("CLOSE   %-3d\n", id->fd);
9397c478bd9Sstevel@tonic-gate   OpenCounter(-1);
9407c478bd9Sstevel@tonic-gate   return SQLITE_OK;
9417c478bd9Sstevel@tonic-gate #endif
9427c478bd9Sstevel@tonic-gate #if OS_WIN
9437c478bd9Sstevel@tonic-gate   CloseHandle(id->h);
9447c478bd9Sstevel@tonic-gate   OpenCounter(-1);
9457c478bd9Sstevel@tonic-gate   return SQLITE_OK;
9467c478bd9Sstevel@tonic-gate #endif
9477c478bd9Sstevel@tonic-gate #if OS_MAC
9487c478bd9Sstevel@tonic-gate   if( id->refNumRF!=-1 )
9497c478bd9Sstevel@tonic-gate     FSClose(id->refNumRF);
9507c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
9517c478bd9Sstevel@tonic-gate   FSCloseFork(id->refNum);
9527c478bd9Sstevel@tonic-gate # else
9537c478bd9Sstevel@tonic-gate   FSClose(id->refNum);
9547c478bd9Sstevel@tonic-gate # endif
9557c478bd9Sstevel@tonic-gate   if( id->delOnClose ){
9567c478bd9Sstevel@tonic-gate     unlink(id->pathToDel);
9577c478bd9Sstevel@tonic-gate     sqliteFree(id->pathToDel);
9587c478bd9Sstevel@tonic-gate   }
9597c478bd9Sstevel@tonic-gate   OpenCounter(-1);
9607c478bd9Sstevel@tonic-gate   return SQLITE_OK;
9617c478bd9Sstevel@tonic-gate #endif
9627c478bd9Sstevel@tonic-gate }
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate /*
9657c478bd9Sstevel@tonic-gate ** Read data from a file into a buffer.  Return SQLITE_OK if all
9667c478bd9Sstevel@tonic-gate ** bytes were read successfully and SQLITE_IOERR if anything goes
9677c478bd9Sstevel@tonic-gate ** wrong.
9687c478bd9Sstevel@tonic-gate */
sqliteOsRead(OsFile * id,void * pBuf,int amt)9697c478bd9Sstevel@tonic-gate int sqliteOsRead(OsFile *id, void *pBuf, int amt){
9707c478bd9Sstevel@tonic-gate #if OS_UNIX
9717c478bd9Sstevel@tonic-gate   int got;
9727c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
9737c478bd9Sstevel@tonic-gate   TIMER_START;
9747c478bd9Sstevel@tonic-gate   got = read(id->fd, pBuf, amt);
9757c478bd9Sstevel@tonic-gate   TIMER_END;
9767c478bd9Sstevel@tonic-gate   TRACE4("READ    %-3d %7d %d\n", id->fd, last_page, elapse);
9777c478bd9Sstevel@tonic-gate   SEEK(0);
9787c478bd9Sstevel@tonic-gate   /* if( got<0 ) got = 0; */
9797c478bd9Sstevel@tonic-gate   if( got==amt ){
9807c478bd9Sstevel@tonic-gate     return SQLITE_OK;
9817c478bd9Sstevel@tonic-gate   }else{
9827c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
9837c478bd9Sstevel@tonic-gate   }
9847c478bd9Sstevel@tonic-gate #endif
9857c478bd9Sstevel@tonic-gate #if OS_WIN
9867c478bd9Sstevel@tonic-gate   DWORD got;
9877c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
9887c478bd9Sstevel@tonic-gate   TRACE2("READ %d\n", last_page);
9897c478bd9Sstevel@tonic-gate   if( !ReadFile(id->h, pBuf, amt, &got, 0) ){
9907c478bd9Sstevel@tonic-gate     got = 0;
9917c478bd9Sstevel@tonic-gate   }
9927c478bd9Sstevel@tonic-gate   if( got==(DWORD)amt ){
9937c478bd9Sstevel@tonic-gate     return SQLITE_OK;
9947c478bd9Sstevel@tonic-gate   }else{
9957c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
9967c478bd9Sstevel@tonic-gate   }
9977c478bd9Sstevel@tonic-gate #endif
9987c478bd9Sstevel@tonic-gate #if OS_MAC
9997c478bd9Sstevel@tonic-gate   int got;
10007c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
10017c478bd9Sstevel@tonic-gate   TRACE2("READ %d\n", last_page);
10027c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
10037c478bd9Sstevel@tonic-gate   FSReadFork(id->refNum, fsAtMark, 0, (ByteCount)amt, pBuf, (ByteCount*)&got);
10047c478bd9Sstevel@tonic-gate # else
10057c478bd9Sstevel@tonic-gate   got = amt;
10067c478bd9Sstevel@tonic-gate   FSRead(id->refNum, &got, pBuf);
10077c478bd9Sstevel@tonic-gate # endif
10087c478bd9Sstevel@tonic-gate   if( got==amt ){
10097c478bd9Sstevel@tonic-gate     return SQLITE_OK;
10107c478bd9Sstevel@tonic-gate   }else{
10117c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
10127c478bd9Sstevel@tonic-gate   }
10137c478bd9Sstevel@tonic-gate #endif
10147c478bd9Sstevel@tonic-gate }
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate /*
10177c478bd9Sstevel@tonic-gate ** Write data from a buffer into a file.  Return SQLITE_OK on success
10187c478bd9Sstevel@tonic-gate ** or some other error code on failure.
10197c478bd9Sstevel@tonic-gate */
sqliteOsWrite(OsFile * id,const void * pBuf,int amt)10207c478bd9Sstevel@tonic-gate int sqliteOsWrite(OsFile *id, const void *pBuf, int amt){
10217c478bd9Sstevel@tonic-gate #if OS_UNIX
10227c478bd9Sstevel@tonic-gate   int wrote = 0;
10237c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
10247c478bd9Sstevel@tonic-gate   TIMER_START;
10257c478bd9Sstevel@tonic-gate   while( amt>0 && (wrote = write(id->fd, pBuf, amt))>0 ){
10267c478bd9Sstevel@tonic-gate     amt -= wrote;
10277c478bd9Sstevel@tonic-gate     pBuf = &((char*)pBuf)[wrote];
10287c478bd9Sstevel@tonic-gate   }
10297c478bd9Sstevel@tonic-gate   TIMER_END;
10307c478bd9Sstevel@tonic-gate   TRACE4("WRITE   %-3d %7d %d\n", id->fd, last_page, elapse);
10317c478bd9Sstevel@tonic-gate   SEEK(0);
10327c478bd9Sstevel@tonic-gate   if( amt>0 ){
10337c478bd9Sstevel@tonic-gate     return SQLITE_FULL;
10347c478bd9Sstevel@tonic-gate   }
10357c478bd9Sstevel@tonic-gate   return SQLITE_OK;
10367c478bd9Sstevel@tonic-gate #endif
10377c478bd9Sstevel@tonic-gate #if OS_WIN
10387c478bd9Sstevel@tonic-gate   int rc;
10397c478bd9Sstevel@tonic-gate   DWORD wrote;
10407c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
10417c478bd9Sstevel@tonic-gate   TRACE2("WRITE %d\n", last_page);
10427c478bd9Sstevel@tonic-gate   while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){
10437c478bd9Sstevel@tonic-gate     amt -= wrote;
10447c478bd9Sstevel@tonic-gate     pBuf = &((char*)pBuf)[wrote];
10457c478bd9Sstevel@tonic-gate   }
10467c478bd9Sstevel@tonic-gate   if( !rc || amt>(int)wrote ){
10477c478bd9Sstevel@tonic-gate     return SQLITE_FULL;
10487c478bd9Sstevel@tonic-gate   }
10497c478bd9Sstevel@tonic-gate   return SQLITE_OK;
10507c478bd9Sstevel@tonic-gate #endif
10517c478bd9Sstevel@tonic-gate #if OS_MAC
10527c478bd9Sstevel@tonic-gate   OSErr oserr;
10537c478bd9Sstevel@tonic-gate   int wrote = 0;
10547c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
10557c478bd9Sstevel@tonic-gate   TRACE2("WRITE %d\n", last_page);
10567c478bd9Sstevel@tonic-gate   while( amt>0 ){
10577c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
10587c478bd9Sstevel@tonic-gate     oserr = FSWriteFork(id->refNum, fsAtMark, 0,
10597c478bd9Sstevel@tonic-gate                         (ByteCount)amt, pBuf, (ByteCount*)&wrote);
10607c478bd9Sstevel@tonic-gate # else
10617c478bd9Sstevel@tonic-gate     wrote = amt;
10627c478bd9Sstevel@tonic-gate     oserr = FSWrite(id->refNum, &wrote, pBuf);
10637c478bd9Sstevel@tonic-gate # endif
10647c478bd9Sstevel@tonic-gate     if( wrote == 0 || oserr != noErr)
10657c478bd9Sstevel@tonic-gate       break;
10667c478bd9Sstevel@tonic-gate     amt -= wrote;
10677c478bd9Sstevel@tonic-gate     pBuf = &((char*)pBuf)[wrote];
10687c478bd9Sstevel@tonic-gate   }
10697c478bd9Sstevel@tonic-gate   if( oserr != noErr || amt>wrote ){
10707c478bd9Sstevel@tonic-gate     return SQLITE_FULL;
10717c478bd9Sstevel@tonic-gate   }
10727c478bd9Sstevel@tonic-gate   return SQLITE_OK;
10737c478bd9Sstevel@tonic-gate #endif
10747c478bd9Sstevel@tonic-gate }
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate /*
10777c478bd9Sstevel@tonic-gate ** Move the read/write pointer in a file.
10787c478bd9Sstevel@tonic-gate */
sqliteOsSeek(OsFile * id,off_t offset)10797c478bd9Sstevel@tonic-gate int sqliteOsSeek(OsFile *id, off_t offset){
10807c478bd9Sstevel@tonic-gate   SEEK(offset/1024 + 1);
10817c478bd9Sstevel@tonic-gate #if OS_UNIX
10827c478bd9Sstevel@tonic-gate   lseek(id->fd, offset, SEEK_SET);
10837c478bd9Sstevel@tonic-gate   return SQLITE_OK;
10847c478bd9Sstevel@tonic-gate #endif
10857c478bd9Sstevel@tonic-gate #if OS_WIN
10867c478bd9Sstevel@tonic-gate   {
10877c478bd9Sstevel@tonic-gate     LONG upperBits = offset>>32;
10887c478bd9Sstevel@tonic-gate     LONG lowerBits = offset & 0xffffffff;
10897c478bd9Sstevel@tonic-gate     DWORD rc;
10907c478bd9Sstevel@tonic-gate     rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN);
10917c478bd9Sstevel@tonic-gate     /* TRACE3("SEEK rc=0x%x upper=0x%x\n", rc, upperBits); */
10927c478bd9Sstevel@tonic-gate   }
10937c478bd9Sstevel@tonic-gate   return SQLITE_OK;
10947c478bd9Sstevel@tonic-gate #endif
10957c478bd9Sstevel@tonic-gate #if OS_MAC
10967c478bd9Sstevel@tonic-gate   {
10977c478bd9Sstevel@tonic-gate     off_t curSize;
10987c478bd9Sstevel@tonic-gate     if( sqliteOsFileSize(id, &curSize) != SQLITE_OK ){
10997c478bd9Sstevel@tonic-gate       return SQLITE_IOERR;
11007c478bd9Sstevel@tonic-gate     }
11017c478bd9Sstevel@tonic-gate     if( offset >= curSize ){
11027c478bd9Sstevel@tonic-gate       if( sqliteOsTruncate(id, offset+1) != SQLITE_OK ){
11037c478bd9Sstevel@tonic-gate         return SQLITE_IOERR;
11047c478bd9Sstevel@tonic-gate       }
11057c478bd9Sstevel@tonic-gate     }
11067c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
11077c478bd9Sstevel@tonic-gate     if( FSSetForkPosition(id->refNum, fsFromStart, offset) != noErr ){
11087c478bd9Sstevel@tonic-gate # else
11097c478bd9Sstevel@tonic-gate     if( SetFPos(id->refNum, fsFromStart, offset) != noErr ){
11107c478bd9Sstevel@tonic-gate # endif
11117c478bd9Sstevel@tonic-gate       return SQLITE_IOERR;
11127c478bd9Sstevel@tonic-gate     }else{
11137c478bd9Sstevel@tonic-gate       return SQLITE_OK;
11147c478bd9Sstevel@tonic-gate     }
11157c478bd9Sstevel@tonic-gate   }
11167c478bd9Sstevel@tonic-gate #endif
11177c478bd9Sstevel@tonic-gate }
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate /*
11207c478bd9Sstevel@tonic-gate ** Make sure all writes to a particular file are committed to disk.
11217c478bd9Sstevel@tonic-gate **
11227c478bd9Sstevel@tonic-gate ** Under Unix, also make sure that the directory entry for the file
11237c478bd9Sstevel@tonic-gate ** has been created by fsync-ing the directory that contains the file.
11247c478bd9Sstevel@tonic-gate ** If we do not do this and we encounter a power failure, the directory
11257c478bd9Sstevel@tonic-gate ** entry for the journal might not exist after we reboot.  The next
11267c478bd9Sstevel@tonic-gate ** SQLite to access the file will not know that the journal exists (because
11277c478bd9Sstevel@tonic-gate ** the directory entry for the journal was never created) and the transaction
11287c478bd9Sstevel@tonic-gate ** will not roll back - possibly leading to database corruption.
11297c478bd9Sstevel@tonic-gate */
11307c478bd9Sstevel@tonic-gate int sqliteOsSync(OsFile *id){
11317c478bd9Sstevel@tonic-gate #if OS_UNIX
11327c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
11337c478bd9Sstevel@tonic-gate   TRACE2("SYNC    %-3d\n", id->fd);
11347c478bd9Sstevel@tonic-gate   if( fsync(id->fd) ){
11357c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
11367c478bd9Sstevel@tonic-gate   }else{
11377c478bd9Sstevel@tonic-gate     if( id->dirfd>=0 ){
11387c478bd9Sstevel@tonic-gate       TRACE2("DIRSYNC %-3d\n", id->dirfd);
11397c478bd9Sstevel@tonic-gate       fsync(id->dirfd);
11407c478bd9Sstevel@tonic-gate       close(id->dirfd);  /* Only need to sync once, so close the directory */
11417c478bd9Sstevel@tonic-gate       id->dirfd = -1;    /* when we are done. */
11427c478bd9Sstevel@tonic-gate     }
11437c478bd9Sstevel@tonic-gate     return SQLITE_OK;
11447c478bd9Sstevel@tonic-gate   }
11457c478bd9Sstevel@tonic-gate #endif
11467c478bd9Sstevel@tonic-gate #if OS_WIN
11477c478bd9Sstevel@tonic-gate   if( FlushFileBuffers(id->h) ){
11487c478bd9Sstevel@tonic-gate     return SQLITE_OK;
11497c478bd9Sstevel@tonic-gate   }else{
11507c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
11517c478bd9Sstevel@tonic-gate   }
11527c478bd9Sstevel@tonic-gate #endif
11537c478bd9Sstevel@tonic-gate #if OS_MAC
11547c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
11557c478bd9Sstevel@tonic-gate   if( FSFlushFork(id->refNum) != noErr ){
11567c478bd9Sstevel@tonic-gate # else
11577c478bd9Sstevel@tonic-gate   ParamBlockRec params;
11587c478bd9Sstevel@tonic-gate   memset(&params, 0, sizeof(ParamBlockRec));
11597c478bd9Sstevel@tonic-gate   params.ioParam.ioRefNum = id->refNum;
11607c478bd9Sstevel@tonic-gate   if( PBFlushFileSync(&params) != noErr ){
11617c478bd9Sstevel@tonic-gate # endif
11627c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
11637c478bd9Sstevel@tonic-gate   }else{
11647c478bd9Sstevel@tonic-gate     return SQLITE_OK;
11657c478bd9Sstevel@tonic-gate   }
11667c478bd9Sstevel@tonic-gate #endif
11677c478bd9Sstevel@tonic-gate }
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate /*
11707c478bd9Sstevel@tonic-gate ** Truncate an open file to a specified size
11717c478bd9Sstevel@tonic-gate */
11727c478bd9Sstevel@tonic-gate int sqliteOsTruncate(OsFile *id, off_t nByte){
11737c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
11747c478bd9Sstevel@tonic-gate #if OS_UNIX
11757c478bd9Sstevel@tonic-gate   return ftruncate(id->fd, nByte)==0 ? SQLITE_OK : SQLITE_IOERR;
11767c478bd9Sstevel@tonic-gate #endif
11777c478bd9Sstevel@tonic-gate #if OS_WIN
11787c478bd9Sstevel@tonic-gate   {
11797c478bd9Sstevel@tonic-gate     LONG upperBits = nByte>>32;
11807c478bd9Sstevel@tonic-gate     SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN);
11817c478bd9Sstevel@tonic-gate     SetEndOfFile(id->h);
11827c478bd9Sstevel@tonic-gate   }
11837c478bd9Sstevel@tonic-gate   return SQLITE_OK;
11847c478bd9Sstevel@tonic-gate #endif
11857c478bd9Sstevel@tonic-gate #if OS_MAC
11867c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
11877c478bd9Sstevel@tonic-gate   if( FSSetForkSize(id->refNum, fsFromStart, nByte) != noErr){
11887c478bd9Sstevel@tonic-gate # else
11897c478bd9Sstevel@tonic-gate   if( SetEOF(id->refNum, nByte) != noErr ){
11907c478bd9Sstevel@tonic-gate # endif
11917c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
11927c478bd9Sstevel@tonic-gate   }else{
11937c478bd9Sstevel@tonic-gate     return SQLITE_OK;
11947c478bd9Sstevel@tonic-gate   }
11957c478bd9Sstevel@tonic-gate #endif
11967c478bd9Sstevel@tonic-gate }
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate /*
11997c478bd9Sstevel@tonic-gate ** Determine the current size of a file in bytes
12007c478bd9Sstevel@tonic-gate */
12017c478bd9Sstevel@tonic-gate int sqliteOsFileSize(OsFile *id, off_t *pSize){
12027c478bd9Sstevel@tonic-gate #if OS_UNIX
12037c478bd9Sstevel@tonic-gate   struct stat buf;
12047c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
12057c478bd9Sstevel@tonic-gate   if( fstat(id->fd, &buf)!=0 ){
12067c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
12077c478bd9Sstevel@tonic-gate   }
12087c478bd9Sstevel@tonic-gate   *pSize = buf.st_size;
12097c478bd9Sstevel@tonic-gate   return SQLITE_OK;
12107c478bd9Sstevel@tonic-gate #endif
12117c478bd9Sstevel@tonic-gate #if OS_WIN
12127c478bd9Sstevel@tonic-gate   DWORD upperBits, lowerBits;
12137c478bd9Sstevel@tonic-gate   SimulateIOError(SQLITE_IOERR);
12147c478bd9Sstevel@tonic-gate   lowerBits = GetFileSize(id->h, &upperBits);
12157c478bd9Sstevel@tonic-gate   *pSize = (((off_t)upperBits)<<32) + lowerBits;
12167c478bd9Sstevel@tonic-gate   return SQLITE_OK;
12177c478bd9Sstevel@tonic-gate #endif
12187c478bd9Sstevel@tonic-gate #if OS_MAC
12197c478bd9Sstevel@tonic-gate # ifdef _LARGE_FILE
12207c478bd9Sstevel@tonic-gate   if( FSGetForkSize(id->refNum, pSize) != noErr){
12217c478bd9Sstevel@tonic-gate # else
12227c478bd9Sstevel@tonic-gate   if( GetEOF(id->refNum, pSize) != noErr ){
12237c478bd9Sstevel@tonic-gate # endif
12247c478bd9Sstevel@tonic-gate     return SQLITE_IOERR;
12257c478bd9Sstevel@tonic-gate   }else{
12267c478bd9Sstevel@tonic-gate     return SQLITE_OK;
12277c478bd9Sstevel@tonic-gate   }
12287c478bd9Sstevel@tonic-gate #endif
12297c478bd9Sstevel@tonic-gate }
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate #if OS_WIN
12327c478bd9Sstevel@tonic-gate /*
12337c478bd9Sstevel@tonic-gate ** Return true (non-zero) if we are running under WinNT, Win2K or WinXP.
12347c478bd9Sstevel@tonic-gate ** Return false (zero) for Win95, Win98, or WinME.
12357c478bd9Sstevel@tonic-gate **
12367c478bd9Sstevel@tonic-gate ** Here is an interesting observation:  Win95, Win98, and WinME lack
12377c478bd9Sstevel@tonic-gate ** the LockFileEx() API.  But we can still statically link against that
12387c478bd9Sstevel@tonic-gate ** API as long as we don't call it win running Win95/98/ME.  A call to
12397c478bd9Sstevel@tonic-gate ** this routine is used to determine if the host is Win95/98/ME or
12407c478bd9Sstevel@tonic-gate ** WinNT/2K/XP so that we will know whether or not we can safely call
12417c478bd9Sstevel@tonic-gate ** the LockFileEx() API.
12427c478bd9Sstevel@tonic-gate */
12437c478bd9Sstevel@tonic-gate int isNT(void){
12447c478bd9Sstevel@tonic-gate   static int osType = 0;   /* 0=unknown 1=win95 2=winNT */
12457c478bd9Sstevel@tonic-gate   if( osType==0 ){
12467c478bd9Sstevel@tonic-gate     OSVERSIONINFO sInfo;
12477c478bd9Sstevel@tonic-gate     sInfo.dwOSVersionInfoSize = sizeof(sInfo);
12487c478bd9Sstevel@tonic-gate     GetVersionEx(&sInfo);
12497c478bd9Sstevel@tonic-gate     osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
12507c478bd9Sstevel@tonic-gate   }
12517c478bd9Sstevel@tonic-gate   return osType==2;
12527c478bd9Sstevel@tonic-gate }
12537c478bd9Sstevel@tonic-gate #endif
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate /*
12567c478bd9Sstevel@tonic-gate ** Windows file locking notes:  [similar issues apply to MacOS]
12577c478bd9Sstevel@tonic-gate **
12587c478bd9Sstevel@tonic-gate ** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
12597c478bd9Sstevel@tonic-gate ** those functions are not available.  So we use only LockFile() and
12607c478bd9Sstevel@tonic-gate ** UnlockFile().
12617c478bd9Sstevel@tonic-gate **
12627c478bd9Sstevel@tonic-gate ** LockFile() prevents not just writing but also reading by other processes.
12637c478bd9Sstevel@tonic-gate ** (This is a design error on the part of Windows, but there is nothing
12647c478bd9Sstevel@tonic-gate ** we can do about that.)  So the region used for locking is at the
12657c478bd9Sstevel@tonic-gate ** end of the file where it is unlikely to ever interfere with an
12667c478bd9Sstevel@tonic-gate ** actual read attempt.
12677c478bd9Sstevel@tonic-gate **
1268*1da57d55SToomas Soome ** A database read lock is obtained by locking a single randomly-chosen
1269*1da57d55SToomas Soome ** byte out of a specific range of bytes. The lock byte is obtained at
1270*1da57d55SToomas Soome ** random so two separate readers can probably access the file at the
12717c478bd9Sstevel@tonic-gate ** same time, unless they are unlucky and choose the same lock byte.
12727c478bd9Sstevel@tonic-gate ** A database write lock is obtained by locking all bytes in the range.
12737c478bd9Sstevel@tonic-gate ** There can only be one writer.
12747c478bd9Sstevel@tonic-gate **
12757c478bd9Sstevel@tonic-gate ** A lock is obtained on the first byte of the lock range before acquiring
12767c478bd9Sstevel@tonic-gate ** either a read lock or a write lock.  This prevents two processes from
1277*1da57d55SToomas Soome ** attempting to get a lock at a same time.  The semantics of
12787c478bd9Sstevel@tonic-gate ** sqliteOsReadLock() require that if there is already a write lock, that
12797c478bd9Sstevel@tonic-gate ** lock is converted into a read lock atomically.  The lock on the first
12807c478bd9Sstevel@tonic-gate ** byte allows us to drop the old write lock and get the read lock without
12817c478bd9Sstevel@tonic-gate ** another process jumping into the middle and messing us up.  The same
12827c478bd9Sstevel@tonic-gate ** argument applies to sqliteOsWriteLock().
12837c478bd9Sstevel@tonic-gate **
12847c478bd9Sstevel@tonic-gate ** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
12857c478bd9Sstevel@tonic-gate ** which means we can use reader/writer locks.  When reader writer locks
12867c478bd9Sstevel@tonic-gate ** are used, the lock is placed on the same range of bytes that is used
12877c478bd9Sstevel@tonic-gate ** for probabilistic locking in Win95/98/ME.  Hence, the locking scheme
12887c478bd9Sstevel@tonic-gate ** will support two or more Win95 readers or two or more WinNT readers.
12897c478bd9Sstevel@tonic-gate ** But a single Win95 reader will lock out all WinNT readers and a single
12907c478bd9Sstevel@tonic-gate ** WinNT reader will lock out all other Win95 readers.
12917c478bd9Sstevel@tonic-gate **
12927c478bd9Sstevel@tonic-gate ** Note: On MacOS we use the resource fork for locking.
12937c478bd9Sstevel@tonic-gate **
12947c478bd9Sstevel@tonic-gate ** The following #defines specify the range of bytes used for locking.
12957c478bd9Sstevel@tonic-gate ** N_LOCKBYTE is the number of bytes available for doing the locking.
12967c478bd9Sstevel@tonic-gate ** The first byte used to hold the lock while the lock is changing does
12977c478bd9Sstevel@tonic-gate ** not count toward this number.  FIRST_LOCKBYTE is the address of
12987c478bd9Sstevel@tonic-gate ** the first byte in the range of bytes used for locking.
12997c478bd9Sstevel@tonic-gate */
13007c478bd9Sstevel@tonic-gate #define N_LOCKBYTE       10239
13017c478bd9Sstevel@tonic-gate #if OS_MAC
13027c478bd9Sstevel@tonic-gate # define FIRST_LOCKBYTE   (0x000fffff - N_LOCKBYTE)
13037c478bd9Sstevel@tonic-gate #else
13047c478bd9Sstevel@tonic-gate # define FIRST_LOCKBYTE   (0xffffffff - N_LOCKBYTE)
13057c478bd9Sstevel@tonic-gate #endif
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate /*
13087c478bd9Sstevel@tonic-gate ** Change the status of the lock on the file "id" to be a readlock.
13097c478bd9Sstevel@tonic-gate ** If the file was write locked, then this reduces the lock to a read.
13107c478bd9Sstevel@tonic-gate ** If the file was read locked, then this acquires a new read lock.
13117c478bd9Sstevel@tonic-gate **
13127c478bd9Sstevel@tonic-gate ** Return SQLITE_OK on success and SQLITE_BUSY on failure.  If this
13137c478bd9Sstevel@tonic-gate ** library was compiled with large file support (LFS) but LFS is not
13147c478bd9Sstevel@tonic-gate ** available on the host, then an SQLITE_NOLFS is returned.
13157c478bd9Sstevel@tonic-gate */
13167c478bd9Sstevel@tonic-gate int sqliteOsReadLock(OsFile *id){
13177c478bd9Sstevel@tonic-gate #if OS_UNIX
13187c478bd9Sstevel@tonic-gate   int rc;
13197c478bd9Sstevel@tonic-gate   sqliteOsEnterMutex();
13207c478bd9Sstevel@tonic-gate   if( id->pLock->cnt>0 ){
13217c478bd9Sstevel@tonic-gate     if( !id->locked ){
13227c478bd9Sstevel@tonic-gate       id->pLock->cnt++;
13237c478bd9Sstevel@tonic-gate       id->locked = 1;
13247c478bd9Sstevel@tonic-gate       id->pOpen->nLock++;
13257c478bd9Sstevel@tonic-gate     }
13267c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
13277c478bd9Sstevel@tonic-gate   }else if( id->locked || id->pLock->cnt==0 ){
13287c478bd9Sstevel@tonic-gate     struct flock lock;
13297c478bd9Sstevel@tonic-gate     int s;
13307c478bd9Sstevel@tonic-gate     lock.l_type = F_RDLCK;
13317c478bd9Sstevel@tonic-gate     lock.l_whence = SEEK_SET;
13327c478bd9Sstevel@tonic-gate     lock.l_start = lock.l_len = 0L;
13337c478bd9Sstevel@tonic-gate     s = fcntl(id->fd, F_SETLK, &lock);
13347c478bd9Sstevel@tonic-gate     if( s!=0 ){
13357c478bd9Sstevel@tonic-gate       rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
13367c478bd9Sstevel@tonic-gate     }else{
13377c478bd9Sstevel@tonic-gate       rc = SQLITE_OK;
13387c478bd9Sstevel@tonic-gate       if( !id->locked ){
13397c478bd9Sstevel@tonic-gate         id->pOpen->nLock++;
13407c478bd9Sstevel@tonic-gate         id->locked = 1;
13417c478bd9Sstevel@tonic-gate       }
13427c478bd9Sstevel@tonic-gate       id->pLock->cnt = 1;
13437c478bd9Sstevel@tonic-gate     }
13447c478bd9Sstevel@tonic-gate   }else{
13457c478bd9Sstevel@tonic-gate     rc = SQLITE_BUSY;
13467c478bd9Sstevel@tonic-gate   }
13477c478bd9Sstevel@tonic-gate   sqliteOsLeaveMutex();
13487c478bd9Sstevel@tonic-gate   return rc;
13497c478bd9Sstevel@tonic-gate #endif
13507c478bd9Sstevel@tonic-gate #if OS_WIN
13517c478bd9Sstevel@tonic-gate   int rc;
13527c478bd9Sstevel@tonic-gate   if( id->locked>0 ){
13537c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
13547c478bd9Sstevel@tonic-gate   }else{
13557c478bd9Sstevel@tonic-gate     int lk;
13567c478bd9Sstevel@tonic-gate     int res;
13577c478bd9Sstevel@tonic-gate     int cnt = 100;
13587c478bd9Sstevel@tonic-gate     sqliteRandomness(sizeof(lk), &lk);
13597c478bd9Sstevel@tonic-gate     lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1;
13607c478bd9Sstevel@tonic-gate     while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){
13617c478bd9Sstevel@tonic-gate       Sleep(1);
13627c478bd9Sstevel@tonic-gate     }
13637c478bd9Sstevel@tonic-gate     if( res ){
13647c478bd9Sstevel@tonic-gate       UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
13657c478bd9Sstevel@tonic-gate       if( isNT() ){
13667c478bd9Sstevel@tonic-gate         OVERLAPPED ovlp;
13677c478bd9Sstevel@tonic-gate         ovlp.Offset = FIRST_LOCKBYTE+1;
13687c478bd9Sstevel@tonic-gate         ovlp.OffsetHigh = 0;
13697c478bd9Sstevel@tonic-gate         ovlp.hEvent = 0;
1370*1da57d55SToomas Soome         res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY,
13717c478bd9Sstevel@tonic-gate                           0, N_LOCKBYTE, 0, &ovlp);
13727c478bd9Sstevel@tonic-gate       }else{
13737c478bd9Sstevel@tonic-gate         res = LockFile(id->h, FIRST_LOCKBYTE+lk, 0, 1, 0);
13747c478bd9Sstevel@tonic-gate       }
13757c478bd9Sstevel@tonic-gate       UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0);
13767c478bd9Sstevel@tonic-gate     }
13777c478bd9Sstevel@tonic-gate     if( res ){
13787c478bd9Sstevel@tonic-gate       id->locked = lk;
13797c478bd9Sstevel@tonic-gate       rc = SQLITE_OK;
13807c478bd9Sstevel@tonic-gate     }else{
13817c478bd9Sstevel@tonic-gate       rc = SQLITE_BUSY;
13827c478bd9Sstevel@tonic-gate     }
13837c478bd9Sstevel@tonic-gate   }
13847c478bd9Sstevel@tonic-gate   return rc;
13857c478bd9Sstevel@tonic-gate #endif
13867c478bd9Sstevel@tonic-gate #if OS_MAC
13877c478bd9Sstevel@tonic-gate   int rc;
13887c478bd9Sstevel@tonic-gate   if( id->locked>0 || id->refNumRF == -1 ){
13897c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
13907c478bd9Sstevel@tonic-gate   }else{
13917c478bd9Sstevel@tonic-gate     int lk;
13927c478bd9Sstevel@tonic-gate     OSErr res;
13937c478bd9Sstevel@tonic-gate     int cnt = 5;
13947c478bd9Sstevel@tonic-gate     ParamBlockRec params;
13957c478bd9Sstevel@tonic-gate     sqliteRandomness(sizeof(lk), &lk);
13967c478bd9Sstevel@tonic-gate     lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1;
13977c478bd9Sstevel@tonic-gate     memset(&params, 0, sizeof(params));
13987c478bd9Sstevel@tonic-gate     params.ioParam.ioRefNum = id->refNumRF;
13997c478bd9Sstevel@tonic-gate     params.ioParam.ioPosMode = fsFromStart;
14007c478bd9Sstevel@tonic-gate     params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
14017c478bd9Sstevel@tonic-gate     params.ioParam.ioReqCount = 1;
14027c478bd9Sstevel@tonic-gate     while( cnt-->0 && (res = PBLockRangeSync(&params))!=noErr ){
14037c478bd9Sstevel@tonic-gate       UInt32 finalTicks;
14047c478bd9Sstevel@tonic-gate       Delay(1, &finalTicks); /* 1/60 sec */
14057c478bd9Sstevel@tonic-gate     }
14067c478bd9Sstevel@tonic-gate     if( res == noErr ){
14077c478bd9Sstevel@tonic-gate       params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1;
14087c478bd9Sstevel@tonic-gate       params.ioParam.ioReqCount = N_LOCKBYTE;
14097c478bd9Sstevel@tonic-gate       PBUnlockRangeSync(&params);
14107c478bd9Sstevel@tonic-gate       params.ioParam.ioPosOffset = FIRST_LOCKBYTE+lk;
14117c478bd9Sstevel@tonic-gate       params.ioParam.ioReqCount = 1;
14127c478bd9Sstevel@tonic-gate       res = PBLockRangeSync(&params);
14137c478bd9Sstevel@tonic-gate       params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
14147c478bd9Sstevel@tonic-gate       params.ioParam.ioReqCount = 1;
14157c478bd9Sstevel@tonic-gate       PBUnlockRangeSync(&params);
14167c478bd9Sstevel@tonic-gate     }
14177c478bd9Sstevel@tonic-gate     if( res == noErr ){
14187c478bd9Sstevel@tonic-gate       id->locked = lk;
14197c478bd9Sstevel@tonic-gate       rc = SQLITE_OK;
14207c478bd9Sstevel@tonic-gate     }else{
14217c478bd9Sstevel@tonic-gate       rc = SQLITE_BUSY;
14227c478bd9Sstevel@tonic-gate     }
14237c478bd9Sstevel@tonic-gate   }
14247c478bd9Sstevel@tonic-gate   return rc;
14257c478bd9Sstevel@tonic-gate #endif
14267c478bd9Sstevel@tonic-gate }
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate /*
14297c478bd9Sstevel@tonic-gate ** Change the lock status to be an exclusive or write lock.  Return
14307c478bd9Sstevel@tonic-gate ** SQLITE_OK on success and SQLITE_BUSY on a failure.  If this
14317c478bd9Sstevel@tonic-gate ** library was compiled with large file support (LFS) but LFS is not
14327c478bd9Sstevel@tonic-gate ** available on the host, then an SQLITE_NOLFS is returned.
14337c478bd9Sstevel@tonic-gate */
14347c478bd9Sstevel@tonic-gate int sqliteOsWriteLock(OsFile *id){
14357c478bd9Sstevel@tonic-gate #if OS_UNIX
14367c478bd9Sstevel@tonic-gate   int rc;
14377c478bd9Sstevel@tonic-gate   sqliteOsEnterMutex();
14387c478bd9Sstevel@tonic-gate   if( id->pLock->cnt==0 || (id->pLock->cnt==1 && id->locked==1) ){
14397c478bd9Sstevel@tonic-gate     struct flock lock;
14407c478bd9Sstevel@tonic-gate     int s;
14417c478bd9Sstevel@tonic-gate     lock.l_type = F_WRLCK;
14427c478bd9Sstevel@tonic-gate     lock.l_whence = SEEK_SET;
14437c478bd9Sstevel@tonic-gate     lock.l_start = lock.l_len = 0L;
14447c478bd9Sstevel@tonic-gate     s = fcntl(id->fd, F_SETLK, &lock);
14457c478bd9Sstevel@tonic-gate     if( s!=0 ){
14467c478bd9Sstevel@tonic-gate       rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
14477c478bd9Sstevel@tonic-gate     }else{
14487c478bd9Sstevel@tonic-gate       rc = SQLITE_OK;
14497c478bd9Sstevel@tonic-gate       if( !id->locked ){
14507c478bd9Sstevel@tonic-gate         id->pOpen->nLock++;
14517c478bd9Sstevel@tonic-gate         id->locked = 1;
14527c478bd9Sstevel@tonic-gate       }
14537c478bd9Sstevel@tonic-gate       id->pLock->cnt = -1;
14547c478bd9Sstevel@tonic-gate     }
14557c478bd9Sstevel@tonic-gate   }else{
14567c478bd9Sstevel@tonic-gate     rc = SQLITE_BUSY;
14577c478bd9Sstevel@tonic-gate   }
14587c478bd9Sstevel@tonic-gate   sqliteOsLeaveMutex();
14597c478bd9Sstevel@tonic-gate   return rc;
14607c478bd9Sstevel@tonic-gate #endif
14617c478bd9Sstevel@tonic-gate #if OS_WIN
14627c478bd9Sstevel@tonic-gate   int rc;
14637c478bd9Sstevel@tonic-gate   if( id->locked<0 ){
14647c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
14657c478bd9Sstevel@tonic-gate   }else{
14667c478bd9Sstevel@tonic-gate     int res;
14677c478bd9Sstevel@tonic-gate     int cnt = 100;
14687c478bd9Sstevel@tonic-gate     while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){
14697c478bd9Sstevel@tonic-gate       Sleep(1);
14707c478bd9Sstevel@tonic-gate     }
14717c478bd9Sstevel@tonic-gate     if( res ){
14727c478bd9Sstevel@tonic-gate       if( id->locked>0 ){
14737c478bd9Sstevel@tonic-gate         if( isNT() ){
14747c478bd9Sstevel@tonic-gate           UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
14757c478bd9Sstevel@tonic-gate         }else{
14767c478bd9Sstevel@tonic-gate           res = UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, 0, 1, 0);
14777c478bd9Sstevel@tonic-gate         }
14787c478bd9Sstevel@tonic-gate       }
14797c478bd9Sstevel@tonic-gate       if( res ){
14807c478bd9Sstevel@tonic-gate         res = LockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
14817c478bd9Sstevel@tonic-gate       }else{
14827c478bd9Sstevel@tonic-gate         res = 0;
14837c478bd9Sstevel@tonic-gate       }
14847c478bd9Sstevel@tonic-gate       UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0);
14857c478bd9Sstevel@tonic-gate     }
14867c478bd9Sstevel@tonic-gate     if( res ){
14877c478bd9Sstevel@tonic-gate       id->locked = -1;
14887c478bd9Sstevel@tonic-gate       rc = SQLITE_OK;
14897c478bd9Sstevel@tonic-gate     }else{
14907c478bd9Sstevel@tonic-gate       rc = SQLITE_BUSY;
14917c478bd9Sstevel@tonic-gate     }
14927c478bd9Sstevel@tonic-gate   }
14937c478bd9Sstevel@tonic-gate   return rc;
14947c478bd9Sstevel@tonic-gate #endif
14957c478bd9Sstevel@tonic-gate #if OS_MAC
14967c478bd9Sstevel@tonic-gate   int rc;
14977c478bd9Sstevel@tonic-gate   if( id->locked<0 || id->refNumRF == -1 ){
14987c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
14997c478bd9Sstevel@tonic-gate   }else{
15007c478bd9Sstevel@tonic-gate     OSErr res;
15017c478bd9Sstevel@tonic-gate     int cnt = 5;
15027c478bd9Sstevel@tonic-gate     ParamBlockRec params;
15037c478bd9Sstevel@tonic-gate     memset(&params, 0, sizeof(params));
15047c478bd9Sstevel@tonic-gate     params.ioParam.ioRefNum = id->refNumRF;
15057c478bd9Sstevel@tonic-gate     params.ioParam.ioPosMode = fsFromStart;
15067c478bd9Sstevel@tonic-gate     params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
15077c478bd9Sstevel@tonic-gate     params.ioParam.ioReqCount = 1;
15087c478bd9Sstevel@tonic-gate     while( cnt-->0 && (res = PBLockRangeSync(&params))!=noErr ){
15097c478bd9Sstevel@tonic-gate       UInt32 finalTicks;
15107c478bd9Sstevel@tonic-gate       Delay(1, &finalTicks); /* 1/60 sec */
15117c478bd9Sstevel@tonic-gate     }
15127c478bd9Sstevel@tonic-gate     if( res == noErr ){
15137c478bd9Sstevel@tonic-gate       params.ioParam.ioPosOffset = FIRST_LOCKBYTE + id->locked;
15147c478bd9Sstevel@tonic-gate       params.ioParam.ioReqCount = 1;
1515*1da57d55SToomas Soome       if( id->locked==0
15167c478bd9Sstevel@tonic-gate             || PBUnlockRangeSync(&params)==noErr ){
15177c478bd9Sstevel@tonic-gate         params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1;
15187c478bd9Sstevel@tonic-gate         params.ioParam.ioReqCount = N_LOCKBYTE;
15197c478bd9Sstevel@tonic-gate         res = PBLockRangeSync(&params);
15207c478bd9Sstevel@tonic-gate       }else{
15217c478bd9Sstevel@tonic-gate         res = afpRangeNotLocked;
15227c478bd9Sstevel@tonic-gate       }
15237c478bd9Sstevel@tonic-gate       params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
15247c478bd9Sstevel@tonic-gate       params.ioParam.ioReqCount = 1;
15257c478bd9Sstevel@tonic-gate       PBUnlockRangeSync(&params);
15267c478bd9Sstevel@tonic-gate     }
15277c478bd9Sstevel@tonic-gate     if( res == noErr ){
15287c478bd9Sstevel@tonic-gate       id->locked = -1;
15297c478bd9Sstevel@tonic-gate       rc = SQLITE_OK;
15307c478bd9Sstevel@tonic-gate     }else{
15317c478bd9Sstevel@tonic-gate       rc = SQLITE_BUSY;
15327c478bd9Sstevel@tonic-gate     }
15337c478bd9Sstevel@tonic-gate   }
15347c478bd9Sstevel@tonic-gate   return rc;
15357c478bd9Sstevel@tonic-gate #endif
15367c478bd9Sstevel@tonic-gate }
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate /*
15397c478bd9Sstevel@tonic-gate ** Unlock the given file descriptor.  If the file descriptor was
15407c478bd9Sstevel@tonic-gate ** not previously locked, then this routine is a no-op.  If this
15417c478bd9Sstevel@tonic-gate ** library was compiled with large file support (LFS) but LFS is not
15427c478bd9Sstevel@tonic-gate ** available on the host, then an SQLITE_NOLFS is returned.
15437c478bd9Sstevel@tonic-gate */
15447c478bd9Sstevel@tonic-gate int sqliteOsUnlock(OsFile *id){
15457c478bd9Sstevel@tonic-gate #if OS_UNIX
15467c478bd9Sstevel@tonic-gate   int rc;
15477c478bd9Sstevel@tonic-gate   if( !id->locked ) return SQLITE_OK;
15487c478bd9Sstevel@tonic-gate   sqliteOsEnterMutex();
15497c478bd9Sstevel@tonic-gate   assert( id->pLock->cnt!=0 );
15507c478bd9Sstevel@tonic-gate   if( id->pLock->cnt>1 ){
15517c478bd9Sstevel@tonic-gate     id->pLock->cnt--;
15527c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
15537c478bd9Sstevel@tonic-gate   }else{
15547c478bd9Sstevel@tonic-gate     struct flock lock;
15557c478bd9Sstevel@tonic-gate     int s;
15567c478bd9Sstevel@tonic-gate     lock.l_type = F_UNLCK;
15577c478bd9Sstevel@tonic-gate     lock.l_whence = SEEK_SET;
15587c478bd9Sstevel@tonic-gate     lock.l_start = lock.l_len = 0L;
15597c478bd9Sstevel@tonic-gate     s = fcntl(id->fd, F_SETLK, &lock);
15607c478bd9Sstevel@tonic-gate     if( s!=0 ){
15617c478bd9Sstevel@tonic-gate       rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
15627c478bd9Sstevel@tonic-gate     }else{
15637c478bd9Sstevel@tonic-gate       rc = SQLITE_OK;
15647c478bd9Sstevel@tonic-gate       id->pLock->cnt = 0;
15657c478bd9Sstevel@tonic-gate     }
15667c478bd9Sstevel@tonic-gate   }
15677c478bd9Sstevel@tonic-gate   if( rc==SQLITE_OK ){
15687c478bd9Sstevel@tonic-gate     /* Decrement the count of locks against this same file.  When the
15697c478bd9Sstevel@tonic-gate     ** count reaches zero, close any other file descriptors whose close
15707c478bd9Sstevel@tonic-gate     ** was deferred because of outstanding locks.
15717c478bd9Sstevel@tonic-gate     */
15727c478bd9Sstevel@tonic-gate     struct openCnt *pOpen = id->pOpen;
15737c478bd9Sstevel@tonic-gate     pOpen->nLock--;
15747c478bd9Sstevel@tonic-gate     assert( pOpen->nLock>=0 );
15757c478bd9Sstevel@tonic-gate     if( pOpen->nLock==0 && pOpen->nPending>0 ){
15767c478bd9Sstevel@tonic-gate       int i;
15777c478bd9Sstevel@tonic-gate       for(i=0; i<pOpen->nPending; i++){
15787c478bd9Sstevel@tonic-gate         close(pOpen->aPending[i]);
15797c478bd9Sstevel@tonic-gate       }
15807c478bd9Sstevel@tonic-gate       sqliteFree(pOpen->aPending);
15817c478bd9Sstevel@tonic-gate       pOpen->nPending = 0;
15827c478bd9Sstevel@tonic-gate       pOpen->aPending = 0;
15837c478bd9Sstevel@tonic-gate     }
15847c478bd9Sstevel@tonic-gate   }
15857c478bd9Sstevel@tonic-gate   sqliteOsLeaveMutex();
15867c478bd9Sstevel@tonic-gate   id->locked = 0;
15877c478bd9Sstevel@tonic-gate   return rc;
15887c478bd9Sstevel@tonic-gate #endif
15897c478bd9Sstevel@tonic-gate #if OS_WIN
15907c478bd9Sstevel@tonic-gate   int rc;
15917c478bd9Sstevel@tonic-gate   if( id->locked==0 ){
15927c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
15937c478bd9Sstevel@tonic-gate   }else if( isNT() || id->locked<0 ){
15947c478bd9Sstevel@tonic-gate     UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
15957c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
15967c478bd9Sstevel@tonic-gate     id->locked = 0;
15977c478bd9Sstevel@tonic-gate   }else{
15987c478bd9Sstevel@tonic-gate     UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, 0, 1, 0);
15997c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
16007c478bd9Sstevel@tonic-gate     id->locked = 0;
16017c478bd9Sstevel@tonic-gate   }
16027c478bd9Sstevel@tonic-gate   return rc;
16037c478bd9Sstevel@tonic-gate #endif
16047c478bd9Sstevel@tonic-gate #if OS_MAC
16057c478bd9Sstevel@tonic-gate   int rc;
16067c478bd9Sstevel@tonic-gate   ParamBlockRec params;
16077c478bd9Sstevel@tonic-gate   memset(&params, 0, sizeof(params));
16087c478bd9Sstevel@tonic-gate   params.ioParam.ioRefNum = id->refNumRF;
16097c478bd9Sstevel@tonic-gate   params.ioParam.ioPosMode = fsFromStart;
16107c478bd9Sstevel@tonic-gate   if( id->locked==0 || id->refNumRF == -1 ){
16117c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
16127c478bd9Sstevel@tonic-gate   }else if( id->locked<0 ){
16137c478bd9Sstevel@tonic-gate     params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1;
16147c478bd9Sstevel@tonic-gate     params.ioParam.ioReqCount = N_LOCKBYTE;
16157c478bd9Sstevel@tonic-gate     PBUnlockRangeSync(&params);
16167c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
16177c478bd9Sstevel@tonic-gate     id->locked = 0;
16187c478bd9Sstevel@tonic-gate   }else{
16197c478bd9Sstevel@tonic-gate     params.ioParam.ioPosOffset = FIRST_LOCKBYTE+id->locked;
16207c478bd9Sstevel@tonic-gate     params.ioParam.ioReqCount = 1;
16217c478bd9Sstevel@tonic-gate     PBUnlockRangeSync(&params);
16227c478bd9Sstevel@tonic-gate     rc = SQLITE_OK;
16237c478bd9Sstevel@tonic-gate     id->locked = 0;
16247c478bd9Sstevel@tonic-gate   }
16257c478bd9Sstevel@tonic-gate   return rc;
16267c478bd9Sstevel@tonic-gate #endif
16277c478bd9Sstevel@tonic-gate }
16287c478bd9Sstevel@tonic-gate 
16297c478bd9Sstevel@tonic-gate /*
16307c478bd9Sstevel@tonic-gate ** Get information to seed the random number generator.  The seed
16317c478bd9Sstevel@tonic-gate ** is written into the buffer zBuf[256].  The calling function must
16327c478bd9Sstevel@tonic-gate ** supply a sufficiently large buffer.
16337c478bd9Sstevel@tonic-gate */
16347c478bd9Sstevel@tonic-gate int sqliteOsRandomSeed(char *zBuf){
16357c478bd9Sstevel@tonic-gate   /* We have to initialize zBuf to prevent valgrind from reporting
16367c478bd9Sstevel@tonic-gate   ** errors.  The reports issued by valgrind are incorrect - we would
16377c478bd9Sstevel@tonic-gate   ** prefer that the randomness be increased by making use of the
16387c478bd9Sstevel@tonic-gate   ** uninitialized space in zBuf - but valgrind errors tend to worry
16397c478bd9Sstevel@tonic-gate   ** some users.  Rather than argue, it seems easier just to initialize
16407c478bd9Sstevel@tonic-gate   ** the whole array and silence valgrind, even if that means less randomness
16417c478bd9Sstevel@tonic-gate   ** in the random seed.
16427c478bd9Sstevel@tonic-gate   **
16437c478bd9Sstevel@tonic-gate   ** When testing, initializing zBuf[] to zero is all we do.  That means
16447c478bd9Sstevel@tonic-gate   ** that we always use the same random number sequence.* This makes the
16457c478bd9Sstevel@tonic-gate   ** tests repeatable.
16467c478bd9Sstevel@tonic-gate   */
16477c478bd9Sstevel@tonic-gate   memset(zBuf, 0, 256);
16487c478bd9Sstevel@tonic-gate #if OS_UNIX && !defined(SQLITE_TEST)
16497c478bd9Sstevel@tonic-gate   {
16507c478bd9Sstevel@tonic-gate     int pid;
16517c478bd9Sstevel@tonic-gate     time((time_t*)zBuf);
16527c478bd9Sstevel@tonic-gate     pid = getpid();
16537c478bd9Sstevel@tonic-gate     memcpy(&zBuf[sizeof(time_t)], &pid, sizeof(pid));
16547c478bd9Sstevel@tonic-gate   }
16557c478bd9Sstevel@tonic-gate #endif
16567c478bd9Sstevel@tonic-gate #if OS_WIN && !defined(SQLITE_TEST)
16577c478bd9Sstevel@tonic-gate   GetSystemTime((LPSYSTEMTIME)zBuf);
16587c478bd9Sstevel@tonic-gate #endif
16597c478bd9Sstevel@tonic-gate #if OS_MAC
16607c478bd9Sstevel@tonic-gate   {
16617c478bd9Sstevel@tonic-gate     int pid;
16627c478bd9Sstevel@tonic-gate     Microseconds((UnsignedWide*)zBuf);
16637c478bd9Sstevel@tonic-gate     pid = getpid();
16647c478bd9Sstevel@tonic-gate     memcpy(&zBuf[sizeof(UnsignedWide)], &pid, sizeof(pid));
16657c478bd9Sstevel@tonic-gate   }
16667c478bd9Sstevel@tonic-gate #endif
16677c478bd9Sstevel@tonic-gate   return SQLITE_OK;
16687c478bd9Sstevel@tonic-gate }
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate /*
16717c478bd9Sstevel@tonic-gate ** Sleep for a little while.  Return the amount of time slept.
16727c478bd9Sstevel@tonic-gate */
16737c478bd9Sstevel@tonic-gate int sqliteOsSleep(int ms){
16747c478bd9Sstevel@tonic-gate #if OS_UNIX
16757c478bd9Sstevel@tonic-gate #if defined(HAVE_USLEEP) && HAVE_USLEEP
16767c478bd9Sstevel@tonic-gate   usleep(ms*1000);
16777c478bd9Sstevel@tonic-gate   return ms;
16787c478bd9Sstevel@tonic-gate #else
16797c478bd9Sstevel@tonic-gate   sleep((ms+999)/1000);
16807c478bd9Sstevel@tonic-gate   return 1000*((ms+999)/1000);
16817c478bd9Sstevel@tonic-gate #endif
16827c478bd9Sstevel@tonic-gate #endif
16837c478bd9Sstevel@tonic-gate #if OS_WIN
16847c478bd9Sstevel@tonic-gate   Sleep(ms);
16857c478bd9Sstevel@tonic-gate   return ms;
16867c478bd9Sstevel@tonic-gate #endif
16877c478bd9Sstevel@tonic-gate #if OS_MAC
16887c478bd9Sstevel@tonic-gate   UInt32 finalTicks;
16897c478bd9Sstevel@tonic-gate   UInt32 ticks = (((UInt32)ms+16)*3)/50;  /* 1/60 sec per tick */
16907c478bd9Sstevel@tonic-gate   Delay(ticks, &finalTicks);
16917c478bd9Sstevel@tonic-gate   return (int)((ticks*50)/3);
16927c478bd9Sstevel@tonic-gate #endif
16937c478bd9Sstevel@tonic-gate }
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate /*
16967c478bd9Sstevel@tonic-gate ** Static variables used for thread synchronization
16977c478bd9Sstevel@tonic-gate */
16987c478bd9Sstevel@tonic-gate static int inMutex = 0;
16997c478bd9Sstevel@tonic-gate #ifdef SQLITE_UNIX_THREADS
17007c478bd9Sstevel@tonic-gate   static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
17017c478bd9Sstevel@tonic-gate #endif
17027c478bd9Sstevel@tonic-gate #ifdef SQLITE_W32_THREADS
17037c478bd9Sstevel@tonic-gate   static CRITICAL_SECTION cs;
17047c478bd9Sstevel@tonic-gate #endif
17057c478bd9Sstevel@tonic-gate #ifdef SQLITE_MACOS_MULTITASKING
17067c478bd9Sstevel@tonic-gate   static MPCriticalRegionID criticalRegion;
17077c478bd9Sstevel@tonic-gate #endif
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate /*
17107c478bd9Sstevel@tonic-gate ** The following pair of routine implement mutual exclusion for
17117c478bd9Sstevel@tonic-gate ** multi-threaded processes.  Only a single thread is allowed to
17127c478bd9Sstevel@tonic-gate ** executed code that is surrounded by EnterMutex() and LeaveMutex().
17137c478bd9Sstevel@tonic-gate **
17147c478bd9Sstevel@tonic-gate ** SQLite uses only a single Mutex.  There is not much critical
17157c478bd9Sstevel@tonic-gate ** code and what little there is executes quickly and without blocking.
17167c478bd9Sstevel@tonic-gate */
17177c478bd9Sstevel@tonic-gate void sqliteOsEnterMutex(){
17187c478bd9Sstevel@tonic-gate #ifdef SQLITE_UNIX_THREADS
17197c478bd9Sstevel@tonic-gate   pthread_mutex_lock(&mutex);
17207c478bd9Sstevel@tonic-gate #endif
17217c478bd9Sstevel@tonic-gate #ifdef SQLITE_W32_THREADS
17227c478bd9Sstevel@tonic-gate   static int isInit = 0;
17237c478bd9Sstevel@tonic-gate   while( !isInit ){
17247c478bd9Sstevel@tonic-gate     static long lock = 0;
17257c478bd9Sstevel@tonic-gate     if( InterlockedIncrement(&lock)==1 ){
17267c478bd9Sstevel@tonic-gate       InitializeCriticalSection(&cs);
17277c478bd9Sstevel@tonic-gate       isInit = 1;
17287c478bd9Sstevel@tonic-gate     }else{
17297c478bd9Sstevel@tonic-gate       Sleep(1);
17307c478bd9Sstevel@tonic-gate     }
17317c478bd9Sstevel@tonic-gate   }
17327c478bd9Sstevel@tonic-gate   EnterCriticalSection(&cs);
17337c478bd9Sstevel@tonic-gate #endif
17347c478bd9Sstevel@tonic-gate #ifdef SQLITE_MACOS_MULTITASKING
17357c478bd9Sstevel@tonic-gate   static volatile int notInit = 1;
17367c478bd9Sstevel@tonic-gate   if( notInit ){
17377c478bd9Sstevel@tonic-gate     if( notInit == 2 ) /* as close as you can get to thread safe init */
17387c478bd9Sstevel@tonic-gate       MPYield();
17397c478bd9Sstevel@tonic-gate     else{
17407c478bd9Sstevel@tonic-gate       notInit = 2;
17417c478bd9Sstevel@tonic-gate       MPCreateCriticalRegion(&criticalRegion);
17427c478bd9Sstevel@tonic-gate       notInit = 0;
17437c478bd9Sstevel@tonic-gate     }
17447c478bd9Sstevel@tonic-gate   }
17457c478bd9Sstevel@tonic-gate   MPEnterCriticalRegion(criticalRegion, kDurationForever);
17467c478bd9Sstevel@tonic-gate #endif
17477c478bd9Sstevel@tonic-gate   assert( !inMutex );
17487c478bd9Sstevel@tonic-gate   inMutex = 1;
17497c478bd9Sstevel@tonic-gate }
17507c478bd9Sstevel@tonic-gate void sqliteOsLeaveMutex(){
17517c478bd9Sstevel@tonic-gate   assert( inMutex );
17527c478bd9Sstevel@tonic-gate   inMutex = 0;
17537c478bd9Sstevel@tonic-gate #ifdef SQLITE_UNIX_THREADS
17547c478bd9Sstevel@tonic-gate   pthread_mutex_unlock(&mutex);
17557c478bd9Sstevel@tonic-gate #endif
17567c478bd9Sstevel@tonic-gate #ifdef SQLITE_W32_THREADS
17577c478bd9Sstevel@tonic-gate   LeaveCriticalSection(&cs);
17587c478bd9Sstevel@tonic-gate #endif
17597c478bd9Sstevel@tonic-gate #ifdef SQLITE_MACOS_MULTITASKING
17607c478bd9Sstevel@tonic-gate   MPExitCriticalRegion(criticalRegion);
17617c478bd9Sstevel@tonic-gate #endif
17627c478bd9Sstevel@tonic-gate }
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate /*
17657c478bd9Sstevel@tonic-gate ** Turn a relative pathname into a full pathname.  Return a pointer
17667c478bd9Sstevel@tonic-gate ** to the full pathname stored in space obtained from sqliteMalloc().
17677c478bd9Sstevel@tonic-gate ** The calling function is responsible for freeing this space once it
17687c478bd9Sstevel@tonic-gate ** is no longer needed.
17697c478bd9Sstevel@tonic-gate */
17707c478bd9Sstevel@tonic-gate char *sqliteOsFullPathname(const char *zRelative){
17717c478bd9Sstevel@tonic-gate #if OS_UNIX
17727c478bd9Sstevel@tonic-gate   char *zFull = 0;
17737c478bd9Sstevel@tonic-gate   if( zRelative[0]=='/' ){
17747c478bd9Sstevel@tonic-gate     sqliteSetString(&zFull, zRelative, (char*)0);
17757c478bd9Sstevel@tonic-gate   }else{
17767c478bd9Sstevel@tonic-gate     char zBuf[5000];
17777c478bd9Sstevel@tonic-gate     sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), "/", zRelative,
17787c478bd9Sstevel@tonic-gate                     (char*)0);
17797c478bd9Sstevel@tonic-gate   }
17807c478bd9Sstevel@tonic-gate   return zFull;
17817c478bd9Sstevel@tonic-gate #endif
17827c478bd9Sstevel@tonic-gate #if OS_WIN
17837c478bd9Sstevel@tonic-gate   char *zNotUsed;
17847c478bd9Sstevel@tonic-gate   char *zFull;
17857c478bd9Sstevel@tonic-gate   int nByte;
17867c478bd9Sstevel@tonic-gate   nByte = GetFullPathName(zRelative, 0, 0, &zNotUsed) + 1;
17877c478bd9Sstevel@tonic-gate   zFull = sqliteMalloc( nByte );
17887c478bd9Sstevel@tonic-gate   if( zFull==0 ) return 0;
17897c478bd9Sstevel@tonic-gate   GetFullPathName(zRelative, nByte, zFull, &zNotUsed);
17907c478bd9Sstevel@tonic-gate   return zFull;
17917c478bd9Sstevel@tonic-gate #endif
17927c478bd9Sstevel@tonic-gate #if OS_MAC
17937c478bd9Sstevel@tonic-gate   char *zFull = 0;
17947c478bd9Sstevel@tonic-gate   if( zRelative[0]==':' ){
17957c478bd9Sstevel@tonic-gate     char zBuf[_MAX_PATH+1];
17967c478bd9Sstevel@tonic-gate     sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), &(zRelative[1]),
17977c478bd9Sstevel@tonic-gate                     (char*)0);
17987c478bd9Sstevel@tonic-gate   }else{
17997c478bd9Sstevel@tonic-gate     if( strchr(zRelative, ':') ){
18007c478bd9Sstevel@tonic-gate       sqliteSetString(&zFull, zRelative, (char*)0);
18017c478bd9Sstevel@tonic-gate     }else{
18027c478bd9Sstevel@tonic-gate     char zBuf[_MAX_PATH+1];
18037c478bd9Sstevel@tonic-gate       sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, (char*)0);
18047c478bd9Sstevel@tonic-gate     }
18057c478bd9Sstevel@tonic-gate   }
18067c478bd9Sstevel@tonic-gate   return zFull;
18077c478bd9Sstevel@tonic-gate #endif
18087c478bd9Sstevel@tonic-gate }
18097c478bd9Sstevel@tonic-gate 
18107c478bd9Sstevel@tonic-gate /*
18117c478bd9Sstevel@tonic-gate ** The following variable, if set to a non-zero value, becomes the result
18127c478bd9Sstevel@tonic-gate ** returned from sqliteOsCurrentTime().  This is used for testing.
18137c478bd9Sstevel@tonic-gate */
18147c478bd9Sstevel@tonic-gate #ifdef SQLITE_TEST
18157c478bd9Sstevel@tonic-gate int sqlite_current_time = 0;
18167c478bd9Sstevel@tonic-gate #endif
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate /*
18197c478bd9Sstevel@tonic-gate ** Find the current time (in Universal Coordinated Time).  Write the
18207c478bd9Sstevel@tonic-gate ** current time and date as a Julian Day number into *prNow and
18217c478bd9Sstevel@tonic-gate ** return 0.  Return 1 if the time and date cannot be found.
18227c478bd9Sstevel@tonic-gate */
18237c478bd9Sstevel@tonic-gate int sqliteOsCurrentTime(double *prNow){
18247c478bd9Sstevel@tonic-gate #if OS_UNIX
18257c478bd9Sstevel@tonic-gate   time_t t;
18267c478bd9Sstevel@tonic-gate   time(&t);
18277c478bd9Sstevel@tonic-gate   *prNow = t/86400.0 + 2440587.5;
18287c478bd9Sstevel@tonic-gate #endif
18297c478bd9Sstevel@tonic-gate #if OS_WIN
18307c478bd9Sstevel@tonic-gate   FILETIME ft;
1831*1da57d55SToomas Soome   /* FILETIME structure is a 64-bit value representing the number of
1832*1da57d55SToomas Soome      100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
18337c478bd9Sstevel@tonic-gate   */
18347c478bd9Sstevel@tonic-gate   double now;
18357c478bd9Sstevel@tonic-gate   GetSystemTimeAsFileTime( &ft );
1836*1da57d55SToomas Soome   now = ((double)ft.dwHighDateTime) * 4294967296.0;
18377c478bd9Sstevel@tonic-gate   *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5;
18387c478bd9Sstevel@tonic-gate #endif
18397c478bd9Sstevel@tonic-gate #ifdef SQLITE_TEST
18407c478bd9Sstevel@tonic-gate   if( sqlite_current_time ){
18417c478bd9Sstevel@tonic-gate     *prNow = sqlite_current_time/86400.0 + 2440587.5;
18427c478bd9Sstevel@tonic-gate   }
18437c478bd9Sstevel@tonic-gate #endif
18447c478bd9Sstevel@tonic-gate   return 0;
18457c478bd9Sstevel@tonic-gate }
1846