winVolume.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include <windows.h>
  23. #include "core/crc.h"
  24. #include "core/frameAllocator.h"
  25. #include "core/util/str.h"
  26. #include "core/strings/stringFunctions.h"
  27. #include "core/strings/unicode.h"
  28. #include "platform/platformVolume.h"
  29. #include "platformWin32/winVolume.h"
  30. #include "console/console.h"
  31. #ifndef NGROUPS_UMAX
  32. #define NGROUPS_UMAX 32
  33. #endif
  34. namespace Torque
  35. {
  36. namespace Win32
  37. {
  38. // If the file is a Directory, Offline, System or Temporary then FALSE
  39. #define S_ISREG(Flags) \
  40. !((Flags) & \
  41. (FILE_ATTRIBUTE_DIRECTORY | \
  42. FILE_ATTRIBUTE_OFFLINE | \
  43. FILE_ATTRIBUTE_SYSTEM | \
  44. FILE_ATTRIBUTE_TEMPORARY))
  45. #define S_ISDIR(Flags) \
  46. ((Flags) & FILE_ATTRIBUTE_DIRECTORY)
  47. //-----------------------------------------------------------------------------
  48. class Win32FileSystemChangeNotifier : public FileSystemChangeNotifier
  49. {
  50. public:
  51. Win32FileSystemChangeNotifier( FileSystem *fs )
  52. : FileSystemChangeNotifier( fs )
  53. {
  54. VECTOR_SET_ASSOCIATION( mHandleList );
  55. VECTOR_SET_ASSOCIATION( mDirs );
  56. }
  57. // for use in the thread itself
  58. U32 getNumHandles() const { return mHandleList.size(); }
  59. HANDLE *getHANDLES() { return mHandleList.address(); }
  60. private:
  61. void internalProcessOnce() override;
  62. bool internalAddNotification( const Path &dir ) override;
  63. bool internalRemoveNotification( const Path &dir ) override;
  64. Vector<Path> mDirs;
  65. Vector<HANDLE> mHandleList;
  66. };
  67. //-----------------------------------------------------------------------------
  68. static String _BuildFileName(const String& prefix,const Path& path)
  69. {
  70. // Need to join the path (minus the root) with our
  71. // internal path name.
  72. String file = prefix;
  73. file = Path::Join(file, '/', path.getPath());
  74. if (path.getFileName().isEmpty() && path.getExtension().isNotEmpty()) //weird, filename-less file, so handle it slightly special-case
  75. file += String("/");
  76. else
  77. file = Path::Join(file, '/', path.getFileName());
  78. file = Path::Join(file, '.', path.getExtension());
  79. return file;
  80. }
  81. /*
  82. static bool _IsFile(const String& file)
  83. {
  84. // Get file info
  85. WIN32_FIND_DATA info;
  86. HANDLE handle = ::FindFirstFile(PathToOS(file).utf16(), &info);
  87. ::FindClose(handle);
  88. if (handle == INVALID_HANDLE_VALUE)
  89. return false;
  90. return S_ISREG(info.dwFileAttributes);
  91. }
  92. */
  93. static bool _IsDirectory(const String& file)
  94. {
  95. // Get file info
  96. WIN32_FIND_DATAW info;
  97. HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
  98. ::FindClose(handle);
  99. if (handle == INVALID_HANDLE_VALUE)
  100. return false;
  101. return S_ISDIR(info.dwFileAttributes);
  102. }
  103. //-----------------------------------------------------------------------------
  104. static void _CopyStatAttributes(const WIN32_FIND_DATAW& info, FileNode::Attributes* attr)
  105. {
  106. // Fill in the return struct.
  107. attr->flags = 0;
  108. if (S_ISDIR(info.dwFileAttributes))
  109. attr->flags |= FileNode::Directory;
  110. if (S_ISREG(info.dwFileAttributes))
  111. attr->flags |= FileNode::File;
  112. if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  113. attr->flags |= FileNode::ReadOnly;
  114. SYSTEMTIME st, stLocal;
  115. FILETIME ftLocal;
  116. attr->size = info.nFileSizeLow;
  117. FileTimeToSystemTime(&(info.ftLastWriteTime), &st);
  118. SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
  119. SystemTimeToFileTime(&stLocal, &ftLocal);
  120. attr->mtime = Win32FileTimeToTime(ftLocal.dwLowDateTime, ftLocal.dwHighDateTime);
  121. FileTimeToSystemTime(&(info.ftLastAccessTime), &st);
  122. SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
  123. SystemTimeToFileTime(&stLocal, &ftLocal);
  124. attr->atime = Win32FileTimeToTime(ftLocal.dwLowDateTime, ftLocal.dwHighDateTime);
  125. FileTimeToSystemTime(&(info.ftCreationTime), &st);
  126. SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
  127. SystemTimeToFileTime(&stLocal, &ftLocal);
  128. attr->ctime = Win32FileTimeToTime(ftLocal.dwLowDateTime, ftLocal.dwHighDateTime);
  129. }
  130. //-----------------------------------------------------------------------------
  131. bool Win32FileSystemChangeNotifier::internalAddNotification( const Path &dir )
  132. {
  133. for ( U32 i = 0; i < mDirs.size(); ++i )
  134. {
  135. if ( mDirs[i] == dir )
  136. return false;
  137. }
  138. Path fullFSPath = mFS->mapTo( dir );
  139. String osPath = PathToOS( fullFSPath );
  140. // Con::printf( "[Win32FileSystemChangeNotifier::internalAddNotification] : [%s]", osPath.c_str() );
  141. HANDLE changeHandle = ::FindFirstChangeNotificationW(
  142. osPath.utf16(), // directory to watch
  143. FALSE, // do not watch subtree
  144. FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES); // watch file write changes
  145. if (changeHandle == INVALID_HANDLE_VALUE || changeHandle == NULL)
  146. {
  147. Con::errorf("[Win32FileSystemChangeNotifier::internalAddNotification] : failed on [%s] [%d]", osPath.c_str(), GetLastError());
  148. return false;
  149. }
  150. mDirs.push_back( dir );
  151. mHandleList.push_back( changeHandle );
  152. return true;
  153. }
  154. bool Win32FileSystemChangeNotifier::internalRemoveNotification( const Path &dir )
  155. {
  156. for ( U32 i = 0; i < mDirs.size(); ++i )
  157. {
  158. if ( mDirs[i] != dir )
  159. continue;
  160. ::FindCloseChangeNotification( mHandleList[i] );
  161. mDirs.erase( i );
  162. mHandleList.erase( i );
  163. return true;
  164. }
  165. return false;
  166. }
  167. void Win32FileSystemChangeNotifier::internalProcessOnce()
  168. {
  169. // WaitForMultipleObjects has a limit of MAXIMUM_WAIT_OBJECTS (64 at
  170. // the moment), so we have to loop till we've handled the entire set.
  171. for ( U32 i=0; i < mHandleList.size(); i += MAXIMUM_WAIT_OBJECTS )
  172. {
  173. U32 numHandles = getMin( (U32)MAXIMUM_WAIT_OBJECTS, (U32)mHandleList.size() - i );
  174. DWORD dwWaitStatus = WaitForMultipleObjects( numHandles, mHandleList.address()+i, FALSE, 0);
  175. if ( dwWaitStatus == WAIT_FAILED || dwWaitStatus == WAIT_TIMEOUT )
  176. continue;
  177. if ( dwWaitStatus >= WAIT_OBJECT_0 && dwWaitStatus <= (WAIT_OBJECT_0 + numHandles - 1))
  178. {
  179. U32 index = i + dwWaitStatus;
  180. // reset our notification
  181. // NOTE: we do this before letting the volume system check mod times so we don't miss any.
  182. // It is going to loop over the files and check their mod time vs. the saved time.
  183. // This may result in extra calls to internalNotifyDirChanged(), but it will simpley check mod times again.
  184. ::FindNextChangeNotification( mHandleList[index] );
  185. internalNotifyDirChanged( mDirs[index] );
  186. }
  187. }
  188. }
  189. //-----------------------------------------------------------------------------
  190. Win32FileSystem::Win32FileSystem(String volume)
  191. {
  192. mVolume = volume;
  193. mChangeNotifier = new Win32FileSystemChangeNotifier( this );
  194. }
  195. Win32FileSystem::~Win32FileSystem()
  196. {
  197. }
  198. void Win32FileSystem::verifyCompatibility(const Path& _path, WIN32_FIND_DATAW _info)
  199. {
  200. #ifndef TORQUE_POSIX_PATH_CASE_INSENSITIVE
  201. if (_path.getFullFileName().isNotEmpty() && _path.getFullFileName().compare(String(_info.cFileName)) != 0)
  202. {
  203. Con::warnf("Linux Compatibility Warning: %s != %s", String(_info.cFileName).c_str(), _path.getFullFileName().c_str());
  204. }
  205. #endif
  206. }
  207. FileNodeRef Win32FileSystem::resolve(const Path& path)
  208. {
  209. String file = _BuildFileName(mVolume,path);
  210. WIN32_FIND_DATAW info;
  211. HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
  212. ::FindClose(handle);
  213. if (handle != INVALID_HANDLE_VALUE)
  214. {
  215. #ifdef TORQUE_DEBUG
  216. verifyCompatibility(path, info);
  217. #endif
  218. if (S_ISREG(info.dwFileAttributes))
  219. return new Win32File(path,file);
  220. if (S_ISDIR(info.dwFileAttributes))
  221. return new Win32Directory(path,file);
  222. }
  223. return 0;
  224. }
  225. FileNodeRef Win32FileSystem::create(const Path& path, FileNode::Mode mode)
  226. {
  227. // The file will be created on disk when it's opened.
  228. if (mode & FileNode::File)
  229. return new Win32File(path,_BuildFileName(mVolume,path));
  230. // Create with default permissions.
  231. if (mode & FileNode::Directory)
  232. {
  233. String file = PathToOS(_BuildFileName(mVolume,path));
  234. if (::CreateDirectoryW(file.utf16(), 0))
  235. return new Win32Directory(path, file);
  236. }
  237. return 0;
  238. }
  239. bool Win32FileSystem::remove(const Path& path)
  240. {
  241. // Should probably check for outstanding files or directory objects.
  242. String file = PathToOS(_BuildFileName(mVolume,path));
  243. WIN32_FIND_DATAW info;
  244. HANDLE handle = ::FindFirstFileW(file.utf16(), &info);
  245. ::FindClose(handle);
  246. if (handle == INVALID_HANDLE_VALUE)
  247. return false;
  248. if (S_ISDIR(info.dwFileAttributes))
  249. return ::RemoveDirectoryW(file.utf16());
  250. return ::DeleteFileW(file.utf16());
  251. }
  252. bool Win32FileSystem::rename(const Path& from,const Path& to)
  253. {
  254. String fa = PathToOS(_BuildFileName(mVolume,from));
  255. String fb = PathToOS(_BuildFileName(mVolume,to));
  256. return MoveFile(fa.utf16(),fb.utf16());
  257. }
  258. Path Win32FileSystem::mapTo(const Path& path)
  259. {
  260. return _BuildFileName(mVolume,path);
  261. }
  262. Path Win32FileSystem::mapFrom(const Path& path)
  263. {
  264. const String::SizeType volumePathLen = mVolume.length();
  265. String pathStr = path.getFullPath();
  266. if ( mVolume.compare( pathStr, volumePathLen, String::NoCase ))
  267. return Path();
  268. return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
  269. }
  270. //-----------------------------------------------------------------------------
  271. Win32File::Win32File(const Path& path,String name)
  272. {
  273. mPath = path;
  274. mName = name;
  275. mStatus = Closed;
  276. mHandle = 0;
  277. }
  278. Win32File::~Win32File()
  279. {
  280. if (mHandle)
  281. close();
  282. }
  283. Path Win32File::getName() const
  284. {
  285. return mPath;
  286. }
  287. FileNode::NodeStatus Win32File::getStatus() const
  288. {
  289. return mStatus;
  290. }
  291. bool Win32File::getAttributes(Attributes* attr)
  292. {
  293. WIN32_FIND_DATAW info;
  294. HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
  295. ::FindClose(handle);
  296. if (handle == INVALID_HANDLE_VALUE)
  297. return false;
  298. _CopyStatAttributes(info,attr);
  299. attr->name = mPath;
  300. return true;
  301. }
  302. U64 Win32File::getSize()
  303. {
  304. U64 size;
  305. if (mStatus == Open)
  306. {
  307. // Special case if file is open (handles unflushed buffers)
  308. if ( !GetFileSizeEx(mHandle, (PLARGE_INTEGER)&size) )
  309. size = 0;
  310. return size;
  311. }
  312. else
  313. {
  314. // Fallback to generic function
  315. size = File::getSize();
  316. }
  317. return size;
  318. }
  319. U32 Win32File::calculateChecksum()
  320. {
  321. if (!open( Read ))
  322. return 0;
  323. U64 fileSize = getSize();
  324. U32 bufSize = 1024 * 1024 * 4; // 4MB
  325. FrameTemp<U8> buf( bufSize );
  326. U32 crc = CRC::INITIAL_CRC_VALUE;
  327. while ( fileSize > 0 )
  328. {
  329. U32 bytesRead = getMin( fileSize, bufSize );
  330. if ( read( buf, bytesRead ) != bytesRead )
  331. {
  332. close();
  333. return 0;
  334. }
  335. fileSize -= bytesRead;
  336. crc = CRC::calculateCRC(buf, bytesRead, crc);
  337. }
  338. close();
  339. return crc;
  340. }
  341. bool Win32File::open(AccessMode mode)
  342. {
  343. close();
  344. if (mName.isEmpty())
  345. return mStatus;
  346. struct Mode
  347. {
  348. DWORD mode,share,open;
  349. } Modes[] =
  350. {
  351. { GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING }, // Read
  352. { GENERIC_WRITE,0,CREATE_ALWAYS }, // Write
  353. { GENERIC_WRITE | GENERIC_READ,0,OPEN_ALWAYS }, // ReadWrite
  354. { GENERIC_WRITE,0,OPEN_ALWAYS } // WriteAppend
  355. };
  356. Mode& m = (mode == Read)? Modes[0]: (mode == Write)? Modes[1]:
  357. (mode == ReadWrite)? Modes[2]: Modes[3];
  358. mHandle = (void*)::CreateFileW(PathToOS(mName).utf16(),
  359. m.mode, m.share,
  360. NULL, m.open,
  361. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  362. NULL);
  363. if ( mHandle == INVALID_HANDLE_VALUE || mHandle == NULL )
  364. {
  365. _updateStatus();
  366. return false;
  367. }
  368. mStatus = Open;
  369. return true;
  370. }
  371. bool Win32File::close()
  372. {
  373. if (mHandle)
  374. {
  375. ::CloseHandle((HANDLE)mHandle);
  376. mHandle = 0;
  377. }
  378. mStatus = Closed;
  379. return true;
  380. }
  381. U32 Win32File::getPosition()
  382. {
  383. if (mStatus == Open || mStatus == EndOfFile)
  384. return ::SetFilePointer((HANDLE)mHandle,0,0,FILE_CURRENT);
  385. return 0;
  386. }
  387. U32 Win32File::setPosition(U32 delta, SeekMode mode)
  388. {
  389. if (mStatus != Open && mStatus != EndOfFile)
  390. return 0;
  391. DWORD fmode;
  392. switch (mode)
  393. {
  394. case Begin: fmode = FILE_BEGIN; break;
  395. case Current: fmode = FILE_CURRENT; break;
  396. case End: fmode = FILE_END; break;
  397. default: fmode = 0; break;
  398. }
  399. DWORD pos = ::SetFilePointer((HANDLE)mHandle,delta,0,fmode);
  400. if (pos == INVALID_SET_FILE_POINTER)
  401. {
  402. mStatus = UnknownError;
  403. return 0;
  404. }
  405. mStatus = Open;
  406. return pos;
  407. }
  408. U32 Win32File::read(void* dst, U32 size)
  409. {
  410. if (mStatus != Open && mStatus != EndOfFile)
  411. return 0;
  412. DWORD bytesRead;
  413. if (!::ReadFile((HANDLE)mHandle,dst,size,&bytesRead,0))
  414. _updateStatus();
  415. else if (bytesRead != size)
  416. mStatus = EndOfFile;
  417. return bytesRead;
  418. }
  419. U32 Win32File::write(const void* src, U32 size)
  420. {
  421. if ((mStatus != Open && mStatus != EndOfFile) || !size)
  422. return 0;
  423. DWORD bytesWritten;
  424. if (!::WriteFile((HANDLE)mHandle,src,size,&bytesWritten,0))
  425. _updateStatus();
  426. return bytesWritten;
  427. }
  428. void Win32File::_updateStatus()
  429. {
  430. switch (::GetLastError())
  431. {
  432. case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break;
  433. case ERROR_TOO_MANY_OPEN_FILES: mStatus = UnknownError; break;
  434. case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break;
  435. case ERROR_FILE_NOT_FOUND: mStatus = NoSuchFile; break;
  436. case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;
  437. case ERROR_HANDLE_DISK_FULL: mStatus = FileSystemFull; break;
  438. case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break;
  439. default: mStatus = UnknownError; break;
  440. }
  441. }
  442. //-----------------------------------------------------------------------------
  443. Win32Directory::Win32Directory(const Path& path,String name)
  444. {
  445. mPath = path;
  446. mName = name;
  447. mStatus = Closed;
  448. mHandle = 0;
  449. }
  450. Win32Directory::~Win32Directory()
  451. {
  452. if (mHandle)
  453. close();
  454. }
  455. Path Win32Directory::getName() const
  456. {
  457. return mPath;
  458. }
  459. bool Win32Directory::open()
  460. {
  461. if (!_IsDirectory(mName))
  462. {
  463. mStatus = NoSuchFile;
  464. return false;
  465. }
  466. mStatus = Open;
  467. return true;
  468. }
  469. bool Win32Directory::close()
  470. {
  471. if (mHandle)
  472. {
  473. ::FindClose((HANDLE)mHandle);
  474. mHandle = 0;
  475. return true;
  476. }
  477. return false;
  478. }
  479. bool Win32Directory::read(Attributes* entry)
  480. {
  481. if (mStatus != Open)
  482. return false;
  483. WIN32_FIND_DATA info;
  484. if (!mHandle)
  485. {
  486. mHandle = ::FindFirstFileW((PathToOS(mName) + "\\*").utf16(), &info);
  487. if (mHandle == NULL)
  488. {
  489. _updateStatus();
  490. return false;
  491. }
  492. }
  493. else
  494. if (!::FindNextFileW((HANDLE)mHandle, &info))
  495. {
  496. _updateStatus();
  497. return false;
  498. }
  499. // Skip "." and ".." entries
  500. if (info.cFileName[0] == '.' && (info.cFileName[1] == '\0' ||
  501. (info.cFileName[1] == '.' && info.cFileName[2] == '\0')))
  502. return read(entry);
  503. _CopyStatAttributes(info,entry);
  504. entry->name = info.cFileName;
  505. return true;
  506. }
  507. U32 Win32Directory::calculateChecksum()
  508. {
  509. // Return checksum of current entry
  510. return 0;
  511. }
  512. bool Win32Directory::getAttributes(Attributes* attr)
  513. {
  514. WIN32_FIND_DATA info;
  515. HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
  516. ::FindClose(handle);
  517. if (handle == INVALID_HANDLE_VALUE)
  518. {
  519. _updateStatus();
  520. return false;
  521. }
  522. _CopyStatAttributes(info,attr);
  523. attr->name = mPath;
  524. return true;
  525. }
  526. FileNode::NodeStatus Win32Directory::getStatus() const
  527. {
  528. return mStatus;
  529. }
  530. void Win32Directory::_updateStatus()
  531. {
  532. switch (::GetLastError())
  533. {
  534. case ERROR_NO_MORE_FILES: mStatus = EndOfFile; break;
  535. case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break;
  536. case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break;
  537. case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;
  538. case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break;
  539. default: mStatus = UnknownError; break;
  540. }
  541. }
  542. } // Namespace Win32
  543. bool FS::VerifyWriteAccess(const Path &path)
  544. {
  545. // due to UAC's habit of creating "virtual stores" when permission isn't actually available
  546. // actually create, write, read, verify, and delete a file to the folder being tested
  547. String temp = path.getFullPath();
  548. temp += "\\torque_writetest.tmp";
  549. // first, (try and) delete the file if it exists
  550. ::DeleteFileW(temp.utf16());
  551. // now, create the file
  552. HANDLE hFile = ::CreateFileW(PathToOS(temp).utf16(),
  553. GENERIC_WRITE, 0,
  554. NULL, CREATE_ALWAYS,
  555. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  556. NULL);
  557. if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
  558. return false;
  559. U32 t = Platform::getTime();
  560. DWORD bytesWritten;
  561. if (!::WriteFile(hFile,&t,sizeof(t),&bytesWritten,0))
  562. {
  563. ::CloseHandle(hFile);
  564. ::DeleteFileW(temp.utf16());
  565. return false;
  566. }
  567. // close the file
  568. ::CloseHandle(hFile);
  569. // open for read
  570. hFile = ::CreateFileW(PathToOS(temp).utf16(),
  571. GENERIC_READ, FILE_SHARE_READ,
  572. NULL, OPEN_EXISTING,
  573. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  574. NULL);
  575. if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
  576. return false;
  577. U32 t2 = 0;
  578. DWORD bytesRead;
  579. if (!::ReadFile(hFile,&t2,sizeof(t2),&bytesRead,0))
  580. {
  581. ::CloseHandle(hFile);
  582. ::DeleteFileW(temp.utf16());
  583. return false;
  584. }
  585. ::CloseHandle(hFile);
  586. ::DeleteFileW(temp.utf16());
  587. return t == t2;
  588. }
  589. } // Namespace Torque
  590. //-----------------------------------------------------------------------------
  591. Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume )
  592. {
  593. return new Win32::Win32FileSystem( volume );
  594. }
  595. String Platform::FS::getAssetDir()
  596. {
  597. char cen_buf[2048];
  598. #ifdef TORQUE_UNICODE
  599. if (!Platform::getWebDeployment())
  600. {
  601. TCHAR buf[ 2048 ];
  602. ::GetModuleFileNameW( NULL, buf, sizeof( buf ) );
  603. convertUTF16toUTF8( buf, cen_buf );
  604. }
  605. else
  606. {
  607. TCHAR buf[ 2048 ];
  608. GetCurrentDirectoryW( sizeof( buf ) / sizeof( buf[ 0 ] ), buf );
  609. convertUTF16toUTF8( buf, cen_buf );
  610. return Path::CleanSeparators(cen_buf);
  611. }
  612. #else
  613. ::GetModuleFileNameA( NULL, cen_buf, 2047);
  614. #endif
  615. char *delimiter = dStrrchr( cen_buf, '\\' );
  616. if( delimiter != NULL )
  617. *delimiter = '\0';
  618. return Path::CleanSeparators(cen_buf);
  619. }
  620. /// Function invoked by the kernel layer to install OS specific
  621. /// file systems.
  622. bool Platform::FS::InstallFileSystems()
  623. {
  624. #ifndef TORQUE_SECURE_VFS
  625. WCHAR buffer[1024];
  626. // [8/24/2009 tomb] This stops Windows from complaining about drives that have no disks in
  627. SetErrorMode(SEM_FAILCRITICALERRORS);
  628. // Load all the Win32 logical drives.
  629. DWORD mask = ::GetLogicalDrives();
  630. char drive[] = "A";
  631. char volume[] = "A:/";
  632. while (mask)
  633. {
  634. if (mask & 1)
  635. {
  636. volume[0] = drive[0];
  637. Platform::FS::Mount(drive, Platform::FS::createNativeFS(volume));
  638. }
  639. mask >>= 1;
  640. drive[0]++;
  641. }
  642. // Set the current working dir. Windows normally returns
  643. // upper case driver letters, but the cygwin bash shell
  644. // seems to make it return lower case drives. Force upper
  645. // to be consistent with the mounts.
  646. ::GetCurrentDirectory(sizeof(buffer), buffer);
  647. if (buffer[1] == ':')
  648. buffer[0] = dToupper(buffer[0]);
  649. String wd = buffer;
  650. wd += '/';
  651. Platform::FS::SetCwd(wd);
  652. #endif
  653. return true;
  654. }