volume.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  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->resolve(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. // skip hidden files
  611. if ( attrs.name.c_str()[0] == '.' )
  612. continue;
  613. String name( attrs.name );
  614. if ( (attrs.flags & FileNode::Directory) && inRecursive )
  615. {
  616. name += '/';
  617. String path = Path::Join( inBasePath, '/', name );
  618. recurseDirs.push_back( path );
  619. }
  620. if ( !multiMatch && FindMatch::isMatch( inFilePattern, attrs.name, false ) )
  621. {
  622. String path = Path::Join( inBasePath, '/', name );
  623. outList.push_back( path );
  624. }
  625. if ( multiMatch && FindMatch::isMatchMultipleExprs( inFilePattern, attrs.name, false ) )
  626. {
  627. String path = Path::Join( inBasePath, '/', name );
  628. outList.push_back( path );
  629. }
  630. }
  631. dir->close();
  632. for ( S32 i = 0; i < recurseDirs.size(); i++ )
  633. findByPattern( recurseDirs[i], inFilePattern, true, outList, includeDirs, multiMatch );
  634. return outList.size();
  635. }
  636. bool MountSystem::isFile(const Path& path)
  637. {
  638. FileNode::Attributes attr;
  639. if (getFileAttributes(path,&attr))
  640. return attr.flags & FileNode::File;
  641. return false;
  642. }
  643. bool MountSystem::isDirectory(const Path& path, FileSystemRef fsRef)
  644. {
  645. FileNode::Attributes attr;
  646. if (fsRef.isNull())
  647. {
  648. if (getFileAttributes(path,&attr))
  649. return attr.flags & FileNode::Directory;
  650. return false;
  651. }
  652. else
  653. {
  654. FileNodeRef fnRef = fsRef->resolve(path);
  655. if (fnRef.isNull())
  656. return false;
  657. FileNode::Attributes attr;
  658. if (fnRef->getAttributes(&attr))
  659. return attr.flags & FileNode::Directory;
  660. return false;
  661. }
  662. }
  663. bool MountSystem::isReadOnly(const Path& path)
  664. {
  665. // first check to see if filesystem is read only
  666. FileSystemRef fs = getFileSystem(path);
  667. if ( fs.isNull() )
  668. // no filesystem owns this file...oh well, return false
  669. return false;
  670. if (fs->isReadOnly())
  671. return true;
  672. // check the file attributes, note that if the file does not exist,
  673. // this function returns false. that should be ok since we know
  674. // the file system is writable at this point.
  675. FileNode::Attributes attr;
  676. if (getFileAttributes(path,&attr))
  677. return attr.flags & FileNode::ReadOnly;
  678. return false;
  679. }
  680. void MountSystem::startFileChangeNotifications()
  681. {
  682. for ( U32 i = 0; i < mMountList.size(); i++ )
  683. {
  684. FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
  685. if ( notifier != NULL && !notifier->isNotifying() )
  686. notifier->startNotifier();
  687. }
  688. }
  689. void MountSystem::stopFileChangeNotifications()
  690. {
  691. for ( U32 i = 0; i < mMountList.size(); i++ )
  692. {
  693. FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
  694. if ( notifier != NULL && notifier->isNotifying() )
  695. notifier->stopNotifier();
  696. }
  697. }
  698. bool MountSystem::createPath(const Path& path)
  699. {
  700. if (path.getPath().isEmpty())
  701. return true;
  702. // See if the pathectory exists
  703. Path dir;
  704. dir.setRoot(path.getRoot());
  705. dir.setPath(path.getPath());
  706. // in a virtual mount system, isDirectory may return true if the directory exists in a read only FS,
  707. // but the directory may not exist on a writeable filesystem that is also mounted.
  708. // So get the target filesystem that will
  709. // be used for the full writable path and and make sure the directory exists on it.
  710. FileSystemRef fsRef = getFileSystem(path);
  711. if (isDirectory(dir,fsRef))
  712. return true;
  713. // Start from the top and work our way down
  714. Path sub;
  715. dir.setPath(path.isAbsolute()? String("/"): String());
  716. for (U32 i = 0; i < path.getDirectoryCount(); i++)
  717. {
  718. sub.setPath(path.getDirectory(i));
  719. dir.appendPath(sub);
  720. if (!isDirectory(dir,fsRef))
  721. {
  722. if (!createDirectory(dir,fsRef))
  723. return false;
  724. }
  725. }
  726. return true;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Default global mount system
  730. #ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM
  731. // Note that the Platform::FS::MountZips() must be called in platformVolume.cpp for zip support to work.
  732. static VirtualMountSystem sgMountSystem;
  733. #else
  734. static MountSystem sgMountSystem;
  735. #endif
  736. namespace FS
  737. {
  738. FileRef CreateFile(const Path &path)
  739. {
  740. return sgMountSystem.createFile(path);
  741. }
  742. DirectoryRef CreateDirectory(const Path &path)
  743. {
  744. return sgMountSystem.createDirectory(path);
  745. }
  746. FileRef OpenFile(const Path &path, File::AccessMode mode)
  747. {
  748. return sgMountSystem.openFile(path,mode);
  749. }
  750. bool ReadFile(const Path &inPath, void *&outData, U32 &outSize, bool inNullTerminate )
  751. {
  752. FileRef fileR = OpenFile( inPath, File::Read );
  753. outData = NULL;
  754. outSize = 0;
  755. // We'll get a NULL file reference if
  756. // the file failed to open.
  757. if ( fileR == NULL )
  758. return false;
  759. outSize = fileR->getSize();
  760. // Its not a failure to read an empty
  761. // file... but we can exit early.
  762. if ( outSize == 0 )
  763. return true;
  764. U32 sizeRead = 0;
  765. if ( inNullTerminate )
  766. {
  767. outData = new char [outSize+1];
  768. sizeRead = fileR->read(outData, outSize);
  769. static_cast<char *>(outData)[outSize] = '\0';
  770. }
  771. else
  772. {
  773. outData = new char [outSize];
  774. sizeRead = fileR->read(outData, outSize);
  775. }
  776. if ( sizeRead != outSize )
  777. {
  778. delete static_cast<char *>(outData);
  779. outData = NULL;
  780. outSize = 0;
  781. return false;
  782. }
  783. return true;
  784. }
  785. DirectoryRef OpenDirectory(const Path &path)
  786. {
  787. return sgMountSystem.openDirectory(path);
  788. }
  789. bool Remove(const Path &path)
  790. {
  791. return sgMountSystem.remove(path);
  792. }
  793. bool Rename(const Path &from, const Path &to)
  794. {
  795. return sgMountSystem.rename(from,to);
  796. }
  797. bool Mount(String root, FileSystemRef fs)
  798. {
  799. return sgMountSystem.mount(root,fs);
  800. }
  801. bool Mount(String root, const Path &path)
  802. {
  803. return sgMountSystem.mount(root,path);
  804. }
  805. FileSystemRef Unmount(String root)
  806. {
  807. return sgMountSystem.unmount(root);
  808. }
  809. bool Unmount(FileSystemRef fs)
  810. {
  811. return sgMountSystem.unmount(fs);
  812. }
  813. bool SetCwd(const Path &file)
  814. {
  815. return sgMountSystem.setCwd(file);
  816. }
  817. const Path& GetCwd()
  818. {
  819. return sgMountSystem.getCwd();
  820. }
  821. FileSystemRef GetFileSystem(const Path &path)
  822. {
  823. return sgMountSystem.getFileSystem(path);
  824. }
  825. bool GetFileAttributes(const Path &path, FileNode::Attributes* attr)
  826. {
  827. return sgMountSystem.getFileAttributes(path,attr);
  828. }
  829. S32 CompareModifiedTimes(const Path& p1, const Path& p2)
  830. {
  831. FileNode::Attributes a1, a2;
  832. if (!Torque::FS::GetFileAttributes(p1, &a1))
  833. return -1;
  834. if (!Torque::FS::GetFileAttributes(p2, &a2))
  835. return -1;
  836. if (a1.mtime < a2.mtime)
  837. return -1;
  838. if (a1.mtime == a2.mtime)
  839. return 0;
  840. return 1;
  841. }
  842. FileNodeRef GetFileNode(const Path &path)
  843. {
  844. return sgMountSystem.getFileNode(path);
  845. }
  846. bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
  847. {
  848. return sgMountSystem.mapFSPath( inRoot, inPath, outPath );
  849. }
  850. bool GetFSPath( const Path &inPath, Path &outPath )
  851. {
  852. FileSystemRef sys = GetFileSystem( inPath );
  853. if ( sys )
  854. {
  855. outPath = sys->mapTo( inPath );
  856. return true;
  857. }
  858. return false;
  859. }
  860. S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool multiMatch )
  861. {
  862. return sgMountSystem.findByPattern(inBasePath, inFilePattern, inRecursive, outList, false, multiMatch);
  863. }
  864. bool IsFile(const Path &path)
  865. {
  866. return sgMountSystem.isFile(path);
  867. }
  868. bool IsDirectory(const Path &path)
  869. {
  870. return sgMountSystem.isDirectory(path);
  871. }
  872. bool IsReadOnly(const Path &path)
  873. {
  874. return sgMountSystem.isReadOnly(path);
  875. }
  876. String MakeUniquePath( const char *path, const char *fileName, const char *ext )
  877. {
  878. Path filePath;
  879. filePath.setPath( path );
  880. filePath.setFileName( fileName );
  881. filePath.setExtension( ext );
  882. // First get an upper bound on the range of filenames to search. This lets us
  883. // quickly skip past a large number of existing filenames.
  884. // Note: upper limit of 2^31 added to handle the degenerate case of a folder
  885. // with files named using the powers of 2, but plenty of space in between!
  886. U32 high = 1;
  887. while ( IsFile( filePath ) && ( high < 0x80000000 ) )
  888. {
  889. high = high * 2;
  890. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  891. }
  892. // Now perform binary search for first filename in the range that doesn't exist
  893. // Note that the returned name will not be strictly numerically *first* if the
  894. // existing filenames are non-sequential (eg. 4,6,7), but it will still be unique.
  895. if ( high > 1 )
  896. {
  897. U32 low = high / 2;
  898. while ( high - low > 1 )
  899. {
  900. U32 probe = low + ( high - low ) / 2;
  901. filePath.setFileName( String::ToString( "%s%d", fileName, probe ) );
  902. if ( IsFile( filePath ) )
  903. low = probe;
  904. else
  905. high = probe;
  906. }
  907. // The 'high' index is guaranteed not to exist
  908. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  909. }
  910. return filePath.getFullPath();
  911. }
  912. void StartFileChangeNotifications() { sgMountSystem.startFileChangeNotifications(); }
  913. void StopFileChangeNotifications() { sgMountSystem.stopFileChangeNotifications(); }
  914. S32 GetNumMounts() { return sgMountSystem.getNumMounts(); }
  915. String GetMountRoot( S32 index ) { return sgMountSystem.getMountRoot(index); }
  916. String GetMountPath( S32 index ) { return sgMountSystem.getMountPath(index); }
  917. String GetMountType( S32 index ) { return sgMountSystem.getMountType(index); }
  918. bool CreatePath(const Path& path)
  919. {
  920. return sgMountSystem.createPath(path);
  921. }
  922. } // Namespace FS
  923. } // Namespace Torque