posixVolume.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  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>
  25. #include "core/crc.h"
  26. #include "core/frameAllocator.h"
  27. #include "core/util/str.h"
  28. #include "core/strings/stringFunctions.h"
  29. #include "platform/platformVolume.h"
  30. #include "platformPOSIX/posixVolume.h"
  31. #ifndef PATH_MAX
  32. #include <sys/syslimits.h>
  33. #endif
  34. #ifndef NGROUPS_UMAX
  35. #define NGROUPS_UMAX 32
  36. #endif
  37. //#define DEBUG_SPEW
  38. namespace Torque
  39. {
  40. namespace Posix
  41. {
  42. //-----------------------------------------------------------------------------
  43. static String buildFileName(const String& prefix,const Path& path)
  44. {
  45. // Need to join the path (minus the root) with our
  46. // internal path name.
  47. String file = prefix;
  48. file = Path::Join(file,'/',path.getPath());
  49. file = Path::Join(file,'/',path.getFileName());
  50. file = Path::Join(file,'.',path.getExtension());
  51. return file;
  52. }
  53. /*
  54. static bool isFile(const String& file)
  55. {
  56. struct stat info;
  57. if (stat(file.c_str(),&info) == 0)
  58. return S_ISREG(info.st_mode);
  59. return false;
  60. }
  61. static bool isDirectory(const String& file)
  62. {
  63. struct stat info;
  64. if (stat(file.c_str(),&info) == 0)
  65. return S_ISDIR(info.st_mode);
  66. return false;
  67. }
  68. */
  69. //-----------------------------------------------------------------------------
  70. static uid_t _Uid; // Current user id
  71. static int _GroupCount; // Number of groups in the table
  72. static gid_t _Groups[NGROUPS_UMAX+1]; // Table of all the user groups
  73. static void copyStatAttributes(const struct stat& info, FileNode::Attributes* attr)
  74. {
  75. // We need to user and group id's in order to determin file
  76. // read-only access permission. This information is only retrieved
  77. // once per execution.
  78. if (!_Uid)
  79. {
  80. _Uid = getuid();
  81. _GroupCount = getgroups(NGROUPS_UMAX,_Groups);
  82. _Groups[_GroupCount++] = getegid();
  83. }
  84. // Fill in the return struct. The read-only flag is determined
  85. // by comparing file user and group ownership.
  86. attr->flags = 0;
  87. if (S_ISDIR(info.st_mode))
  88. attr->flags |= FileNode::Directory;
  89. if (S_ISREG(info.st_mode))
  90. attr->flags |= FileNode::File;
  91. if (info.st_uid == _Uid)
  92. {
  93. if (!(info.st_mode & S_IWUSR))
  94. attr->flags |= FileNode::ReadOnly;
  95. }
  96. else
  97. {
  98. S32 i = 0;
  99. for (; i < _GroupCount; i++)
  100. {
  101. if (_Groups[i] == info.st_gid)
  102. break;
  103. }
  104. if (i != _GroupCount)
  105. {
  106. if (!(info.st_mode & S_IWGRP))
  107. attr->flags |= FileNode::ReadOnly;
  108. }
  109. else
  110. {
  111. if (!(info.st_mode & S_IWOTH))
  112. attr->flags |= FileNode::ReadOnly;
  113. }
  114. }
  115. attr->size = info.st_size;
  116. attr->mtime = UnixTimeToTime(info.st_mtime);
  117. attr->atime = UnixTimeToTime(info.st_atime);
  118. }
  119. //-----------------------------------------------------------------------------
  120. PosixFileSystem::PosixFileSystem(String volume)
  121. {
  122. _volume = volume;
  123. }
  124. PosixFileSystem::~PosixFileSystem()
  125. {
  126. }
  127. FileNodeRef PosixFileSystem::resolve(const Path& path)
  128. {
  129. String file = buildFileName(_volume,path);
  130. struct stat info;
  131. if (stat(file.c_str(),&info) == 0)
  132. {
  133. // Construct the appropriate object
  134. if (S_ISREG(info.st_mode))
  135. return new PosixFile(path,file);
  136. if (S_ISDIR(info.st_mode))
  137. return new PosixDirectory(path,file);
  138. }
  139. return 0;
  140. }
  141. FileNodeRef PosixFileSystem::create(const Path& path, FileNode::Mode mode)
  142. {
  143. // The file will be created on disk when it's opened.
  144. if (mode & FileNode::File)
  145. return new PosixFile(path,buildFileName(_volume,path));
  146. // Default permissions are read/write/search/executate by everyone,
  147. // though this will be modified by the current umask
  148. if (mode & FileNode::Directory)
  149. {
  150. String file = buildFileName(_volume,path);
  151. if (mkdir(file.c_str(),S_IRWXU | S_IRWXG | S_IRWXO))
  152. return new PosixDirectory(path,file);
  153. }
  154. return 0;
  155. }
  156. bool PosixFileSystem::remove(const Path& path)
  157. {
  158. // Should probably check for outstanding files or directory objects.
  159. String file = buildFileName(_volume,path);
  160. struct stat info;
  161. int error = stat(file.c_str(),&info);
  162. if (error < 0)
  163. return false;
  164. if (S_ISDIR(info.st_mode))
  165. return !rmdir(file);
  166. return !unlink(file);
  167. }
  168. bool PosixFileSystem::rename(const Path& from,const Path& to)
  169. {
  170. String fa = buildFileName(_volume,from);
  171. String fb = buildFileName(_volume,to);
  172. if (!::rename(fa.c_str(),fb.c_str()))
  173. return true;
  174. return false;
  175. }
  176. Path PosixFileSystem::mapTo(const Path& path)
  177. {
  178. return buildFileName(_volume,path);
  179. }
  180. Path PosixFileSystem::mapFrom(const Path& path)
  181. {
  182. const String::SizeType volumePathLen = _volume.length();
  183. String pathStr = path.getFullPath();
  184. if ( _volume.compare( pathStr, volumePathLen, String::NoCase ))
  185. return Path();
  186. return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
  187. }
  188. //-----------------------------------------------------------------------------
  189. PosixFile::PosixFile(const Path& path,String name)
  190. {
  191. _path = path;
  192. _name = name;
  193. _status = Closed;
  194. _handle = 0;
  195. }
  196. PosixFile::~PosixFile()
  197. {
  198. if (_handle)
  199. close();
  200. }
  201. Path PosixFile::getName() const
  202. {
  203. return _path;
  204. }
  205. FileNode::NodeStatus PosixFile::getStatus() const
  206. {
  207. return _status;
  208. }
  209. bool PosixFile::getAttributes(Attributes* attr)
  210. {
  211. struct stat info;
  212. int error = _handle? fstat(fileno(_handle),&info): stat(_name.c_str(),&info);
  213. if (error < 0)
  214. {
  215. _updateStatus();
  216. return false;
  217. }
  218. copyStatAttributes(info,attr);
  219. attr->name = _path;
  220. return true;
  221. }
  222. U32 PosixFile::calculateChecksum()
  223. {
  224. if (!open( Read ))
  225. return 0;
  226. U64 fileSize = getSize();
  227. U32 bufSize = 1024 * 1024 * 4;
  228. FrameTemp< U8 > buf( bufSize );
  229. U32 crc = CRC::INITIAL_CRC_VALUE;
  230. while( fileSize > 0 )
  231. {
  232. U32 bytesRead = getMin( fileSize, bufSize );
  233. if( read( buf, bytesRead ) != bytesRead )
  234. {
  235. close();
  236. return 0;
  237. }
  238. fileSize -= bytesRead;
  239. crc = CRC::calculateCRC( buf, bytesRead, crc );
  240. }
  241. close();
  242. return crc;
  243. }
  244. bool PosixFile::open(AccessMode mode)
  245. {
  246. close();
  247. if (_name.isEmpty())
  248. {
  249. return _status;
  250. }
  251. #ifdef DEBUG_SPEW
  252. Platform::outputDebugString( "[PosixFile] opening '%s'", _name.c_str() );
  253. #endif
  254. const char* fmode = "r";
  255. switch (mode)
  256. {
  257. case Read: fmode = "r"; break;
  258. case Write: fmode = "w"; break;
  259. case ReadWrite:
  260. {
  261. fmode = "r+";
  262. // Ensure the file exists.
  263. FILE* temp = fopen( _name.c_str(), "a+" );
  264. fclose( temp );
  265. break;
  266. }
  267. case WriteAppend: fmode = "a"; break;
  268. default: break;
  269. }
  270. if (!(_handle = fopen(_name.c_str(), fmode)))
  271. {
  272. _updateStatus();
  273. return false;
  274. }
  275. _status = Open;
  276. return true;
  277. }
  278. bool PosixFile::close()
  279. {
  280. if (_handle)
  281. {
  282. #ifdef DEBUG_SPEW
  283. Platform::outputDebugString( "[PosixFile] closing '%s'", _name.c_str() );
  284. #endif
  285. fflush(_handle);
  286. fclose(_handle);
  287. _handle = 0;
  288. }
  289. _status = Closed;
  290. return true;
  291. }
  292. U32 PosixFile::getPosition()
  293. {
  294. if (_status == Open || _status == EndOfFile)
  295. return ftell(_handle);
  296. return 0;
  297. }
  298. U32 PosixFile::setPosition(U32 delta, SeekMode mode)
  299. {
  300. if (_status != Open && _status != EndOfFile)
  301. return 0;
  302. S32 fmode = 0;
  303. switch (mode)
  304. {
  305. case Begin: fmode = SEEK_SET; break;
  306. case Current: fmode = SEEK_CUR; break;
  307. case End: fmode = SEEK_END; break;
  308. default: break;
  309. }
  310. if (fseek(_handle, delta, fmode))
  311. {
  312. _status = UnknownError;
  313. return 0;
  314. }
  315. _status = Open;
  316. return ftell(_handle);
  317. }
  318. U32 PosixFile::read(void* dst, U32 size)
  319. {
  320. if (_status != Open && _status != EndOfFile)
  321. return 0;
  322. U32 bytesRead = fread(dst, 1, size, _handle);
  323. if (bytesRead != size)
  324. {
  325. if (feof(_handle))
  326. _status = EndOfFile;
  327. else
  328. _updateStatus();
  329. }
  330. return bytesRead;
  331. }
  332. U32 PosixFile::write(const void* src, U32 size)
  333. {
  334. if ((_status != Open && _status != EndOfFile) || !size)
  335. return 0;
  336. U32 bytesWritten = fwrite(src, 1, size, _handle);
  337. if (bytesWritten != size)
  338. _updateStatus();
  339. return bytesWritten;
  340. }
  341. void PosixFile::_updateStatus()
  342. {
  343. switch (errno)
  344. {
  345. case EACCES: _status = AccessDenied; break;
  346. case ENOSPC: _status = FileSystemFull; break;
  347. case ENOTDIR: _status = NoSuchFile; break;
  348. case ENOENT: _status = NoSuchFile; break;
  349. case EISDIR: _status = AccessDenied; break;
  350. case EROFS: _status = AccessDenied; break;
  351. default: _status = UnknownError; break;
  352. }
  353. }
  354. //-----------------------------------------------------------------------------
  355. PosixDirectory::PosixDirectory(const Path& path,String name)
  356. {
  357. _path = path;
  358. _name = name;
  359. _status = Closed;
  360. _handle = 0;
  361. }
  362. PosixDirectory::~PosixDirectory()
  363. {
  364. if (_handle)
  365. close();
  366. }
  367. Path PosixDirectory::getName() const
  368. {
  369. return _path;
  370. }
  371. bool PosixDirectory::open()
  372. {
  373. if ((_handle = opendir(_name)) == 0)
  374. {
  375. _updateStatus();
  376. return false;
  377. }
  378. _status = Open;
  379. return true;
  380. }
  381. bool PosixDirectory::close()
  382. {
  383. if (_handle)
  384. {
  385. closedir(_handle);
  386. _handle = NULL;
  387. return true;
  388. }
  389. return false;
  390. }
  391. bool PosixDirectory::read(Attributes* entry)
  392. {
  393. if (_status != Open)
  394. return false;
  395. struct dirent* de = readdir(_handle);
  396. if (!de)
  397. {
  398. _status = EndOfFile;
  399. return false;
  400. }
  401. // Skip "." and ".." entries
  402. if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
  403. (de->d_name[1] == '.' && de->d_name[2] == '\0')))
  404. return read(entry);
  405. // The dirent structure doesn't actually return much beside
  406. // the name, so we must call stat for more info.
  407. struct stat info;
  408. String file = _name + "/" + de->d_name;
  409. int error = stat(file.c_str(),&info);
  410. if (error < 0)
  411. {
  412. _updateStatus();
  413. return false;
  414. }
  415. copyStatAttributes(info,entry);
  416. entry->name = de->d_name;
  417. return true;
  418. }
  419. U32 PosixDirectory::calculateChecksum()
  420. {
  421. // Return checksum of current entry
  422. return 0;
  423. }
  424. bool PosixDirectory::getAttributes(Attributes* attr)
  425. {
  426. struct stat info;
  427. if (stat(_name.c_str(),&info))
  428. {
  429. _updateStatus();
  430. return false;
  431. }
  432. copyStatAttributes(info,attr);
  433. attr->name = _path;
  434. return true;
  435. }
  436. FileNode::NodeStatus PosixDirectory::getStatus() const
  437. {
  438. return _status;
  439. }
  440. void PosixDirectory::_updateStatus()
  441. {
  442. switch (errno)
  443. {
  444. case EACCES: _status = AccessDenied; break;
  445. case ENOTDIR: _status = NoSuchFile; break;
  446. case ENOENT: _status = NoSuchFile; break;
  447. default: _status = UnknownError; break;
  448. }
  449. }
  450. } // Namespace POSIX
  451. } // Namespace Torque
  452. //-----------------------------------------------------------------------------
  453. #ifndef TORQUE_OS_MAC // Mac has its own native FS build on top of the POSIX one.
  454. Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume )
  455. {
  456. return new Posix::PosixFileSystem( volume );
  457. }
  458. #endif
  459. String Platform::FS::getAssetDir()
  460. {
  461. return Platform::getExecutablePath();
  462. }
  463. /// Function invoked by the kernel layer to install OS specific
  464. /// file systems.
  465. bool Platform::FS::InstallFileSystems()
  466. {
  467. Platform::FS::Mount( "/", Platform::FS::createNativeFS( String() ) );
  468. // Setup the current working dir.
  469. char buffer[PATH_MAX];
  470. if (::getcwd(buffer,sizeof(buffer)))
  471. {
  472. // add trailing '/' if it isn't there
  473. if (buffer[dStrlen(buffer) - 1] != '/')
  474. dStrcat(buffer, "/");
  475. Platform::FS::SetCwd(buffer);
  476. }
  477. // Mount the home directory
  478. if (char* home = getenv("HOME"))
  479. Platform::FS::Mount( "home", Platform::FS::createNativeFS(home) );
  480. return true;
  481. }