/ Check-in [9d2b0f8b]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Win32 portability fixes to the 'fileio' extension.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | testFixes
Files: files | file ages | folders
SHA3-256: 9d2b0f8b84aac862b0572b183e3ba53ea8c0d8742aaa3c3fbe59f6036054fd1a
User & Date: mistachkin 2018-03-16 23:54:36
Context
2018-03-17
00:44
Another Win32 portability fix for the 'zipfile' tests. check-in: 9f604418 user: mistachkin tags: testFixes
2018-03-16
23:54
Win32 portability fixes to the 'fileio' extension. check-in: 9d2b0f8b user: mistachkin tags: testFixes
23:54
Enable more 'zipfile' tests on Win32. check-in: 49d2566c user: mistachkin tags: testFixes
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/misc/fileio.c.

   154    154     va_list ap;
   155    155     va_start(ap, zFmt);
   156    156     zMsg = sqlite3_vmprintf(zFmt, ap);
   157    157     sqlite3_result_error(ctx, zMsg, -1);
   158    158     sqlite3_free(zMsg);
   159    159     va_end(ap);
   160    160   }
          161  +
          162  +#if defined(_WIN32)
          163  +/*
          164  +** This function is designed to convert a Win32 FILETIME structure into the
          165  +** number of seconds since the Unix Epoch (1970-01-01 00:00:00 UTC).
          166  +*/
          167  +static sqlite3_uint64 fileTimeToUnixTime(
          168  +  LPFILETIME pFileTime
          169  +){
          170  +  SYSTEMTIME epochSystemTime;
          171  +  ULARGE_INTEGER epochIntervals;
          172  +  FILETIME epochFileTime;
          173  +  ULARGE_INTEGER fileIntervals;
          174  +
          175  +  memset(&epochSystemTime, 0, sizeof(SYSTEMTIME));
          176  +  epochSystemTime.wYear = 1970;
          177  +  epochSystemTime.wMonth = 1;
          178  +  epochSystemTime.wDay = 1;
          179  +  SystemTimeToFileTime(&epochSystemTime, &epochFileTime);
          180  +  epochIntervals.LowPart = epochFileTime.dwLowDateTime;
          181  +  epochIntervals.HighPart = epochFileTime.dwHighDateTime;
          182  +
          183  +  fileIntervals.LowPart = pFileTime->dwLowDateTime;
          184  +  fileIntervals.HighPart = pFileTime->dwHighDateTime;
          185  +
          186  +  return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
          187  +}
          188  +
          189  +/*
          190  +** This function attempts to normalize the time values found in the stat()
          191  +** buffer to UTC.  This is necessary on Win32, where the runtime library
          192  +** appears to return these values as local times.
          193  +*/
          194  +static void statTimesToUtc(
          195  +  const char *zPath,
          196  +  struct stat *pStatBuf
          197  +){
          198  +  HANDLE hFindFile;
          199  +  WIN32_FIND_DATAW fd;
          200  +  LPWSTR zUnicodeName;
          201  +  extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
          202  +  zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
          203  +  if( zUnicodeName ){
          204  +    memset(&fd, 0, sizeof(WIN32_FIND_DATA));
          205  +    hFindFile = FindFirstFileW(zUnicodeName, &fd);
          206  +    if( hFindFile!=NULL ){
          207  +      pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
          208  +      pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
          209  +      pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
          210  +      FindClose(hFindFile);
          211  +    }
          212  +    sqlite3_free(zUnicodeName);
          213  +  }
          214  +}
          215  +#endif
          216  +
          217  +/*
          218  +** This function is used in place of stat().  On Windows, special handling
          219  +** is required in order for the included time to be returned as UTC.  On all
          220  +** other systems, this function simply calls stat().
          221  +*/
          222  +static int fileStat(
          223  +  const char *zPath,
          224  +  struct stat *pStatBuf
          225  +){
          226  +#if defined(_WIN32)
          227  +  int rc = stat(zPath, pStatBuf);
          228  +  if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
          229  +  return rc;
          230  +#else
          231  +  return stat(zPath, pStatBuf);
          232  +#endif
          233  +}
          234  +
          235  +/*
          236  +** This function is used in place of lstat().  On Windows, special handling
          237  +** is required in order for the included time to be returned as UTC.  On all
          238  +** other systems, this function simply calls lstat().
          239  +*/
          240  +static int fileLinkStat(
          241  +  const char *zPath,
          242  +  struct stat *pStatBuf
          243  +){
          244  +#if defined(_WIN32)
          245  +  int rc = lstat(zPath, pStatBuf);
          246  +  if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
          247  +  return rc;
          248  +#else
          249  +  return lstat(zPath, pStatBuf);
          250  +#endif
          251  +}
   161    252   
   162    253   /*
   163    254   ** Argument zFile is the name of a file that will be created and/or written
   164    255   ** by SQL function writefile(). This function ensures that the directory
   165    256   ** zFile will be written to exists, creating it if required. The permissions
   166    257   ** for any path components created by this function are set to (mode&0777).
   167    258   **
................................................................................
   186    277         struct stat sStat;
   187    278         int rc2;
   188    279   
   189    280         for(; zCopy[i]!='/' && i<nCopy; i++);
   190    281         if( i==nCopy ) break;
   191    282         zCopy[i] = '\0';
   192    283   
   193         -      rc2 = stat(zCopy, &sStat);
          284  +      rc2 = fileStat(zCopy, &sStat);
   194    285         if( rc2!=0 ){
   195    286           if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR;
   196    287         }else{
   197    288           if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
   198    289         }
   199    290         zCopy[i] = '/';
   200    291         i++;
................................................................................
   228    319         if( mkdir(zFile, mode) ){
   229    320           /* The mkdir() call to create the directory failed. This might not
   230    321           ** be an error though - if there is already a directory at the same
   231    322           ** path and either the permissions already match or can be changed
   232    323           ** to do so using chmod(), it is not an error.  */
   233    324           struct stat sStat;
   234    325           if( errno!=EEXIST
   235         -         || 0!=stat(zFile, &sStat)
          326  +         || 0!=fileStat(zFile, &sStat)
   236    327            || !S_ISDIR(sStat.st_mode)
   237    328            || ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
   238    329           ){
   239    330             return 1;
   240    331           }
   241    332         }
   242    333       }else{
................................................................................
   275    366   
   276    367       GetSystemTime(&currentTime);
   277    368       SystemTimeToFileTime(&currentTime, &lastAccess);
   278    369       intervals = Int32x32To64(mtime, 10000000) + 116444736000000000;
   279    370       lastWrite.dwLowDateTime = (DWORD)intervals;
   280    371       lastWrite.dwHighDateTime = intervals >> 32;
   281    372       zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile);
          373  +    if( zUnicodeName==0 ){
          374  +      return 1;
          375  +    }
   282    376       hFile = CreateFileW(
   283    377         zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
   284    378         FILE_FLAG_BACKUP_SEMANTICS, NULL
   285    379       );
   286    380       sqlite3_free(zUnicodeName);
   287    381       if( hFile!=INVALID_HANDLE_VALUE ){
   288    382         BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite);
   289    383         CloseHandle(hFile);
   290    384         return !bResult;
   291    385       }else{
   292    386         return 1;
   293    387       }
   294         -#elif defined(AT_FDCWD) && 0 /* utimensat() is not univerally available */
          388  +#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
   295    389       /* Recent unix */
   296    390       struct timespec times[2];
   297    391       times[0].tv_nsec = times[1].tv_nsec = 0;
   298    392       times[0].tv_sec = time(0);
   299    393       times[1].tv_sec = mtime;
   300    394       if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
   301    395         return 1;
................................................................................
   564    658         if( pEntry->d_name[0]=='.' ){
   565    659          if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue;
   566    660          if( pEntry->d_name[1]=='\0' ) continue;
   567    661         }
   568    662         sqlite3_free(pCur->zPath);
   569    663         pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name);
   570    664         if( pCur->zPath==0 ) return SQLITE_NOMEM;
   571         -      if( lstat(pCur->zPath, &pCur->sStat) ){
          665  +      if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
   572    666           fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
   573    667           return SQLITE_ERROR;
   574    668         }
   575    669         return SQLITE_OK;
   576    670       }
   577    671       closedir(pLvl->pDir);
   578    672       sqlite3_free(pLvl->zDir);
................................................................................
   698    792     }else{
   699    793       pCur->zPath = sqlite3_mprintf("%s", zDir);
   700    794     }
   701    795   
   702    796     if( pCur->zPath==0 ){
   703    797       return SQLITE_NOMEM;
   704    798     }
   705         -  if( lstat(pCur->zPath, &pCur->sStat) ){
          799  +  if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
   706    800       fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
   707    801       return SQLITE_ERROR;
   708    802     }
   709    803   
   710    804     return SQLITE_OK;
   711    805   }
   712    806