1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318 |
- //-----------------------------------------------------------------------------
- // 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() {}
- bool Directory::dump(Vector<Path>& out)
- {
- const Path sourcePath = getName();
- FileNode::Attributes currentAttributes;
- while (read(¤tAttributes))
- {
- Path currentPath = sourcePath;
- currentPath.appendPath(currentPath.getFileName());
- currentPath.setFileName(currentAttributes.name);
- out.push_back(currentPath);
- }
- return true;
- }
- bool Directory::dumpFiles(Vector<Path>& out)
- {
- const Path sourcePath = getName();
- FileNode::Attributes currentAttributes;
- while (read(¤tAttributes))
- {
- Path currentPath = sourcePath;
- currentPath.appendPath(currentPath.getFileName());
- currentPath.setFileName(currentAttributes.name);
- if (IsFile(currentPath))
- {
- out.push_back(currentPath);
- }
- }
- return true;
- }
- bool Directory::dumpDirectories(Vector<Path>& out)
- {
- const Path sourcePath = getName();
- FileNode::Attributes currentAttributes;
- while (read(¤tAttributes))
- {
- Path currentPath = sourcePath;
- currentPath.appendPath(currentPath.getFileName());
- currentPath.setFileName(currentAttributes.name);
- const bool result = IsDirectory(currentPath);
- if (result)
- {
- out.push_back(currentPath);
- }
- }
- return true;
- }
- FileNode::FileNode()
- : mChecksum(0)
- {
- }
- Time FileNode::getModifiedTime()
- {
- Attributes attrs;
- bool success = getAttributes( &attrs );
- if ( !success )
- return Time();
- return attrs.mtime;
- }
- Time FileNode::getCreatedTime()
- {
- Attributes attrs;
- bool success = getAttributes(&attrs);
- if (!success)
- return Time();
- return attrs.ctime;
- }
- 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;
- }
- bool MountSystem::copyFile(const Path& source, const Path& destination, bool noOverwrite)
- {
- // Exit out early if we're not overriding
- if (isFile(destination) && noOverwrite)
- {
- return true;
- }
- FileRef sourceFile = openFile(source, FS::File::AccessMode::Read);
- if (!sourceFile)
- return false;
- const U64 sourceFileSize = sourceFile->getSize();
- void* writeBuffer = dMalloc(sourceFileSize);
- sourceFile->read(writeBuffer, sourceFileSize);
- FileRef destinationFile = openFile(destination, FS::File::AccessMode::Write);
- const bool success = destinationFile->write(writeBuffer, sourceFileSize) == sourceFileSize;
- dFree(writeBuffer);
- return success;
- }
- bool MountSystem::_dumpDirectories(DirectoryRef directory, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath, S32 currentDepth, const Path& basePath)
- {
- Vector<Torque::Path> directoryPaths;
- if (!directory->dumpDirectories(directoryPaths))
- {
- return false;
- }
- // 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
- const U32 basePathDirectoryCount = String::compare(basePath.getFullPathWithoutRoot(), "/") == 0 ? basePath.getDirectoryCount() - 1 : basePath.getDirectoryCount();
- for (U32 iteration = 0; iteration < directoryPaths.size(); ++iteration)
- {
- const Path& directoryPath = directoryPaths[iteration];
- // Load the full path to the directory unless we're not supposed to include base paths
- String directoryPathString = directoryPath.getFullPath().c_str();
- if (noBasePath)
- {
- // Build a path representing the directory tree *after* the base path query but excluding the base
- // So if we queried for data/ and are currently processing data/ExampleModule/datablocks we want to output
- // ExampleModule/datablocks
- Path newDirectoryPath;
- for (U32 iteration = basePathDirectoryCount; iteration < directoryPath.getDirectoryCount(); ++iteration)
- {
- if (iteration > basePathDirectoryCount)
- {
- newDirectoryPath.setPath(newDirectoryPath.getPath() + "/");
- }
- newDirectoryPath.setPath(newDirectoryPath.getPath() + directoryPath.getDirectory(iteration));
- }
- newDirectoryPath.setFileName(directoryPath.getFileName());
- newDirectoryPath.setExtension(directoryPath.getExtension());
- directoryPathString = newDirectoryPath.getFullPathWithoutRoot();
- }
- // Output result and enumerate subdirectories if we're not too deep according to the depth parameter
- directories.push_back(StringTable->insert(directoryPathString, true));
- if (currentDepth <= depth)
- {
- const String subdirectoryPath = directoryPath.getFullPath() + "/";
- DirectoryRef nextDirectory = OpenDirectory(subdirectoryPath);
- _dumpDirectories(nextDirectory, directories, depth, noBasePath, currentDepth + 1, basePath);
- }
- }
- return true;
- }
- bool MountSystem::dumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath)
- {
- if (!isDirectory(path))
- {
- return false;
- }
- DirectoryRef sourceDirectory = openDirectory(path);
- return _dumpDirectories(sourceDirectory, directories, depth, noBasePath, 1, path);
- }
- 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->resolveLoose(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 ) )
- {
- 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;
- bool result = false;
- if (fsRef.isNull())
- {
- if (getFileAttributes(path, &attr))
- result = (attr.flags & FileNode::Directory) != 0;
- }
- else
- {
- FileNodeRef fnRef = fsRef->resolve(path);
- if (!fnRef.isNull() && fnRef->getAttributes(&attr))
- result = (attr.flags & FileNode::Directory) != 0;
- }
- return result;
- }
- 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);
- }
- bool CopyFile(const Path& source, const Path& destination, bool noOverwrite)
- {
- return sgMountSystem.copyFile(source, destination, noOverwrite);
- }
- bool DumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath)
- {
- return sgMountSystem.dumpDirectories(path, directories, depth, noBasePath);
- }
- 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];
- if( !outData )
- {
- // out of memory
- return false;
- }
- sizeRead = fileR->read(outData, outSize);
- static_cast<char *>(outData)[outSize] = '\0';
- }
- else
- {
- outData = new char [outSize];
- if( !outData )
- {
- // out of memory
- return false;
- }
- 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 IsScriptFile(const char* pFilePath)
- {
- return (sgMountSystem.isFile(pFilePath)
- || sgMountSystem.isFile(pFilePath + String(".dso"))
- || sgMountSystem.isFile(pFilePath + String(".mis"))
- || sgMountSystem.isFile(pFilePath + String(".mis.dso"))
- || sgMountSystem.isFile(pFilePath + String(".gui"))
- || sgMountSystem.isFile(pFilePath + String(".gui.dso"))
- || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION))
- || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION) + String(".dso")));
- }
- 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
|