Support database file names longer than 260 characters using the new 'win32-longpath' VFS variant.
FossilOrigin-Name: 37e85e444cde18f061049437980b965d4485f43c
This commit is contained in:
parent
b324bc79e9
commit
3741827ea1
15
manifest
15
manifest
@ -1,5 +1,5 @@
|
||||
C Make\snames\sof\sprivate\sfunctions\sin\sthe\sWin32\sVFS\sconsistent.\s\sFix\scomment\stypo\sin\sWin32\smutex\simplementation.
|
||||
D 2013-08-28T02:37:29.527
|
||||
C Support\sdatabase\sfile\snames\slonger\sthan\s260\scharacters\susing\sthe\snew\s'win32-longpath'\sVFS\svariant.
|
||||
D 2013-08-28T05:49:39.789
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -203,7 +203,7 @@ F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be
|
||||
F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_unix.c c27a14a05061e4e690bd3949dc0246bda35e399d
|
||||
F src/os_win.c 0b9ab09dd3ccf8933f888c542a9b11b0bfa62659
|
||||
F src/os_win.c 26d752736dff0c7e4e384ab65b353cce1e7e19c5
|
||||
F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8
|
||||
F src/pager.h f094af9f6ececfaa8a1e93876905a4f34233fb0c
|
||||
F src/parse.y 27c6b4138497d6f8360ba7847da6ed48033f957f
|
||||
@ -226,7 +226,7 @@ F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c 659dad8ef30b54831306a244b43e37af4725a444
|
||||
F src/test1.c 870fc648a48cb6d6808393174f7ebe82b8c840fa
|
||||
F src/test1.c 031d00dbf1b48378e6d47af1a0f728c767320b96
|
||||
F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35
|
||||
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
|
||||
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
|
||||
@ -1057,6 +1057,7 @@ F test/whereF.test 136a7301512d72a08a272806c8767066311b7bc1
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
|
||||
F test/win32longpath.test 3018b1199270cb91194fc8cd8413912b6950577e
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
|
||||
F tool/build-all-msvc.bat c55f64ca200308fb5fa5c1ee751ea95a13977b5a x
|
||||
@ -1105,7 +1106,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P fc98092f4bd42d64059024f09547904c1d70a517
|
||||
R 1438e04cfc27e16b0dae733cf906e3e9
|
||||
P c3b82c5bf97cfb35544c5b1fbcdf7b9c4827d1cf
|
||||
R e20bd90758596867c642a3a14073401d
|
||||
U mistachkin
|
||||
Z 1716564dd5627ab9e624a1f87aa67863
|
||||
Z e8ceaa194e29622b0b6b8c77bb0db1ea
|
||||
|
@ -1 +1 @@
|
||||
c3b82c5bf97cfb35544c5b1fbcdf7b9c4827d1cf
|
||||
37e85e444cde18f061049437980b965d4485f43c
|
164
src/os_win.c
164
src/os_win.c
@ -2871,7 +2871,7 @@ static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
|
||||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static int winGetTempname(int nBuf, char *zBuf);
|
||||
static int winGetTempname(sqlite3_vfs *, char **);
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
static int winMapfile(winFile*, sqlite3_int64);
|
||||
#endif
|
||||
@ -2947,13 +2947,13 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_TEMPFILENAME: {
|
||||
char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname );
|
||||
if( zTFile ){
|
||||
winGetTempname(pFile->pVfs->mxPathname, zTFile);
|
||||
char *zTFile = 0;
|
||||
int rc = winGetTempname(pFile->pVfs, &zTFile);
|
||||
if( rc==SQLITE_OK ){
|
||||
*(char**)pArg = zTFile;
|
||||
}
|
||||
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
||||
return SQLITE_OK;
|
||||
OSTRACE(("FCNTL file=%p, rc=%d\n", pFile->h, rc));
|
||||
return rc;
|
||||
}
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
case SQLITE_FCNTL_MMAP_SIZE: {
|
||||
@ -3930,17 +3930,29 @@ static void *winConvertUtf8Filename(const char *zFilename){
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a temporary file name in zBuf. zBuf must be big enough to
|
||||
** hold at pVfs->mxPathname characters.
|
||||
** This function returns non-zero if the specified UTF-8 string buffer
|
||||
** ends with a directory separator character.
|
||||
*/
|
||||
static int winGetTempname(int nBuf, char *zBuf){
|
||||
static int winEndsInDirSep(char *zBuf){
|
||||
if( zBuf ){
|
||||
int nLen = sqlite3Strlen30(zBuf);
|
||||
return nLen>0 && winIsDirSep(zBuf[nLen-1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a temporary file name and store the resulting pointer into pzBuf.
|
||||
** The pointer returned in pzBuf must be freed via sqlite3_free().
|
||||
*/
|
||||
static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
|
||||
static char zChars[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789";
|
||||
size_t i, j;
|
||||
int nTempPath;
|
||||
char zTempPath[SQLITE_WIN32_MAX_PATH_BYTES+2];
|
||||
int nBuf, nLen;
|
||||
char *zBuf;
|
||||
|
||||
/* It's odd to simulate an io-error here, but really this is just
|
||||
** using the io-error infrastructure to test that SQLite handles this
|
||||
@ -3948,23 +3960,49 @@ static int winGetTempname(int nBuf, char *zBuf){
|
||||
*/
|
||||
SimulateIOError( return SQLITE_IOERR );
|
||||
|
||||
/* Allocate a temporary buffer to store the fully qualified file
|
||||
** name for the temporary file. If this fails, we cannot continue.
|
||||
*/
|
||||
nBuf = pVfs->mxPathname;
|
||||
zBuf = sqlite3MallocZero( nBuf+2 );
|
||||
if( !zBuf ){
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
|
||||
/* Figure out the effective temporary directory. First, check if one
|
||||
** has been explicitly set by the application; otherwise, use the one
|
||||
** configured by the operating system.
|
||||
*/
|
||||
assert( nBuf>30 );
|
||||
if( sqlite3_temp_directory ){
|
||||
sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s",
|
||||
sqlite3_temp_directory);
|
||||
sqlite3_snprintf(nBuf-30, zBuf, "%s%s", sqlite3_temp_directory,
|
||||
winEndsInDirSep(sqlite3_temp_directory) ? "" :
|
||||
winGetDirDep());
|
||||
}
|
||||
#if !SQLITE_OS_WINRT
|
||||
else if( osIsNT() ){
|
||||
char *zMulti;
|
||||
WCHAR zWidePath[SQLITE_WIN32_MAX_PATH_CHARS];
|
||||
if( osGetTempPathW(SQLITE_WIN32_MAX_PATH_CHARS-30, zWidePath)==0 ){
|
||||
LPWSTR zWidePath = sqlite3MallocZero( nBuf*sizeof(WCHAR) );
|
||||
if( !zWidePath ){
|
||||
sqlite3_free(zBuf);
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
if( osGetTempPathW(nBuf, zWidePath)==0 ){
|
||||
sqlite3_free(zWidePath);
|
||||
sqlite3_free(zBuf);
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
|
||||
return SQLITE_IOERR_GETTEMPPATH;
|
||||
}
|
||||
zMulti = winUnicodeToUtf8(zWidePath);
|
||||
if( zMulti ){
|
||||
sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s", zMulti);
|
||||
sqlite3_snprintf(nBuf-30, zBuf, "%s", zMulti);
|
||||
sqlite3_free(zMulti);
|
||||
sqlite3_free(zWidePath);
|
||||
}else{
|
||||
sqlite3_free(zWidePath);
|
||||
sqlite3_free(zBuf);
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
@ -3972,55 +4010,43 @@ static int winGetTempname(int nBuf, char *zBuf){
|
||||
#ifdef SQLITE_WIN32_HAS_ANSI
|
||||
else{
|
||||
char *zUtf8;
|
||||
char zMbcsPath[SQLITE_WIN32_MAX_PATH_BYTES];
|
||||
if( osGetTempPathA(SQLITE_WIN32_MAX_PATH_BYTES-30, zMbcsPath)==0 ){
|
||||
char *zMbcsPath = sqlite3MallocZero( nBuf );
|
||||
if( !zMbcsPath ){
|
||||
sqlite3_free(zBuf);
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
if( osGetTempPathA(nBuf, zMbcsPath)==0 ){
|
||||
sqlite3_free(zBuf);
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
|
||||
return SQLITE_IOERR_GETTEMPPATH;
|
||||
}
|
||||
zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
|
||||
if( zUtf8 ){
|
||||
sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s", zUtf8);
|
||||
sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8);
|
||||
sqlite3_free(zUtf8);
|
||||
}else{
|
||||
sqlite3_free(zBuf);
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
}
|
||||
#else
|
||||
else{
|
||||
/*
|
||||
** Compiled without ANSI support and the current operating system
|
||||
** is not Windows NT; therefore, just zero the temporary buffer.
|
||||
*/
|
||||
memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH_BYTES+2);
|
||||
}
|
||||
#endif /* SQLITE_WIN32_HAS_ANSI */
|
||||
#else
|
||||
else{
|
||||
/*
|
||||
** Compiled for WinRT and the sqlite3_temp_directory is not set;
|
||||
** therefore, just zero the temporary buffer.
|
||||
*/
|
||||
memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH_BYTES+2);
|
||||
}
|
||||
#endif /* !SQLITE_OS_WINRT */
|
||||
|
||||
/* Check that the output buffer is large enough for the temporary file
|
||||
** name. If it is not, return SQLITE_ERROR.
|
||||
*/
|
||||
nTempPath = sqlite3Strlen30(zTempPath);
|
||||
nLen = sqlite3Strlen30(zBuf);
|
||||
|
||||
if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
|
||||
if( (nLen + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
|
||||
sqlite3_free(zBuf);
|
||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
for(i=nTempPath; i>0 && winIsDirSep(zTempPath[i-1]); i--){}
|
||||
zTempPath[i] = 0;
|
||||
sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
|
||||
|
||||
sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ?
|
||||
"%s%s" SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX,
|
||||
zTempPath, winGetDirDep());
|
||||
j = sqlite3Strlen30(zBuf);
|
||||
sqlite3_randomness(15, &zBuf[j]);
|
||||
for(i=0; i<15; i++, j++){
|
||||
@ -4028,6 +4054,7 @@ static int winGetTempname(int nBuf, char *zBuf){
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
zBuf[j+1] = 0;
|
||||
*pzBuf = zBuf;
|
||||
|
||||
OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
|
||||
return SQLITE_OK;
|
||||
@ -4066,7 +4093,7 @@ static int winIsDir(const void *zConverted){
|
||||
** Open a file.
|
||||
*/
|
||||
static int winOpen(
|
||||
sqlite3_vfs *pVfs, /* Not used */
|
||||
sqlite3_vfs *pVfs, /* Used to get maximum path name length */
|
||||
const char *zName, /* Name of the file (UTF-8) */
|
||||
sqlite3_file *id, /* Write the SQLite file handle here */
|
||||
int flags, /* Open mode flags */
|
||||
@ -4089,7 +4116,7 @@ static int winOpen(
|
||||
/* If argument zPath is a NULL pointer, this function is required to open
|
||||
** a temporary file. Use this buffer to store the file name in.
|
||||
*/
|
||||
char zTmpname[SQLITE_WIN32_MAX_PATH_BYTES+2]; /* Buffer for temp filename */
|
||||
char *zTmpname = 0; /* For temporary filename, if necessary. */
|
||||
|
||||
int rc = SQLITE_OK; /* Function Return Code */
|
||||
#if !defined(NDEBUG) || SQLITE_OS_WINCE
|
||||
@ -4155,7 +4182,7 @@ static int winOpen(
|
||||
*/
|
||||
if( !zUtf8Name ){
|
||||
assert( isDelete && !isOpenJournal );
|
||||
rc = winGetTempname(SQLITE_WIN32_MAX_PATH_BYTES+2, zTmpname);
|
||||
rc = winGetTempname(pVfs, &zTmpname);
|
||||
if( rc!=SQLITE_OK ){
|
||||
OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
|
||||
return rc;
|
||||
@ -4173,12 +4200,14 @@ static int winOpen(
|
||||
/* Convert the filename to the system encoding. */
|
||||
zConverted = winConvertUtf8Filename(zUtf8Name);
|
||||
if( zConverted==0 ){
|
||||
sqlite3_free(zTmpname);
|
||||
OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
|
||||
if( winIsDir(zConverted) ){
|
||||
sqlite3_free(zConverted);
|
||||
sqlite3_free(zTmpname);
|
||||
OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
|
||||
return SQLITE_CANTOPEN_ISDIR;
|
||||
}
|
||||
@ -4277,6 +4306,7 @@ static int winOpen(
|
||||
pFile->lastErrno = lastErrno;
|
||||
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
|
||||
sqlite3_free(zConverted);
|
||||
sqlite3_free(zTmpname);
|
||||
if( isReadWrite && !isExclusive ){
|
||||
return winOpen(pVfs, zName, id,
|
||||
((flags|SQLITE_OPEN_READONLY) &
|
||||
@ -4305,6 +4335,7 @@ static int winOpen(
|
||||
){
|
||||
osCloseHandle(h);
|
||||
sqlite3_free(zConverted);
|
||||
sqlite3_free(zTmpname);
|
||||
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
|
||||
return rc;
|
||||
}
|
||||
@ -4314,6 +4345,7 @@ static int winOpen(
|
||||
#endif
|
||||
{
|
||||
sqlite3_free(zConverted);
|
||||
sqlite3_free(zTmpname);
|
||||
}
|
||||
|
||||
pFile->pMethod = &winIoMethod;
|
||||
@ -4587,7 +4619,6 @@ static int winFullPathname(
|
||||
#if defined(__CYGWIN__)
|
||||
SimulateIOError( return SQLITE_ERROR );
|
||||
UNUSED_PARAMETER(nFull);
|
||||
assert( pVfs->mxPathname>=SQLITE_WIN32_MAX_PATH_BYTES );
|
||||
assert( nFull>=pVfs->mxPathname );
|
||||
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
||||
/*
|
||||
@ -4596,15 +4627,21 @@ static int winFullPathname(
|
||||
** for converting the relative path name to an absolute
|
||||
** one by prepending the data directory and a slash.
|
||||
*/
|
||||
char zOut[SQLITE_WIN32_MAX_PATH_BYTES+1];
|
||||
char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
|
||||
if( !zOut ){
|
||||
winLogError(SQLITE_IOERR_NOMEM, 0, "winFullPathname", zRelative);
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut,
|
||||
SQLITE_WIN32_MAX_PATH_BYTES+1)<0 ){
|
||||
pVfs->mxPathname+1)<0 ){
|
||||
winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path",
|
||||
zRelative);
|
||||
sqlite3_free(zOut);
|
||||
return SQLITE_CANTOPEN_FULLPATH;
|
||||
}
|
||||
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s",
|
||||
sqlite3_data_directory, winGetDirDep(), zOut);
|
||||
sqlite3_free(zOut);
|
||||
}else{
|
||||
if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){
|
||||
winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path",
|
||||
@ -4973,6 +5010,32 @@ int sqlite3_os_init(void){
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
};
|
||||
#if defined(SQLITE_WIN32_HAS_WIDE)
|
||||
static sqlite3_vfs winLongPathVfs = {
|
||||
3, /* iVersion */
|
||||
sizeof(winFile), /* szOsFile */
|
||||
SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"win32-longpath", /* zName */
|
||||
0, /* pAppData */
|
||||
winOpen, /* xOpen */
|
||||
winDelete, /* xDelete */
|
||||
winAccess, /* xAccess */
|
||||
winFullPathname, /* xFullPathname */
|
||||
winDlOpen, /* xDlOpen */
|
||||
winDlError, /* xDlError */
|
||||
winDlSym, /* xDlSym */
|
||||
winDlClose, /* xDlClose */
|
||||
winRandomness, /* xRandomness */
|
||||
winSleep, /* xSleep */
|
||||
winCurrentTime, /* xCurrentTime */
|
||||
winGetLastError, /* xGetLastError */
|
||||
winCurrentTimeInt64, /* xCurrentTimeInt64 */
|
||||
winSetSystemCall, /* xSetSystemCall */
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Double-check that the aSyscall[] array has been constructed
|
||||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||||
@ -4989,6 +5052,11 @@ int sqlite3_os_init(void){
|
||||
assert( winSysInfo.dwPageSize>0 );
|
||||
|
||||
sqlite3_vfs_register(&winVfs, 1);
|
||||
|
||||
#if defined(SQLITE_WIN32_HAS_WIDE)
|
||||
sqlite3_vfs_register(&winLongPathVfs, 0);
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
144
src/test1.c
144
src/test1.c
@ -5934,6 +5934,145 @@ static int win32_file_lock(
|
||||
CloseHandle(ev);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** exists_win32_path PATH
|
||||
**
|
||||
** Returns non-zero if the specified path exists, whose fully qualified name
|
||||
** may exceed 248 characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_exists_path(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATH");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
|
||||
GetFileAttributesW( Tcl_GetUnicode(objv[1]))!=INVALID_FILE_ATTRIBUTES ));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** find_win32_file PATTERN
|
||||
**
|
||||
** Returns a list of entries in a directory that match the specified pattern,
|
||||
** whose fully qualified name may exceed 248 characters if it is prefixed with
|
||||
** "\\?\".
|
||||
*/
|
||||
static int win32_find_file(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
HANDLE hFindFile = INVALID_HANDLE_VALUE;
|
||||
WIN32_FIND_DATAW findData;
|
||||
Tcl_Obj *listObj;
|
||||
DWORD lastErrno;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATTERN");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
hFindFile = FindFirstFileW(Tcl_GetUnicode(objv[1]), &findData);
|
||||
if( hFindFile==INVALID_HANDLE_VALUE ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
listObj = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(listObj);
|
||||
do {
|
||||
Tcl_ListObjAppendElement(interp, listObj, Tcl_NewUnicodeObj(
|
||||
findData.cFileName, -1));
|
||||
Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(
|
||||
findData.dwFileAttributes));
|
||||
} while( FindNextFileW(hFindFile, &findData) );
|
||||
lastErrno = GetLastError();
|
||||
if( lastErrno!=NO_ERROR && lastErrno!=ERROR_NO_MORE_FILES ){
|
||||
FindClose(hFindFile);
|
||||
Tcl_DecrRefCount(listObj);
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
FindClose(hFindFile);
|
||||
Tcl_SetObjResult(interp, listObj);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** delete_win32_file FILENAME
|
||||
**
|
||||
** Deletes the specified file, whose fully qualified name may exceed 248
|
||||
** characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_delete_file(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( !DeleteFileW(Tcl_GetUnicode(objv[1])) ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** make_win32_dir DIRECTORY
|
||||
**
|
||||
** Creates the specified directory, whose fully qualified name may exceed 248
|
||||
** characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_mkdir(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( !CreateDirectoryW(Tcl_GetUnicode(objv[1]), NULL) ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** remove_win32_dir DIRECTORY
|
||||
**
|
||||
** Removes the specified directory, whose fully qualified name may exceed 248
|
||||
** characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_rmdir(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( !RemoveDirectoryW(Tcl_GetUnicode(objv[1])) ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -6193,6 +6332,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "optimization_control", optimization_control,0},
|
||||
#if SQLITE_OS_WIN
|
||||
{ "lock_win32_file", win32_file_lock, 0 },
|
||||
{ "exists_win32_path", win32_exists_path, 0 },
|
||||
{ "find_win32_file", win32_find_file, 0 },
|
||||
{ "delete_win32_file", win32_delete_file, 0 },
|
||||
{ "make_win32_dir", win32_mkdir, 0 },
|
||||
{ "remove_win32_dir", win32_rmdir, 0 },
|
||||
#endif
|
||||
{ "tcl_objproc", runAsObjProc, 0 },
|
||||
|
||||
|
160
test/win32longpath.test
Normal file
160
test/win32longpath.test
Normal file
@ -0,0 +1,160 @@
|
||||
# 2013 August 27
|
||||
#
|
||||
# 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 implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the file name handling provided
|
||||
# by the "win32-longpath" VFS.
|
||||
#
|
||||
|
||||
if {$tcl_platform(platform)!="windows"} return
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix win32longpath
|
||||
|
||||
proc do_remove_win32_dir {args} {
|
||||
set nRetry [getFileRetries] ;# Maximum number of retries.
|
||||
set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
|
||||
|
||||
foreach dirName $args {
|
||||
# On windows, sometimes even a [remove_win32_dir] can fail just after
|
||||
# a directory is emptied. The cause is usually "tag-alongs" - programs
|
||||
# like anti-virus software, automatic backup tools and various explorer
|
||||
# extensions that keep a file open a little longer than we expect,
|
||||
# causing the delete to fail.
|
||||
#
|
||||
# The solution is to wait a short amount of time before retrying the
|
||||
# removal.
|
||||
#
|
||||
if {$nRetry > 0} {
|
||||
for {set i 0} {$i < $nRetry} {incr i} {
|
||||
set rc [catch {
|
||||
remove_win32_dir $dirName
|
||||
} msg]
|
||||
if {$rc == 0} break
|
||||
if {$nDelay > 0} { after $nDelay }
|
||||
}
|
||||
if {$rc} { error $msg }
|
||||
} else {
|
||||
remove_win32_dir $dirName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc do_delete_win32_file {args} {
|
||||
set nRetry [getFileRetries] ;# Maximum number of retries.
|
||||
set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
|
||||
|
||||
foreach fileName $args {
|
||||
# On windows, sometimes even a [delete_win32_file] can fail just after
|
||||
# a file is closed. The cause is usually "tag-alongs" - programs like
|
||||
# anti-virus software, automatic backup tools and various explorer
|
||||
# extensions that keep a file open a little longer than we expect,
|
||||
# causing the delete to fail.
|
||||
#
|
||||
# The solution is to wait a short amount of time before retrying the
|
||||
# delete.
|
||||
#
|
||||
if {$nRetry > 0} {
|
||||
for {set i 0} {$i < $nRetry} {incr i} {
|
||||
set rc [catch {
|
||||
delete_win32_file $fileName
|
||||
} msg]
|
||||
if {$rc == 0} break
|
||||
if {$nDelay > 0} { after $nDelay }
|
||||
}
|
||||
if {$rc} { error $msg }
|
||||
} else {
|
||||
delete_win32_file $fileName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db close
|
||||
set path [file nativename [get_pwd]]
|
||||
sqlite3 db [file join $path test.db] -vfs win32-longpath
|
||||
|
||||
do_test 1.1 {
|
||||
db eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {1 2 3 4}
|
||||
|
||||
set longPath(1) \\\\?\\$path\\[pid]
|
||||
make_win32_dir $longPath(1)
|
||||
|
||||
set longPath(2) $longPath(1)\\[string repeat X 255]
|
||||
make_win32_dir $longPath(2)
|
||||
|
||||
set longPath(3) $longPath(2)\\[string repeat Y 255]
|
||||
make_win32_dir $longPath(3)
|
||||
|
||||
set fileName $longPath(3)\\test.db
|
||||
|
||||
do_test 1.2 {
|
||||
list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg
|
||||
} {1 {unable to open database file}}
|
||||
|
||||
sqlite3 db3 $fileName -vfs win32-longpath
|
||||
|
||||
do_test 1.3 {
|
||||
db3 eval {
|
||||
PRAGMA journal_mode = WAL;
|
||||
}
|
||||
} {wal}
|
||||
|
||||
do_test 1.4 {
|
||||
db3 eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(5);
|
||||
INSERT INTO t1 VALUES(6);
|
||||
INSERT INTO t1 VALUES(7);
|
||||
INSERT INTO t1 VALUES(8);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {5 6 7 8}
|
||||
|
||||
db3 close
|
||||
# puts " Database exists \{[exists_win32_path $fileName]\}"
|
||||
|
||||
sqlite3 db3 $fileName -vfs win32-longpath
|
||||
|
||||
do_test 1.5 {
|
||||
db3 eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
INSERT INTO t1 VALUES(9);
|
||||
INSERT INTO t1 VALUES(10);
|
||||
INSERT INTO t1 VALUES(11);
|
||||
INSERT INTO t1 VALUES(12);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {5 6 7 8 9 10 11 12}
|
||||
|
||||
db3 close
|
||||
# puts " Database exists \{[exists_win32_path $fileName]\}"
|
||||
|
||||
do_delete_win32_file $fileName
|
||||
# puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}"
|
||||
|
||||
do_remove_win32_dir $longPath(3)
|
||||
do_remove_win32_dir $longPath(2)
|
||||
do_remove_win32_dir $longPath(1)
|
||||
|
||||
finish_test
|
Loading…
x
Reference in New Issue
Block a user