Torque3D Documentation / _generateds / posixVolume.cpp

posixVolume.cpp

Engine/source/platformPOSIX/posixVolume.cpp

More...

Namespaces:

namespace
namespace

Public Defines

define

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