1 //-----------------------------------------------------------------------------
2 // Copyright (c) 2012 GarageGames, LLC
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to
6 // deal in the Software without restriction, including without limitation the
7 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // sell copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 //-----------------------------------------------------------------------------
27 #include "core/frameAllocator.h"
29 #include "core/util/str.h"
30 #include "core/strings/stringFunctions.h"
32 #include "platform/platformVolume.h"
33 #include "platformPOSIX/posixVolume.h"
36 #include <sys/syslimits.h>
40 #define NGROUPS_UMAX 32
52 //-----------------------------------------------------------------------------
54 static String
buildFileName(const String
& prefix
,const Path
& path
)
56 // Need to join the path (minus the root) with our
57 // internal path name.
59 file
= Path::Join(file
,'/',path
.getPath());
60 file
= Path::Join(file
,'/',path
.getFileName());
61 file
= Path::Join(file
,'.',path
.getExtension());
66 static bool isFile(const String& file)
69 if (stat(file.c_str(),&info) == 0)
70 return S_ISREG(info.st_mode);
74 static bool isDirectory(const String& file)
77 if (stat(file.c_str(),&info) == 0)
78 return S_ISDIR(info.st_mode);
83 //-----------------------------------------------------------------------------
85 static uid_t _Uid
; // Current user id
86 static int _GroupCount
; // Number of groups in the table
87 static gid_t _Groups
[NGROUPS_UMAX
+1]; // Table of all the user groups
89 static void copyStatAttributes(const struct stat
& info
, FileNode::Attributes
* attr
)
91 // We need to user and group id's in order to determin file
92 // read-only access permission. This information is only retrieved
93 // once per execution.
97 _GroupCount
= getgroups(NGROUPS_UMAX
,_Groups
);
98 _Groups
[_GroupCount
++] = getegid();
101 // Fill in the return struct. The read-only flag is determined
102 // by comparing file user and group ownership.
104 if (S_ISDIR(info
.st_mode
))
105 attr
->flags
|= FileNode::Directory
;
107 if (S_ISREG(info
.st_mode
))
108 attr
->flags
|= FileNode::File
;
110 if (info
.st_uid
== _Uid
)
112 if (!(info
.st_mode
& S_IWUSR
))
113 attr
->flags
|= FileNode::ReadOnly
;
118 for (; i
< _GroupCount
; i
++)
120 if (_Groups
[i
] == info
.st_gid
)
123 if (i
!= _GroupCount
)
125 if (!(info
.st_mode
& S_IWGRP
))
126 attr
->flags
|= FileNode::ReadOnly
;
130 if (!(info
.st_mode
& S_IWOTH
))
131 attr
->flags
|= FileNode::ReadOnly
;
135 attr
->size
= info
.st_size
;
136 attr
->mtime
= UnixTimeToTime(info
.st_mtime
);
137 attr
->atime
= UnixTimeToTime(info
.st_atime
);
141 //-----------------------------------------------------------------------------
143 PosixFileSystem::PosixFileSystem(String volume
)
148 PosixFileSystem::~PosixFileSystem()
152 FileNodeRef
PosixFileSystem::resolve(const Path
& path
)
154 String file
= buildFileName(_volume
,path
);
156 if (stat(file
.c_str(),&info
) == 0)
158 // Construct the appropriate object
159 if (S_ISREG(info
.st_mode
))
160 return new PosixFile(path
,file
);
162 if (S_ISDIR(info
.st_mode
))
163 return new PosixDirectory(path
,file
);
169 FileNodeRef
PosixFileSystem::create(const Path
& path
, FileNode::Mode mode
)
171 // The file will be created on disk when it's opened.
172 if (mode
& FileNode::File
)
173 return new PosixFile(path
,buildFileName(_volume
,path
));
175 // Default permissions are read/write/search/executate by everyone,
176 // though this will be modified by the current umask
177 if (mode
& FileNode::Directory
)
179 String file
= buildFileName(_volume
,path
);
181 if (mkdir(file
.c_str(),S_IRWXU
| S_IRWXG
| S_IRWXO
))
182 return new PosixDirectory(path
,file
);
188 bool PosixFileSystem::remove(const Path
& path
)
190 // Should probably check for outstanding files or directory objects.
191 String file
= buildFileName(_volume
,path
);
194 int error
= stat(file
.c_str(),&info
);
198 if (S_ISDIR(info
.st_mode
))
201 return !unlink(file
);
204 bool PosixFileSystem::rename(const Path
& from
,const Path
& to
)
206 String fa
= buildFileName(_volume
,from
);
207 String fb
= buildFileName(_volume
,to
);
209 if (!::rename(fa
.c_str(),fb
.c_str()))
215 Path
PosixFileSystem::mapTo(const Path
& path
)
217 return buildFileName(_volume
,path
);
221 Path
PosixFileSystem::mapFrom(const Path
& path
)
223 const String::SizeType volumePathLen
= _volume
.length();
225 String pathStr
= path
.getFullPath();
227 if ( _volume
.compare( pathStr
, volumePathLen
, String::NoCase
))
230 return pathStr
.substr( volumePathLen
, pathStr
.length() - volumePathLen
);
233 //-----------------------------------------------------------------------------
235 PosixFile::PosixFile(const Path
& path
,String name
)
243 PosixFile::~PosixFile()
249 Path
PosixFile::getName() const
254 FileNode::NodeStatus
PosixFile::getStatus() const
259 bool PosixFile::getAttributes(Attributes
* attr
)
262 int error
= _handle
? fstat(fileno(_handle
),&info
): stat(_name
.c_str(),&info
);
270 copyStatAttributes(info
,attr
);
276 U32
PosixFile::calculateChecksum()
281 U64 fileSize
= getSize();
282 U32 bufSize
= 1024 * 1024 * 4;
283 FrameTemp
< U8
> buf( bufSize
);
284 U32 crc
= CRC::INITIAL_CRC_VALUE
;
286 while( fileSize
> 0 )
288 U32 bytesRead
= getMin( fileSize
, bufSize
);
289 if( read( buf
, bytesRead
) != bytesRead
)
295 fileSize
-= bytesRead
;
296 crc
= CRC::calculateCRC( buf
, bytesRead
, crc
);
304 bool PosixFile::open(AccessMode mode
)
314 Platform::outputDebugString( "[PosixFile] opening '%s'", _name
.c_str() );
317 const char* fmode
= "r";
320 case Read
: fmode
= "r"; break;
321 case Write
: fmode
= "w"; break;
325 // Ensure the file exists.
326 FILE* temp
= fopen( _name
.c_str(), "a+" );
330 case WriteAppend
: fmode
= "a"; break;
334 if (!(_handle
= fopen(_name
.c_str(), fmode
)))
344 bool PosixFile::close()
349 Platform::outputDebugString( "[PosixFile] closing '%s'", _name
.c_str() );
361 U32
PosixFile::getPosition()
363 if (_status
== Open
|| _status
== EndOfFile
)
364 return ftell(_handle
);
369 U32
PosixFile::setPosition(U32 delta
, SeekMode mode
)
371 if (_status
!= Open
&& _status
!= EndOfFile
)
377 case Begin
: fmode
= SEEK_SET
; break;
378 case Current
: fmode
= SEEK_CUR
; break;
379 case End
: fmode
= SEEK_END
; break;
383 if (fseek(_handle
, delta
, fmode
))
385 _status
= UnknownError
;
391 return ftell(_handle
);
394 U32
PosixFile::read(void* dst
, U32 size
)
396 if (_status
!= Open
&& _status
!= EndOfFile
)
399 U32 bytesRead
= fread(dst
, 1, size
, _handle
);
401 if (bytesRead
!= size
)
412 U32
PosixFile::write(const void* src
, U32 size
)
414 if ((_status
!= Open
&& _status
!= EndOfFile
) || !size
)
417 U32 bytesWritten
= fwrite(src
, 1, size
, _handle
);
419 if (bytesWritten
!= size
)
425 void PosixFile::_updateStatus()
429 case EACCES
: _status
= AccessDenied
; break;
430 case ENOSPC
: _status
= FileSystemFull
; break;
431 case ENOTDIR
: _status
= NoSuchFile
; break;
432 case ENOENT
: _status
= NoSuchFile
; break;
433 case EISDIR
: _status
= AccessDenied
; break;
434 case EROFS
: _status
= AccessDenied
; break;
435 default: _status
= UnknownError
; break;
439 //-----------------------------------------------------------------------------
441 PosixDirectory::PosixDirectory(const Path
& path
,String name
)
449 PosixDirectory::~PosixDirectory()
455 Path
PosixDirectory::getName() const
460 bool PosixDirectory::open()
462 if ((_handle
= opendir(_name
)) == 0)
472 bool PosixDirectory::close()
484 bool PosixDirectory::read(Attributes
* entry
)
489 struct dirent
* de
= readdir(_handle
);
497 // Skip "." and ".." entries
498 if (de
->d_name
[0] == '.' && (de
->d_name
[1] == '\0' ||
499 (de
->d_name
[1] == '.' && de
->d_name
[2] == '\0')))
502 // The dirent structure doesn't actually return much beside
503 // the name, so we must call stat for more info.
505 String file
= _name
+ "/" + de
->d_name
;
507 int error
= stat(file
.c_str(),&info
);
514 copyStatAttributes(info
,entry
);
515 entry
->name
= de
->d_name
;
519 U32
PosixDirectory::calculateChecksum()
521 // Return checksum of current entry
525 bool PosixDirectory::getAttributes(Attributes
* attr
)
528 if (stat(_name
.c_str(),&info
))
534 copyStatAttributes(info
,attr
);
539 FileNode::NodeStatus
PosixDirectory::getStatus() const
544 void PosixDirectory::_updateStatus()
548 case EACCES
: _status
= AccessDenied
; break;
549 case ENOTDIR
: _status
= NoSuchFile
; break;
550 case ENOENT
: _status
= NoSuchFile
; break;
551 default: _status
= UnknownError
; break;
557 } // Namespace Torque
560 //-----------------------------------------------------------------------------
562 #ifndef TORQUE_OS_MAC // Mac has its own native FS build on top of the POSIX one.
564 Torque::FS::FileSystemRef
Platform::FS::createNativeFS( const String
&volume
)
566 return new Posix::PosixFileSystem( volume
);
571 String
Platform::FS::getAssetDir()
573 return Platform::getExecutablePath();
576 /// Function invoked by the kernel layer to install OS specific
578 bool Platform::FS::InstallFileSystems()
580 Platform::FS::Mount( "/", Platform::FS::createNativeFS( String() ) );
582 // Setup the current working dir.
583 char buffer
[PATH_MAX
];
584 if (::getcwd(buffer
,sizeof(buffer
)))
586 // add trailing '/' if it isn't there
587 if (buffer
[dStrlen(buffer
) - 1] != '/')
588 dStrcat(buffer
, "/");
590 Platform::FS::SetCwd(buffer
);
593 // Mount the home directory
594 if (char* home
= getenv("HOME"))
595 Platform::FS::Mount( "home", Platform::FS::createNativeFS(home
) );