volume.cpp 33 KB

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