volume.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  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. if( !outData )
  769. {
  770. // out of memory
  771. return false;
  772. }
  773. sizeRead = fileR->read(outData, outSize);
  774. static_cast<char *>(outData)[outSize] = '\0';
  775. }
  776. else
  777. {
  778. outData = new char [outSize];
  779. if( !outData )
  780. {
  781. // out of memory
  782. return false;
  783. }
  784. sizeRead = fileR->read(outData, outSize);
  785. }
  786. if ( sizeRead != outSize )
  787. {
  788. delete static_cast<char *>(outData);
  789. outData = NULL;
  790. outSize = 0;
  791. return false;
  792. }
  793. return true;
  794. }
  795. DirectoryRef OpenDirectory(const Path &path)
  796. {
  797. return sgMountSystem.openDirectory(path);
  798. }
  799. bool Remove(const Path &path)
  800. {
  801. return sgMountSystem.remove(path);
  802. }
  803. bool Rename(const Path &from, const Path &to)
  804. {
  805. return sgMountSystem.rename(from,to);
  806. }
  807. bool Mount(String root, FileSystemRef fs)
  808. {
  809. return sgMountSystem.mount(root,fs);
  810. }
  811. bool Mount(String root, const Path &path)
  812. {
  813. return sgMountSystem.mount(root,path);
  814. }
  815. FileSystemRef Unmount(String root)
  816. {
  817. return sgMountSystem.unmount(root);
  818. }
  819. bool Unmount(FileSystemRef fs)
  820. {
  821. return sgMountSystem.unmount(fs);
  822. }
  823. bool SetCwd(const Path &file)
  824. {
  825. return sgMountSystem.setCwd(file);
  826. }
  827. const Path& GetCwd()
  828. {
  829. return sgMountSystem.getCwd();
  830. }
  831. FileSystemRef GetFileSystem(const Path &path)
  832. {
  833. return sgMountSystem.getFileSystem(path);
  834. }
  835. bool GetFileAttributes(const Path &path, FileNode::Attributes* attr)
  836. {
  837. return sgMountSystem.getFileAttributes(path,attr);
  838. }
  839. S32 CompareModifiedTimes(const Path& p1, const Path& p2)
  840. {
  841. FileNode::Attributes a1, a2;
  842. if (!Torque::FS::GetFileAttributes(p1, &a1))
  843. return -1;
  844. if (!Torque::FS::GetFileAttributes(p2, &a2))
  845. return -1;
  846. if (a1.mtime < a2.mtime)
  847. return -1;
  848. if (a1.mtime == a2.mtime)
  849. return 0;
  850. return 1;
  851. }
  852. FileNodeRef GetFileNode(const Path &path)
  853. {
  854. return sgMountSystem.getFileNode(path);
  855. }
  856. bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
  857. {
  858. return sgMountSystem.mapFSPath( inRoot, inPath, outPath );
  859. }
  860. bool GetFSPath( const Path &inPath, Path &outPath )
  861. {
  862. FileSystemRef sys = GetFileSystem( inPath );
  863. if ( sys )
  864. {
  865. outPath = sys->mapTo( inPath );
  866. return true;
  867. }
  868. return false;
  869. }
  870. S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool multiMatch )
  871. {
  872. return sgMountSystem.findByPattern(inBasePath, inFilePattern, inRecursive, outList, false, multiMatch);
  873. }
  874. bool IsFile(const Path &path)
  875. {
  876. return sgMountSystem.isFile(path);
  877. }
  878. bool IsDirectory(const Path &path)
  879. {
  880. return sgMountSystem.isDirectory(path);
  881. }
  882. bool IsReadOnly(const Path &path)
  883. {
  884. return sgMountSystem.isReadOnly(path);
  885. }
  886. String MakeUniquePath( const char *path, const char *fileName, const char *ext )
  887. {
  888. Path filePath;
  889. filePath.setPath( path );
  890. filePath.setFileName( fileName );
  891. filePath.setExtension( ext );
  892. // First get an upper bound on the range of filenames to search. This lets us
  893. // quickly skip past a large number of existing filenames.
  894. // Note: upper limit of 2^31 added to handle the degenerate case of a folder
  895. // with files named using the powers of 2, but plenty of space in between!
  896. U32 high = 1;
  897. while ( IsFile( filePath ) && ( high < 0x80000000 ) )
  898. {
  899. high = high * 2;
  900. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  901. }
  902. // Now perform binary search for first filename in the range that doesn't exist
  903. // Note that the returned name will not be strictly numerically *first* if the
  904. // existing filenames are non-sequential (eg. 4,6,7), but it will still be unique.
  905. if ( high > 1 )
  906. {
  907. U32 low = high / 2;
  908. while ( high - low > 1 )
  909. {
  910. U32 probe = low + ( high - low ) / 2;
  911. filePath.setFileName( String::ToString( "%s%d", fileName, probe ) );
  912. if ( IsFile( filePath ) )
  913. low = probe;
  914. else
  915. high = probe;
  916. }
  917. // The 'high' index is guaranteed not to exist
  918. filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
  919. }
  920. return filePath.getFullPath();
  921. }
  922. void StartFileChangeNotifications() { sgMountSystem.startFileChangeNotifications(); }
  923. void StopFileChangeNotifications() { sgMountSystem.stopFileChangeNotifications(); }
  924. S32 GetNumMounts() { return sgMountSystem.getNumMounts(); }
  925. String GetMountRoot( S32 index ) { return sgMountSystem.getMountRoot(index); }
  926. String GetMountPath( S32 index ) { return sgMountSystem.getMountPath(index); }
  927. String GetMountType( S32 index ) { return sgMountSystem.getMountType(index); }
  928. bool CreatePath(const Path& path)
  929. {
  930. return sgMountSystem.createPath(path);
  931. }
  932. } // Namespace FS
  933. } // Namespace Torque