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(¶ms, 0, sizeof(ParamBlockRec));
11597c478bd9Sstevel@tonic-gate params.ioParam.ioRefNum = id->refNum;
11607c478bd9Sstevel@tonic-gate if( PBFlushFileSync(¶ms) != 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(¶ms, 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(¶ms))!=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(¶ms);
14107c478bd9Sstevel@tonic-gate params.ioParam.ioPosOffset = FIRST_LOCKBYTE+lk;
14117c478bd9Sstevel@tonic-gate params.ioParam.ioReqCount = 1;
14127c478bd9Sstevel@tonic-gate res = PBLockRangeSync(¶ms);
14137c478bd9Sstevel@tonic-gate params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
14147c478bd9Sstevel@tonic-gate params.ioParam.ioReqCount = 1;
14157c478bd9Sstevel@tonic-gate PBUnlockRangeSync(¶ms);
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(¶ms, 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(¶ms))!=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(¶ms)==noErr ){
15177c478bd9Sstevel@tonic-gate params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1;
15187c478bd9Sstevel@tonic-gate params.ioParam.ioReqCount = N_LOCKBYTE;
15197c478bd9Sstevel@tonic-gate res = PBLockRangeSync(¶ms);
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(¶ms);
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(¶ms, 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(¶ms);
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(¶ms);
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