volume.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
  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 "platform/platform.h"
  23. #include "core/volume.h"
  24. #include "core/virtualMountSystem.h"
  25. #include "core/strings/findMatch.h"
  26. #include "core/util/journal/process.h"
  27. #include "core/util/safeDelete.h"
  28. #include "console/console.h"
  29. namespace Torque
  30. {
  31. using namespace FS;
  32. //-----------------------------------------------------------------------------
  33. bool FileSystemChangeNotifier::addNotification( const Path &path, ChangeDelegate callback )
  34. {
  35. // Notifications are for files... if the path is empty
  36. // then there is nothing to do.
  37. if ( path.isEmpty() )
  38. return false;
  39. // strip the filename and extension - we notify on dirs
  40. Path dir(cleanPath(path));
  41. if (dir.isEmpty())
  42. return false;
  43. dir.setFileName( String() );
  44. dir.setExtension( String () );
  45. // first lookup the dir to see if we already have an entry for it...
  46. DirMap::Iterator itr = mDirMap.find( dir );
  47. FileInfoList *fileList = NULL;
  48. bool addedDir = false;
  49. // Note that GetFileAttributes can fail here if the file doesn't
  50. // exist, but thats ok and expected. You can have notifications
  51. // on files that don't exist yet.
  52. FileNode::Attributes attr;
  53. GetFileAttributes(path, &attr);
  54. if ( itr != mDirMap.end() )
  55. {
  56. fileList = &(itr->value);
  57. // look for the file and return if we find it
  58. for ( U32 i = 0; i < fileList->getSize(); i++ )
  59. {
  60. FileInfo &fInfo = (*fileList)[i];
  61. if ( fInfo.filePath == path )
  62. {
  63. // NOTE: This is bad... we should store the mod
  64. // time for each callback seperately in the future.
  65. //
  66. fInfo.savedLastModTime = attr.mtime;
  67. fInfo.signal.notify(callback);
  68. return true;
  69. }
  70. }
  71. }
  72. else
  73. {
  74. // otherwise we need to add the dir to our map and let the inherited class add it
  75. itr = mDirMap.insert( dir, FileInfoList() );
  76. fileList = &(itr->value);
  77. addedDir = true;
  78. }
  79. FileInfo newInfo;
  80. newInfo.signal.notify(callback);
  81. newInfo.filePath = path;
  82. newInfo.savedLastModTime = attr.mtime;
  83. fileList->pushBack( newInfo );
  84. return addedDir ? internalAddNotification( dir ) : true;
  85. }
  86. bool FileSystemChangeNotifier::removeNotification( const Path &path, ChangeDelegate callback )
  87. {
  88. if (path.isEmpty())
  89. return false;
  90. // strip the filename and extension - we notify on dirs
  91. Path dir(cleanPath(path));
  92. if (dir.isEmpty())
  93. return false;
  94. dir.setFileName( String() );
  95. dir.setExtension( String () );
  96. DirMap::Iterator itr = mDirMap.find( dir );
  97. if ( itr == mDirMap.end() )
  98. return false;
  99. FileInfoList &fileList = itr->value;
  100. // look for the file and return if we find it
  101. for ( U32 i = 0; i < fileList.getSize(); i++ )
  102. {
  103. FileInfo &fInfo = fileList[i];
  104. if ( fInfo.filePath == path )
  105. {
  106. fInfo.signal.remove(callback);
  107. if (fInfo.signal.isEmpty())
  108. fileList.erase( i );
  109. break;
  110. }
  111. }
  112. // IF we removed the last file
  113. // THEN get rid of the dir from our map and notify inherited classes
  114. if ( fileList.getSize() == 0 )
  115. {
  116. mDirMap.erase( dir );
  117. return internalRemoveNotification( dir );
  118. }
  119. return true;
  120. }
  121. void FileSystemChangeNotifier::startNotifier()
  122. {
  123. // update the timestamps of all the files we are managing
  124. DirMap::Iterator itr = mDirMap.begin();
  125. for ( ; itr != mDirMap.end(); ++itr )
  126. {
  127. FileInfoList &fileList = itr->value;
  128. for ( U32 i = 0; i < fileList.getSize(); i++ )
  129. {
  130. FileInfo &fInfo = fileList[i];
  131. // This may fail if the file doesn't exist... thats ok.
  132. FileNode::Attributes attr;
  133. GetFileAttributes(fInfo.filePath, &attr);
  134. fInfo.savedLastModTime = attr.mtime;
  135. }
  136. }
  137. mNotifying = true;
  138. Process::notify( this, &FileSystemChangeNotifier::process, PROCESS_LAST_ORDER );
  139. }
  140. void FileSystemChangeNotifier::process()
  141. {
  142. internalProcessOnce();
  143. }
  144. void FileSystemChangeNotifier::stopNotifier()
  145. {
  146. mNotifying = false;
  147. Process::remove( this, &FileSystemChangeNotifier::process );
  148. }
  149. /// Makes sure paths going in and out of the notifier will have the same format
  150. String FileSystemChangeNotifier::cleanPath(const Path& dir)
  151. {
  152. // This "cleans up" the path, if we don't do this we can get mismatches on the path
  153. // coming from the notifier
  154. FileSystemRef fs = Torque::FS::GetFileSystem(dir);
  155. if (!fs)
  156. return String::EmptyString;
  157. return fs->mapFrom(fs->mapTo(dir));
  158. }
  159. void FileSystemChangeNotifier::internalNotifyDirChanged( const Path &dir )
  160. {
  161. DirMap::Iterator itr = mDirMap.find( dir );
  162. if ( itr == mDirMap.end() )
  163. return;
  164. // Gather the changed file info.
  165. FileInfoList changedList;
  166. FileInfoList &fileList = itr->value;
  167. for ( U32 i = 0; i < fileList.getSize(); i++ )
  168. {
  169. FileInfo &fInfo = fileList[i];
  170. FileNode::Attributes attr;
  171. bool success = GetFileAttributes(fInfo.filePath, &attr);
  172. // Ignore the file if we couldn't get the attributes (it must have
  173. // been deleted) or the last modification time isn't newer.
  174. if ( !success || attr.mtime <= fInfo.savedLastModTime )
  175. continue;
  176. // Store the new mod time.
  177. fInfo.savedLastModTime = attr.mtime;
  178. // We're taking a copy of the FileInfo struct here so that the
  179. // callback can safely remove the notification without crashing.
  180. changedList.pushBack( fInfo );
  181. }
  182. // Now signal all the changed files.
  183. for ( U32 i = 0; i < changedList.getSize(); i++ )
  184. {
  185. FileInfo &fInfo = changedList[i];
  186. Con::warnf( " : file changed [%s]", fInfo.filePath.getFullPath().c_str() );
  187. fInfo.signal.trigger( fInfo.filePath );
  188. }
  189. }
  190. //-----------------------------------------------------------------------------
  191. FileSystem::FileSystem()
  192. : mChangeNotifier( NULL ),
  193. mReadOnly(false)
  194. {
  195. }
  196. FileSystem::~FileSystem()
  197. {
  198. delete mChangeNotifier;
  199. mChangeNotifier = NULL;
  200. }
  201. File::File() {}
  202. File::~File() {}
  203. Directory::Directory() {}
  204. Directory::~Directory() {}
  205. bool Directory::dump(Vector<Path>& out)
  206. {
  207. const Path sourcePath = getName();
  208. FileNode::Attributes currentAttributes;
  209. while (read(&currentAttributes))
  210. {
  211. Path currentPath = sourcePath;
  212. currentPath.appendPath(currentPath.getFileName());
  213. currentPath.setFileName(currentAttributes.name);
  214. out.push_back(currentPath);
  215. }
  216. return true;
  217. }
  218. bool Directory::dumpFiles(Vector<Path>& out)
  219. {
  220. const Path sourcePath = getName();
  221. FileNode::Attributes currentAttributes;
  222. while (read(&currentAttributes))
  223. {
  224. Path currentPath = sourcePath;
  225. currentPath.appendPath(currentPath.getFileName());
  226. currentPath.setFileName(currentAttributes.name);
  227. if (IsFile(currentPath))
  228. {
  229. out.push_back(currentPath);
  230. }
  231. }
  232. return true;
  233. }
  234. bool Directory::dumpDirectories(Vector<Path>& out)
  235. {
  236. const Path sourcePath = getName();
  237. FileNode::Attributes currentAttributes;
  238. while (read(&currentAttributes))
  239. {
  240. Path currentPath = sourcePath;
  241. currentPath.appendPath(currentPath.getFileName());
  242. currentPath.setFileName(currentAttributes.name);
  243. const bool result = IsDirectory(currentPath);
  244. if (result)
  245. {
  246. out.push_back(currentPath);
  247. }
  248. }
  249. return true;
  250. }
  251. FileNode::FileNode()
  252. : mChecksum(0)
  253. {
  254. }
  255. Time FileNode::getModifiedTime()
  256. {
  257. Attributes attrs;
  258. bool success = getAttributes( &attrs );
  259. if ( !success )
  260. return Time();
  261. return attrs.mtime;
  262. }
  263. Time FileNode::getCreatedTime()
  264. {
  265. Attributes attrs;
  266. bool success = getAttributes(&attrs);
  267. if (!success)
  268. return Time();
  269. return attrs.ctime;
  270. }
  271. U64 FileNode::getSize()
  272. {
  273. Attributes attrs;
  274. bool success = getAttributes( &attrs );
  275. if ( !success )
  276. return 0;
  277. return attrs.size;
  278. }
  279. U32 FileNode::getChecksum()
  280. {
  281. bool calculateCRC = (mLastChecksum == Torque::Time());
  282. if ( !calculateCRC )
  283. {
  284. Torque::Time modTime = getModifiedTime();
  285. calculateCRC = (modTime > mLastChecksum);
  286. }
  287. if ( calculateCRC )
  288. mChecksum = calculateChecksum();
  289. if ( mChecksum )
  290. mLastChecksum = Time::getCurrentTime();
  291. return mChecksum;
  292. }
  293. //-----------------------------------------------------------------------------
  294. class FileSystemRedirect: public FileSystem
  295. {
  296. friend class FileSystemRedirectChangeNotifier;
  297. public:
  298. FileSystemRedirect(MountSystem* mfs,const Path& path);
  299. String getTypeStr() const { return "Redirect"; }
  300. FileNodeRef resolve(const Path& path);
  301. FileNodeRef create(const Path& path,FileNode::Mode);
  302. bool remove(const Path& path);
  303. bool rename(const Path& a,const Path& b);
  304. Path mapTo(const Path& path);
  305. Path mapFrom(const Path& path);
  306. private:
  307. Path _merge(const Path& path);
  308. Path mPath;
  309. MountSystem *mMFS;
  310. };
  311. class FileSystemRedirectChangeNotifier : public FileSystemChangeNotifier
  312. {
  313. public:
  314. FileSystemRedirectChangeNotifier( FileSystem *fs );
  315. bool addNotification( const Path &path, ChangeDelegate callback );
  316. bool removeNotification( const Path &path, ChangeDelegate callback );
  317. protected:
  318. virtual void internalProcessOnce() {}
  319. virtual bool internalAddNotification( const Path &dir ) { return false; }
  320. virtual bool internalRemoveNotification( const Path &dir ) { return false; }
  321. };
  322. FileSystemRedirectChangeNotifier::FileSystemRedirectChangeNotifier( FileSystem *fs )
  323. : FileSystemChangeNotifier( fs )
  324. {
  325. }
  326. bool FileSystemRedirectChangeNotifier::addNotification( const Path &path, ChangeDelegate callback )
  327. {
  328. FileSystemRedirect *rfs = (FileSystemRedirect*)mFS;
  329. Path redirectPath = rfs->_merge( path );
  330. FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath );
  331. if ( !fs || !fs->getChangeNotifier() )
  332. return false;
  333. return fs->getChangeNotifier()->addNotification( redirectPath, callback );
  334. }
  335. bool FileSystemRedirectChangeNotifier::removeNotification( const Path &path, ChangeDelegate callback )
  336. {
  337. FileSystemRedirect *rfs = (FileSystemRedirect*)mFS;
  338. Path redirectPath = rfs->_merge( path );
  339. FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath );
  340. if ( !fs || !fs->getChangeNotifier() )
  341. return false;
  342. return fs->getChangeNotifier()->removeNotification( redirectPath, callback );
  343. }
  344. FileSystemRedirect::FileSystemRedirect(MountSystem* mfs,const Path& path)
  345. {
  346. mMFS = mfs;
  347. mPath.setRoot(path.getRoot());
  348. mPath.setPath(path.getPath());
  349. mChangeNotifier = new FileSystemRedirectChangeNotifier( this );
  350. }
  351. Path FileSystemRedirect::_merge(const Path& path)
  352. {
  353. Path p = mPath;
  354. p.setPath(Path::Join(p.getPath(),'/',Path::CompressPath(path.getPath())));
  355. p.setFileName(path.getFileName());
  356. p.setExtension(path.getExtension());
  357. return p;
  358. }
  359. FileNodeRef FileSystemRedirect::resolve(const Path& path)
  360. {
  361. Path p = _merge(path);
  362. FileSystemRef fs = mMFS->getFileSystem(p);
  363. if (fs != NULL)
  364. return fs->resolve(p);
  365. return NULL;
  366. }
  367. FileNodeRef FileSystemRedirect::create(const Path& path,FileNode::Mode mode)
  368. {
  369. Path p = _merge(path);
  370. FileSystemRef fs = mMFS->getFileSystem(p);
  371. if (fs != NULL)
  372. return fs->create(p,mode);
  373. return NULL;
  374. }
  375. bool FileSystemRedirect::remove(const Path& path)
  376. {
  377. Path p = _merge(path);
  378. FileSystemRef fs = mMFS->getFileSystem(p);
  379. if (fs != NULL)
  380. return fs->remove(p);
  381. return false;
  382. }
  383. bool FileSystemRedirect::rename(const Path& a,const Path& b)
  384. {
  385. Path na = _merge(a);
  386. Path nb = _merge(b);
  387. FileSystemRef fsa = mMFS->getFileSystem(na);
  388. FileSystemRef fsb = mMFS->getFileSystem(nb);
  389. if (fsa.getPointer() == fsb.getPointer())
  390. return fsa->rename(na,nb);
  391. return false;
  392. }
  393. Path FileSystemRedirect::mapTo(const Path& path)
  394. {
  395. Path p = _merge(path);
  396. FileSystemRef fs = mMFS->getFileSystem(p);
  397. if (fs != NULL)
  398. return fs->mapTo(p);
  399. return NULL;
  400. }
  401. Path FileSystemRedirect::mapFrom(const Path& path)
  402. {
  403. Path p = _merge(path);
  404. FileSystemRef fs = mMFS->getFileSystem(p);
  405. if (fs != NULL)
  406. return fs->mapFrom(p);
  407. return NULL;
  408. }
  409. //-----------------------------------------------------------------------------
  410. void MountSystem::_log(const String& msg)
  411. {
  412. String newMsg = "MountSystem: " + msg;
  413. Con::warnf("%s", newMsg.c_str());
  414. }
  415. FileSystemRef MountSystem::_removeMountFromList(String root)
  416. {
  417. for (Vector<MountFS>::iterator itr = mMountList.begin(); itr != mMountList.end(); itr++)
  418. {
  419. if (root.equal( itr->root, String::NoCase ))
  420. {
  421. FileSystemRef fs = itr->fileSystem;
  422. mMountList.erase(itr);
  423. return fs;
  424. }
  425. }
  426. return NULL;
  427. }
  428. FileSystemRef MountSystem::_getFileSystemFromList(const Path& path) const
  429. {
  430. for (Vector<MountFS>::const_iterator itr = mMountList.begin(); itr != mMountList.end(); itr++)
  431. {
  432. if (itr->root.equal( path.getRoot(), String::NoCase ))
  433. return itr->fileSystem;
  434. }
  435. return NULL;
  436. }
  437. Path MountSystem::_normalize(const Path& path)
  438. {
  439. Path po = path;
  440. // Assign to cwd root if none is specified.
  441. if( po.getRoot().isEmpty() )
  442. po.setRoot( mCWD.getRoot() );
  443. // Merge in current working directory if the path is relative to
  444. // the current cwd.
  445. if( po.getRoot().equal( mCWD.getRoot(), String::NoCase ) && po.isRelative() )
  446. {
  447. po.setPath( Path::CompressPath( Path::Join( mCWD.getPath(),'/',po.getPath() ) ) );
  448. }
  449. return po;
  450. }
  451. bool MountSystem::copyFile(const Path& source, const Path& destination, bool noOverwrite)
  452. {
  453. // Exit out early if we're not overriding
  454. if (isFile(destination) && noOverwrite)
  455. {
  456. return true;
  457. }
  458. FileRef sourceFile = openFile(source, FS::File::AccessMode::Read);
  459. const U64 sourceFileSize = sourceFile->getSize();
  460. void* writeBuffer = dMalloc(sourceFileSize);
  461. sourceFile->read(writeBuffer, sourceFileSize);
  462. FileRef destinationFile = openFile(destination, FS::File::AccessMode::Write);
  463. const bool success = destinationFile->write(writeBuffer, sourceFileSize) == sourceFileSize;
  464. dFree(writeBuffer);
  465. return success;
  466. }
  467. bool MountSystem::_dumpDirectories(DirectoryRef directory, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath, S32 currentDepth, const Path& basePath)
  468. {
  469. Vector<Torque::Path> directoryPaths;
  470. if (!directory->dumpDirectories(directoryPaths))
  471. {
  472. return false;
  473. }
  474. // Queries against / will return a directory count of 1, but the code relies on actual directory entries (Eg. /data) so we handle that special case
  475. const U32 basePathDirectoryCount = String::compare(basePath.getFullPathWithoutRoot(), "/") == 0 ? basePath.getDirectoryCount() - 1 : basePath.getDirectoryCount();
  476. for (U32 iteration = 0; iteration < directoryPaths.size(); ++iteration)
  477. {
  478. const Path& directoryPath = directoryPaths[iteration];
  479. // Load the full path to the directory unless we're not supposed to include base paths
  480. String directoryPathString = directoryPath.getFullPath().c_str();
  481. if (noBasePath)
  482. {
  483. // Build a path representing the directory tree *after* the base path query but excluding the base
  484. // So if we queried for data/ and are currently processing data/ExampleModule/datablocks we want to output
  485. // ExampleModule/datablocks
  486. Path newDirectoryPath;
  487. for (U32 iteration = basePathDirectoryCount; iteration < directoryPath.getDirectoryCount(); ++iteration)
  488. {
  489. if (iteration > basePathDirectoryCount)
  490. {
  491. newDirectoryPath.setPath(newDirectoryPath.getPath() + "/");
  492. }
  493. newDirectoryPath.setPath(newDirectoryPath.getPath() + directoryPath.getDirectory(iteration));
  494. }
  495. newDirectoryPath.setFileName(directoryPath.getFileName());
  496. newDirectoryPath.setExtension(directoryPath.getExtension());
  497. directoryPathString = newDirectoryPath.getFullPathWithoutRoot();
  498. }
  499. // Output result and enumerate subdirectories if we're not too deep according to the depth parameter
  500. directories.push_back(StringTable->insert(directoryPathString, true));
  501. if (currentDepth <= depth)
  502. {
  503. const String subdirectoryPath = directoryPath.getFullPath() + "/";
  504. DirectoryRef nextDirectory = OpenDirectory(subdirectoryPath);
  505. _dumpDirectories(nextDirectory, directories, depth, noBasePath, currentDepth + 1, basePath);
  506. }
  507. }
  508. return true;
  509. }
  510. bool MountSystem::dumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath)
  511. {
  512. if (!isDirectory(path))
  513. {
  514. return false;
  515. }
  516. DirectoryRef sourceDirectory = openDirectory(path);
  517. return _dumpDirectories(sourceDirectory, directories, depth, noBasePath, 1, path);
  518. }
  519. FileRef MountSystem::createFile(const Path& path)
  520. {
  521. Path np = _normalize(path);
  522. FileSystemRef fs = _getFileSystemFromList(np);
  523. if (fs && fs->isReadOnly())
  524. {
  525. _log(String::ToString("Cannot create file %s, filesystem is read-only", path.getFullPath().c_str()));
  526. return NULL;
  527. }
  528. if (fs != NULL)
  529. return static_cast<File*>(fs->create(np,FileNode::File).getPointer());
  530. return NULL;
  531. }
  532. DirectoryRef MountSystem::createDirectory(const Path& path, FileSystemRef fs)
  533. {
  534. Path np = _normalize(path);
  535. if (fs.isNull())
  536. fs = _getFileSystemFromList(np);
  537. if (fs && fs->isReadOnly())
  538. {
  539. _log(String::ToString("Cannot create directory %s, filesystem is read-only", path.getFullPath().c_str()));
  540. return NULL;
  541. }
  542. if (fs != NULL)
  543. return static_cast<Directory*>(fs->create(np,FileNode::Directory).getPointer());
  544. return NULL;
  545. }
  546. FileRef MountSystem::openFile(const Path& path,File::AccessMode mode)
  547. {
  548. FileNodeRef node = getFileNode(path);
  549. if (node != NULL)
  550. {
  551. FileRef file = dynamic_cast<File*>(node.getPointer());
  552. if (file != NULL)
  553. {
  554. if (file->open(mode))
  555. return file;
  556. else
  557. return NULL;
  558. }
  559. }
  560. else
  561. {
  562. if (mode != File::Read)
  563. {
  564. FileRef file = createFile(path);
  565. if (file != NULL)
  566. {
  567. file->open(mode);
  568. return file;
  569. }
  570. }
  571. }
  572. return NULL;
  573. }
  574. DirectoryRef MountSystem::openDirectory(const Path& path)
  575. {
  576. FileNodeRef node = getFileNode(path);
  577. if (node != NULL)
  578. {
  579. DirectoryRef dir = dynamic_cast<Directory*>(node.getPointer());
  580. if (dir != NULL)
  581. {
  582. dir->open();
  583. return dir;
  584. }
  585. }
  586. return NULL;
  587. }
  588. bool MountSystem::remove(const Path& path)
  589. {
  590. Path np = _normalize(path);
  591. FileSystemRef fs = _getFileSystemFromList(np);
  592. if (fs && fs->isReadOnly())
  593. {
  594. _log(String::ToString("Cannot remove path %s, filesystem is read-only", path.getFullPath().c_str()));
  595. return false;
  596. }
  597. if (fs != NULL)
  598. return fs->remove(np);
  599. return false;
  600. }
  601. bool MountSystem::rename(const Path& from,const Path& to)
  602. {
  603. // Will only rename files on the same filesystem
  604. Path pa = _normalize(from);
  605. Path pb = _normalize(to);
  606. FileSystemRef fsa = _getFileSystemFromList(pa);
  607. FileSystemRef fsb = _getFileSystemFromList(pb);
  608. if (!fsa || !fsb)
  609. return false;
  610. if (fsa.getPointer() != fsb.getPointer())
  611. {
  612. _log(String::ToString("Cannot rename path %s to a different filesystem", from.getFullPath().c_str()));
  613. return false;
  614. }
  615. if (fsa->isReadOnly() || fsb->isReadOnly())
  616. {
  617. _log(String::ToString("Cannot rename path %s; source or target filesystem is read-only", from.getFullPath().c_str()));
  618. return false;
  619. }
  620. return fsa->rename(pa,pb);
  621. }
  622. bool MountSystem::mount(String root,FileSystemRef fs)
  623. {
  624. MountFS mount;
  625. mount.root = root;
  626. mount.path = "/";
  627. mount.fileSystem = fs;
  628. mMountList.push_back(mount);
  629. return true;
  630. }
  631. bool MountSystem::mount(String root,const Path &path)
  632. {
  633. return mount(root,new FileSystemRedirect(this,_normalize(path)));
  634. }
  635. FileSystemRef MountSystem::unmount(String root)
  636. {
  637. FileSystemRef first = _removeMountFromList(root);
  638. // remove remaining FSes on this root
  639. while (!_removeMountFromList(root).isNull())
  640. ;
  641. return first;
  642. }
  643. bool MountSystem::unmount(FileSystemRef fs)
  644. {
  645. if (fs.isNull())
  646. return false;
  647. // iterate back to front in case FS is in list multiple times.
  648. // also check that fs is not null each time since its a strong ref
  649. // so it could be nulled during removal.
  650. bool unmounted = false;
  651. for (S32 i = mMountList.size() - 1; !fs.isNull() && i >= 0; --i)
  652. {
  653. if (mMountList[i].fileSystem.getPointer() == fs.getPointer())
  654. {
  655. mMountList.erase(i);
  656. unmounted = true;
  657. }
  658. }
  659. return unmounted;
  660. }
  661. bool MountSystem::setCwd(const Path& file)
  662. {
  663. if (file.getPath().isEmpty())
  664. return false;
  665. mCWD.setRoot(file.getRoot());
  666. mCWD.setPath(file.getPath());
  667. return true;
  668. }
  669. const Path& MountSystem::getCwd() const
  670. {
  671. return mCWD;
  672. }
  673. FileSystemRef MountSystem::getFileSystem(const Path& path)
  674. {
  675. return _getFileSystemFromList(_normalize(path));
  676. }
  677. bool MountSystem::getFileAttributes(const Path& path,FileNode::Attributes* attr)
  678. {
  679. FileNodeRef file = getFileNode(path);
  680. if (file != NULL)
  681. {
  682. bool result = file->getAttributes(attr);
  683. return result;
  684. }
  685. return false;
  686. }
  687. FileNodeRef MountSystem::getFileNode(const Path& path)
  688. {
  689. Path np = _normalize(path);
  690. FileSystemRef fs = _getFileSystemFromList(np);
  691. if (fs != NULL)
  692. return fs->resolve(np);
  693. return NULL;
  694. }
  695. bool MountSystem::mapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
  696. {
  697. FileSystemRef fs = _getFileSystemFromList(inRoot);
  698. if ( fs == NULL )
  699. {
  700. outPath = Path();
  701. return false;
  702. }
  703. outPath = fs->mapFrom( inPath );
  704. return outPath.getFullPath() != String();
  705. }
  706. S32 MountSystem::findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool includeDirs/* =false */, bool multiMatch /* = true */ )
  707. {
  708. if (mFindByPatternOverrideFS.isNull() && !inBasePath.isDirectory() )
  709. return -1;
  710. DirectoryRef dir = NULL;
  711. if (mFindByPatternOverrideFS.isNull())
  712. // open directory using standard mount system search
  713. dir = openDirectory( inBasePath );
  714. else
  715. {
  716. // use specified filesystem to open directory
  717. FileNodeRef fNode = mFindByPatternOverrideFS->resolveLoose(inBasePath);
  718. if (fNode && (dir = dynamic_cast<Directory*>(fNode.getPointer())) != NULL)
  719. dir->open();
  720. }
  721. if ( dir == NULL )
  722. return -1;
  723. if (includeDirs)
  724. {
  725. // prepend cheesy "DIR:" annotation for directories
  726. outList.push_back(String("DIR:") + inBasePath.getPath());
  727. }
  728. FileNode::Attributes attrs;
  729. Vector<String> recurseDirs;
  730. while ( dir->read( &attrs ) )
  731. {
  732. // skip hidden files
  733. if ( attrs.name.c_str()[0] == '.' )
  734. continue;
  735. String name( attrs.name );
  736. if ( (attrs.flags & FileNode::Directory) && inRecursive )
  737. {
  738. name += '/';
  739. String path = Path::Join( inBasePath, '/', name );
  740. recurseDirs.push_back( path );
  741. }
  742. if ( !multiMatch && FindMatch::isMatch( inFilePattern, attrs.name, false ) )
  743. {
  744. String path = Path::Join( inBasePath, '/', name );
  745. outList.push_back( path );
  746. }
  747. if ( multiMatch && FindMatch::isMatchMultipleExprs( inFilePattern, attrs.name, false ) )
  748. {
  749. String path = Path::Join( inBasePath, '/', name );
  750. outList.push_back( path );
  751. }
  752. }
  753. dir->close();
  754. for ( S32 i = 0; i < recurseDirs.size(); i++ )
  755. findByPattern( recurseDirs[i], inFilePattern, true, outList, includeDirs, multiMatch );
  756. return outList.size();
  757. }
  758. bool MountSystem::isFile(const Path& path)
  759. {
  760. FileNode::Attributes attr;
  761. if (getFileAttributes(path,&attr))
  762. return attr.flags & FileNode::File;
  763. return false;
  764. }
  765. bool MountSystem::isDirectory(const Path& path, FileSystemRef fsRef)
  766. {
  767. FileNode::Attributes attr;
  768. bool result = false;
  769. if (fsRef.isNull())
  770. {
  771. if (getFileAttributes(path, &attr))
  772. result = (attr.flags & FileNode::Directory) != 0;
  773. }
  774. else
  775. {
  776. FileNodeRef fnRef = fsRef->resolve(path);
  777. if (!fnRef.isNull() && fnRef->getAttributes(&attr))
  778. result = (attr.flags & FileNode::Directory) != 0;
  779. }
  780. return result;
  781. }
  782. bool MountSystem::isReadOnly(const Path& path)
  783. {
  784. // first check to see if filesystem is read only
  785. FileSystemRef fs = getFileSystem(path);
  786. if ( fs.isNull() )
  787. // no filesystem owns this file...oh well, return false
  788. return false;
  789. if (fs->isReadOnly())
  790. return true;
  791. // check the file attributes, note that if the file does not exist,
  792. // this function returns false. that should be ok since we know
  793. // the file system is writable at this point.
  794. FileNode::Attributes attr;
  795. if (getFileAttributes(path,&attr))
  796. return attr.flags & FileNode::ReadOnly;
  797. return false;
  798. }
  799. void MountSystem::startFileChangeNotifications()
  800. {
  801. for ( U32 i = 0; i < mMountList.size(); i++ )
  802. {
  803. FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
  804. if ( notifier != NULL && !notifier->isNotifying() )
  805. notifier->startNotifier();
  806. }
  807. }
  808. void MountSystem::stopFileChangeNotifications()
  809. {
  810. for ( U32 i = 0; i < mMountList.size(); i++ )
  811. {
  812. FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
  813. if ( notifier != NULL && notifier->isNotifying() )
  814. notifier->stopNotifier();
  815. }
  816. }
  817. bool MountSystem::createPath(const Path& path)
  818. {
  819. if (path.getPath().isEmpty())
  820. return true;
  821. // See if the pathectory exists
  822. Path dir;
  823. dir.setRoot(path.getRoot());
  824. dir.setPath(path.getPath());
  825. // in a virtual mount system, isDirectory may return true if the directory exists in a read only FS,
  826. // but the directory may not exist on a writeable filesystem that is also mounted.
  827. // So get the target filesystem that will
  828. // be used for the full writable path and and make sure the directory exists on it.
  829. FileSystemRef fsRef = getFileSystem(path);
  830. if (isDirectory(dir,fsRef))
  831. return true;
  832. // Start from the top and work our way down
  833. Path sub;
  834. dir.setPath(path.isAbsolute()? String("/"): String());
  835. for (U32 i = 0; i < path.getDirectoryCount(); i++)
  836. {
  837. sub.setPath(path.getDirectory(i));
  838. dir.appendPath(sub);
  839. if (!isDirectory(dir,fsRef))
  840. {
  841. if (!createDirectory(dir,fsRef))
  842. return false;
  843. }
  844. }
  845. return true;
  846. }
  847. //-----------------------------------------------------------------------------
  848. // Default global mount system
  849. #ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM
  850. // Note that the Platform::FS::MountZips() must be called in platformVolume.cpp for zip support to work.
  851. static VirtualMountSystem sgMountSystem;
  852. #else
  853. static MountSystem sgMountSystem;
  854. #endif
  855. namespace FS
  856. {
  857. FileRef CreateFile(const Path &path)
  858. {
  859. return sgMountSystem.createFile(path);
  860. }
  861. bool CopyFile(const Path& source, const Path& destination, bool noOverwrite)
  862. {
  863. return sgMountSystem.copyFile(source, destination, noOverwrite);
  864. }
  865. bool DumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath)
  866. {
  867. return sgMountSystem.dumpDirectories(path, directories, depth, noBasePath);
  868. }
  869. DirectoryRef CreateDirectory(const Path &path)
  870. {
  871. return sgMountSystem.createDirectory(path);
  872. }
  873. FileRef OpenFile(const Path &path, File::AccessMode mode)
  874. {
  875. return sgMountSystem.openFile(path,mode);
  876. }
  877. bool ReadFile(const Path &inPath, void *&outData, U32 &outSize, bool inNullTerminate )
  878. {
  879. FileRef fileR = OpenFile( inPath, File::Read );
  880. outData = NULL;
  881. outSize = 0;
  882. // We'll get a NULL file reference if
  883. // the file failed to open.
  884. if ( fileR == NULL )
  885. return false;
  886. outSize = fileR->getSize();
  887. // Its not a failure to read an empty
  888. // file... but we can exit early.
  889. if ( outSize == 0 )
  890. return true;
  891. U32 sizeRead = 0;
  892. if ( inNullTerminate )
  893. {
  894. outData = new char [outSize+1];
  895. if( !outData )
  896. {
  897. // out of memory
  898. return false;
  899. }
  900. sizeRead = fileR->read(outData, outSize);
  901. static_cast<char *>(outData)[outSize] = '\0';
  902. }
  903. else
  904. {
  905. outData = new char [outSize];
  906. if( !outData )
  907. {
  908. // out of memory
  909. return false;
  910. }
  911. sizeRead = fileR->read(outData, outSize);
  912. }
  913. if ( sizeRead != outSize )
  914. {
  915. delete static_cast<char *>(outData);
  916. outData = NULL;
  917. outSize = 0;
  918. return false;
  919. }
  920. return true;
  921. }
  922. DirectoryRef OpenDirectory(const Path &path)
  923. {
  924. return sgMountSystem.openDirectory(path);
  925. }
  926. bool Remove(const Path &path)
  927. {
  928. return sgMountSystem.remove(path);
  929. }
  930. bool Rename(const Path &from, const Path &to)
  931. {
  932. return sgMountSystem.rename(from,to);
  933. }
  934. bool Mount(String root, FileSystemRef fs)
  935. {
  936. return sgMountSystem.mount(root,fs);
  937. }
  938. bool Mount(String root, const Path &path)
  939. {
  940. return sgMountSystem.mount(root,path);
  941. }
  942. FileSystemRef Unmount(String root)
  943. {
  944. return sgMountSystem.unmount(root);
  945. }
  946. bool Unmount(FileSystemRef fs)
  947. {
  948. return sgMountSystem.unmount(fs);
  949. }
  950. bool SetCwd(const Path &file)
  951. {
  952. return sgMountSystem.setCwd(file);
  953. }
  954. const Path& GetCwd()
  955. {
  956. return sgMountSystem.getCwd();
  957. }
  958. FileSystemRef GetFileSystem(const Path &path)
  959. {
  960. return sgMountSystem.getFileSystem(path);
  961. }
  962. bool GetFileAttributes(const Path &path, FileNode::Attributes* attr)
  963. {
  964. return sgMountSystem.getFileAttributes(path,attr);
  965. }
  966. S32 CompareModifiedTimes(const Path& p1, const Path& p2)
  967. {
  968. FileNode::Attributes a1, a2;
  969. if (!Torque::FS::GetFileAttributes(p1, &a1))
  970. return -1;
  971. if (!Torque::FS::GetFileAttributes(p2, &a2))
  972. return -1;
  973. if (a1.mtime < a2.mtime)
  974. return -1;
  975. if (a1.mtime == a2.mtime)
  976. return 0;
  977. return 1;
  978. }
  979. FileNodeRef GetFileNode(const Path &path)
  980. {
  981. return sgMountSystem.getFileNode(path);
  982. }
  983. bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
  984. {
  985. return sgMountSystem.mapFSPath( inRoot, inPath, outPath );
  986. }
  987. bool GetFSPath( const Path &inPath, Path &outPath )
  988. {
  989. FileSystemRef sys = GetFileSystem( inPath );
  990. if ( sys )
  991. {
  992. outPath = sys->mapTo( inPath );
  993. return true;
  994. }
  995. return false;
  996. }
  997. S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool multiMatch )
  998. {
  999. return sgMountSystem.findByPattern(inBasePath, inFilePattern, inRecursive, outList, false, multiMatch);
  1000. }
  1001. bool IsFile(const Path &path)
  1002. {
  1003. return sgMountSystem.isFile(path);
  1004. }
  1005. bool IsScriptFile(const char* pFilePath)
  1006. {
  1007. return (sgMountSystem.isFile(pFilePath)
  1008. || sgMountSystem.isFile(pFilePath + String(".dso"))
  1009. || sgMountSystem.isFile(pFilePath + String(".mis"))
  1010. || sgMountSystem.isFile(pFilePath + String(".mis.dso"))
  1011. || sgMountSystem.isFile(pFilePath + String(".gui"))
  1012. || sgMountSystem.isFile(pFilePath + String(".gui.dso"))
  1013. || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION))
  1014. || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION) + String(".dso")));
  1015. }
  1016. bool IsDirectory(const Path &path)
  1017. {
  1018. return sgMountSystem.isDirectory(path);
  1019. }
  1020. bool IsReadOnly(const Path &path)
  1021. {
  1022. return sgMountSystem.isReadOnly(path);
  1023. }
  1024. String MakeUniquePath( const char *path, const char *fileName, const char *ext )
  1025. {
  1026. Path filePath;
  1027. filePath.setPath( path );
  1028. filePath.setFileName( fileName );
  1029. filePath.setExtension( ext );
  1030. // First get an upper bound on the range of filenames to search. This lets us
  1031. // quickly skip past a large number of existing filenames.
  1032. // Note: upper limit of 2^31 added to handle the degenerate case of a folder
  1033. // with files named using the powers of 2, but plenty of space in between!
  1034. U32 high = 1;
  1035. while ( IsFile( filePath ) && ( high < 0x80000000 ) )
  1036. {
  1037. high = high * 2;
  1038. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  1039. }
  1040. // Now perform binary search for first filename in the range that doesn't exist
  1041. // Note that the returned name will not be strictly numerically *first* if the
  1042. // existing filenames are non-sequential (eg. 4,6,7), but it will still be unique.
  1043. if ( high > 1 )
  1044. {
  1045. U32 low = high / 2;
  1046. while ( high - low > 1 )
  1047. {
  1048. U32 probe = low + ( high - low ) / 2;
  1049. filePath.setFileName( String::ToString( "%s%d", fileName, probe ) );
  1050. if ( IsFile( filePath ) )
  1051. low = probe;
  1052. else
  1053. high = probe;
  1054. }
  1055. // The 'high' index is guaranteed not to exist
  1056. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  1057. }
  1058. return filePath.getFullPath();
  1059. }
  1060. void StartFileChangeNotifications() { sgMountSystem.startFileChangeNotifications(); }
  1061. void StopFileChangeNotifications() { sgMountSystem.stopFileChangeNotifications(); }
  1062. S32 GetNumMounts() { return sgMountSystem.getNumMounts(); }
  1063. String GetMountRoot( S32 index ) { return sgMountSystem.getMountRoot(index); }
  1064. String GetMountPath( S32 index ) { return sgMountSystem.getMountPath(index); }
  1065. String GetMountType( S32 index ) { return sgMountSystem.getMountType(index); }
  1066. bool CreatePath(const Path& path)
  1067. {
  1068. return sgMountSystem.createPath(path);
  1069. }
  1070. } // Namespace FS
  1071. } // Namespace Torque