| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- //-----------------------------------------------------------------------------
- // 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 <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- #include "core/crc.h"
- #include "core/frameAllocator.h"
- #include "core/util/str.h"
- #include "core/strings/stringFunctions.h"
- #include "platform/platformVolume.h"
- #include "platformPOSIX/posixVolume.h"
- #ifndef PATH_MAX
- #include <sys/syslimits.h>
- #endif
- #ifndef NGROUPS_UMAX
- #define NGROUPS_UMAX 32
- #endif
- //#define DEBUG_SPEW
- namespace Torque
- {
- namespace Posix
- {
- //-----------------------------------------------------------------------------
- static String buildFileName(const String& prefix,const Path& path)
- {
- // Need to join the path (minus the root) with our
- // internal path name.
- String file = prefix;
- file = Path::Join(file,'/',path.getPath());
- file = Path::Join(file,'/',path.getFileName());
- file = Path::Join(file,'.',path.getExtension());
- return file;
- }
- /*
- static bool isFile(const String& file)
- {
- struct stat info;
- if (stat(file.c_str(),&info) == 0)
- return S_ISREG(info.st_mode);
- return false;
- }
- static bool isDirectory(const String& file)
- {
- struct stat info;
- if (stat(file.c_str(),&info) == 0)
- return S_ISDIR(info.st_mode);
- return false;
- }
- */
- //-----------------------------------------------------------------------------
- static uid_t _Uid; // Current user id
- static int _GroupCount; // Number of groups in the table
- static gid_t _Groups[NGROUPS_UMAX+1]; // Table of all the user groups
- static void copyStatAttributes(const struct stat& info, FileNode::Attributes* attr)
- {
- // We need to user and group id's in order to determin file
- // read-only access permission. This information is only retrieved
- // once per execution.
- if (!_Uid)
- {
- _Uid = getuid();
- _GroupCount = getgroups(NGROUPS_UMAX,_Groups);
- _Groups[_GroupCount++] = getegid();
- }
- // Fill in the return struct. The read-only flag is determined
- // by comparing file user and group ownership.
- attr->flags = 0;
- if (S_ISDIR(info.st_mode))
- attr->flags |= FileNode::Directory;
-
- if (S_ISREG(info.st_mode))
- attr->flags |= FileNode::File;
-
- if (info.st_uid == _Uid)
- {
- if (!(info.st_mode & S_IWUSR))
- attr->flags |= FileNode::ReadOnly;
- }
- else
- {
- S32 i = 0;
- for (; i < _GroupCount; i++)
- {
- if (_Groups[i] == info.st_gid)
- break;
- }
- if (i != _GroupCount)
- {
- if (!(info.st_mode & S_IWGRP))
- attr->flags |= FileNode::ReadOnly;
- }
- else
- {
- if (!(info.st_mode & S_IWOTH))
- attr->flags |= FileNode::ReadOnly;
- }
- }
- attr->size = info.st_size;
- attr->mtime = UnixTimeToTime(info.st_mtime);
- attr->atime = UnixTimeToTime(info.st_atime);
- }
- //-----------------------------------------------------------------------------
- PosixFileSystem::PosixFileSystem(String volume)
- {
- _volume = volume;
- }
- PosixFileSystem::~PosixFileSystem()
- {
- }
- static void MungeCase(char* pathName, S32 pathNameSize)
- {
- const int MaxPath = PATH_MAX;
- char tempBuf[MaxPath];
- dStrncpy(tempBuf, pathName, pathNameSize);
- AssertFatal(pathName[0] == '/', "PATH must be absolute");
- struct stat filestat;
- const int MaxPathEl = 200;
- char *currChar = pathName;
- char testPath[MaxPath];
- char pathEl[MaxPathEl];
- bool done = false;
- dStrncpy(tempBuf, "/", MaxPath);
- currChar++;
- while (!done)
- {
- char* termChar = dStrchr(currChar, '/');
- if (termChar == NULL)
- termChar = dStrchr(currChar, '\0');
- AssertFatal(termChar, "Can't find / or NULL terminator");
- S32 pathElLen = (termChar - currChar);
- dStrncpy(pathEl, currChar, pathElLen);
- pathEl[pathElLen] = '\0';
- dStrncpy(testPath, tempBuf, MaxPath);
- dStrcat(testPath, pathEl, MaxPath);
- if (stat(testPath, &filestat) != -1)
- {
- dStrncpy(tempBuf, testPath, MaxPath);
- }
- else
- {
- DIR *dir = opendir(tempBuf);
- struct dirent* ent;
- bool foundMatch = false;
- while (dir != NULL && (ent = readdir(dir)) != NULL)
- {
- if (dStricmp(pathEl, ent->d_name) == 0)
- {
- foundMatch = true;
- dStrcat(tempBuf, ent->d_name, MaxPath);
- break;
- }
- }
- if (!foundMatch)
- dStrncpy(tempBuf, testPath, MaxPath);
- if (dir)
- closedir(dir);
- }
- if (*termChar == '/')
- {
- dStrcat(tempBuf, "/", MaxPath);
- termChar++;
- currChar = termChar;
- }
- else
- done = true;
- }
- dStrncpy(pathName, tempBuf, pathNameSize);
- }
- // static void MungeCase(char* pathName, S32 pathNameSize)
- FileNodeRef PosixFileSystem::resolve(const Path& path)
- {
- String file = buildFileName(_volume,path);
- struct stat info;
- UTF8 testPath[1024];
- dMemcpy(testPath, file.c_str(), file.length());
- testPath[file.length()] = 0x00;
- MungeCase(testPath, file.length());
- String realFile(testPath);
- if (stat(realFile.c_str(),&info) == 0)
- {
- // Construct the appropriate object
- if (S_ISREG(info.st_mode))
- return new PosixFile(path,realFile);
-
- if (S_ISDIR(info.st_mode))
- return new PosixDirectory(path,realFile);
- }
-
- return 0;
- }
- FileNodeRef PosixFileSystem::create(const Path& path, FileNode::Mode mode)
- {
- // The file will be created on disk when it's opened.
- if (mode & FileNode::File)
- return new PosixFile(path,buildFileName(_volume,path));
- // Default permissions are read/write/search/executate by everyone,
- // though this will be modified by the current umask
- if (mode & FileNode::Directory)
- {
- String file = buildFileName(_volume,path);
-
- if (mkdir(file.c_str(),S_IRWXU | S_IRWXG | S_IRWXO))
- return new PosixDirectory(path,file);
- }
-
- return 0;
- }
- bool PosixFileSystem::remove(const Path& path)
- {
- // Should probably check for outstanding files or directory objects.
- String file = buildFileName(_volume,path);
- struct stat info;
- int error = stat(file.c_str(),&info);
- if (error < 0)
- return false;
- if (S_ISDIR(info.st_mode))
- return !rmdir(file);
-
- return !unlink(file);
- }
- bool PosixFileSystem::rename(const Path& from,const Path& to)
- {
- String fa = buildFileName(_volume,from);
- String fb = buildFileName(_volume,to);
-
- if (!::rename(fa.c_str(),fb.c_str()))
- return true;
-
- return false;
- }
- Path PosixFileSystem::mapTo(const Path& path)
- {
- return buildFileName(_volume,path);
- }
- Path PosixFileSystem::mapFrom(const Path& path)
- {
- const String::SizeType volumePathLen = _volume.length();
- String pathStr = path.getFullPath();
- if ( _volume.compare( pathStr, volumePathLen, String::NoCase ))
- return Path();
- return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
- }
- //-----------------------------------------------------------------------------
- PosixFile::PosixFile(const Path& path,String name)
- {
- _path = path;
- _name = name;
- _status = Closed;
- _handle = 0;
- }
- PosixFile::~PosixFile()
- {
- if (_handle)
- close();
- }
- Path PosixFile::getName() const
- {
- return _path;
- }
- FileNode::NodeStatus PosixFile::getStatus() const
- {
- return _status;
- }
- bool PosixFile::getAttributes(Attributes* attr)
- {
- struct stat info;
- int error = _handle? fstat(fileno(_handle),&info): stat(_name.c_str(),&info);
-
- if (error < 0)
- {
- _updateStatus();
- return false;
- }
- copyStatAttributes(info,attr);
- attr->name = _path;
-
- return true;
- }
- U32 PosixFile::calculateChecksum()
- {
- if (!open( Read ))
- return 0;
- U64 fileSize = getSize();
- U32 bufSize = 1024 * 1024 * 4;
- FrameTemp< U8 > buf( bufSize );
- U32 crc = CRC::INITIAL_CRC_VALUE;
- while( fileSize > 0 )
- {
- U32 bytesRead = getMin( fileSize, bufSize );
- if( read( buf, bytesRead ) != bytesRead )
- {
- close();
- return 0;
- }
-
- fileSize -= bytesRead;
- crc = CRC::calculateCRC( buf, bytesRead, crc );
- }
- close();
- return crc;
- }
- bool PosixFile::open(AccessMode mode)
- {
- close();
-
- if (_name.isEmpty())
- {
- return _status;
- }
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[PosixFile] opening '%s'", _name.c_str() );
- #endif
- const char* fmode = "r";
- switch (mode)
- {
- case Read: fmode = "r"; break;
- case Write: fmode = "w"; break;
- case ReadWrite:
- {
- fmode = "r+";
- // Ensure the file exists.
- FILE* temp = fopen( _name.c_str(), "a+" );
- fclose( temp );
- break;
- }
- case WriteAppend: fmode = "a"; break;
- default: break;
- }
- if (!(_handle = fopen(_name.c_str(), fmode)))
- {
- _updateStatus();
- return false;
- }
-
- _status = Open;
- return true;
- }
- bool PosixFile::close()
- {
- if (_handle)
- {
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[PosixFile] closing '%s'", _name.c_str() );
- #endif
-
- fflush(_handle);
- fclose(_handle);
- _handle = 0;
- }
-
- _status = Closed;
- return true;
- }
- U32 PosixFile::getPosition()
- {
- if (_status == Open || _status == EndOfFile)
- return ftell(_handle);
-
- return 0;
- }
- U32 PosixFile::setPosition(U32 delta, SeekMode mode)
- {
- if (_status != Open && _status != EndOfFile)
- return 0;
- S32 fmode = 0;
- switch (mode)
- {
- case Begin: fmode = SEEK_SET; break;
- case Current: fmode = SEEK_CUR; break;
- case End: fmode = SEEK_END; break;
- default: break;
- }
-
- if (fseek(_handle, delta, fmode))
- {
- _status = UnknownError;
- return 0;
- }
-
- _status = Open;
-
- return ftell(_handle);
- }
- U32 PosixFile::read(void* dst, U32 size)
- {
- if (_status != Open && _status != EndOfFile)
- return 0;
- U32 bytesRead = fread(dst, 1, size, _handle);
-
- if (bytesRead != size)
- {
- if (feof(_handle))
- _status = EndOfFile;
- else
- _updateStatus();
- }
-
- return bytesRead;
- }
- U32 PosixFile::write(const void* src, U32 size)
- {
- if ((_status != Open && _status != EndOfFile) || !size)
- return 0;
- U32 bytesWritten = fwrite(src, 1, size, _handle);
-
- if (bytesWritten != size)
- _updateStatus();
-
- return bytesWritten;
- }
- void PosixFile::_updateStatus()
- {
- switch (errno)
- {
- case EACCES: _status = AccessDenied; break;
- case ENOSPC: _status = FileSystemFull; break;
- case ENOTDIR: _status = NoSuchFile; break;
- case ENOENT: _status = NoSuchFile; break;
- case EISDIR: _status = AccessDenied; break;
- case EROFS: _status = AccessDenied; break;
- default: _status = UnknownError; break;
- }
- }
- //-----------------------------------------------------------------------------
- PosixDirectory::PosixDirectory(const Path& path,String name)
- {
- _path = path;
- _name = name;
- _status = Closed;
- _handle = 0;
- }
- PosixDirectory::~PosixDirectory()
- {
- if (_handle)
- close();
- }
- Path PosixDirectory::getName() const
- {
- return _path;
- }
- bool PosixDirectory::open()
- {
- if ((_handle = opendir(_name)) == 0)
- {
- _updateStatus();
- return false;
- }
-
- _status = Open;
- return true;
- }
- bool PosixDirectory::close()
- {
- if (_handle)
- {
- closedir(_handle);
- _handle = NULL;
- return true;
- }
-
- return false;
- }
- bool PosixDirectory::read(Attributes* entry)
- {
- if (_status != Open)
- return false;
- struct dirent* de = readdir(_handle);
-
- if (!de)
- {
- _status = EndOfFile;
- return false;
- }
- // Skip "." and ".." entries
- if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
- (de->d_name[1] == '.' && de->d_name[2] == '\0')))
- return read(entry);
- // The dirent structure doesn't actually return much beside
- // the name, so we must call stat for more info.
- struct stat info;
- String file = _name + "/" + de->d_name;
-
- int error = stat(file.c_str(),&info);
-
- if (error < 0)
- {
- _updateStatus();
- return false;
- }
- copyStatAttributes(info,entry);
- entry->name = de->d_name;
- return true;
- }
- U32 PosixDirectory::calculateChecksum()
- {
- // Return checksum of current entry
- return 0;
- }
- bool PosixDirectory::getAttributes(Attributes* attr)
- {
- struct stat info;
- if (stat(_name.c_str(),&info))
- {
- _updateStatus();
- return false;
- }
- copyStatAttributes(info,attr);
- attr->name = _path;
- return true;
- }
- FileNode::NodeStatus PosixDirectory::getStatus() const
- {
- return _status;
- }
- void PosixDirectory::_updateStatus()
- {
- switch (errno)
- {
- case EACCES: _status = AccessDenied; break;
- case ENOTDIR: _status = NoSuchFile; break;
- case ENOENT: _status = NoSuchFile; break;
- default: _status = UnknownError; break;
- }
- }
- } // Namespace POSIX
- } // Namespace Torque
- //-----------------------------------------------------------------------------
- #ifndef TORQUE_OS_MAC // Mac has its own native FS build on top of the POSIX one.
- Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume )
- {
- return new Posix::PosixFileSystem( volume );
- }
- #endif
- String Platform::FS::getAssetDir()
- {
- return Platform::getExecutablePath();
- }
- /// Function invoked by the kernel layer to install OS specific
- /// file systems.
- bool Platform::FS::InstallFileSystems()
- {
- Platform::FS::Mount( "/", Platform::FS::createNativeFS( String() ) );
- // Setup the current working dir.
- char buffer[PATH_MAX];
- if (::getcwd(buffer,sizeof(buffer)))
- {
- // add trailing '/' if it isn't there
- if (buffer[dStrlen(buffer) - 1] != '/')
- dStrcat(buffer, "/", PATH_MAX);
-
- Platform::FS::SetCwd(buffer);
- }
-
- // Mount the home directory
- if (char* home = getenv("HOME"))
- Platform::FS::Mount( "home", Platform::FS::createNativeFS(home) );
- return true;
- }
|