/ Check-in [37e85e44]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Support database file names longer than 260 characters using the new 'win32-longpath' VFS variant.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | mmapDisabled
Files: files | file ages | folders
SHA1: 37e85e444cde18f061049437980b965d4485f43c
User & Date: mistachkin 2013-08-28 05:49:39
Context
2013-08-28
07:42
In the win32longpath test, move the journal mode change down where it was intended to be. check-in: 5cead293 user: mistachkin tags: mmapDisabled
05:49
Support database file names longer than 260 characters using the new 'win32-longpath' VFS variant. check-in: 37e85e44 user: mistachkin tags: mmapDisabled
02:37
Make names of private functions in the Win32 VFS consistent. Fix comment typo in Win32 mutex implementation. check-in: c3b82c5b user: mistachkin tags: mmapDisabled
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/os_win.c.

  2867   2867       pFile->ctrlFlags &= ~mask;
  2868   2868     }else{
  2869   2869       pFile->ctrlFlags |= mask;
  2870   2870     }
  2871   2871   }
  2872   2872   
  2873   2873   /* Forward declaration */
  2874         -static int winGetTempname(int nBuf, char *zBuf);
         2874  +static int winGetTempname(sqlite3_vfs *, char **);
  2875   2875   #if SQLITE_MAX_MMAP_SIZE>0
  2876   2876   static int winMapfile(winFile*, sqlite3_int64);
  2877   2877   #endif
  2878   2878   
  2879   2879   /*
  2880   2880   ** Control and query of the open file handle.
  2881   2881   */
................................................................................
  2943   2943         }else{
  2944   2944           a[1] = winIoerrRetryDelay;
  2945   2945         }
  2946   2946         OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
  2947   2947         return SQLITE_OK;
  2948   2948       }
  2949   2949       case SQLITE_FCNTL_TEMPFILENAME: {
  2950         -      char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname );
  2951         -      if( zTFile ){
  2952         -        winGetTempname(pFile->pVfs->mxPathname, zTFile);
         2950  +      char *zTFile = 0;
         2951  +      int rc = winGetTempname(pFile->pVfs, &zTFile);
         2952  +      if( rc==SQLITE_OK ){
  2953   2953           *(char**)pArg = zTFile;
  2954   2954         }
  2955         -      OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
  2956         -      return SQLITE_OK;
         2955  +      OSTRACE(("FCNTL file=%p, rc=%d\n", pFile->h, rc));
         2956  +      return rc;
  2957   2957       }
  2958   2958   #if SQLITE_MAX_MMAP_SIZE>0
  2959   2959       case SQLITE_FCNTL_MMAP_SIZE: {
  2960   2960         i64 newLimit = *(i64*)pArg;
  2961   2961         int rc = SQLITE_OK;
  2962   2962         if( newLimit>sqlite3GlobalConfig.mxMmap ){
  2963   2963           newLimit = sqlite3GlobalConfig.mxMmap;
................................................................................
  3926   3926     }
  3927   3927   #endif
  3928   3928     /* caller will handle out of memory */
  3929   3929     return zConverted;
  3930   3930   }
  3931   3931   
  3932   3932   /*
  3933         -** Create a temporary file name in zBuf.  zBuf must be big enough to
  3934         -** hold at pVfs->mxPathname characters.
         3933  +** This function returns non-zero if the specified UTF-8 string buffer
         3934  +** ends with a directory separator character.
  3935   3935   */
  3936         -static int winGetTempname(int nBuf, char *zBuf){
         3936  +static int winEndsInDirSep(char *zBuf){
         3937  +  if( zBuf ){
         3938  +    int nLen = sqlite3Strlen30(zBuf);
         3939  +    return nLen>0 && winIsDirSep(zBuf[nLen-1]);
         3940  +  }
         3941  +  return 0;
         3942  +}
         3943  +
         3944  +/*
         3945  +** Create a temporary file name and store the resulting pointer into pzBuf.
         3946  +** The pointer returned in pzBuf must be freed via sqlite3_free().
         3947  +*/
         3948  +static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
  3937   3949     static char zChars[] =
  3938   3950       "abcdefghijklmnopqrstuvwxyz"
  3939   3951       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  3940   3952       "0123456789";
  3941   3953     size_t i, j;
  3942         -  int nTempPath;
  3943         -  char zTempPath[SQLITE_WIN32_MAX_PATH_BYTES+2];
         3954  +  int nBuf, nLen;
         3955  +  char *zBuf;
  3944   3956   
  3945   3957     /* It's odd to simulate an io-error here, but really this is just
  3946   3958     ** using the io-error infrastructure to test that SQLite handles this
  3947   3959     ** function failing. 
  3948   3960     */
  3949   3961     SimulateIOError( return SQLITE_IOERR );
  3950   3962   
         3963  +  /* Allocate a temporary buffer to store the fully qualified file
         3964  +  ** name for the temporary file.  If this fails, we cannot continue.
         3965  +  */
         3966  +  nBuf = pVfs->mxPathname;
         3967  +  zBuf = sqlite3MallocZero( nBuf+2 );
         3968  +  if( !zBuf ){
         3969  +    OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
         3970  +    return SQLITE_IOERR_NOMEM;
         3971  +  }
         3972  +
         3973  +  /* Figure out the effective temporary directory.  First, check if one
         3974  +  ** has been explicitly set by the application; otherwise, use the one
         3975  +  ** configured by the operating system.
         3976  +  */
         3977  +  assert( nBuf>30 );
  3951   3978     if( sqlite3_temp_directory ){
  3952         -    sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s",
  3953         -                     sqlite3_temp_directory);
         3979  +    sqlite3_snprintf(nBuf-30, zBuf, "%s%s", sqlite3_temp_directory,
         3980  +                     winEndsInDirSep(sqlite3_temp_directory) ? "" :
         3981  +                     winGetDirDep());
  3954   3982     }
  3955   3983   #if !SQLITE_OS_WINRT
  3956   3984     else if( osIsNT() ){
  3957   3985       char *zMulti;
  3958         -    WCHAR zWidePath[SQLITE_WIN32_MAX_PATH_CHARS];
  3959         -    if( osGetTempPathW(SQLITE_WIN32_MAX_PATH_CHARS-30, zWidePath)==0 ){
         3986  +    LPWSTR zWidePath = sqlite3MallocZero( nBuf*sizeof(WCHAR) );
         3987  +    if( !zWidePath ){
         3988  +      sqlite3_free(zBuf);
         3989  +      OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
         3990  +      return SQLITE_IOERR_NOMEM;
         3991  +    }
         3992  +    if( osGetTempPathW(nBuf, zWidePath)==0 ){
         3993  +      sqlite3_free(zWidePath);
         3994  +      sqlite3_free(zBuf);
  3960   3995         OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
  3961   3996         return SQLITE_IOERR_GETTEMPPATH;
  3962   3997       }
  3963   3998       zMulti = winUnicodeToUtf8(zWidePath);
  3964   3999       if( zMulti ){
  3965         -      sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s", zMulti);
         4000  +      sqlite3_snprintf(nBuf-30, zBuf, "%s", zMulti);
  3966   4001         sqlite3_free(zMulti);
         4002  +      sqlite3_free(zWidePath);
  3967   4003       }else{
         4004  +      sqlite3_free(zWidePath);
         4005  +      sqlite3_free(zBuf);
  3968   4006         OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
  3969   4007         return SQLITE_IOERR_NOMEM;
  3970   4008       }
  3971   4009     }
  3972   4010   #ifdef SQLITE_WIN32_HAS_ANSI
  3973   4011     else{
  3974   4012       char *zUtf8;
  3975         -    char zMbcsPath[SQLITE_WIN32_MAX_PATH_BYTES];
  3976         -    if( osGetTempPathA(SQLITE_WIN32_MAX_PATH_BYTES-30, zMbcsPath)==0 ){
         4013  +    char *zMbcsPath = sqlite3MallocZero( nBuf );
         4014  +    if( !zMbcsPath ){
         4015  +      sqlite3_free(zBuf);
         4016  +      OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
         4017  +      return SQLITE_IOERR_NOMEM;
         4018  +    }
         4019  +    if( osGetTempPathA(nBuf, zMbcsPath)==0 ){
         4020  +      sqlite3_free(zBuf);
  3977   4021         OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
  3978   4022         return SQLITE_IOERR_GETTEMPPATH;
  3979   4023       }
  3980   4024       zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
  3981   4025       if( zUtf8 ){
  3982         -      sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s", zUtf8);
         4026  +      sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8);
  3983   4027         sqlite3_free(zUtf8);
  3984   4028       }else{
         4029  +      sqlite3_free(zBuf);
  3985   4030         OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
  3986   4031         return SQLITE_IOERR_NOMEM;
  3987   4032       }
  3988   4033     }
  3989         -#else
  3990         -  else{
  3991         -    /*
  3992         -    ** Compiled without ANSI support and the current operating system
  3993         -    ** is not Windows NT; therefore, just zero the temporary buffer.
  3994         -    */
  3995         -    memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH_BYTES+2);
  3996         -  }
  3997   4034   #endif /* SQLITE_WIN32_HAS_ANSI */
  3998         -#else
  3999         -  else{
  4000         -    /*
  4001         -    ** Compiled for WinRT and the sqlite3_temp_directory is not set;
  4002         -    ** therefore, just zero the temporary buffer.
  4003         -    */
  4004         -    memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH_BYTES+2);
  4005         -  }
  4006   4035   #endif /* !SQLITE_OS_WINRT */
  4007   4036   
  4008   4037     /* Check that the output buffer is large enough for the temporary file 
  4009   4038     ** name. If it is not, return SQLITE_ERROR.
  4010   4039     */
  4011         -  nTempPath = sqlite3Strlen30(zTempPath);
         4040  +  nLen = sqlite3Strlen30(zBuf);
  4012   4041   
  4013         -  if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
         4042  +  if( (nLen + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
         4043  +    sqlite3_free(zBuf);
  4014   4044       OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
  4015   4045       return SQLITE_ERROR;
  4016   4046     }
  4017   4047   
  4018         -  for(i=nTempPath; i>0 && winIsDirSep(zTempPath[i-1]); i--){}
  4019         -  zTempPath[i] = 0;
         4048  +  sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
  4020   4049   
  4021         -  sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ?
  4022         -                       "%s%s" SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX,
  4023         -                   zTempPath, winGetDirDep());
  4024   4050     j = sqlite3Strlen30(zBuf);
  4025   4051     sqlite3_randomness(15, &zBuf[j]);
  4026   4052     for(i=0; i<15; i++, j++){
  4027   4053       zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
  4028   4054     }
  4029   4055     zBuf[j] = 0;
  4030   4056     zBuf[j+1] = 0;
         4057  +  *pzBuf = zBuf;
  4031   4058   
  4032   4059     OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
  4033   4060     return SQLITE_OK;
  4034   4061   }
  4035   4062   
  4036   4063   /*
  4037   4064   ** Return TRUE if the named file is really a directory.  Return false if
................................................................................
  4062   4089     return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
  4063   4090   }
  4064   4091   
  4065   4092   /*
  4066   4093   ** Open a file.
  4067   4094   */
  4068   4095   static int winOpen(
  4069         -  sqlite3_vfs *pVfs,        /* Not used */
         4096  +  sqlite3_vfs *pVfs,        /* Used to get maximum path name length */
  4070   4097     const char *zName,        /* Name of the file (UTF-8) */
  4071   4098     sqlite3_file *id,         /* Write the SQLite file handle here */
  4072   4099     int flags,                /* Open mode flags */
  4073   4100     int *pOutFlags            /* Status return flags */
  4074   4101   ){
  4075   4102     HANDLE h;
  4076   4103     DWORD lastErrno;
................................................................................
  4085   4112     void *zConverted;              /* Filename in OS encoding */
  4086   4113     const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
  4087   4114     int cnt = 0;
  4088   4115   
  4089   4116     /* If argument zPath is a NULL pointer, this function is required to open
  4090   4117     ** a temporary file. Use this buffer to store the file name in.
  4091   4118     */
  4092         -  char zTmpname[SQLITE_WIN32_MAX_PATH_BYTES+2]; /* Buffer for temp filename */
         4119  +  char *zTmpname = 0; /* For temporary filename, if necessary. */
  4093   4120   
  4094   4121     int rc = SQLITE_OK;            /* Function Return Code */
  4095   4122   #if !defined(NDEBUG) || SQLITE_OS_WINCE
  4096   4123     int eType = flags&0xFFFFFF00;  /* Type of file to open */
  4097   4124   #endif
  4098   4125   
  4099   4126     int isExclusive  = (flags & SQLITE_OPEN_EXCLUSIVE);
................................................................................
  4151   4178   #endif
  4152   4179   
  4153   4180     /* If the second argument to this function is NULL, generate a 
  4154   4181     ** temporary file name to use 
  4155   4182     */
  4156   4183     if( !zUtf8Name ){
  4157   4184       assert( isDelete && !isOpenJournal );
  4158         -    rc = winGetTempname(SQLITE_WIN32_MAX_PATH_BYTES+2, zTmpname);
         4185  +    rc = winGetTempname(pVfs, &zTmpname);
  4159   4186       if( rc!=SQLITE_OK ){
  4160   4187         OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
  4161   4188         return rc;
  4162   4189       }
  4163   4190       zUtf8Name = zTmpname;
  4164   4191     }
  4165   4192   
................................................................................
  4169   4196     */
  4170   4197     assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) ||
  4171   4198          zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
  4172   4199   
  4173   4200     /* Convert the filename to the system encoding. */
  4174   4201     zConverted = winConvertUtf8Filename(zUtf8Name);
  4175   4202     if( zConverted==0 ){
         4203  +    sqlite3_free(zTmpname);
  4176   4204       OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
  4177   4205       return SQLITE_IOERR_NOMEM;
  4178   4206     }
  4179   4207   
  4180   4208     if( winIsDir(zConverted) ){
  4181   4209       sqlite3_free(zConverted);
         4210  +    sqlite3_free(zTmpname);
  4182   4211       OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
  4183   4212       return SQLITE_CANTOPEN_ISDIR;
  4184   4213     }
  4185   4214   
  4186   4215     if( isReadWrite ){
  4187   4216       dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
  4188   4217     }else{
................................................................................
  4273   4302     OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name,
  4274   4303              dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
  4275   4304   
  4276   4305     if( h==INVALID_HANDLE_VALUE ){
  4277   4306       pFile->lastErrno = lastErrno;
  4278   4307       winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
  4279   4308       sqlite3_free(zConverted);
         4309  +    sqlite3_free(zTmpname);
  4280   4310       if( isReadWrite && !isExclusive ){
  4281   4311         return winOpen(pVfs, zName, id, 
  4282   4312            ((flags|SQLITE_OPEN_READONLY) &
  4283   4313                        ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
  4284   4314            pOutFlags);
  4285   4315       }else{
  4286   4316         return SQLITE_CANTOPEN_BKPT;
................................................................................
  4301   4331   
  4302   4332   #if SQLITE_OS_WINCE
  4303   4333     if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
  4304   4334          && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
  4305   4335     ){
  4306   4336       osCloseHandle(h);
  4307   4337       sqlite3_free(zConverted);
         4338  +    sqlite3_free(zTmpname);
  4308   4339       OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
  4309   4340       return rc;
  4310   4341     }
  4311   4342     if( isTemp ){
  4312   4343       pFile->zDeleteOnClose = zConverted;
  4313   4344     }else
  4314   4345   #endif
  4315   4346     {
  4316   4347       sqlite3_free(zConverted);
         4348  +    sqlite3_free(zTmpname);
  4317   4349     }
  4318   4350   
  4319   4351     pFile->pMethod = &winIoMethod;
  4320   4352     pFile->pVfs = pVfs;
  4321   4353     pFile->h = h;
  4322   4354     if( isReadonly ){
  4323   4355       pFile->ctrlFlags |= WINFILE_RDONLY;
................................................................................
  4583   4615     int nFull,                    /* Size of output buffer in bytes */
  4584   4616     char *zFull                   /* Output buffer */
  4585   4617   ){
  4586   4618     
  4587   4619   #if defined(__CYGWIN__)
  4588   4620     SimulateIOError( return SQLITE_ERROR );
  4589   4621     UNUSED_PARAMETER(nFull);
  4590         -  assert( pVfs->mxPathname>=SQLITE_WIN32_MAX_PATH_BYTES );
  4591   4622     assert( nFull>=pVfs->mxPathname );
  4592   4623     if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
  4593   4624       /*
  4594   4625       ** NOTE: We are dealing with a relative path name and the data
  4595   4626       **       directory has been set.  Therefore, use it as the basis
  4596   4627       **       for converting the relative path name to an absolute
  4597   4628       **       one by prepending the data directory and a slash.
  4598   4629       */
  4599         -    char zOut[SQLITE_WIN32_MAX_PATH_BYTES+1];
         4630  +    char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
         4631  +    if( !zOut ){
         4632  +      winLogError(SQLITE_IOERR_NOMEM, 0, "winFullPathname", zRelative);
         4633  +      return SQLITE_IOERR_NOMEM;
         4634  +    }
  4600   4635       if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut,
  4601         -                         SQLITE_WIN32_MAX_PATH_BYTES+1)<0 ){
         4636  +                         pVfs->mxPathname+1)<0 ){
  4602   4637         winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path",
  4603   4638                     zRelative);
         4639  +      sqlite3_free(zOut);
  4604   4640         return SQLITE_CANTOPEN_FULLPATH;
  4605   4641       }
  4606   4642       sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s",
  4607   4643                        sqlite3_data_directory, winGetDirDep(), zOut);
         4644  +    sqlite3_free(zOut);
  4608   4645     }else{
  4609   4646       if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){
  4610   4647         winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path",
  4611   4648                     zRelative);
  4612   4649         return SQLITE_CANTOPEN_FULLPATH;
  4613   4650       }
  4614   4651     }
................................................................................
  4969   5006       winCurrentTime,      /* xCurrentTime */
  4970   5007       winGetLastError,     /* xGetLastError */
  4971   5008       winCurrentTimeInt64, /* xCurrentTimeInt64 */
  4972   5009       winSetSystemCall,    /* xSetSystemCall */
  4973   5010       winGetSystemCall,    /* xGetSystemCall */
  4974   5011       winNextSystemCall,   /* xNextSystemCall */
  4975   5012     };
         5013  +#if defined(SQLITE_WIN32_HAS_WIDE)
         5014  +  static sqlite3_vfs winLongPathVfs = {
         5015  +    3,                   /* iVersion */
         5016  +    sizeof(winFile),     /* szOsFile */
         5017  +    SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
         5018  +    0,                   /* pNext */
         5019  +    "win32-longpath",    /* zName */
         5020  +    0,                   /* pAppData */
         5021  +    winOpen,             /* xOpen */
         5022  +    winDelete,           /* xDelete */
         5023  +    winAccess,           /* xAccess */
         5024  +    winFullPathname,     /* xFullPathname */
         5025  +    winDlOpen,           /* xDlOpen */
         5026  +    winDlError,          /* xDlError */
         5027  +    winDlSym,            /* xDlSym */
         5028  +    winDlClose,          /* xDlClose */
         5029  +    winRandomness,       /* xRandomness */
         5030  +    winSleep,            /* xSleep */
         5031  +    winCurrentTime,      /* xCurrentTime */
         5032  +    winGetLastError,     /* xGetLastError */
         5033  +    winCurrentTimeInt64, /* xCurrentTimeInt64 */
         5034  +    winSetSystemCall,    /* xSetSystemCall */
         5035  +    winGetSystemCall,    /* xGetSystemCall */
         5036  +    winNextSystemCall,   /* xNextSystemCall */
         5037  +  };
         5038  +#endif
  4976   5039   
  4977   5040     /* Double-check that the aSyscall[] array has been constructed
  4978   5041     ** correctly.  See ticket [bb3a86e890c8e96ab] */
  4979   5042     assert( ArraySize(aSyscall)==74 );
  4980   5043   
  4981   5044     /* get memory map allocation granularity */
  4982   5045     memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
................................................................................
  4985   5048   #else
  4986   5049     osGetSystemInfo(&winSysInfo);
  4987   5050   #endif
  4988   5051     assert( winSysInfo.dwAllocationGranularity>0 );
  4989   5052     assert( winSysInfo.dwPageSize>0 );
  4990   5053   
  4991   5054     sqlite3_vfs_register(&winVfs, 1);
         5055  +
         5056  +#if defined(SQLITE_WIN32_HAS_WIDE)
         5057  +  sqlite3_vfs_register(&winLongPathVfs, 0);
         5058  +#endif
         5059  +
  4992   5060     return SQLITE_OK; 
  4993   5061   }
  4994   5062   
  4995   5063   int sqlite3_os_end(void){ 
  4996   5064   #if SQLITE_OS_WINRT
  4997   5065     if( sleepObj!=NULL ){
  4998   5066       osCloseHandle(sleepObj);

Changes to src/test1.c.

  5930   5930       Tcl_AppendResult(interp, "wait failed: ", zBuf, (char*)0);
  5931   5931       CloseHandle(ev);
  5932   5932       return TCL_ERROR;
  5933   5933     }
  5934   5934     CloseHandle(ev);
  5935   5935     return TCL_OK;
  5936   5936   }
         5937  +
         5938  +/*
         5939  +**      exists_win32_path PATH
         5940  +**
         5941  +** Returns non-zero if the specified path exists, whose fully qualified name
         5942  +** may exceed 248 characters if it is prefixed with "\\?\".
         5943  +*/
         5944  +static int win32_exists_path(
         5945  +  void *clientData,
         5946  +  Tcl_Interp *interp,
         5947  +  int objc,
         5948  +  Tcl_Obj *CONST objv[]
         5949  +){
         5950  +  if( objc!=2 ){
         5951  +    Tcl_WrongNumArgs(interp, 1, objv, "PATH");
         5952  +    return TCL_ERROR;
         5953  +  }
         5954  +  Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
         5955  +      GetFileAttributesW( Tcl_GetUnicode(objv[1]))!=INVALID_FILE_ATTRIBUTES ));
         5956  +  return TCL_OK;
         5957  +}
         5958  +
         5959  +/*
         5960  +**      find_win32_file PATTERN
         5961  +**
         5962  +** Returns a list of entries in a directory that match the specified pattern,
         5963  +** whose fully qualified name may exceed 248 characters if it is prefixed with
         5964  +** "\\?\".
         5965  +*/
         5966  +static int win32_find_file(
         5967  +  void *clientData,
         5968  +  Tcl_Interp *interp,
         5969  +  int objc,
         5970  +  Tcl_Obj *CONST objv[]
         5971  +){
         5972  +  HANDLE hFindFile = INVALID_HANDLE_VALUE;
         5973  +  WIN32_FIND_DATAW findData;
         5974  +  Tcl_Obj *listObj;
         5975  +  DWORD lastErrno;
         5976  +  if( objc!=2 ){
         5977  +    Tcl_WrongNumArgs(interp, 1, objv, "PATTERN");
         5978  +    return TCL_ERROR;
         5979  +  }
         5980  +  hFindFile = FindFirstFileW(Tcl_GetUnicode(objv[1]), &findData);
         5981  +  if( hFindFile==INVALID_HANDLE_VALUE ){
         5982  +    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
         5983  +    return TCL_ERROR;
         5984  +  }
         5985  +  listObj = Tcl_NewObj();
         5986  +  Tcl_IncrRefCount(listObj);
         5987  +  do {
         5988  +    Tcl_ListObjAppendElement(interp, listObj, Tcl_NewUnicodeObj(
         5989  +        findData.cFileName, -1));
         5990  +    Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(
         5991  +        findData.dwFileAttributes));
         5992  +  } while( FindNextFileW(hFindFile, &findData) );
         5993  +  lastErrno = GetLastError();
         5994  +  if( lastErrno!=NO_ERROR && lastErrno!=ERROR_NO_MORE_FILES ){
         5995  +    FindClose(hFindFile);
         5996  +    Tcl_DecrRefCount(listObj);
         5997  +    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
         5998  +    return TCL_ERROR;
         5999  +  }
         6000  +  FindClose(hFindFile);
         6001  +  Tcl_SetObjResult(interp, listObj);
         6002  +  return TCL_OK;
         6003  +}
         6004  +
         6005  +/*
         6006  +**      delete_win32_file FILENAME
         6007  +**
         6008  +** Deletes the specified file, whose fully qualified name may exceed 248
         6009  +** characters if it is prefixed with "\\?\".
         6010  +*/
         6011  +static int win32_delete_file(
         6012  +  void *clientData,
         6013  +  Tcl_Interp *interp,
         6014  +  int objc,
         6015  +  Tcl_Obj *CONST objv[]
         6016  +){
         6017  +  if( objc!=2 ){
         6018  +    Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
         6019  +    return TCL_ERROR;
         6020  +  }
         6021  +  if( !DeleteFileW(Tcl_GetUnicode(objv[1])) ){
         6022  +    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
         6023  +    return TCL_ERROR;
         6024  +  }
         6025  +  Tcl_ResetResult(interp);
         6026  +  return TCL_OK;
         6027  +}
         6028  +
         6029  +/*
         6030  +**      make_win32_dir DIRECTORY
         6031  +**
         6032  +** Creates the specified directory, whose fully qualified name may exceed 248
         6033  +** characters if it is prefixed with "\\?\".
         6034  +*/
         6035  +static int win32_mkdir(
         6036  +  void *clientData,
         6037  +  Tcl_Interp *interp,
         6038  +  int objc,
         6039  +  Tcl_Obj *CONST objv[]
         6040  +){
         6041  +  if( objc!=2 ){
         6042  +    Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
         6043  +    return TCL_ERROR;
         6044  +  }
         6045  +  if( !CreateDirectoryW(Tcl_GetUnicode(objv[1]), NULL) ){
         6046  +    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
         6047  +    return TCL_ERROR;
         6048  +  }
         6049  +  Tcl_ResetResult(interp);
         6050  +  return TCL_OK;
         6051  +}
         6052  +
         6053  +/*
         6054  +**      remove_win32_dir DIRECTORY
         6055  +**
         6056  +** Removes the specified directory, whose fully qualified name may exceed 248
         6057  +** characters if it is prefixed with "\\?\".
         6058  +*/
         6059  +static int win32_rmdir(
         6060  +  void *clientData,
         6061  +  Tcl_Interp *interp,
         6062  +  int objc,
         6063  +  Tcl_Obj *CONST objv[]
         6064  +){
         6065  +  if( objc!=2 ){
         6066  +    Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
         6067  +    return TCL_ERROR;
         6068  +  }
         6069  +  if( !RemoveDirectoryW(Tcl_GetUnicode(objv[1])) ){
         6070  +    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
         6071  +    return TCL_ERROR;
         6072  +  }
         6073  +  Tcl_ResetResult(interp);
         6074  +  return TCL_OK;
         6075  +}
  5937   6076   #endif
  5938   6077   
  5939   6078   
  5940   6079   /*
  5941   6080   **      optimization_control DB OPT BOOLEAN
  5942   6081   **
  5943   6082   ** Enable or disable query optimizations using the sqlite3_test_control()
................................................................................
  6189   6328   
  6190   6329        { "save_prng_state",               save_prng_state,    0 },
  6191   6330        { "restore_prng_state",            restore_prng_state, 0 },
  6192   6331        { "reset_prng_state",              reset_prng_state,   0 },
  6193   6332        { "optimization_control",          optimization_control,0},
  6194   6333   #if SQLITE_OS_WIN
  6195   6334        { "lock_win32_file",               win32_file_lock,    0 },
         6335  +     { "exists_win32_path",             win32_exists_path,  0 },
         6336  +     { "find_win32_file",               win32_find_file,    0 },
         6337  +     { "delete_win32_file",             win32_delete_file,  0 },
         6338  +     { "make_win32_dir",                win32_mkdir,        0 },
         6339  +     { "remove_win32_dir",              win32_rmdir,        0 },
  6196   6340   #endif
  6197   6341        { "tcl_objproc",                   runAsObjProc,       0 },
  6198   6342   
  6199   6343        /* sqlite3_column_*() API */
  6200   6344        { "sqlite3_column_count",          test_column_count  ,0 },
  6201   6345        { "sqlite3_data_count",            test_data_count    ,0 },
  6202   6346        { "sqlite3_column_type",           test_column_type   ,0 },

Added test/win32longpath.test.

            1  +# 2013 August 27
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this script is testing the file name handling provided
           13  +# by the "win32-longpath" VFS.
           14  +#
           15  +
           16  +if {$tcl_platform(platform)!="windows"} return
           17  +
           18  +set testdir [file dirname $argv0]
           19  +source $testdir/tester.tcl
           20  +set testprefix win32longpath
           21  +
           22  +proc do_remove_win32_dir {args} {
           23  +  set nRetry [getFileRetries]     ;# Maximum number of retries.
           24  +  set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
           25  +
           26  +  foreach dirName $args {
           27  +    # On windows, sometimes even a [remove_win32_dir] can fail just after
           28  +    # a directory is emptied. The cause is usually "tag-alongs" - programs
           29  +    # like anti-virus software, automatic backup tools and various explorer
           30  +    # extensions that keep a file open a little longer than we expect,
           31  +    # causing the delete to fail.
           32  +    #
           33  +    # The solution is to wait a short amount of time before retrying the
           34  +    # removal.
           35  +    #
           36  +    if {$nRetry > 0} {
           37  +      for {set i 0} {$i < $nRetry} {incr i} {
           38  +        set rc [catch {
           39  +          remove_win32_dir $dirName
           40  +        } msg]
           41  +        if {$rc == 0} break
           42  +        if {$nDelay > 0} { after $nDelay }
           43  +      }
           44  +      if {$rc} { error $msg }
           45  +    } else {
           46  +      remove_win32_dir $dirName
           47  +    }
           48  +  }
           49  +}
           50  +
           51  +proc do_delete_win32_file {args} {
           52  +  set nRetry [getFileRetries]     ;# Maximum number of retries.
           53  +  set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
           54  +
           55  +  foreach fileName $args {
           56  +    # On windows, sometimes even a [delete_win32_file] can fail just after
           57  +    # a file is closed. The cause is usually "tag-alongs" - programs like
           58  +    # anti-virus software, automatic backup tools and various explorer
           59  +    # extensions that keep a file open a little longer than we expect,
           60  +    # causing the delete to fail.
           61  +    #
           62  +    # The solution is to wait a short amount of time before retrying the
           63  +    # delete.
           64  +    #
           65  +    if {$nRetry > 0} {
           66  +      for {set i 0} {$i < $nRetry} {incr i} {
           67  +        set rc [catch {
           68  +          delete_win32_file $fileName
           69  +        } msg]
           70  +        if {$rc == 0} break
           71  +        if {$nDelay > 0} { after $nDelay }
           72  +      }
           73  +      if {$rc} { error $msg }
           74  +    } else {
           75  +      delete_win32_file $fileName
           76  +    }
           77  +  }
           78  +}
           79  +
           80  +db close
           81  +set path [file nativename [get_pwd]]
           82  +sqlite3 db [file join $path test.db] -vfs win32-longpath
           83  +
           84  +do_test 1.1 {
           85  +  db eval {
           86  +    BEGIN EXCLUSIVE;
           87  +    CREATE TABLE t1(x);
           88  +    INSERT INTO t1 VALUES(1);
           89  +    INSERT INTO t1 VALUES(2);
           90  +    INSERT INTO t1 VALUES(3);
           91  +    INSERT INTO t1 VALUES(4);
           92  +    SELECT x FROM t1 ORDER BY x;
           93  +    COMMIT;
           94  +  }
           95  +} {1 2 3 4}
           96  +
           97  +set longPath(1) \\\\?\\$path\\[pid]
           98  +make_win32_dir $longPath(1)
           99  +
          100  +set longPath(2) $longPath(1)\\[string repeat X 255]
          101  +make_win32_dir $longPath(2)
          102  +
          103  +set longPath(3) $longPath(2)\\[string repeat Y 255]
          104  +make_win32_dir $longPath(3)
          105  +
          106  +set fileName $longPath(3)\\test.db
          107  +
          108  +do_test 1.2 {
          109  +  list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg
          110  +} {1 {unable to open database file}}
          111  +
          112  +sqlite3 db3 $fileName -vfs win32-longpath
          113  +
          114  +do_test 1.3 {
          115  +  db3 eval {
          116  +    PRAGMA journal_mode = WAL;
          117  +  }
          118  +} {wal}
          119  +
          120  +do_test 1.4 {
          121  +  db3 eval {
          122  +    BEGIN EXCLUSIVE;
          123  +    CREATE TABLE t1(x);
          124  +    INSERT INTO t1 VALUES(5);
          125  +    INSERT INTO t1 VALUES(6);
          126  +    INSERT INTO t1 VALUES(7);
          127  +    INSERT INTO t1 VALUES(8);
          128  +    SELECT x FROM t1 ORDER BY x;
          129  +    COMMIT;
          130  +  }
          131  +} {5 6 7 8}
          132  +
          133  +db3 close
          134  +# puts "  Database exists \{[exists_win32_path $fileName]\}"
          135  +
          136  +sqlite3 db3 $fileName -vfs win32-longpath
          137  +
          138  +do_test 1.5 {
          139  +  db3 eval {
          140  +    BEGIN EXCLUSIVE;
          141  +    INSERT INTO t1 VALUES(9);
          142  +    INSERT INTO t1 VALUES(10);
          143  +    INSERT INTO t1 VALUES(11);
          144  +    INSERT INTO t1 VALUES(12);
          145  +    SELECT x FROM t1 ORDER BY x;
          146  +    COMMIT;
          147  +  }
          148  +} {5 6 7 8 9 10 11 12}
          149  +
          150  +db3 close
          151  +# puts "  Database exists \{[exists_win32_path $fileName]\}"
          152  +
          153  +do_delete_win32_file $fileName
          154  +# puts "  Files remaining \{[find_win32_file $longPath(3)\\*]\}"
          155  +
          156  +do_remove_win32_dir $longPath(3)
          157  +do_remove_win32_dir $longPath(2)
          158  +do_remove_win32_dir $longPath(1)
          159  +
          160  +finish_test