123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "core/volume.h"
- #include "core/virtualMountSystem.h"
- #include "core/strings/findMatch.h"
- #include "core/util/journal/process.h"
- #include "core/util/safeDelete.h"
- #include "console/console.h"
- namespace Torque
- {
- using namespace FS;
- //-----------------------------------------------------------------------------
- bool FileSystemChangeNotifier::addNotification( const Path &path, ChangeDelegate callback )
- {
- // Notifications are for files... if the path is empty
- // then there is nothing to do.
- if ( path.isEmpty() )
- return false;
- // strip the filename and extension - we notify on dirs
- Path dir(cleanPath(path));
- if (dir.isEmpty())
- return false;
- dir.setFileName( String() );
- dir.setExtension( String () );
-
- // first lookup the dir to see if we already have an entry for it...
- DirMap::Iterator itr = mDirMap.find( dir );
- FileInfoList *fileList = NULL;
- bool addedDir = false;
- // Note that GetFileAttributes can fail here if the file doesn't
- // exist, but thats ok and expected. You can have notifications
- // on files that don't exist yet.
- FileNode::Attributes attr;
- GetFileAttributes(path, &attr);
- if ( itr != mDirMap.end() )
- {
- fileList = &(itr->value);
- // look for the file and return if we find it
- for ( U32 i = 0; i < fileList->getSize(); i++ )
- {
- FileInfo &fInfo = (*fileList)[i];
- if ( fInfo.filePath == path )
- {
- // NOTE: This is bad... we should store the mod
- // time for each callback seperately in the future.
- //
- fInfo.savedLastModTime = attr.mtime;
- fInfo.signal.notify(callback);
- return true;
- }
- }
- }
- else
- {
- // otherwise we need to add the dir to our map and let the inherited class add it
- itr = mDirMap.insert( dir, FileInfoList() );
- fileList = &(itr->value);
- addedDir = true;
- }
- FileInfo newInfo;
- newInfo.signal.notify(callback);
- newInfo.filePath = path;
- newInfo.savedLastModTime = attr.mtime;
- fileList->pushBack( newInfo );
- return addedDir ? internalAddNotification( dir ) : true;
- }
- bool FileSystemChangeNotifier::removeNotification( const Path &path, ChangeDelegate callback )
- {
- if (path.isEmpty())
- return false;
- // strip the filename and extension - we notify on dirs
- Path dir(cleanPath(path));
- if (dir.isEmpty())
- return false;
- dir.setFileName( String() );
- dir.setExtension( String () );
- DirMap::Iterator itr = mDirMap.find( dir );
- if ( itr == mDirMap.end() )
- return false;
- FileInfoList &fileList = itr->value;
- // look for the file and return if we find it
- for ( U32 i = 0; i < fileList.getSize(); i++ )
- {
- FileInfo &fInfo = fileList[i];
- if ( fInfo.filePath == path )
- {
- fInfo.signal.remove(callback);
- if (fInfo.signal.isEmpty())
- fileList.erase( i );
- break;
- }
- }
- // IF we removed the last file
- // THEN get rid of the dir from our map and notify inherited classes
- if ( fileList.getSize() == 0 )
- {
- mDirMap.erase( dir );
- return internalRemoveNotification( dir );
- }
- return true;
- }
- void FileSystemChangeNotifier::startNotifier()
- {
- // update the timestamps of all the files we are managing
- DirMap::Iterator itr = mDirMap.begin();
- for ( ; itr != mDirMap.end(); ++itr )
- {
- FileInfoList &fileList = itr->value;
- for ( U32 i = 0; i < fileList.getSize(); i++ )
- {
- FileInfo &fInfo = fileList[i];
- // This may fail if the file doesn't exist... thats ok.
- FileNode::Attributes attr;
- GetFileAttributes(fInfo.filePath, &attr);
- fInfo.savedLastModTime = attr.mtime;
- }
- }
- mNotifying = true;
- Process::notify( this, &FileSystemChangeNotifier::process, PROCESS_LAST_ORDER );
- }
- void FileSystemChangeNotifier::process()
- {
- internalProcessOnce();
- }
- void FileSystemChangeNotifier::stopNotifier()
- {
- mNotifying = false;
- Process::remove( this, &FileSystemChangeNotifier::process );
- }
- /// Makes sure paths going in and out of the notifier will have the same format
- String FileSystemChangeNotifier::cleanPath(const Path& dir)
- {
- // This "cleans up" the path, if we don't do this we can get mismatches on the path
- // coming from the notifier
- FileSystemRef fs = Torque::FS::GetFileSystem(dir);
- if (!fs)
- return String::EmptyString;
- return fs->mapFrom(fs->mapTo(dir));
- }
- void FileSystemChangeNotifier::internalNotifyDirChanged( const Path &dir )
- {
- DirMap::Iterator itr = mDirMap.find( dir );
- if ( itr == mDirMap.end() )
- return;
- // Gather the changed file info.
- FileInfoList changedList;
- FileInfoList &fileList = itr->value;
- for ( U32 i = 0; i < fileList.getSize(); i++ )
- {
- FileInfo &fInfo = fileList[i];
- FileNode::Attributes attr;
- bool success = GetFileAttributes(fInfo.filePath, &attr);
- // Ignore the file if we couldn't get the attributes (it must have
- // been deleted) or the last modification time isn't newer.
- if ( !success || attr.mtime <= fInfo.savedLastModTime )
- continue;
- // Store the new mod time.
- fInfo.savedLastModTime = attr.mtime;
- // We're taking a copy of the FileInfo struct here so that the
- // callback can safely remove the notification without crashing.
- changedList.pushBack( fInfo );
- }
- // Now signal all the changed files.
- for ( U32 i = 0; i < changedList.getSize(); i++ )
- {
- FileInfo &fInfo = changedList[i];
- Con::warnf( " : file changed [%s]", fInfo.filePath.getFullPath().c_str() );
- fInfo.signal.trigger( fInfo.filePath );
- }
- }
- //-----------------------------------------------------------------------------
- FileSystem::FileSystem()
- : mChangeNotifier( NULL ),
- mReadOnly(false)
- {
- }
- FileSystem::~FileSystem()
- {
- delete mChangeNotifier;
- mChangeNotifier = NULL;
- }
- File::File() {}
- File::~File() {}
- Directory::Directory() {}
- Directory::~Directory() {}
- FileNode::FileNode()
- : mChecksum(0)
- {
- }
- Time FileNode::getModifiedTime()
- {
- Attributes attrs;
- bool success = getAttributes( &attrs );
- if ( !success )
- return Time();
- return attrs.mtime;
- }
- U64 FileNode::getSize()
- {
- Attributes attrs;
- bool success = getAttributes( &attrs );
- if ( !success )
- return 0;
- return attrs.size;
- }
- U32 FileNode::getChecksum()
- {
- bool calculateCRC = (mLastChecksum == Torque::Time());
- if ( !calculateCRC )
- {
- Torque::Time modTime = getModifiedTime();
- calculateCRC = (modTime > mLastChecksum);
- }
- if ( calculateCRC )
- mChecksum = calculateChecksum();
-
- if ( mChecksum )
- mLastChecksum = Time::getCurrentTime();
- return mChecksum;
- }
- //-----------------------------------------------------------------------------
- class FileSystemRedirect: public FileSystem
- {
- friend class FileSystemRedirectChangeNotifier;
- public:
- FileSystemRedirect(MountSystem* mfs,const Path& path);
- String getTypeStr() const { return "Redirect"; }
- FileNodeRef resolve(const Path& path);
- FileNodeRef create(const Path& path,FileNode::Mode);
- bool remove(const Path& path);
- bool rename(const Path& a,const Path& b);
- Path mapTo(const Path& path);
- Path mapFrom(const Path& path);
-
- private:
- Path _merge(const Path& path);
- Path mPath;
- MountSystem *mMFS;
- };
- class FileSystemRedirectChangeNotifier : public FileSystemChangeNotifier
- {
- public:
- FileSystemRedirectChangeNotifier( FileSystem *fs );
- bool addNotification( const Path &path, ChangeDelegate callback );
- bool removeNotification( const Path &path, ChangeDelegate callback );
- protected:
- virtual void internalProcessOnce() {}
- virtual bool internalAddNotification( const Path &dir ) { return false; }
- virtual bool internalRemoveNotification( const Path &dir ) { return false; }
- };
- FileSystemRedirectChangeNotifier::FileSystemRedirectChangeNotifier( FileSystem *fs )
- : FileSystemChangeNotifier( fs )
- {
- }
- bool FileSystemRedirectChangeNotifier::addNotification( const Path &path, ChangeDelegate callback )
- {
- FileSystemRedirect *rfs = (FileSystemRedirect*)mFS;
- Path redirectPath = rfs->_merge( path );
- FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath );
- if ( !fs || !fs->getChangeNotifier() )
- return false;
- return fs->getChangeNotifier()->addNotification( redirectPath, callback );
- }
- bool FileSystemRedirectChangeNotifier::removeNotification( const Path &path, ChangeDelegate callback )
- {
- FileSystemRedirect *rfs = (FileSystemRedirect*)mFS;
- Path redirectPath = rfs->_merge( path );
- FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath );
- if ( !fs || !fs->getChangeNotifier() )
- return false;
- return fs->getChangeNotifier()->removeNotification( redirectPath, callback );
- }
- FileSystemRedirect::FileSystemRedirect(MountSystem* mfs,const Path& path)
- {
- mMFS = mfs;
- mPath.setRoot(path.getRoot());
- mPath.setPath(path.getPath());
- mChangeNotifier = new FileSystemRedirectChangeNotifier( this );
- }
- Path FileSystemRedirect::_merge(const Path& path)
- {
- Path p = mPath;
- p.setPath(Path::Join(p.getPath(),'/',Path::CompressPath(path.getPath())));
- p.setFileName(path.getFileName());
- p.setExtension(path.getExtension());
- return p;
- }
- FileNodeRef FileSystemRedirect::resolve(const Path& path)
- {
- Path p = _merge(path);
- FileSystemRef fs = mMFS->getFileSystem(p);
- if (fs != NULL)
- return fs->resolve(p);
- return NULL;
- }
- FileNodeRef FileSystemRedirect::create(const Path& path,FileNode::Mode mode)
- {
- Path p = _merge(path);
- FileSystemRef fs = mMFS->getFileSystem(p);
- if (fs != NULL)
- return fs->create(p,mode);
- return NULL;
- }
- bool FileSystemRedirect::remove(const Path& path)
- {
- Path p = _merge(path);
- FileSystemRef fs = mMFS->getFileSystem(p);
- if (fs != NULL)
- return fs->remove(p);
- return false;
- }
- bool FileSystemRedirect::rename(const Path& a,const Path& b)
- {
- Path na = _merge(a);
- Path nb = _merge(b);
- FileSystemRef fsa = mMFS->getFileSystem(na);
- FileSystemRef fsb = mMFS->getFileSystem(nb);
- if (fsa.getPointer() == fsb.getPointer())
- return fsa->rename(na,nb);
- return false;
- }
- Path FileSystemRedirect::mapTo(const Path& path)
- {
- Path p = _merge(path);
- FileSystemRef fs = mMFS->getFileSystem(p);
- if (fs != NULL)
- return fs->mapTo(p);
- return NULL;
- }
- Path FileSystemRedirect::mapFrom(const Path& path)
- {
- Path p = _merge(path);
- FileSystemRef fs = mMFS->getFileSystem(p);
- if (fs != NULL)
- return fs->mapFrom(p);
- return NULL;
- }
- //-----------------------------------------------------------------------------
- void MountSystem::_log(const String& msg)
- {
- String newMsg = "MountSystem: " + msg;
- Con::warnf("%s", newMsg.c_str());
- }
- FileSystemRef MountSystem::_removeMountFromList(String root)
- {
- for (Vector<MountFS>::iterator itr = mMountList.begin(); itr != mMountList.end(); itr++)
- {
- if (root.equal( itr->root, String::NoCase ))
- {
- FileSystemRef fs = itr->fileSystem;
- mMountList.erase(itr);
- return fs;
- }
- }
- return NULL;
- }
- FileSystemRef MountSystem::_getFileSystemFromList(const Path& path) const
- {
- for (Vector<MountFS>::const_iterator itr = mMountList.begin(); itr != mMountList.end(); itr++)
- {
- if (itr->root.equal( path.getRoot(), String::NoCase ))
- return itr->fileSystem;
- }
- return NULL;
- }
- Path MountSystem::_normalize(const Path& path)
- {
- Path po = path;
- // Assign to cwd root if none is specified.
- if( po.getRoot().isEmpty() )
- po.setRoot( mCWD.getRoot() );
- // Merge in current working directory if the path is relative to
- // the current cwd.
- if( po.getRoot().equal( mCWD.getRoot(), String::NoCase ) && po.isRelative() )
- {
- po.setPath( Path::CompressPath( Path::Join( mCWD.getPath(),'/',po.getPath() ) ) );
- }
- return po;
- }
- FileRef MountSystem::createFile(const Path& path)
- {
- Path np = _normalize(path);
- FileSystemRef fs = _getFileSystemFromList(np);
- if (fs && fs->isReadOnly())
- {
- _log(String::ToString("Cannot create file %s, filesystem is read-only", path.getFullPath().c_str()));
- return NULL;
- }
- if (fs != NULL)
- return static_cast<File*>(fs->create(np,FileNode::File).getPointer());
- return NULL;
- }
- DirectoryRef MountSystem::createDirectory(const Path& path, FileSystemRef fs)
- {
- Path np = _normalize(path);
- if (fs.isNull())
- fs = _getFileSystemFromList(np);
- if (fs && fs->isReadOnly())
- {
- _log(String::ToString("Cannot create directory %s, filesystem is read-only", path.getFullPath().c_str()));
- return NULL;
- }
- if (fs != NULL)
- return static_cast<Directory*>(fs->create(np,FileNode::Directory).getPointer());
- return NULL;
- }
- FileRef MountSystem::openFile(const Path& path,File::AccessMode mode)
- {
- FileNodeRef node = getFileNode(path);
- if (node != NULL)
- {
- FileRef file = dynamic_cast<File*>(node.getPointer());
- if (file != NULL)
- {
- if (file->open(mode))
- return file;
- else
- return NULL;
- }
- }
- else
- {
- if (mode != File::Read)
- {
- FileRef file = createFile(path);
- if (file != NULL)
- {
- file->open(mode);
- return file;
- }
- }
- }
- return NULL;
- }
- DirectoryRef MountSystem::openDirectory(const Path& path)
- {
- FileNodeRef node = getFileNode(path);
- if (node != NULL)
- {
- DirectoryRef dir = dynamic_cast<Directory*>(node.getPointer());
- if (dir != NULL)
- {
- dir->open();
- return dir;
- }
- }
- return NULL;
- }
- bool MountSystem::remove(const Path& path)
- {
- Path np = _normalize(path);
- FileSystemRef fs = _getFileSystemFromList(np);
- if (fs && fs->isReadOnly())
- {
- _log(String::ToString("Cannot remove path %s, filesystem is read-only", path.getFullPath().c_str()));
- return false;
- }
- if (fs != NULL)
- return fs->remove(np);
- return false;
- }
- bool MountSystem::rename(const Path& from,const Path& to)
- {
- // Will only rename files on the same filesystem
- Path pa = _normalize(from);
- Path pb = _normalize(to);
- FileSystemRef fsa = _getFileSystemFromList(pa);
- FileSystemRef fsb = _getFileSystemFromList(pb);
- if (!fsa || !fsb)
- return false;
- if (fsa.getPointer() != fsb.getPointer())
- {
- _log(String::ToString("Cannot rename path %s to a different filesystem", from.getFullPath().c_str()));
- return false;
- }
- if (fsa->isReadOnly() || fsb->isReadOnly())
- {
- _log(String::ToString("Cannot rename path %s; source or target filesystem is read-only", from.getFullPath().c_str()));
- return false;
- }
- return fsa->rename(pa,pb);
- }
- bool MountSystem::mount(String root,FileSystemRef fs)
- {
- MountFS mount;
- mount.root = root;
- mount.path = "/";
- mount.fileSystem = fs;
- mMountList.push_back(mount);
- return true;
- }
- bool MountSystem::mount(String root,const Path &path)
- {
- return mount(root,new FileSystemRedirect(this,_normalize(path)));
- }
- FileSystemRef MountSystem::unmount(String root)
- {
- FileSystemRef first = _removeMountFromList(root);
- // remove remaining FSes on this root
- while (!_removeMountFromList(root).isNull())
- ;
-
- return first;
- }
- bool MountSystem::unmount(FileSystemRef fs)
- {
- if (fs.isNull())
- return false;
- // iterate back to front in case FS is in list multiple times.
- // also check that fs is not null each time since its a strong ref
- // so it could be nulled during removal.
- bool unmounted = false;
- for (S32 i = mMountList.size() - 1; !fs.isNull() && i >= 0; --i)
- {
- if (mMountList[i].fileSystem.getPointer() == fs.getPointer())
- {
- mMountList.erase(i);
- unmounted = true;
- }
- }
- return unmounted;
- }
- bool MountSystem::setCwd(const Path& file)
- {
- if (file.getPath().isEmpty())
- return false;
- mCWD.setRoot(file.getRoot());
- mCWD.setPath(file.getPath());
- return true;
- }
- const Path& MountSystem::getCwd() const
- {
- return mCWD;
- }
- FileSystemRef MountSystem::getFileSystem(const Path& path)
- {
- return _getFileSystemFromList(_normalize(path));
- }
- bool MountSystem::getFileAttributes(const Path& path,FileNode::Attributes* attr)
- {
- FileNodeRef file = getFileNode(path);
- if (file != NULL)
- {
- bool result = file->getAttributes(attr);
- return result;
- }
- return false;
- }
- FileNodeRef MountSystem::getFileNode(const Path& path)
- {
- Path np = _normalize(path);
- FileSystemRef fs = _getFileSystemFromList(np);
- if (fs != NULL)
- return fs->resolve(np);
- return NULL;
- }
- bool MountSystem::mapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
- {
- FileSystemRef fs = _getFileSystemFromList(inRoot);
- if ( fs == NULL )
- {
- outPath = Path();
- return false;
- }
- outPath = fs->mapFrom( inPath );
- return outPath.getFullPath() != String();
- }
- S32 MountSystem::findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool includeDirs/* =false */, bool multiMatch /* = true */ )
- {
- if (mFindByPatternOverrideFS.isNull() && !inBasePath.isDirectory() )
- return -1;
- DirectoryRef dir = NULL;
- if (mFindByPatternOverrideFS.isNull())
- // open directory using standard mount system search
- dir = openDirectory( inBasePath );
- else
- {
- // use specified filesystem to open directory
- FileNodeRef fNode = mFindByPatternOverrideFS->resolve(inBasePath);
- if (fNode && (dir = dynamic_cast<Directory*>(fNode.getPointer())) != NULL)
- dir->open();
- }
- if ( dir == NULL )
- return -1;
- if (includeDirs)
- {
- // prepend cheesy "DIR:" annotation for directories
- outList.push_back(String("DIR:") + inBasePath.getPath());
- }
- FileNode::Attributes attrs;
- Vector<String> recurseDirs;
- while ( dir->read( &attrs ) )
- {
- // skip hidden files
- if ( attrs.name.c_str()[0] == '.' )
- continue;
- String name( attrs.name );
- if ( (attrs.flags & FileNode::Directory) && inRecursive )
- {
- name += '/';
- String path = Path::Join( inBasePath, '/', name );
- recurseDirs.push_back( path );
- }
-
- if ( !multiMatch && FindMatch::isMatch( inFilePattern, attrs.name, false ) )
- {
- String path = Path::Join( inBasePath, '/', name );
- outList.push_back( path );
- }
-
- if ( multiMatch && FindMatch::isMatchMultipleExprs( inFilePattern, attrs.name, false ) )
- {
- String path = Path::Join( inBasePath, '/', name );
- outList.push_back( path );
- }
- }
- dir->close();
- for ( S32 i = 0; i < recurseDirs.size(); i++ )
- findByPattern( recurseDirs[i], inFilePattern, true, outList, includeDirs, multiMatch );
- return outList.size();
- }
- bool MountSystem::isFile(const Path& path)
- {
- FileNode::Attributes attr;
- if (getFileAttributes(path,&attr))
- return attr.flags & FileNode::File;
- return false;
- }
- bool MountSystem::isDirectory(const Path& path, FileSystemRef fsRef)
- {
- FileNode::Attributes attr;
- if (fsRef.isNull())
- {
- if (getFileAttributes(path,&attr))
- return attr.flags & FileNode::Directory;
- return false;
- }
- else
- {
- FileNodeRef fnRef = fsRef->resolve(path);
- if (fnRef.isNull())
- return false;
- FileNode::Attributes attr;
- if (fnRef->getAttributes(&attr))
- return attr.flags & FileNode::Directory;
- return false;
- }
- }
- bool MountSystem::isReadOnly(const Path& path)
- {
- // first check to see if filesystem is read only
- FileSystemRef fs = getFileSystem(path);
- if ( fs.isNull() )
- // no filesystem owns this file...oh well, return false
- return false;
- if (fs->isReadOnly())
- return true;
- // check the file attributes, note that if the file does not exist,
- // this function returns false. that should be ok since we know
- // the file system is writable at this point.
- FileNode::Attributes attr;
- if (getFileAttributes(path,&attr))
- return attr.flags & FileNode::ReadOnly;
- return false;
- }
- void MountSystem::startFileChangeNotifications()
- {
- for ( U32 i = 0; i < mMountList.size(); i++ )
- {
- FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
- if ( notifier != NULL && !notifier->isNotifying() )
- notifier->startNotifier();
- }
- }
- void MountSystem::stopFileChangeNotifications()
- {
- for ( U32 i = 0; i < mMountList.size(); i++ )
- {
- FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier();
- if ( notifier != NULL && notifier->isNotifying() )
- notifier->stopNotifier();
- }
- }
- bool MountSystem::createPath(const Path& path)
- {
- if (path.getPath().isEmpty())
- return true;
-
- // See if the pathectory exists
- Path dir;
- dir.setRoot(path.getRoot());
- dir.setPath(path.getPath());
- // in a virtual mount system, isDirectory may return true if the directory exists in a read only FS,
- // but the directory may not exist on a writeable filesystem that is also mounted.
- // So get the target filesystem that will
- // be used for the full writable path and and make sure the directory exists on it.
- FileSystemRef fsRef = getFileSystem(path);
-
- if (isDirectory(dir,fsRef))
- return true;
-
- // Start from the top and work our way down
- Path sub;
- dir.setPath(path.isAbsolute()? String("/"): String());
- for (U32 i = 0; i < path.getDirectoryCount(); i++)
- {
- sub.setPath(path.getDirectory(i));
- dir.appendPath(sub);
- if (!isDirectory(dir,fsRef))
- {
- if (!createDirectory(dir,fsRef))
- return false;
- }
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- // Default global mount system
- #ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM
- // Note that the Platform::FS::MountZips() must be called in platformVolume.cpp for zip support to work.
- static VirtualMountSystem sgMountSystem;
- #else
- static MountSystem sgMountSystem;
- #endif
- namespace FS
- {
- FileRef CreateFile(const Path &path)
- {
- return sgMountSystem.createFile(path);
- }
- DirectoryRef CreateDirectory(const Path &path)
- {
- return sgMountSystem.createDirectory(path);
- }
- FileRef OpenFile(const Path &path, File::AccessMode mode)
- {
- return sgMountSystem.openFile(path,mode);
- }
- bool ReadFile(const Path &inPath, void *&outData, U32 &outSize, bool inNullTerminate )
- {
- FileRef fileR = OpenFile( inPath, File::Read );
-
- outData = NULL;
- outSize = 0;
- // We'll get a NULL file reference if
- // the file failed to open.
- if ( fileR == NULL )
- return false;
- outSize = fileR->getSize();
- // Its not a failure to read an empty
- // file... but we can exit early.
- if ( outSize == 0 )
- return true;
- U32 sizeRead = 0;
- if ( inNullTerminate )
- {
- outData = new char [outSize+1];
- sizeRead = fileR->read(outData, outSize);
- static_cast<char *>(outData)[outSize] = '\0';
- }
- else
- {
- outData = new char [outSize];
- sizeRead = fileR->read(outData, outSize);
- }
- if ( sizeRead != outSize )
- {
- delete static_cast<char *>(outData);
- outData = NULL;
- outSize = 0;
- return false;
- }
- return true;
- }
- DirectoryRef OpenDirectory(const Path &path)
- {
- return sgMountSystem.openDirectory(path);
- }
- bool Remove(const Path &path)
- {
- return sgMountSystem.remove(path);
- }
- bool Rename(const Path &from, const Path &to)
- {
- return sgMountSystem.rename(from,to);
- }
- bool Mount(String root, FileSystemRef fs)
- {
- return sgMountSystem.mount(root,fs);
- }
- bool Mount(String root, const Path &path)
- {
- return sgMountSystem.mount(root,path);
- }
- FileSystemRef Unmount(String root)
- {
- return sgMountSystem.unmount(root);
- }
- bool Unmount(FileSystemRef fs)
- {
- return sgMountSystem.unmount(fs);
- }
- bool SetCwd(const Path &file)
- {
- return sgMountSystem.setCwd(file);
- }
- const Path& GetCwd()
- {
- return sgMountSystem.getCwd();
- }
- FileSystemRef GetFileSystem(const Path &path)
- {
- return sgMountSystem.getFileSystem(path);
- }
- bool GetFileAttributes(const Path &path, FileNode::Attributes* attr)
- {
- return sgMountSystem.getFileAttributes(path,attr);
- }
- S32 CompareModifiedTimes(const Path& p1, const Path& p2)
- {
- FileNode::Attributes a1, a2;
- if (!Torque::FS::GetFileAttributes(p1, &a1))
- return -1;
- if (!Torque::FS::GetFileAttributes(p2, &a2))
- return -1;
- if (a1.mtime < a2.mtime)
- return -1;
- if (a1.mtime == a2.mtime)
- return 0;
- return 1;
- }
- FileNodeRef GetFileNode(const Path &path)
- {
- return sgMountSystem.getFileNode(path);
- }
- bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath )
- {
- return sgMountSystem.mapFSPath( inRoot, inPath, outPath );
- }
- bool GetFSPath( const Path &inPath, Path &outPath )
- {
- FileSystemRef sys = GetFileSystem( inPath );
- if ( sys )
- {
- outPath = sys->mapTo( inPath );
- return true;
- }
- return false;
- }
- S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector<String> &outList, bool multiMatch )
- {
- return sgMountSystem.findByPattern(inBasePath, inFilePattern, inRecursive, outList, false, multiMatch);
- }
- bool IsFile(const Path &path)
- {
- return sgMountSystem.isFile(path);
- }
- bool IsDirectory(const Path &path)
- {
- return sgMountSystem.isDirectory(path);
- }
- bool IsReadOnly(const Path &path)
- {
- return sgMountSystem.isReadOnly(path);
- }
- String MakeUniquePath( const char *path, const char *fileName, const char *ext )
- {
- Path filePath;
- filePath.setPath( path );
- filePath.setFileName( fileName );
- filePath.setExtension( ext );
- // First get an upper bound on the range of filenames to search. This lets us
- // quickly skip past a large number of existing filenames.
- // Note: upper limit of 2^31 added to handle the degenerate case of a folder
- // with files named using the powers of 2, but plenty of space in between!
- U32 high = 1;
- while ( IsFile( filePath ) && ( high < 0x80000000 ) )
- {
- high = high * 2;
- filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
- }
- // Now perform binary search for first filename in the range that doesn't exist
- // Note that the returned name will not be strictly numerically *first* if the
- // existing filenames are non-sequential (eg. 4,6,7), but it will still be unique.
- if ( high > 1 )
- {
- U32 low = high / 2;
- while ( high - low > 1 )
- {
- U32 probe = low + ( high - low ) / 2;
- filePath.setFileName( String::ToString( "%s%d", fileName, probe ) );
- if ( IsFile( filePath ) )
- low = probe;
- else
- high = probe;
- }
- // The 'high' index is guaranteed not to exist
- filePath.setFileName( String::ToString( "%s%d", fileName, high ) );
- }
- return filePath.getFullPath();
- }
- void StartFileChangeNotifications() { sgMountSystem.startFileChangeNotifications(); }
- void StopFileChangeNotifications() { sgMountSystem.stopFileChangeNotifications(); }
- S32 GetNumMounts() { return sgMountSystem.getNumMounts(); }
- String GetMountRoot( S32 index ) { return sgMountSystem.getMountRoot(index); }
- String GetMountPath( S32 index ) { return sgMountSystem.getMountPath(index); }
- String GetMountType( S32 index ) { return sgMountSystem.getMountType(index); }
- bool CreatePath(const Path& path)
- {
- return sgMountSystem.createPath(path);
- }
- } // Namespace FS
- } // Namespace Torque
|