| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include <windows.h>#include "core/crc.h"#include "core/frameAllocator.h"#include "core/util/str.h"#include "core/strings/stringFunctions.h"#include "core/strings/unicode.h"#include "platform/platformVolume.h"#include "platformWin32/winVolume.h"#include "console/console.h"#ifndef NGROUPS_UMAX   #define NGROUPS_UMAX 32#endifnamespace Torque{namespace Win32{   // If the file is a Directory, Offline, System or Temporary then FALSE#define S_ISREG(Flags) \   !((Flags) & \   (FILE_ATTRIBUTE_DIRECTORY | \   FILE_ATTRIBUTE_OFFLINE | \   FILE_ATTRIBUTE_SYSTEM | \   FILE_ATTRIBUTE_TEMPORARY))#define S_ISDIR(Flags) \   ((Flags) & FILE_ATTRIBUTE_DIRECTORY)//-----------------------------------------------------------------------------   class Win32FileSystemChangeNotifier : public FileSystemChangeNotifier   {   public:      Win32FileSystemChangeNotifier( FileSystem *fs )         :  FileSystemChangeNotifier( fs )      {         VECTOR_SET_ASSOCIATION( mHandleList );         VECTOR_SET_ASSOCIATION( mDirs );      }      // for use in the thread itself      U32      getNumHandles() const { return mHandleList.size(); }      HANDLE   *getHANDLES() { return mHandleList.address(); }   private:      virtual void   internalProcessOnce();      virtual bool   internalAddNotification( const Path &dir );      virtual bool   internalRemoveNotification( const Path &dir );      Vector<Path>   mDirs;      Vector<HANDLE> mHandleList;   };//-----------------------------------------------------------------------------static String _BuildFileName(const String& prefix,const Path& path){   // Need to join the path (minus the root) with our   // internal path name.   String file = prefix;   file = Path::Join(file, '/', path.getPath());   file = Path::Join(file, '/', path.getFileName());   file = Path::Join(file, '.', path.getExtension());   return file;}/*static bool _IsFile(const String& file){   // Get file info   WIN32_FIND_DATA info;   HANDLE handle = ::FindFirstFile(PathToOS(file).utf16(), &info);   ::FindClose(handle);   if (handle == INVALID_HANDLE_VALUE)      return false;   return S_ISREG(info.dwFileAttributes);}*/static bool _IsDirectory(const String& file){   // Get file info   WIN32_FIND_DATAW info;   HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);   ::FindClose(handle);   if (handle == INVALID_HANDLE_VALUE)      return false;   return S_ISDIR(info.dwFileAttributes);}//-----------------------------------------------------------------------------static void _CopyStatAttributes(const WIN32_FIND_DATAW& info, FileNode::Attributes* attr){   // Fill in the return struct.   attr->flags = 0;   if (S_ISDIR(info.dwFileAttributes))      attr->flags |= FileNode::Directory;   if (S_ISREG(info.dwFileAttributes))      attr->flags |= FileNode::File;   if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)      attr->flags |= FileNode::ReadOnly;   attr->size = info.nFileSizeLow;   attr->mtime = Win32FileTimeToTime(      info.ftLastWriteTime.dwLowDateTime,      info.ftLastWriteTime.dwHighDateTime);   attr->atime = Win32FileTimeToTime(      info.ftLastAccessTime.dwLowDateTime,      info.ftLastAccessTime.dwHighDateTime);}//-----------------------------------------------------------------------------bool Win32FileSystemChangeNotifier::internalAddNotification( const Path &dir ){   for ( U32 i = 0; i < mDirs.size(); ++i )   {      if ( mDirs[i] == dir )         return false;   }   Path     fullFSPath = mFS->mapTo( dir );   String   osPath = PathToOS( fullFSPath );//   Con::printf( "[Win32FileSystemChangeNotifier::internalAddNotification] : [%s]", osPath.c_str() );   HANDLE   changeHandle = ::FindFirstChangeNotificationW(                                  osPath.utf16(),      // directory to watch                                  FALSE,                           // do not watch subtree                                  FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES);  // watch file write changes   if (changeHandle == INVALID_HANDLE_VALUE || changeHandle == NULL)    {      Con::errorf("[Win32FileSystemChangeNotifier::internalAddNotification] : failed on [%s] [%d]", osPath.c_str(), GetLastError());            return false;    }   mDirs.push_back( dir );   mHandleList.push_back( changeHandle );   return true;}bool Win32FileSystemChangeNotifier::internalRemoveNotification( const Path &dir ){   for ( U32 i = 0; i < mDirs.size(); ++i )   {      if ( mDirs[i] != dir )         continue;      ::FindCloseChangeNotification( mHandleList[i] );      mDirs.erase( i );      mHandleList.erase( i );      return true;   }   return false;}void  Win32FileSystemChangeNotifier::internalProcessOnce(){   // WaitForMultipleObjects has a limit of MAXIMUM_WAIT_OBJECTS (64 at    // the moment), so we have to loop till we've handled the entire set.   for ( U32 i=0; i < mHandleList.size(); i += MAXIMUM_WAIT_OBJECTS )   {      U32 numHandles = getMin( (U32)MAXIMUM_WAIT_OBJECTS, (U32)mHandleList.size() - i );      DWORD dwWaitStatus = WaitForMultipleObjects( numHandles, mHandleList.address()+i, FALSE, 0);      if ( dwWaitStatus == WAIT_FAILED || dwWaitStatus == WAIT_TIMEOUT )         continue;      if ( dwWaitStatus >= WAIT_OBJECT_0 && dwWaitStatus <= (WAIT_OBJECT_0 + numHandles - 1))      {         U32 index = i + dwWaitStatus;         // reset our notification         // NOTE: we do this before letting the volume system check mod times so we don't miss any.         //    It is going to loop over the files and check their mod time vs. the saved time.         //    This may result in extra calls to internalNotifyDirChanged(), but it will simpley check mod times again.         ::FindNextChangeNotification( mHandleList[index] );         internalNotifyDirChanged( mDirs[index] );      }   }}//-----------------------------------------------------------------------------Win32FileSystem::Win32FileSystem(String volume){   mVolume = volume;   mChangeNotifier = new Win32FileSystemChangeNotifier( this );}Win32FileSystem::~Win32FileSystem(){}void Win32FileSystem::verifyCompatibility(const Path& _path, WIN32_FIND_DATAW _info){   if (_path.getFullFileName().isNotEmpty() && _path.getFullFileName().compare(String(_info.cFileName)) != 0)   {      Con::warnf("Linux Compatibility Warning: %s != %s", String(_info.cFileName).c_str(), _path.getFullFileName().c_str());   }}FileNodeRef Win32FileSystem::resolve(const Path& path){   String file = _BuildFileName(mVolume,path);   WIN32_FIND_DATAW info;   HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);   ::FindClose(handle);   if (handle != INVALID_HANDLE_VALUE)   {#ifdef TORQUE_DEBUG      verifyCompatibility(path, info);#endif      if (S_ISREG(info.dwFileAttributes))         return new Win32File(path,file);      if (S_ISDIR(info.dwFileAttributes))         return new Win32Directory(path,file);   }   return 0;}FileNodeRef Win32FileSystem::create(const Path& path, FileNode::Mode mode){   // The file will be created on disk when it's opened.   if (mode & FileNode::File)      return new Win32File(path,_BuildFileName(mVolume,path));   // Create with default permissions.   if (mode & FileNode::Directory)   {      String file = PathToOS(_BuildFileName(mVolume,path));      if (::CreateDirectoryW(file.utf16(), 0))         return new Win32Directory(path, file);   }   return 0;}bool Win32FileSystem::remove(const Path& path){   // Should probably check for outstanding files or directory objects.   String file = PathToOS(_BuildFileName(mVolume,path));   WIN32_FIND_DATAW info;   HANDLE handle = ::FindFirstFileW(file.utf16(), &info);   ::FindClose(handle);   if (handle == INVALID_HANDLE_VALUE)      return false;   if (S_ISDIR(info.dwFileAttributes))      return ::RemoveDirectoryW(file.utf16());   return ::DeleteFileW(file.utf16());}bool Win32FileSystem::rename(const Path& from,const Path& to){   String fa = PathToOS(_BuildFileName(mVolume,from));   String fb = PathToOS(_BuildFileName(mVolume,to));   return MoveFile(fa.utf16(),fb.utf16());}Path Win32FileSystem::mapTo(const Path& path){   return _BuildFileName(mVolume,path);}Path Win32FileSystem::mapFrom(const Path& path){   const String::SizeType  volumePathLen = mVolume.length();      String   pathStr = path.getFullPath();   if ( mVolume.compare( pathStr, volumePathLen, String::NoCase ))      return Path();   return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );}//-----------------------------------------------------------------------------Win32File::Win32File(const Path& path,String name){   mPath = path;   mName = name;   mStatus = Closed;   mHandle = 0;}Win32File::~Win32File(){   if (mHandle)      close();}Path Win32File::getName() const{   return mPath;}FileNode::NodeStatus Win32File::getStatus() const{   return mStatus;}bool Win32File::getAttributes(Attributes* attr){   WIN32_FIND_DATAW info;   HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);   ::FindClose(handle);   if (handle == INVALID_HANDLE_VALUE)      return false;   _CopyStatAttributes(info,attr);   attr->name = mPath;   return true;}U64 Win32File::getSize(){   U64 size;   if (mStatus == Open)   {      // Special case if file is open (handles unflushed buffers)      if ( !GetFileSizeEx(mHandle, (PLARGE_INTEGER)&size) )         size = 0;      return size;   }   else   {      // Fallback to generic function      size = File::getSize();   }   return size;}U32 Win32File::calculateChecksum(){   if (!open( Read ))      return 0;   U64 fileSize = getSize();   U32 bufSize = 1024 * 1024 * 4; // 4MB   FrameTemp<U8> buf( bufSize );   U32 crc = CRC::INITIAL_CRC_VALUE;   while ( fileSize > 0 )   {            U32 bytesRead = getMin( fileSize, bufSize );      if ( read( buf, bytesRead ) != bytesRead )      {         close();         return 0;      }      fileSize -= bytesRead;      crc = CRC::calculateCRC(buf, bytesRead, crc);   }      close();   return crc;}bool Win32File::open(AccessMode mode){   close();   if (mName.isEmpty())      return mStatus;   struct Mode   {      DWORD mode,share,open;   } Modes[] =   {      { GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING }, // Read      { GENERIC_WRITE,0,CREATE_ALWAYS },              // Write      { GENERIC_WRITE | GENERIC_READ,0,OPEN_ALWAYS }, // ReadWrite      { GENERIC_WRITE,0,OPEN_ALWAYS }                 // WriteAppend   };   Mode& m = (mode == Read)? Modes[0]: (mode == Write)? Modes[1]:         (mode == ReadWrite)? Modes[2]: Modes[3];   mHandle = (void*)::CreateFileW(PathToOS(mName).utf16(),               m.mode, m.share,               NULL, m.open,               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,               NULL);   if ( mHandle == INVALID_HANDLE_VALUE || mHandle == NULL )   {      _updateStatus();      return false;   }   mStatus = Open;   return true;}bool Win32File::close(){   if (mHandle)   {      ::CloseHandle((HANDLE)mHandle);      mHandle = 0;   }   mStatus = Closed;   return true;}U32 Win32File::getPosition(){   if (mStatus == Open || mStatus == EndOfFile)      return ::SetFilePointer((HANDLE)mHandle,0,0,FILE_CURRENT);   return 0;}U32 Win32File::setPosition(U32 delta, SeekMode mode){   if (mStatus != Open && mStatus != EndOfFile)      return 0;   DWORD fmode;   switch (mode)   {      case Begin:    fmode = FILE_BEGIN; break;      case Current:  fmode = FILE_CURRENT; break;      case End:      fmode = FILE_END; break;      default:       fmode = 0; break;   }   DWORD pos = ::SetFilePointer((HANDLE)mHandle,delta,0,fmode);   if (pos == INVALID_SET_FILE_POINTER)   {      mStatus = UnknownError;      return 0;   }   mStatus = Open;   return pos;}U32 Win32File::read(void* dst, U32 size){   if (mStatus != Open && mStatus != EndOfFile)      return 0;   DWORD bytesRead;   if (!::ReadFile((HANDLE)mHandle,dst,size,&bytesRead,0))      _updateStatus();   else if (bytesRead != size)      mStatus = EndOfFile;   return bytesRead;}U32 Win32File::write(const void* src, U32 size){   if ((mStatus != Open && mStatus != EndOfFile) || !size)      return 0;   DWORD bytesWritten;   if (!::WriteFile((HANDLE)mHandle,src,size,&bytesWritten,0))      _updateStatus();   return bytesWritten;}void Win32File::_updateStatus(){   switch (::GetLastError())   {      case ERROR_INVALID_ACCESS:       mStatus = AccessDenied;     break;      case ERROR_TOO_MANY_OPEN_FILES:  mStatus = UnknownError;     break;      case ERROR_PATH_NOT_FOUND:       mStatus = NoSuchFile;       break;      case ERROR_FILE_NOT_FOUND:       mStatus = NoSuchFile;       break;      case ERROR_SHARING_VIOLATION:    mStatus = SharingViolation; break;      case ERROR_HANDLE_DISK_FULL:     mStatus = FileSystemFull;   break;      case ERROR_ACCESS_DENIED:        mStatus = AccessDenied;     break;      default:                         mStatus = UnknownError;     break;   }}//-----------------------------------------------------------------------------Win32Directory::Win32Directory(const Path& path,String name){   mPath = path;   mName = name;   mStatus = Closed;   mHandle = 0;}Win32Directory::~Win32Directory(){   if (mHandle)      close();}Path Win32Directory::getName() const{   return mPath;}bool Win32Directory::open(){   if (!_IsDirectory(mName))   {      mStatus = NoSuchFile;      return false;   }   mStatus = Open;   return true;}bool Win32Directory::close(){   if (mHandle)   {      ::FindClose((HANDLE)mHandle);      mHandle = 0;      return true;   }   return false;}bool Win32Directory::read(Attributes* entry){   if (mStatus != Open)      return false;   WIN32_FIND_DATA info;   if (!mHandle)   {      mHandle = ::FindFirstFileW((PathToOS(mName) + "\\*").utf16(), &info);      if (mHandle == NULL)      {         _updateStatus();         return false;      }   }   else      if (!::FindNextFileW((HANDLE)mHandle, &info))      {         _updateStatus();         return false;      }   // Skip "." and ".." entries   if (info.cFileName[0] == '.' && (info.cFileName[1] == '\0' ||      (info.cFileName[1] == '.' && info.cFileName[2] == '\0')))      return read(entry);   _CopyStatAttributes(info,entry);   entry->name = info.cFileName;   return true;}U32 Win32Directory::calculateChecksum(){   // Return checksum of current entry   return 0;}bool Win32Directory::getAttributes(Attributes* attr){   WIN32_FIND_DATA info;   HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);   ::FindClose(handle);   if (handle == INVALID_HANDLE_VALUE)   {      _updateStatus();      return false;   }   _CopyStatAttributes(info,attr);   attr->name = mPath;   return true;}FileNode::NodeStatus Win32Directory::getStatus() const{   return mStatus;}void Win32Directory::_updateStatus(){   switch (::GetLastError())   {      case ERROR_NO_MORE_FILES:     mStatus = EndOfFile;        break;      case ERROR_INVALID_ACCESS:    mStatus = AccessDenied;     break;      case ERROR_PATH_NOT_FOUND:    mStatus = NoSuchFile;       break;      case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;      case ERROR_ACCESS_DENIED:     mStatus = AccessDenied;     break;      default:                      mStatus = UnknownError;     break;   }}} // Namespace Win32bool FS::VerifyWriteAccess(const Path &path){   // due to UAC's habit of creating "virtual stores" when permission isn't actually available   // actually create, write, read, verify, and delete a file to the folder being tested   String temp = path.getFullPath();   temp += "\\torque_writetest.tmp";   // first, (try and) delete the file if it exists      ::DeleteFileW(temp.utf16());   // now, create the file   HANDLE hFile = ::CreateFileW(PathToOS(temp).utf16(),               GENERIC_WRITE, 0,               NULL, CREATE_ALWAYS,               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,               NULL);   if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )      return false;   U32 t = Platform::getTime();   DWORD bytesWritten;   if (!::WriteFile(hFile,&t,sizeof(t),&bytesWritten,0))   {      ::CloseHandle(hFile);      ::DeleteFileW(temp.utf16());      return false;   }   // close the file   ::CloseHandle(hFile);   // open for read   hFile = ::CreateFileW(PathToOS(temp).utf16(),               GENERIC_READ, FILE_SHARE_READ,               NULL, OPEN_EXISTING,               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,               NULL);   if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )      return false;   U32 t2 = 0;   DWORD bytesRead;   if (!::ReadFile(hFile,&t2,sizeof(t2),&bytesRead,0))   {      ::CloseHandle(hFile);      ::DeleteFileW(temp.utf16());      return false;   }   ::CloseHandle(hFile);   ::DeleteFileW(temp.utf16());   return t == t2;}} // Namespace Torque//-----------------------------------------------------------------------------Torque::FS::FileSystemRef  Platform::FS::createNativeFS( const String &volume ){   return new Win32::Win32FileSystem( volume );}String   Platform::FS::getAssetDir(){   char cen_buf[2048];#ifdef TORQUE_UNICODE   if (!Platform::getWebDeployment())   {      TCHAR buf[ 2048 ];      ::GetModuleFileNameW( NULL, buf, sizeof( buf ) );      convertUTF16toUTF8( buf, cen_buf );   }   else   {      TCHAR buf[ 2048 ];      GetCurrentDirectoryW( sizeof( buf ) / sizeof( buf[ 0 ] ), buf );      convertUTF16toUTF8( buf, cen_buf );      return Path::CleanSeparators(cen_buf);   }#else   ::GetModuleFileNameA( NULL, cen_buf, 2047);#endif   char *delimiter = dStrrchr( cen_buf, '\\' );   if( delimiter != NULL )      *delimiter = '\0';   return Path::CleanSeparators(cen_buf);}/// Function invoked by the kernel layer to install OS specific/// file systems.bool Platform::FS::InstallFileSystems(){   WCHAR buffer[1024];   // [8/24/2009 tomb] This stops Windows from complaining about drives that have no disks in   SetErrorMode(SEM_FAILCRITICALERRORS);   // Load all the Win32 logical drives.   DWORD mask = ::GetLogicalDrives();   char drive[] = "A";   char volume[] = "A:/";   while (mask)   {      if (mask & 1)      {         volume[0] = drive[0];         Platform::FS::Mount(drive, Platform::FS::createNativeFS(volume));      }      mask >>= 1;      drive[0]++;   }   // Set the current working dir.  Windows normally returns   // upper case driver letters, but the cygwin bash shell   // seems to make it return lower case drives. Force upper   // to be consistent with the mounts.   ::GetCurrentDirectory(sizeof(buffer), buffer);   if (buffer[1] == ':')      buffer[0] = dToupper(buffer[0]);   String   wd = buffer;      wd += '/';   Platform::FS::SetCwd(wd);   return true;}
 |