volume.cpp 34 KB

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