📄 os.c
字号:
/*** 2001 September 16**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.************************************************************************************ This file contains code that is specific to particular operating** systems. The purpose of this file is to provide a uniform abstraction** on which the rest of SQLite can operate.*/#include "os.h" /* Must be first to enable large file support */#include "sqliteInt.h"#if OS_UNIX# include <time.h># include <errno.h># include <unistd.h># ifndef O_LARGEFILE# define O_LARGEFILE 0# endif# ifdef SQLITE_DISABLE_LFS# undef O_LARGEFILE# define O_LARGEFILE 0# endif# ifndef O_NOFOLLOW# define O_NOFOLLOW 0# endif# ifndef O_BINARY# define O_BINARY 0# endif#endif#if OS_WIN# include <winbase.h>#endif#if OS_MAC# include <extras.h># include <path2fss.h># include <TextUtils.h># include <FinderRegistry.h># include <Folders.h># include <Timer.h># include <OSUtils.h>#endif/*** The DJGPP compiler environment looks mostly like Unix, but it** lacks the fcntl() system call. So redefine fcntl() to be something** that always succeeds. This means that locking does not occur under** DJGPP. But its DOS - what did you expect?*/#ifdef __DJGPP__# define fcntl(A,B,C) 0#endif/*** Macros for performance tracing. Normally turned off*/#if 0static int last_page = 0;__inline__ unsigned long long int hwtime(void){ unsigned long long int x; __asm__("rdtsc\n\t" "mov %%edx, %%ecx\n\t" :"=A" (x)); return x;}static unsigned long long int g_start;static unsigned int elapse;#define TIMER_START g_start=hwtime()#define TIMER_END elapse=hwtime()-g_start#define SEEK(X) last_page=(X)#define TRACE1(X) fprintf(stderr,X)#define TRACE2(X,Y) fprintf(stderr,X,Y)#define TRACE3(X,Y,Z) fprintf(stderr,X,Y,Z)#define TRACE4(X,Y,Z,A) fprintf(stderr,X,Y,Z,A)#define TRACE5(X,Y,Z,A,B) fprintf(stderr,X,Y,Z,A,B)#else#define TIMER_START#define TIMER_END#define SEEK(X)#define TRACE1(X)#define TRACE2(X,Y)#define TRACE3(X,Y,Z)#define TRACE4(X,Y,Z,A)#define TRACE5(X,Y,Z,A,B)#endif#if OS_UNIX/*** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996)** section 6.5.2.2 lines 483 through 490 specify that when a process** sets or clears a lock, that operation overrides any prior locks set** by the same process. It does not explicitly say so, but this implies** that it overrides locks set by the same process using a different** file descriptor. Consider this test case:**** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);**** Suppose ./file1 and ./file2 are really the same file (because** one is a hard or symbolic link to the other) then if you set** an exclusive lock on fd1, then try to get an exclusive lock** on fd2, it works. I would have expected the second lock to** fail since there was already a lock on the file due to fd1.** But not so. Since both locks came from the same process, the** second overrides the first, even though they were on different** file descriptors opened on different file names.**** Bummer. If you ask me, this is broken. Badly broken. It means** that we cannot use POSIX locks to synchronize file access among** competing threads of the same process. POSIX locks will work fine** to synchronize access for threads in separate processes, but not** threads within the same process.**** To work around the problem, SQLite has to manage file locks internally** on its own. Whenever a new database is opened, we have to find the** specific inode of the database file (the inode is determined by the** st_dev and st_ino fields of the stat structure that fstat() fills in)** and check for locks already existing on that inode. When locks are** created or removed, we have to look at our own internal record of the** locks to see if another thread has previously set a lock on that same** inode.**** The OsFile structure for POSIX is no longer just an integer file** descriptor. It is now a structure that holds the integer file** descriptor and a pointer to a structure that describes the internal** locks on the corresponding inode. There is one locking structure** per inode, so if the same inode is opened twice, both OsFile structures** point to the same locking structure. The locking structure keeps** a reference count (so we will know when to delete it) and a "cnt"** field that tells us its internal lock status. cnt==0 means the** file is unlocked. cnt==-1 means the file has an exclusive lock.** cnt>0 means there are cnt shared locks on the file.**** Any attempt to lock or unlock a file first checks the locking** structure. The fcntl() system call is only invoked to set a ** POSIX lock if the internal lock structure transitions between** a locked and an unlocked state.*//*** An instance of the following structure serves as the key used** to locate a particular lockInfo structure given its inode. */struct inodeKey { dev_t dev; /* Device number */ ino_t ino; /* Inode number */};/*** An instance of the following structure is allocated for each inode.** A single inode can have multiple file descriptors, so each OsFile** structure contains a pointer to an instance of this object and this** object keeps a count of the number of OsFiles pointing to it.*/struct lockInfo { struct inodeKey key; /* The lookup key */ int cnt; /* 0: unlocked. -1: write lock. 1...: read lock. */ int nRef; /* Number of pointers to this structure */};/* ** This hash table maps inodes (in the form of inodeKey structures) into** pointers to lockInfo structures.*/static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };/*** Given a file descriptor, locate a lockInfo structure that describes** that file descriptor. Create a new one if necessary. NULL might** be returned if malloc() fails.*/static struct lockInfo *findLockInfo(int fd){ int rc; struct inodeKey key; struct stat statbuf; struct lockInfo *pInfo; rc = fstat(fd, &statbuf); if( rc!=0 ) return 0; memset(&key, 0, sizeof(key)); key.dev = statbuf.st_dev; key.ino = statbuf.st_ino; pInfo = (struct lockInfo*)sqliteHashFind(&lockHash, &key, sizeof(key)); if( pInfo==0 ){ struct lockInfo *pOld; pInfo = sqliteMalloc( sizeof(*pInfo) ); if( pInfo==0 ) return 0; pInfo->key = key; pInfo->nRef = 1; pInfo->cnt = 0; pOld = sqliteHashInsert(&lockHash, &pInfo->key, sizeof(key), pInfo); if( pOld!=0 ){ assert( pOld==pInfo ); sqliteFree(pInfo); pInfo = 0; } }else{ pInfo->nRef++; } return pInfo;}/*** Release a lockInfo structure previously allocated by findLockInfo().*/static void releaseLockInfo(struct lockInfo *pInfo){ pInfo->nRef--; if( pInfo->nRef==0 ){ sqliteHashInsert(&lockHash, &pInfo->key, sizeof(pInfo->key), 0); sqliteFree(pInfo); }}#endif /** POSIX advisory lock work-around **//*** If we compile with the SQLITE_TEST macro set, then the following block** of code will give us the ability to simulate a disk I/O error. This** is used for testing the I/O recovery logic.*/#ifdef SQLITE_TESTint sqlite_io_error_pending = 0;#define SimulateIOError(A) \ if( sqlite_io_error_pending ) \ if( sqlite_io_error_pending-- == 1 ){ local_ioerr(); return A; }static void local_ioerr(){ sqlite_io_error_pending = 0; /* Really just a place to set a breakpoint */}#else#define SimulateIOError(A)#endif/*** When testing, keep a count of the number of open files.*/#ifdef SQLITE_TESTint sqlite_open_file_count = 0;#define OpenCounter(X) sqlite_open_file_count+=(X)#else#define OpenCounter(X)#endif/*** Delete the named file*/int sqliteOsDelete(const char *zFilename){#if OS_UNIX unlink(zFilename);#endif#if OS_WIN DeleteFile(zFilename);#endif#if OS_MAC unlink(zFilename);#endif return SQLITE_OK;}/*** Return TRUE if the named file exists.*/int sqliteOsFileExists(const char *zFilename){#if OS_UNIX return access(zFilename, 0)==0;#endif#if OS_WIN return GetFileAttributes(zFilename) != 0xffffffff;#endif#if OS_MAC return access(zFilename, 0)==0;#endif}#if 0 /* NOT USED *//*** Change the name of an existing file.*/int sqliteOsFileRename(const char *zOldName, const char *zNewName){#if OS_UNIX if( link(zOldName, zNewName) ){ return SQLITE_ERROR; } unlink(zOldName); return SQLITE_OK;#endif#if OS_WIN if( !MoveFile(zOldName, zNewName) ){ return SQLITE_ERROR; } return SQLITE_OK;#endif#if OS_MAC /**** FIX ME ***/ return SQLITE_ERROR;#endif}#endif /* NOT USED *//*** Attempt to open a file for both reading and writing. If that** fails, try opening it read-only. If the file does not exist,** try to create it.**** On success, a handle for the open file is written to *id** and *pReadonly is set to 0 if the file was opened for reading and** writing or 1 if the file was opened read-only. The function returns** SQLITE_OK.**** On failure, the function returns SQLITE_CANTOPEN and leaves** *id and *pReadonly unchanged.*/int sqliteOsOpenReadWrite( const char *zFilename, OsFile *id, int *pReadonly){#if OS_UNIX id->dirfd = -1; id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644); if( id->fd<0 ){ id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); if( id->fd<0 ){ return SQLITE_CANTOPEN; } *pReadonly = 1; }else{ *pReadonly = 0; } sqliteOsEnterMutex(); id->pLock = findLockInfo(id->fd); sqliteOsLeaveMutex(); if( id->pLock==0 ){ close(id->fd); return SQLITE_NOMEM; } id->locked = 0; TRACE3("OPEN %-3d %s\n", id->fd, zFilename); OpenCounter(+1); return SQLITE_OK;#endif#if OS_WIN HANDLE h = CreateFile(zFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL ); if( h==INVALID_HANDLE_VALUE ){ h = CreateFile(zFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL ); if( h==INVALID_HANDLE_VALUE ){ return SQLITE_CANTOPEN; } *pReadonly = 1; }else{ *pReadonly = 0; } id->h = h; id->locked = 0; OpenCounter(+1); return SQLITE_OK;#endif#if OS_MAC FSSpec fsSpec;# ifdef _LARGE_FILE HFSUniStr255 dfName; FSRef fsRef; if( __path2fss(zFilename, &fsSpec) != noErr ){ if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) return SQLITE_CANTOPEN; } if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) return SQLITE_CANTOPEN; FSGetDataForkName(&dfName); if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, fsRdWrShPerm, &(id->refNum)) != noErr ){ if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, fsRdWrPerm, &(id->refNum)) != noErr ){ if (FSOpenFork(&fsRef, dfName.length, dfName.unicode, fsRdPerm, &(id->refNum)) != noErr ) return SQLITE_CANTOPEN; else *pReadonly = 1; } else *pReadonly = 0; } else *pReadonly = 0;# else __path2fss(zFilename, &fsSpec); if( !sqliteOsFileExists(zFilename) ){ if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) return SQLITE_CANTOPEN; } if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNum)) != noErr ){ if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ){ if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr ) return SQLITE_CANTOPEN; else *pReadonly = 1; } else *pReadonly = 0; } else *pReadonly = 0;# endif if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){ id->refNumRF = -1; } id->locked = 0; id->delOnClose = 0; OpenCounter(+1); return SQLITE_OK;#endif}/*** Attempt to open a new file for exclusive access by this process.** The file will be opened for both reading and writing. To avoid** a potential security problem, we do not allow the file to have** previously existed. Nor do we allow the file to be a symbolic** link.**** If delFlag is true, then make arrangements to automatically delete** the file when it is closed.**** On success, write the file handle into *id and return SQLITE_OK.**** On failure, return SQLITE_CANTOPEN.*/int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){#if OS_UNIX if( access(zFilename, 0)==0 ){ return SQLITE_CANTOPEN; } id->dirfd = -1; id->fd = open(zFilename, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600); if( id->fd<0 ){ return SQLITE_CANTOPEN; } sqliteOsEnterMutex(); id->pLock = findLockInfo(id->fd); sqliteOsLeaveMutex(); if( id->pLock==0 ){ close(id->fd); unlink(zFilename); return SQLITE_NOMEM; } id->locked = 0; if( delFlag ){ unlink(zFilename); } TRACE3("OPEN-EX %-3d %s\n", id->fd, zFilename); OpenCounter(+1); return SQLITE_OK;#endif#if OS_WIN HANDLE h; int fileflags; if( delFlag ){ fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE; }else{ fileflags = FILE_FLAG_RANDOM_ACCESS; } h = CreateFile(zFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, fileflags, NULL ); if( h==INVALID_HANDLE_VALUE ){ return SQLITE_CANTOPEN; } id->h = h; id->locked = 0; OpenCounter(+1); return SQLITE_OK;#endif#if OS_MAC FSSpec fsSpec;# ifdef _LARGE_FILE HFSUniStr255 dfName; FSRef fsRef; __path2fss(zFilename, &fsSpec); if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) return SQLITE_CANTOPEN; if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) return SQLITE_CANTOPEN; FSGetDataForkName(&dfName); if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, fsRdWrPerm, &(id->refNum)) != noErr ) return SQLITE_CANTOPEN;# else __path2fss(zFilename, &fsSpec); if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) return SQLITE_CANTOPEN; if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ) return SQLITE_CANTOPEN;# endif id->refNumRF = -1; id->locked = 0; id->delOnClose = delFlag; if (delFlag) id->pathToDel = sqliteOsFullPathname(zFilename); OpenCounter(+1); return SQLITE_OK;#endif}/*** Attempt to open a new file for read-only access.**** On success, write the file handle into *id and return SQLITE_OK.**** On failure, return SQLITE_CANTOPEN.*/int sqliteOsOpenReadOnly(const char *zFilename, OsFile *id){#if OS_UNIX id->dirfd = -1; id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); if( id->fd<0 ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -