posixVolume.cpp
Engine/source/platformPOSIX/posixVolume.cpp
Namespaces:
namespace
namespace
Public Defines
define
NGROUPS_UMAX() 32
Detailed Description
Public Defines
NGROUPS_UMAX() 32
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23#include <unistd.h> 24#include <stdlib.h> 25#include <errno.h> 26 27#include "core/crc.h" 28#include "core/frameAllocator.h" 29 30#include "core/util/str.h" 31#include "core/strings/stringFunctions.h" 32 33#include "platform/platformVolume.h" 34#include "platformPOSIX/posixVolume.h" 35 36#ifndef PATH_MAX 37#include <sys/syslimits.h> 38#endif 39 40#ifndef NGROUPS_UMAX 41 #define NGROUPS_UMAX 32 42#endif 43 44 45//#define DEBUG_SPEW 46 47 48namespace Torque 49{ 50namespace Posix 51{ 52 53//----------------------------------------------------------------------------- 54 55static String buildFileName(const String& prefix,const Path& path) 56{ 57 // Need to join the path (minus the root) with our 58 // internal path name. 59 String file = prefix; 60 file = Path::Join(file,'/',path.getPath()); 61 file = Path::Join(file,'/',path.getFileName()); 62 file = Path::Join(file,'.',path.getExtension()); 63 return file; 64} 65 66/* 67static bool isFile(const String& file) 68{ 69 struct stat info; 70 if (stat(file.c_str(),&info) == 0) 71 return S_ISREG(info.st_mode); 72 return false; 73} 74 75static bool isDirectory(const String& file) 76{ 77 struct stat info; 78 if (stat(file.c_str(),&info) == 0) 79 return S_ISDIR(info.st_mode); 80 return false; 81} 82*/ 83 84//----------------------------------------------------------------------------- 85 86static uid_t _Uid; // Current user id 87static int _GroupCount; // Number of groups in the table 88static gid_t _Groups[NGROUPS_UMAX+1]; // Table of all the user groups 89 90static void copyStatAttributes(const struct stat& info, FileNode::Attributes* attr) 91{ 92 // We need to user and group id's in order to determin file 93 // read-only access permission. This information is only retrieved 94 // once per execution. 95 if (!_Uid) 96 { 97 _Uid = getuid(); 98 _GroupCount = getgroups(NGROUPS_UMAX,_Groups); 99 _Groups[_GroupCount++] = getegid(); 100 } 101 102 // Fill in the return struct. The read-only flag is determined 103 // by comparing file user and group ownership. 104 attr->flags = 0; 105 if (S_ISDIR(info.st_mode)) 106 attr->flags |= FileNode::Directory; 107 108 if (S_ISREG(info.st_mode)) 109 attr->flags |= FileNode::File; 110 111 if (info.st_uid == _Uid) 112 { 113 if (!(info.st_mode & S_IWUSR)) 114 attr->flags |= FileNode::ReadOnly; 115 } 116 else 117 { 118 S32 i = 0; 119 for (; i < _GroupCount; i++) 120 { 121 if (_Groups[i] == info.st_gid) 122 break; 123 } 124 if (i != _GroupCount) 125 { 126 if (!(info.st_mode & S_IWGRP)) 127 attr->flags |= FileNode::ReadOnly; 128 } 129 else 130 { 131 if (!(info.st_mode & S_IWOTH)) 132 attr->flags |= FileNode::ReadOnly; 133 } 134 } 135 136 attr->size = info.st_size; 137 attr->mtime = UnixTimeToTime(info.st_mtime); 138 attr->atime = UnixTimeToTime(info.st_atime); 139} 140 141 142//----------------------------------------------------------------------------- 143 144PosixFileSystem::PosixFileSystem(String volume) 145{ 146 _volume = volume; 147} 148 149PosixFileSystem::~PosixFileSystem() 150{ 151} 152 153FileNodeRef PosixFileSystem::resolve(const Path& path) 154{ 155 String file = buildFileName(_volume,path); 156 struct stat info; 157 if (stat(file.c_str(),&info) == 0) 158 { 159 // Construct the appropriate object 160 if (S_ISREG(info.st_mode)) 161 return new PosixFile(path,file); 162 163 if (S_ISDIR(info.st_mode)) 164 return new PosixDirectory(path,file); 165 } 166 167 return 0; 168} 169 170FileNodeRef PosixFileSystem::create(const Path& path, FileNode::Mode mode) 171{ 172 // The file will be created on disk when it's opened. 173 if (mode & FileNode::File) 174 return new PosixFile(path,buildFileName(_volume,path)); 175 176 // Default permissions are read/write/search/executate by everyone, 177 // though this will be modified by the current umask 178 if (mode & FileNode::Directory) 179 { 180 String file = buildFileName(_volume,path); 181 182 if (mkdir(file.c_str(),S_IRWXU | S_IRWXG | S_IRWXO)) 183 return new PosixDirectory(path,file); 184 } 185 186 return 0; 187} 188 189bool PosixFileSystem::remove(const Path& path) 190{ 191 // Should probably check for outstanding files or directory objects. 192 String file = buildFileName(_volume,path); 193 194 struct stat info; 195 int error = stat(file.c_str(),&info); 196 if (error < 0) 197 return false; 198 199 if (S_ISDIR(info.st_mode)) 200 return !rmdir(file); 201 202 return !unlink(file); 203} 204 205bool PosixFileSystem::rename(const Path& from,const Path& to) 206{ 207 String fa = buildFileName(_volume,from); 208 String fb = buildFileName(_volume,to); 209 210 if (!::rename(fa.c_str(),fb.c_str())) 211 return true; 212 213 return false; 214} 215 216Path PosixFileSystem::mapTo(const Path& path) 217{ 218 return buildFileName(_volume,path); 219} 220 221 222Path PosixFileSystem::mapFrom(const Path& path) 223{ 224 const String::SizeType volumePathLen = _volume.length(); 225 226 String pathStr = path.getFullPath(); 227 228 if ( _volume.compare( pathStr, volumePathLen, String::NoCase )) 229 return Path(); 230 231 return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen ); 232} 233 234//----------------------------------------------------------------------------- 235 236PosixFile::PosixFile(const Path& path,String name) 237{ 238 _path = path; 239 _name = name; 240 _status = Closed; 241 _handle = 0; 242} 243 244PosixFile::~PosixFile() 245{ 246 if (_handle) 247 close(); 248} 249 250Path PosixFile::getName() const 251{ 252 return _path; 253} 254 255FileNode::NodeStatus PosixFile::getStatus() const 256{ 257 return _status; 258} 259 260bool PosixFile::getAttributes(Attributes* attr) 261{ 262 struct stat info; 263 int error = _handle? fstat(fileno(_handle),&info): stat(_name.c_str(),&info); 264 265 if (error < 0) 266 { 267 _updateStatus(); 268 return false; 269 } 270 271 copyStatAttributes(info,attr); 272 attr->name = _path; 273 274 return true; 275} 276 277U32 PosixFile::calculateChecksum() 278{ 279 if (!open( Read )) 280 return 0; 281 282 U64 fileSize = getSize(); 283 U32 bufSize = 1024 * 1024 * 4; 284 FrameTemp< U8> buf( bufSize ); 285 U32 crc = CRC::INITIAL_CRC_VALUE; 286 287 while( fileSize > 0 ) 288 { 289 U32 bytesRead = getMin( fileSize, bufSize ); 290 if( read( buf, bytesRead ) != bytesRead ) 291 { 292 close(); 293 return 0; 294 } 295 296 fileSize -= bytesRead; 297 crc = CRC::calculateCRC( buf, bytesRead, crc ); 298 } 299 300 close(); 301 302 return crc; 303} 304 305bool PosixFile::open(AccessMode mode) 306{ 307 close(); 308 309 if (_name.isEmpty()) 310 { 311 return _status; 312 } 313 314 #ifdef DEBUG_SPEW 315 Platform::outputDebugString( "[PosixFile] opening '%s'", _name.c_str() ); 316 #endif 317 318 const char* fmode = "r"; 319 switch (mode) 320 { 321 case Read: fmode = "r"; break; 322 case Write: fmode = "w"; break; 323 case ReadWrite: 324 { 325 fmode = "r+"; 326 // Ensure the file exists. 327 FILE* temp = fopen( _name.c_str(), "a+" ); 328 fclose( temp ); 329 break; 330 } 331 case WriteAppend: fmode = "a"; break; 332 default: break; 333 } 334 335 if (!(_handle = fopen(_name.c_str(), fmode))) 336 { 337 _updateStatus(); 338 return false; 339 } 340 341 _status = Open; 342 return true; 343} 344 345bool PosixFile::close() 346{ 347 if (_handle) 348 { 349 #ifdef DEBUG_SPEW 350 Platform::outputDebugString( "[PosixFile] closing '%s'", _name.c_str() ); 351 #endif 352 353 fflush(_handle); 354 fclose(_handle); 355 _handle = 0; 356 } 357 358 _status = Closed; 359 return true; 360} 361 362U32 PosixFile::getPosition() 363{ 364 if (_status == Open || _status == EndOfFile) 365 return ftell(_handle); 366 367 return 0; 368} 369 370U32 PosixFile::setPosition(U32 delta, SeekMode mode) 371{ 372 if (_status != Open && _status != EndOfFile) 373 return 0; 374 375 S32 fmode = 0; 376 switch (mode) 377 { 378 case Begin: fmode = SEEK_SET; break; 379 case Current: fmode = SEEK_CUR; break; 380 case End: fmode = SEEK_END; break; 381 default: break; 382 } 383 384 if (fseek(_handle, delta, fmode)) 385 { 386 _status = UnknownError; 387 return 0; 388 } 389 390 _status = Open; 391 392 return ftell(_handle); 393} 394 395U32 PosixFile::read(void* dst, U32 size) 396{ 397 if (_status != Open && _status != EndOfFile) 398 return 0; 399 400 U32 bytesRead = fread(dst, 1, size, _handle); 401 402 if (bytesRead != size) 403 { 404 if (feof(_handle)) 405 _status = EndOfFile; 406 else 407 _updateStatus(); 408 } 409 410 return bytesRead; 411} 412 413U32 PosixFile::write(const void* src, U32 size) 414{ 415 if ((_status != Open && _status != EndOfFile) || !size) 416 return 0; 417 418 U32 bytesWritten = fwrite(src, 1, size, _handle); 419 420 if (bytesWritten != size) 421 _updateStatus(); 422 423 return bytesWritten; 424} 425 426void PosixFile::_updateStatus() 427{ 428 switch (errno) 429 { 430 case EACCES: _status = AccessDenied; break; 431 case ENOSPC: _status = FileSystemFull; break; 432 case ENOTDIR: _status = NoSuchFile; break; 433 case ENOENT: _status = NoSuchFile; break; 434 case EISDIR: _status = AccessDenied; break; 435 case EROFS: _status = AccessDenied; break; 436 default: _status = UnknownError; break; 437 } 438} 439 440//----------------------------------------------------------------------------- 441 442PosixDirectory::PosixDirectory(const Path& path,String name) 443{ 444 _path = path; 445 _name = name; 446 _status = Closed; 447 _handle = 0; 448} 449 450PosixDirectory::~PosixDirectory() 451{ 452 if (_handle) 453 close(); 454} 455 456Path PosixDirectory::getName() const 457{ 458 return _path; 459} 460 461bool PosixDirectory::open() 462{ 463 if ((_handle = opendir(_name)) == 0) 464 { 465 _updateStatus(); 466 return false; 467 } 468 469 _status = Open; 470 return true; 471} 472 473bool PosixDirectory::close() 474{ 475 if (_handle) 476 { 477 closedir(_handle); 478 _handle = NULL; 479 return true; 480 } 481 482 return false; 483} 484 485bool PosixDirectory::read(Attributes* entry) 486{ 487 if (_status != Open) 488 return false; 489 490 struct dirent* de = readdir(_handle); 491 492 if (!de) 493 { 494 _status = EndOfFile; 495 return false; 496 } 497 498 // Skip "." and ".." entries 499 if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || 500 (de->d_name[1] == '.' && de->d_name[2] == '\0'))) 501 return read(entry); 502 503 // The dirent structure doesn't actually return much beside 504 // the name, so we must call stat for more info. 505 struct stat info; 506 String file = _name + "/" + de->d_name; 507 508 int error = stat(file.c_str(),&info); 509 510 if (error < 0) 511 { 512 _updateStatus(); 513 return false; 514 } 515 copyStatAttributes(info,entry); 516 entry->name = de->d_name; 517 return true; 518} 519 520U32 PosixDirectory::calculateChecksum() 521{ 522 // Return checksum of current entry 523 return 0; 524} 525 526bool PosixDirectory::getAttributes(Attributes* attr) 527{ 528 struct stat info; 529 if (stat(_name.c_str(),&info)) 530 { 531 _updateStatus(); 532 return false; 533 } 534 535 copyStatAttributes(info,attr); 536 attr->name = _path; 537 return true; 538} 539 540FileNode::NodeStatus PosixDirectory::getStatus() const 541{ 542 return _status; 543} 544 545void PosixDirectory::_updateStatus() 546{ 547 switch (errno) 548 { 549 case EACCES: _status = AccessDenied; break; 550 case ENOTDIR: _status = NoSuchFile; break; 551 case ENOENT: _status = NoSuchFile; break; 552 default: _status = UnknownError; break; 553 } 554} 555 556} // Namespace POSIX 557 558} // Namespace Torque 559 560 561//----------------------------------------------------------------------------- 562 563#ifndef TORQUE_OS_MAC // Mac has its own native FS build on top of the POSIX one. 564 565Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume ) 566{ 567 return new Posix::PosixFileSystem( volume ); 568} 569 570#endif 571 572String Platform::FS::getAssetDir() 573{ 574 return Platform::getExecutablePath(); 575} 576 577/// Function invoked by the kernel layer to install OS specific 578/// file systems. 579bool Platform::FS::InstallFileSystems() 580{ 581 Platform::FS::Mount( "/", Platform::FS::createNativeFS( String() ) ); 582 583 // Setup the current working dir. 584 char buffer[PATH_MAX]; 585 if (::getcwd(buffer,sizeof(buffer))) 586 { 587 // add trailing '/' if it isn't there 588 if (buffer[dStrlen(buffer) - 1] != '/') 589 dStrcat(buffer, "/", PATH_MAX); 590 591 Platform::FS::SetCwd(buffer); 592 } 593 594 // Mount the home directory 595 if (char* home = getenv("HOME")) 596 Platform::FS::Mount( "home", Platform::FS::createNativeFS(home) ); 597 598 return true; 599} 600