volume.cpp 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  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. FileNode::FileNode()
  206. : mChecksum(0)
  207. {
  208. }
  209. Time FileNode::getModifiedTime()
  210. {
  211. Attributes attrs;
  212. bool success = getAttributes( &attrs );
  213. if ( !success )
  214. return Time();
  215. return attrs.mtime;
  216. }
  217. U64 FileNode::getSize()
  218. {
  219. Attributes attrs;
  220. bool success = getAttributes( &attrs );
  221. if ( !success )
  222. return 0;
  223. return attrs.size;
  224. }
  225. U32 FileNode::getChecksum()
  226. {
  227. bool calculateCRC = (mLastChecksum == Torque::Time());
  228. if ( !calculateCRC )
  229. {
  230. Torque::Time modTime = getModifiedTime();
  231. calculateCRC = (modTime > mLastChecksum);
  232. }
  233. if ( calculateCRC )
  234. mChecksum = calculateChecksum();
  235. if ( mChecksum )
  236. mLastChecksum = Time::getCurrentTime();
  237. return mChecksum;
  238. }
  239. //-----------------------------------------------------------------------------
  240. class FileSystemRedirect: public FileSystem
  241. {
  242. friend class FileSystemRedirectChangeNotifier;
  243. public:
  244. FileSystemRedirect(MountSystem* mfs,const Path& path);
  245. String getTypeStr() const { return "Redirect"; }
  246. FileNodeRef resolve(const Path& path);
  247. FileNodeRef create(const Path& path,FileNode::Mode);
  248. bool remove(const Path& path);
  249. bool rename(const Path& a,const Path& b);
  250. Path mapTo(const Path& path);
  251. Path mapFrom(const Path& path);
  252. private:
  253. Path _merge(const Path& path);
  254. Path mPath;
  255. MountSystem *mMFS;
  256. };
  257. class FileSystemRedirectChangeNotifier : public FileSystemChangeNotifier
  258. {
  259. public:
  260. FileSystemRedirectChangeNotifier( FileSystem *fs );
  261. bool addNotification( const Path &path, ChangeDelegate callback );
  262. bool removeNotification( const Path &path, ChangeDelegate callback );
  263. protected:
  264. virtual void internalProcessOnce() {}
  265. virtual bool internalAddNotification( const Path &dir ) { return false; }
  266. virtual bool internalRemoveNotification( const Path &dir ) { return false; }
  267. };
  268. FileSystemRedirectChangeNotifier::FileSystemRedirectChangeNotifier( FileSystem *fs )
  269. : FileSystemChangeNotifier( fs )
  270. {
  271. }
  272. bool FileSystemRedirectChangeNotifier::addNotification( const Path &path, ChangeDelegate callback )
  273. {
  274. FileSystemRedirect *rfs = (FileSystemRedirect*)mFS;
  275. Path redirectPath = rfs->_merge( path );
  276. FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath );
  277. if ( !fs || !fs->getChangeNotifier() )
  278. return false;
  279. return fs->getChangeNotifier()->addNotification( redirectPath, callback );
  280. }
  281. bool FileSystemRedirectChangeNotifier::removeNotification( const Path &path, ChangeDelegate callback )
  282. {
  283. FileSystemRedirect *rfs = (FileSystemRedirect*)mFS;
  284. Path redirectPath = rfs->_merge( path );
  285. FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath );
  286. if ( !fs || !fs->getChangeNotifier() )
  287. return false;
  288. return fs->getChangeNotifier()->removeNotification( redirectPath, callback );
  289. }
  290. FileSystemRedirect::FileSystemRedirect(MountSystem* mfs,const Path& path)
  291. {
  292. mMFS = mfs;
  293. mPath.setRoot(path.getRoot());
  294. mPath.setPath(path.getPath());
  295. mChangeNotifier = new FileSystemRedirectChangeNotifier( this );
  296. }
  297. Path FileSystemRedirect::_merge(const Path& path)
  298. {
  299. Path p = mPath;
  300. p.setPath(Path::Join(p.getPath(),'/',Path::CompressPath(path.getPath())));
  301. p.setFileName(path.getFileName());
  302. p.setExtension(path.getExtension());
  303. return p;
  304. }
  305. FileNodeRef FileSystemRedirect::resolve(const Path& path)
  306. {
  307. Path p = _merge(path);
  308. FileSystemRef fs = mMFS->getFileSystem(p);
  309. if (fs != NULL)
  310. return fs->resolve(p);
  311. return NULL;
  312. }
  313. FileNodeRef FileSystemRedirect::create(const Path& path,FileNode::Mode mode)
  314. {
  315. Path p = _merge(path);
  316. FileSystemRef fs = mMFS->getFileSystem(p);
  317. if (fs != NULL)
  318. return fs->create(p,mode);
  319. return NULL;
  320. }
  321. bool FileSystemRedirect::remove(const Path& path)
  322. {
  323. Path p = _merge(path);
  324. FileSystemRef fs = mMFS->getFileSystem(p);
  325. if (fs != NULL)
  326. return fs->remove(p);
  327. return false;
  328. }
  329. bool FileSystemRedirect::rename(const Path& a,const Path& b)
  330. {
  331. Path na = _merge(a);
  332. Path nb = _merge(b);
  333. FileSystemRef fsa = mMFS->getFileSystem(na);
  334. FileSystemRef fsb = mMFS->getFileSystem(nb);
  335. if (fsa.getPointer() == fsb.getPointer())
  336. return fsa->rename(na,nb);
  337. return false;
  338. }
  339. Path FileSystemRedirect::mapTo(const Path& path)
  340. {
  341. Path p = _merge(path);
  342. FileSystemRef fs = mMFS->getFileSystem(p);
  343. if (fs != NULL)
  344. return fs->mapTo(p);
  345. return NULL;
  346. }
  347. Path FileSystemRedirect::mapFrom(const Path& path)
  348. {
  349. Path p = _merge(path);
  350. FileSystemRef fs = mMFS->getFileSystem(p);
  351. if (fs != NULL)
  352. return fs->mapFrom(p);
  353. return NULL;
  354. }
  355. //-----------------------------------------------------------------------------
  356. void MountSystem::_log(const String& msg)
  357. {
  358. String newMsg = "MountSystem: " + msg;
  359. Con::warnf("%s", newMsg.c_str());
  360. }
  361. FileSystemRef MountSystem::_removeMountFromList(String root)
  362. {
  363. for (Vector<MountFS>::iterator itr = mMountList.begin(); itr != mMountList.end(); itr++)
  364. {
  365. if (root.equal( itr->root, String::NoCase ))
  366. {
  367. FileSystemRef fs = itr->fileSystem;
  368. mMountList.erase(itr);
  369. return fs;
  370. }
  371. }
  372. return NULL;
  373. }
  374. FileSystemRef MountSystem::_getFileSystemFromList(const Path& path) const
  375. {
  376. for (Vector<MountFS>::const_iterator itr = mMountList.begin(); itr != mMountList.end(); itr++)
  377. {
  378. if (itr->root.equal( path.getRoot(), String::NoCase ))
  379. return itr->fileSystem;
  380. }
  381. return NULL;
  382. }
  383. Path MountSystem::_normalize(const Path& path)
  384. {
  385. Path po = path;
  386. // Assign to cwd root if none is specified.
  387. if( po.getRoot().isEmpty() )
  388. po.setRoot( mCWD.getRoot() );
  389. // Merge in current working directory if the path is relative to
  390. // the current cwd.
  391. if( po.getRoot().equal( mCWD.getRoot(), String::NoCase ) && po.isRelative() )
  392. {
  393. po.setPath( Path::CompressPath( Path::Join( mCWD.getPath(),'/',po.getPath() ) ) );
  394. }
  395. return po;
  396. }
  397. FileRef MountSystem::createFile(const Path& path)
  398. {
  399. Path np = _normalize(path);
  400. FileSystemRef fs = _getFileSystemFromList(np);
  401. if (fs && fs->isReadOnly())
  402. {
  403. _log(String::ToString("Cannot create file %s, filesystem is read-only", path.getFullPath().c_str()));
  404. return NULL;
  405. }
  406. if (fs != NULL)
  407. return static_cast<File*>(fs->create(np,FileNode::File).getPointer());
  408. return NULL;
  409. }
  410. DirectoryRef MountSystem::createDirectory(const Path& path, FileSystemRef fs)
  411. {
  412. Path np = _normalize(path);
  413. if (fs.isNull())
  414. fs = _getFileSystemFromList(np);
  415. if (fs && fs->isReadOnly())
  416. {
  417. _log(String::ToString("Cannot create directory %s, filesystem is read-only", path.getFullPath().c_str()));
  418. return NULL;
  419. }
  420. if (fs != NULL)
  421. return static_cast<Directory*>(fs->create(np,FileNode::Directory).getPointer());
  422. return NULL;
  423. }
  424. FileRef MountSystem::openFile(const Path& path,File::AccessMode mode)
  425. {
  426. FileNodeRef node = getFileNode(path);
  427. if (node != NULL)
  428. {
  429. FileRef file = dynamic_cast<File*>(node.getPointer());
  430. if (file != NULL)
  431. {
  432. if (file->open(mode))
  433. return file;
  434. else
  435. return NULL;
  436. }
  437. }
  438. else
  439. {
  440. if (mode != File::Read)
  441. {
  442. FileRef file = createFile(path);
  443. if (file != NULL)
  444. {
  445. file->open(mode);
  446. return file;
  447. }
  448. }
  449. }
  450. return NULL;
  451. }
  452. DirectoryRef MountSystem::openDirectory(const Path& path)
  453. {
  454. FileNodeRef node = getFileNode(path);
  455. if (node != NULL)
  456. {
  457. DirectoryRef dir = dynamic_cast<Directory*>(node.getPointer());
  458. if (dir != NULL)
  459. {
  460. dir->open();
  461. return dir;
  462. }
  463. }
  464. return NULL;
  465. }
  466. bool MountSystem::remove(const Path& path)
  467. {
  468. Path np = _normalize(path);
  469. FileSystemRef fs = _getFileSystemFromList(np);
  470. if (fs && fs->isReadOnly())
  471. {
  472. _log(String::ToString("Cannot remove path %s, filesystem is read-only", path.getFullPath().c_str()));
  473. return false;
  474. }
  475. if (fs != NULL)
  476. return fs->remove(np);
  477. return false;
  478. }
  479. bool MountSystem::rename(const Path& from,const Path& to)
  480. {
  481. // Will only rename files on the same filesystem
  482. Path pa = _normalize(from);
  483. Path pb = _normalize(to);
  484. FileSystemRef fsa = _getFileSystemFromList(pa);
  485. FileSystemRef fsb = _getFileSystemFromList(pb);
  486. if (!fsa || !fsb)
  487. return false;
  488. if (fsa.getPointer() != fsb.getPointer())
  489. {
  490. _log(String::ToString("Cannot rename path %s to a different filesystem", from.getFullPath().c_str()));
  491. return false;
  492. }
  493. if (fsa->isReadOnly() || fsb->isReadOnly())
  494. {
  495. _log(String::ToString("Cannot rename path %s; source or target filesystem is read-only", from.getFullPath().c_str()));
  496. return false;
  497. }
  498. return fsa->rename(pa,pb);
  499. }
  500. bool MountSystem::mount(String root,FileSystemRef fs)
  501. {
  502. MountFS mount;
  503. mount.root = root;
  504. mount.path = "/";
  505. mount.fileSystem = fs;
  506. mMountList.push_back(mount);
  507. return true;
  508. }
  509. bool MountSystem::mount(String root,const Path &path)
  510. {
  511. return mount(root,new FileSystemRedirect(this,_normalize(path)));
  512. }
  513. FileSystemRef MountSystem::unmount(String root)
  514. {
  515. FileSystemRef first = _removeMountFromList(root);
  516. // remove remaining FSes on this root
  517. while (!_removeMountFromList(root).isNull())
  518. ;
  519. return first;
  520. }
  521. bool MountSystem::unmount(FileSystemRef fs)
  522. {
  523. if (fs.isNull())
  524. return false;
  525. // iterate back to front in case FS is in list multiple times.
  526. // also check that fs is not null each time since its a strong ref
  527. // so it could be nulled during removal.
  528. bool unmounted = false;
  529. for (S32 i = mMountList.size() - 1; !fs.isNull() && i >= 0; --i)
  530. {
  531. if (mMountList[i].fileSystem.getPointer() == fs.getPointer())
  532. {
  533. mMountList.erase(i);
  534. unmounted = true;
  535. }
  536. }
  537. return unmounted;
  538. }
  539. bool MountSystem::setCwd(const Path& file)
  540. {
  541. if (file.getPath().isEmpty())
  542. return false;
  543. mCWD.setRoot(file.getRoot());
  544. mCWD.setPath(file.getPath());
  545. return true;
  546. }
  547. const Path& MountSystem::getCwd() const
  548. {
  549. return mCWD;
  550. }
  551. FileSystemRef MountSystem::getFileSystem(const Path& path)
  552. {
  553. return _getFileSystemFromList(_normalize(path));
  554. }
  555. bool MountSystem::getFileAttributes(const Path& path,FileNode::Attributes* attr)
  556. {
  557. FileNodeRef file = getFileNode(path);
  558. if (file != NULL)
  559. {
  560. bool result = file->getAttributes(attr);
  561. return result;
  562. }
  563. return false;
  564. }
  565. FileNodeRef MountSystem::getFileNode(const Path& path)
  566. {
  567. Path np = _normalize(path);
  568. FileSystemRef fs = _getFileSystemFromList(np);
  569. if (fs != NULL)
  570. return fs->resolve(np);
  571. return NULL;
  572. }
  573. bool MountSystem::mapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
  574. {
  575. FileSystemRef fs = _getFileSystemFromList(inRoot);
  576. if ( fs == NULL )
  577. {
  578. outPath = Path();
  579. return false;
  580. }
  581. outPath = fs->mapFrom( inPath );
  582. return outPath.getFullPath() != String();
  583. }
  584. S32 MountSystem::findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool includeDirs/* =false */, bool multiMatch /* = true */ )
  585. {
  586. if (mFindByPatternOverrideFS.isNull() && !inBasePath.isDirectory() )
  587. return -1;
  588. DirectoryRef dir = NULL;
  589. if (mFindByPatternOverrideFS.isNull())
  590. // open directory using standard mount system search
  591. dir = openDirectory( inBasePath );
  592. else
  593. {
  594. // use specified filesystem to open directory
  595. FileNodeRef fNode = mFindByPatternOverrideFS->resolveLoose(inBasePath);
  596. if (fNode && (dir = dynamic_cast<Directory*>(fNode.getPointer())) != NULL)
  597. dir->open();
  598. }
  599. if ( dir == NULL )
  600. return -1;
  601. if (includeDirs)
  602. {
  603. // prepend cheesy "DIR:" annotation for directories
  604. outList.push_back(String("DIR:") + inBasePath.getPath());
  605. }
  606. FileNode::Attributes attrs;
  607. Vector<String> recurseDirs;
  608. while ( dir->read( &attrs ) )
  609. {
  610. String name( attrs.name );
  611. if ( (attrs.flags & FileNode::Directory) && inRecursive )
  612. {
  613. name += '/';
  614. String path = Path::Join( inBasePath, '/', name );
  615. recurseDirs.push_back( path );
  616. }
  617. if ( !multiMatch && FindMatch::isMatch( inFilePattern, attrs.name, false ) )
  618. {
  619. String path = Path::Join( inBasePath, '/', name );
  620. outList.push_back( path );
  621. }
  622. if ( multiMatch && FindMatch::isMatchMultipleExprs( inFilePattern, attrs.name, false ) )
  623. {
  624. String path = Path::Join( inBasePath, '/', name );
  625. outList.push_back( path );
  626. }
  627. }
  628. dir->close();
  629. for ( S32 i = 0; i < recurseDirs.size(); i++ )
  630. findByPattern( recurseDirs[i], inFilePattern, true, outList, includeDirs, multiMatch );
  631. return outList.size();
  632. }
  633. bool MountSystem::isFile(const Path& path)
  634. {
  635. FileNode::Attributes attr;
  636. if (getFileAttributes(path,&attr))
  637. return attr.flags & FileNode::File;
  638. return false;
  639. }
  640. bool MountSystem::isDirectory(const Path& path, FileSystemRef fsRef)
  641. {
  642. FileNode::Attributes attr;
  643. if (fsRef.isNull())
  644. {
  645. if (getFileAttributes(path,&attr))
  646. return attr.flags & FileNode::Directory;
  647. return false;
  648. }
  649. else
  650. {
  651. FileNodeRef fnRef = fsRef->resolve(path);
  652. if (fnRef.isNull())
  653. return false;
  654. if (fnRef->getAttributes(&attr))
  655. return attr.flags & FileNode::Directory;
  656. return false;
  657. }
  658. }
  659. bool MountSystem::isReadOnly(const Path& path)
  660. {
  661. // first check to see if filesystem is read only
  662. FileSystemRef fs = getFileSystem(path);
  663. if ( fs.isNull() )
  664. // no filesystem owns this file...oh well, return false
  665. return false;
  666. if (fs->isReadOnly())
  667. return true;
  668. // check the file attributes, note that if the file does not exist,
  669. // this function returns false. that should be ok since we know
  670. // the file system is writable at this point.
  671. FileNode::Attributes attr;
  672. if (getFileAttributes(path,&attr))
  673. return attr.flags & FileNode::ReadOnly;
  674. return false;
  675. }
  676. void MountSystem::startFileChangeNotifications()
  677. {
  678. for ( U32 i = 0; i < mMountList.size(); i++ )
  679. {
  680. FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
  681. if ( notifier != NULL && !notifier->isNotifying() )
  682. notifier->startNotifier();
  683. }
  684. }
  685. void MountSystem::stopFileChangeNotifications()
  686. {
  687. for ( U32 i = 0; i < mMountList.size(); i++ )
  688. {
  689. FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
  690. if ( notifier != NULL && notifier->isNotifying() )
  691. notifier->stopNotifier();
  692. }
  693. }
  694. bool MountSystem::createPath(const Path& path)
  695. {
  696. if (path.getPath().isEmpty())
  697. return true;
  698. // See if the pathectory exists
  699. Path dir;
  700. dir.setRoot(path.getRoot());
  701. dir.setPath(path.getPath());
  702. // in a virtual mount system, isDirectory may return true if the directory exists in a read only FS,
  703. // but the directory may not exist on a writeable filesystem that is also mounted.
  704. // So get the target filesystem that will
  705. // be used for the full writable path and and make sure the directory exists on it.
  706. FileSystemRef fsRef = getFileSystem(path);
  707. if (isDirectory(dir,fsRef))
  708. return true;
  709. // Start from the top and work our way down
  710. Path sub;
  711. dir.setPath(path.isAbsolute()? String("/"): String());
  712. for (U32 i = 0; i < path.getDirectoryCount(); i++)
  713. {
  714. sub.setPath(path.getDirectory(i));
  715. dir.appendPath(sub);
  716. if (!isDirectory(dir,fsRef))
  717. {
  718. if (!createDirectory(dir,fsRef))
  719. return false;
  720. }
  721. }
  722. return true;
  723. }
  724. //-----------------------------------------------------------------------------
  725. // Default global mount system
  726. #ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM
  727. // Note that the Platform::FS::MountZips() must be called in platformVolume.cpp for zip support to work.
  728. static VirtualMountSystem sgMountSystem;
  729. #else
  730. static MountSystem sgMountSystem;
  731. #endif
  732. namespace FS
  733. {
  734. FileRef CreateFile(const Path &path)
  735. {
  736. return sgMountSystem.createFile(path);
  737. }
  738. DirectoryRef CreateDirectory(const Path &path)
  739. {
  740. return sgMountSystem.createDirectory(path);
  741. }
  742. FileRef OpenFile(const Path &path, File::AccessMode mode)
  743. {
  744. return sgMountSystem.openFile(path,mode);
  745. }
  746. bool ReadFile(const Path &inPath, void *&outData, U32 &outSize, bool inNullTerminate )
  747. {
  748. FileRef fileR = OpenFile( inPath, File::Read );
  749. outData = NULL;
  750. outSize = 0;
  751. // We'll get a NULL file reference if
  752. // the file failed to open.
  753. if ( fileR == NULL )
  754. return false;
  755. outSize = fileR->getSize();
  756. // Its not a failure to read an empty
  757. // file... but we can exit early.
  758. if ( outSize == 0 )
  759. return true;
  760. U32 sizeRead = 0;
  761. if ( inNullTerminate )
  762. {
  763. outData = new char [outSize+1];
  764. if( !outData )
  765. {
  766. // out of memory
  767. return false;
  768. }
  769. sizeRead = fileR->read(outData, outSize);
  770. static_cast<char *>(outData)[outSize] = '\0';
  771. }
  772. else
  773. {
  774. outData = new char [outSize];
  775. if( !outData )
  776. {
  777. // out of memory
  778. return false;
  779. }
  780. sizeRead = fileR->read(outData, outSize);
  781. }
  782. if ( sizeRead != outSize )
  783. {
  784. delete static_cast<char *>(outData);
  785. outData = NULL;
  786. outSize = 0;
  787. return false;
  788. }
  789. return true;
  790. }
  791. DirectoryRef OpenDirectory(const Path &path)
  792. {
  793. return sgMountSystem.openDirectory(path);
  794. }
  795. bool Remove(const Path &path)
  796. {
  797. return sgMountSystem.remove(path);
  798. }
  799. bool Rename(const Path &from, const Path &to)
  800. {
  801. return sgMountSystem.rename(from,to);
  802. }
  803. bool Mount(String root, FileSystemRef fs)
  804. {
  805. return sgMountSystem.mount(root,fs);
  806. }
  807. bool Mount(String root, const Path &path)
  808. {
  809. return sgMountSystem.mount(root,path);
  810. }
  811. FileSystemRef Unmount(String root)
  812. {
  813. return sgMountSystem.unmount(root);
  814. }
  815. bool Unmount(FileSystemRef fs)
  816. {
  817. return sgMountSystem.unmount(fs);
  818. }
  819. bool SetCwd(const Path &file)
  820. {
  821. return sgMountSystem.setCwd(file);
  822. }
  823. const Path& GetCwd()
  824. {
  825. return sgMountSystem.getCwd();
  826. }
  827. FileSystemRef GetFileSystem(const Path &path)
  828. {
  829. return sgMountSystem.getFileSystem(path);
  830. }
  831. bool GetFileAttributes(const Path &path, FileNode::Attributes* attr)
  832. {
  833. return sgMountSystem.getFileAttributes(path,attr);
  834. }
  835. S32 CompareModifiedTimes(const Path& p1, const Path& p2)
  836. {
  837. FileNode::Attributes a1, a2;
  838. if (!Torque::FS::GetFileAttributes(p1, &a1))
  839. return -1;
  840. if (!Torque::FS::GetFileAttributes(p2, &a2))
  841. return -1;
  842. if (a1.mtime < a2.mtime)
  843. return -1;
  844. if (a1.mtime == a2.mtime)
  845. return 0;
  846. return 1;
  847. }
  848. FileNodeRef GetFileNode(const Path &path)
  849. {
  850. return sgMountSystem.getFileNode(path);
  851. }
  852. bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
  853. {
  854. return sgMountSystem.mapFSPath( inRoot, inPath, outPath );
  855. }
  856. bool GetFSPath( const Path &inPath, Path &outPath )
  857. {
  858. FileSystemRef sys = GetFileSystem( inPath );
  859. if ( sys )
  860. {
  861. outPath = sys->mapTo( inPath );
  862. return true;
  863. }
  864. return false;
  865. }
  866. S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool multiMatch )
  867. {
  868. return sgMountSystem.findByPattern(inBasePath, inFilePattern, inRecursive, outList, false, multiMatch);
  869. }
  870. bool IsFile(const Path &path)
  871. {
  872. return sgMountSystem.isFile(path);
  873. }
  874. bool IsScriptFile(const char* pFilePath)
  875. {
  876. return (sgMountSystem.isFile(pFilePath)
  877. || sgMountSystem.isFile(pFilePath + String(".dso"))
  878. || sgMountSystem.isFile(pFilePath + String(".mis"))
  879. || sgMountSystem.isFile(pFilePath + String(".mis.dso"))
  880. || sgMountSystem.isFile(pFilePath + String(".gui"))
  881. || sgMountSystem.isFile(pFilePath + String(".gui.dso"))
  882. || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION))
  883. || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION) + String(".dso")));
  884. }
  885. bool IsDirectory(const Path &path)
  886. {
  887. return sgMountSystem.isDirectory(path);
  888. }
  889. bool IsReadOnly(const Path &path)
  890. {
  891. return sgMountSystem.isReadOnly(path);
  892. }
  893. String MakeUniquePath( const char *path, const char *fileName, const char *ext )
  894. {
  895. Path filePath;
  896. filePath.setPath( path );
  897. filePath.setFileName( fileName );
  898. filePath.setExtension( ext );
  899. // First get an upper bound on the range of filenames to search. This lets us
  900. // quickly skip past a large number of existing filenames.
  901. // Note: upper limit of 2^31 added to handle the degenerate case of a folder
  902. // with files named using the powers of 2, but plenty of space in between!
  903. U32 high = 1;
  904. while ( IsFile( filePath ) && ( high < 0x80000000 ) )
  905. {
  906. high = high * 2;
  907. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  908. }
  909. // Now perform binary search for first filename in the range that doesn't exist
  910. // Note that the returned name will not be strictly numerically *first* if the
  911. // existing filenames are non-sequential (eg. 4,6,7), but it will still be unique.
  912. if ( high > 1 )
  913. {
  914. U32 low = high / 2;
  915. while ( high - low > 1 )
  916. {
  917. U32 probe = low + ( high - low ) / 2;
  918. filePath.setFileName( String::ToString( "%s%d", fileName, probe ) );
  919. if ( IsFile( filePath ) )
  920. low = probe;
  921. else
  922. high = probe;
  923. }
  924. // The 'high' index is guaranteed not to exist
  925. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  926. }
  927. return filePath.getFullPath();
  928. }
  929. void StartFileChangeNotifications() { sgMountSystem.startFileChangeNotifications(); }
  930. void StopFileChangeNotifications() { sgMountSystem.stopFileChangeNotifications(); }
  931. S32 GetNumMounts() { return sgMountSystem.getNumMounts(); }
  932. String GetMountRoot( S32 index ) { return sgMountSystem.getMountRoot(index); }
  933. String GetMountPath( S32 index ) { return sgMountSystem.getMountPath(index); }
  934. String GetMountType( S32 index ) { return sgMountSystem.getMountType(index); }
  935. bool CreatePath(const Path& path)
  936. {
  937. return sgMountSystem.createPath(path);
  938. }
  939. } // Namespace FS
  940. } // Namespace Torque