123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- //-----------------------------------------------------------------------------
- // 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
- extern bool ResolvePathCaseInsensitive(char* pathName, S32 pathNameSize, bool requiredAbsolute);
- 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);
- attr->ctime = UnixTimeToTime(info.st_ctime);
- }
- //-----------------------------------------------------------------------------
- PosixFileSystem::PosixFileSystem(String volume)
- {
- _volume = volume;
- }
- PosixFileSystem::~PosixFileSystem()
- {
- }
- FileNodeRef PosixFileSystem::resolve(const Path& path)
- {
- String file = buildFileName(_volume,path);
- struct stat info;
- #ifdef TORQUE_POSIX_PATH_CASE_INSENSITIVE
- // Resolve the case sensitive filepath
- String::SizeType fileLength = file.length();
- UTF8* caseSensitivePath = new UTF8[fileLength + 1];
- dMemcpy(caseSensitivePath, file.c_str(), fileLength);
- caseSensitivePath[fileLength] = 0x00;
- ResolvePathCaseInsensitive(caseSensitivePath, fileLength, false);
- String caseSensitiveFile(caseSensitivePath);
- #else
- String caseSensitiveFile = file;
- #endif
- FileNodeRef result = 0;
- if (stat(caseSensitiveFile.c_str(),&info) == 0)
- {
- // Construct the appropriate object
- if (S_ISREG(info.st_mode))
- result = new PosixFile(path,caseSensitiveFile);
-
- if (S_ISDIR(info.st_mode))
- result = new PosixDirectory(path,caseSensitiveFile);
- }
- #ifdef TORQUE_POSIX_PATH_CASE_INSENSITIVE
- delete[] caseSensitivePath;
- #endif
- return result;
- }
- 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) == 0)
- 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()
- {
- #ifndef TORQUE_SECURE_VFS
- 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) );
- #endif
- return true;
- }
|