Call the correct system rename
[Torque-3d.git] / Engine / source / platformPOSIX / posixVolume.cpp
blob0f39bea3cf85dd0beb43355a0f967195c2ddecad
1 //-----------------------------------------------------------------------------
2 // Copyright (c) 2012 GarageGames, LLC
3 //
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
20 // IN THE SOFTWARE.
21 //-----------------------------------------------------------------------------
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <errno.h>
26 #include "core/crc.h"
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"
35 #ifndef PATH_MAX
36 #include <sys/syslimits.h>
37 #endif
39 #ifndef NGROUPS_UMAX
40 #define NGROUPS_UMAX 32
41 #endif
44 //#define DEBUG_SPEW
47 namespace Torque
49 namespace Posix
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.
58 String file = prefix;
59 file = Path::Join(file,'/',path.getPath());
60 file = Path::Join(file,'/',path.getFileName());
61 file = Path::Join(file,'.',path.getExtension());
62 return file;
66 static bool isFile(const String& file)
68 struct stat info;
69 if (stat(file.c_str(),&info) == 0)
70 return S_ISREG(info.st_mode);
71 return false;
74 static bool isDirectory(const String& file)
76 struct stat info;
77 if (stat(file.c_str(),&info) == 0)
78 return S_ISDIR(info.st_mode);
79 return false;
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.
94 if (!_Uid)
96 _Uid = getuid();
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.
103 attr->flags = 0;
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;
115 else
117 S32 i = 0;
118 for (; i < _GroupCount; i++)
120 if (_Groups[i] == info.st_gid)
121 break;
123 if (i != _GroupCount)
125 if (!(info.st_mode & S_IWGRP))
126 attr->flags |= FileNode::ReadOnly;
128 else
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)
145 _volume = volume;
148 PosixFileSystem::~PosixFileSystem()
152 FileNodeRef PosixFileSystem::resolve(const Path& path)
154 String file = buildFileName(_volume,path);
155 struct stat info;
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);
166 return 0;
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);
185 return 0;
188 bool PosixFileSystem::remove(const Path& path)
190 // Should probably check for outstanding files or directory objects.
191 String file = buildFileName(_volume,path);
193 struct stat info;
194 int error = stat(file.c_str(),&info);
195 if (error < 0)
196 return false;
198 if (S_ISDIR(info.st_mode))
199 return !rmdir(file);
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()))
210 return true;
212 return false;
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 ))
228 return Path();
230 return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
233 //-----------------------------------------------------------------------------
235 PosixFile::PosixFile(const Path& path,String name)
237 _path = path;
238 _name = name;
239 _status = Closed;
240 _handle = 0;
243 PosixFile::~PosixFile()
245 if (_handle)
246 close();
249 Path PosixFile::getName() const
251 return _path;
254 FileNode::NodeStatus PosixFile::getStatus() const
256 return _status;
259 bool PosixFile::getAttributes(Attributes* attr)
261 struct stat info;
262 int error = _handle? fstat(fileno(_handle),&info): stat(_name.c_str(),&info);
264 if (error < 0)
266 _updateStatus();
267 return false;
270 copyStatAttributes(info,attr);
271 attr->name = _path;
273 return true;
276 U32 PosixFile::calculateChecksum()
278 if (!open( Read ))
279 return 0;
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 )
291 close();
292 return 0;
295 fileSize -= bytesRead;
296 crc = CRC::calculateCRC( buf, bytesRead, crc );
299 close();
301 return crc;
304 bool PosixFile::open(AccessMode mode)
306 close();
308 if (_name.isEmpty())
310 return _status;
313 #ifdef DEBUG_SPEW
314 Platform::outputDebugString( "[PosixFile] opening '%s'", _name.c_str() );
315 #endif
317 const char* fmode = "r";
318 switch (mode)
320 case Read: fmode = "r"; break;
321 case Write: fmode = "w"; break;
322 case ReadWrite:
324 fmode = "r+";
325 // Ensure the file exists.
326 FILE* temp = fopen( _name.c_str(), "a+" );
327 fclose( temp );
328 break;
330 case WriteAppend: fmode = "a"; break;
331 default: break;
334 if (!(_handle = fopen(_name.c_str(), fmode)))
336 _updateStatus();
337 return false;
340 _status = Open;
341 return true;
344 bool PosixFile::close()
346 if (_handle)
348 #ifdef DEBUG_SPEW
349 Platform::outputDebugString( "[PosixFile] closing '%s'", _name.c_str() );
350 #endif
352 fflush(_handle);
353 fclose(_handle);
354 _handle = 0;
357 _status = Closed;
358 return true;
361 U32 PosixFile::getPosition()
363 if (_status == Open || _status == EndOfFile)
364 return ftell(_handle);
366 return 0;
369 U32 PosixFile::setPosition(U32 delta, SeekMode mode)
371 if (_status != Open && _status != EndOfFile)
372 return 0;
374 S32 fmode = 0;
375 switch (mode)
377 case Begin: fmode = SEEK_SET; break;
378 case Current: fmode = SEEK_CUR; break;
379 case End: fmode = SEEK_END; break;
380 default: break;
383 if (fseek(_handle, delta, fmode))
385 _status = UnknownError;
386 return 0;
389 _status = Open;
391 return ftell(_handle);
394 U32 PosixFile::read(void* dst, U32 size)
396 if (_status != Open && _status != EndOfFile)
397 return 0;
399 U32 bytesRead = fread(dst, 1, size, _handle);
401 if (bytesRead != size)
403 if (feof(_handle))
404 _status = EndOfFile;
405 else
406 _updateStatus();
409 return bytesRead;
412 U32 PosixFile::write(const void* src, U32 size)
414 if ((_status != Open && _status != EndOfFile) || !size)
415 return 0;
417 U32 bytesWritten = fwrite(src, 1, size, _handle);
419 if (bytesWritten != size)
420 _updateStatus();
422 return bytesWritten;
425 void PosixFile::_updateStatus()
427 switch (errno)
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)
443 _path = path;
444 _name = name;
445 _status = Closed;
446 _handle = 0;
449 PosixDirectory::~PosixDirectory()
451 if (_handle)
452 close();
455 Path PosixDirectory::getName() const
457 return _path;
460 bool PosixDirectory::open()
462 if ((_handle = opendir(_name)) == 0)
464 _updateStatus();
465 return false;
468 _status = Open;
469 return true;
472 bool PosixDirectory::close()
474 if (_handle)
476 closedir(_handle);
477 _handle = NULL;
478 return true;
481 return false;
484 bool PosixDirectory::read(Attributes* entry)
486 if (_status != Open)
487 return false;
489 struct dirent* de = readdir(_handle);
491 if (!de)
493 _status = EndOfFile;
494 return false;
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')))
500 return read(entry);
502 // The dirent structure doesn't actually return much beside
503 // the name, so we must call stat for more info.
504 struct stat info;
505 String file = _name + "/" + de->d_name;
507 int error = stat(file.c_str(),&info);
509 if (error < 0)
511 _updateStatus();
512 return false;
514 copyStatAttributes(info,entry);
515 entry->name = de->d_name;
516 return true;
519 U32 PosixDirectory::calculateChecksum()
521 // Return checksum of current entry
522 return 0;
525 bool PosixDirectory::getAttributes(Attributes* attr)
527 struct stat info;
528 if (stat(_name.c_str(),&info))
530 _updateStatus();
531 return false;
534 copyStatAttributes(info,attr);
535 attr->name = _path;
536 return true;
539 FileNode::NodeStatus PosixDirectory::getStatus() const
541 return _status;
544 void PosixDirectory::_updateStatus()
546 switch (errno)
548 case EACCES: _status = AccessDenied; break;
549 case ENOTDIR: _status = NoSuchFile; break;
550 case ENOENT: _status = NoSuchFile; break;
551 default: _status = UnknownError; break;
555 } // Namespace POSIX
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 );
569 #endif
571 String Platform::FS::getAssetDir()
573 return Platform::getExecutablePath();
576 /// Function invoked by the kernel layer to install OS specific
577 /// file systems.
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) );
597 return true;