blocxx
PosixFileSystem.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Quest Software, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of
14 * Quest Software, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
41 #include "blocxx/BLOCXX_config.h"
42 #include "blocxx/FileSystem.hpp"
43 #include "blocxx/Mutex.hpp"
44 #include "blocxx/MutexLock.hpp"
45 #include "blocxx/GlobalMutex.hpp"
46 #include "blocxx/File.hpp"
47 #include "blocxx/Array.hpp"
48 #include "blocxx/Format.hpp"
49 #include "blocxx/ExceptionIds.hpp"
50 #include "blocxx/Assertion.hpp"
51 #include "blocxx/GlobalPtr.hpp"
53 #include "blocxx/AutoPtr.hpp"
54 #include "blocxx/SafeCString.hpp"
55 #include "blocxx/Logger.hpp"
56 #include "blocxx/GlobalString.hpp"
57 
58 extern "C"
59 {
60 #ifdef BLOCXX_WIN32
61 
62  #include <direct.h>
63  #include <io.h>
64  #include <share.h>
65  #include <AccCtrl.h.>
66  #include <Aclapi.h>
67  #include "blocxx/PathSecurity.hpp"
68  using namespace BLOCXX_NAMESPACE;
69 
71  static unsigned long MapPosixPermissionsMask( PACCESS_ALLOWED_ACE pAce, int PermissionMask )
72  {
73  pAce->Mask = 0;
74  pAce->Mask |= ((PermissionMask & S_IROTH) == S_IROTH) ? BLOCXX_WIN32_ACCESSMASK_GENERIC_READ : 0;
75  pAce->Mask |= ((PermissionMask & S_IWOTH) == S_IWOTH) ? BLOCXX_WIN32_ACCESSMASK_GENERIC_WRITE : 0;
76  pAce->Mask |= ((PermissionMask & S_IXOTH) == S_IXOTH) ? BLOCXX_WIN32_ACCESSMASK_GENERIC_EXEC : 0;
77  return pAce->Mask;
78  }
79 
81  /*
82  * A wrapper over POSIX chmod functionality for Windows
83  * It tries to set the appropriate permissions via discretionary access control list
84  * onto the path security descriptor associated.
85  */
86  static int posix_chmod(const char* path, int mode)
87  {
88  int result, nLenghtNeeded;
89  PSID ppOwnerSid = NULL, ppGroupSid = NULL, pSecurityDescriptor = NULL;
90  PACL pAcl = NULL;
91  if ( (result = GetNamedSecurityInfo( (LPTSTR)path,
92  SE_FILE_OBJECT,
93  DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
94  &ppOwnerSid,
95  &ppGroupSid,
96  &pAcl,
97  NULL,
98  &pSecurityDescriptor) ) )
99  {
100  return result;
101  }
102  /*
103  * We got the NTFS security information and DACL associated to path specified.
104  * Now we gonna change it according to UNIX access mode.
105  */
106  for (unsigned short aceIdx = 0; aceIdx < pAcl->AceCount; aceIdx++)
107  {
108  ACE_HEADER* pAce;
109  if (!::GetAce(pAcl, aceIdx, (void**)&pAce))
110  {
111  continue;
112  }
113  switch( pAce->AceType )
114  {
115  case ACCESS_ALLOWED_ACE_TYPE:
116  {
117  PACCESS_ALLOWED_ACE pAllowedAce = (PACCESS_ALLOWED_ACE) pAce;
118  unsigned long sNameLen, sDNameLen = sNameLen = MAX_PATH;
119  char sName[MAX_PATH] = {0}, sDName[MAX_PATH] = {0};
120  SID_NAME_USE eUse;
121 
122  if ( !::LookupAccountSid( NULL, &(pAllowedAce->SidStart), sName, &sNameLen, sDName, &sDNameLen, &eUse) )
123  {
124  continue;
125  }
126 
127  if ( EqualSid( ppOwnerSid, &pAllowedAce->SidStart ) || (eUse == SidTypeWellKnownGroup && !strcmp(sName, "CREATOR OWNER")) )
128  {
129  // modifying permissions for the object's owner
130  int hundreds = mode / 100;
131  MapPosixPermissionsMask( pAllowedAce, (hundreds - (hundreds/10)*10) );
132  break;
133  }
134  if ( EqualSid( ppGroupSid, &pAllowedAce->SidStart ) || eUse == WinCreatorGroupSid )
135  {
136  // modifying permissions for the object's group
137  int decimals = mode / 10;
138  MapPosixPermissionsMask( pAllowedAce, (decimals - (decimals/10)*10) );
139  break;
140  }
141  // modifying permissions for others
142  MapPosixPermissionsMask( pAllowedAce, (mode - (mode/10)*10) );
143  }
144  break;
145 
146  case ACCESS_DENIED_ACE_TYPE:
147  {
148  /*
149  * There is no need to change it, because the permission modes
150  * that limitates security descriptor usage are set via allowed ACE
151  */
152  DeleteAce(pAcl, aceIdx);
153  }
154  break;
155  }
156  }
157  /*
158  * All the operations with dACL and its ACEs were made in the shared memory.
159  * That's why we don't need to make a copy of the new dACL, just apply the
160  * changed dACL onto path security descriptor.
161  */
162  result = SetNamedSecurityInfo((LPTSTR)path,
163  SE_FILE_OBJECT,
164  PROTECTED_DACL_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
165  ppOwnerSid,
166  ppGroupSid,
167  pAcl,
168  NULL);
169 
170  if (pSecurityDescriptor) LocalFree((HLOCAL)pSecurityDescriptor);
171 
172  return result;
173  }
174 
176  static int posix_mkdir(const char* path, int mode)
177  {
178  int result;
179  if ( result = _mkdir(path) ) return result;
185  return ((mode!=-1) ? result = posix_chmod(path, mode) : result);
186  }
187 
189  #define _ACCESS ::_access
190  #define R_OK 4
191  #define F_OK 0
192  #define W_OK 2
193  #define _CHDIR _chdir
194  #define _MKDIR(a,b) posix_mkdir((a), (b))
195  #define _RMDIR _rmdir
196  #define _UNLINK _unlink
197 
198 #else
199 
200  #ifdef BLOCXX_HAVE_UNISTD_H
201  #include <unistd.h>
202  #endif
203  #ifdef BLOCXX_HAVE_DIRENT_H
204  #include <dirent.h>
205  #endif
206 
207  #define _ACCESS ::access
208  #define _CHDIR chdir
209  #define _MKDIR(a,b) mkdir((a),(b))
210  #define _RMDIR rmdir
211  #define _UNLINK unlink
212 
213 #ifdef BLOCXX_NETWARE
214 #define MAXSYMLINKS 20
215 #endif
216 
217 #endif
218 
219 #include <sys/stat.h>
220 #include <sys/types.h>
221 #include <fcntl.h>
222 }
223 
224 #include <cstdio> // for rename
225 #include <fstream>
226 #include <cerrno>
227 
228 namespace BLOCXX_NAMESPACE
229 {
230 
232 
233 namespace FileSystem
234 {
235 
238 
240 
242 // STATIC
243 int
244 changeFileOwner(const String& filename,
245  const UserId& userId)
246 {
247 #ifdef BLOCXX_WIN32
248  return 0; // File ownership on Win32?
249 #else
250  return ::chown(filename.c_str(), userId, gid_t(-1));
251 #endif
252 }
254 // STATIC
255 File
256 openFile(const String& path)
257 {
259  {
260  return g_fileSystemMockObject->openFile(path);
261  }
262 #ifdef BLOCXX_WIN32
263  HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
264  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
265  FILE_ATTRIBUTE_NORMAL, NULL);
266 
267  return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File();
268 #else
269  return File(::open(path.c_str(), O_RDWR));
270 #endif
271 }
273 // STATIC
274 File
275 createFile(const String& path)
276 {
278  {
279  return g_fileSystemMockObject->createFile(path);
280  }
281 #ifdef BLOCXX_WIN32
282  HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
283  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW,
284  FILE_ATTRIBUTE_NORMAL, NULL);
285  return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File();
286 #else
287  int fd = ::open(path.c_str(), O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0660);
288  if (fd != -1)
289  {
290  return File(fd);
291  }
292  return File();
293 #endif
294 
295 }
297 // STATIC
298 File
300 {
302  {
303  return g_fileSystemMockObject->openOrCreateFile(path);
304  }
305 #ifdef BLOCXX_WIN32
306  HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
307  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
308  FILE_ATTRIBUTE_NORMAL, NULL);
309  return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File();
310 #else
311  return File(::open(path.c_str(), O_RDWR | O_CREAT, 0660));
312 #endif
313 }
314 
316 // STATIC
317 File
319 {
321  {
322  return g_fileSystemMockObject->openForAppendOrCreateFile(path);
323  }
324 #ifdef BLOCXX_WIN32
325  //Exclude FILE_WRITE_DATA flag, because it truncates file if it exists
326  //Add FILE_SHARE_DELETE flag, because file can be deleted (renamed) by another process
327  HANDLE fh = ::CreateFile(path.c_str(), FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE,
328  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS,
329  FILE_ATTRIBUTE_NORMAL, NULL);
330  return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File();
331 #else
332  return File(::open(path.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0660));
333 #endif
334 }
335 
337 namespace
338 {
339 GlobalMutex tmpfileMutex = BLOCXX_GLOBAL_MUTEX_INIT();
340 }
341 
342 #ifndef BLOCXX_WIN32
343 
344 File
345 createTempFile(String& filePath, const String& dir)
346 {
347  filePath.erase();
349  {
350  return g_fileSystemMockObject->createTempFile(filePath, dir);
351  }
352 
353  String sfname;
354  if (dir.empty())
355  {
356  const char* envtmp = ::getenv("TMPDIR");
357  if (!envtmp)
358  {
359  sfname = "/tmp/";
360  }
361  else
362  {
363  sfname = envtmp;
364  if (!sfname.endsWith('/'))
365  sfname += '/';
366  }
367  }
368  else
369  {
370  sfname = (dir.endsWith('/')) ? dir : dir+"/";
371  }
372 
373  sfname += "blocxxtmpfileXXXXXX";
374  size_t len = sfname.length();
375 
376  AutoPtrVec<char> filename(new char[len + 1]);
377  SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str());
378  MutexLock tmpfileML(tmpfileMutex);
379  int hdl = mkstemp(filename.get());
380  if (hdl == -1)
381  {
382  return File();
383  }
384  filePath = filename.get();
385  return File(hdl);
386 }
387 
389 File
391 {
393  {
394  return g_fileSystemMockObject->createTempFile(dir);
395  }
396 
397  String sfname;
398  if (dir.empty())
399  {
400  const char* envtmp = ::getenv("TMPDIR");
401  if (!envtmp)
402  {
403  sfname = "/tmp/";
404  }
405  else
406  {
407  sfname = envtmp;
408  if (!sfname.endsWith('/'))
409  sfname += '/';
410  }
411  }
412  else
413  {
414  sfname = (dir.endsWith('/')) ? dir : dir+"/";
415  }
416 
417  sfname += "blocxxtmpfileXXXXXX";
418  size_t len = sfname.length();
419  AutoPtrVec<char> filename(new char[len + 1]);
420  SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str());
421  MutexLock tmpfileML(tmpfileMutex);
422  int hdl = mkstemp(filename.get());
423  if (hdl == -1)
424  {
425  return File();
426  }
427  else
428  {
429  if (::unlink(filename.get()) != 0)
430  {
431  Logger lgr(COMPONENT_NAME);
432  BLOCXX_LOG_ERROR(lgr, Format("PosixFileSystem::createTempFile: unlink failed: %1", errno));
433  }
434  }
435  return File(hdl);
436 }
437 #else
438 
439 File
440 createTempFile(String& filePath, const String& dir)
441 {
442  filePath.erase();
444  {
445  return g_fileSystemMockObject->createTempFile(filePath, dir);
446  }
447 
448  int rc = 0 ;
449  String sfname;
450  if (dir.empty())
451  {
452  char envtmp[MAX_PATH];
453  rc = ::GetTempPath(MAX_PATH, envtmp);
454  if (rc == 0)
455  {
456  sfname = "c:/tmp/";
457  }
458  else
459  {
460  sfname = envtmp;
461  if (!sfname.endsWith('/'))
462  sfname += '/';
463  }
464  }
465  else
466  {
467  sfname = (dir.endsWith('/')) ? dir : dir+"/";
468  }
469 
470  char szTempName[MAX_PATH];
471  // Create a temporary file name.
472  rc = ::GetTempFileName(sfname.c_str(), // directory for tmp files
473  "blocxxtmpfile", // temp file name prefix - The function uses the first three characters of this string as the prefix of the file name.
474  0, // create unique name
475  szTempName); // buffer for name
476  if (rc == 0)
477  {
478  return File();
479  }
480 
481  sfname = szTempName;
482  size_t len = sfname.length();
483  AutoPtrVec<char> filename(new char[len + 1]);
484  SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str());
485  MutexLock tmpfileML(tmpfileMutex);
486 
487  // Create the new file to write the upper-case version to.
488  FileHandle hdl = ::CreateFile((LPTSTR)sfname.c_str(), // file name
489  GENERIC_READ | GENERIC_WRITE, // open r-w
490  0, // do not share
491  NULL, // default security
492  CREATE_ALWAYS, // overwrite existing
493  FILE_ATTRIBUTE_NORMAL,// normal file
494  NULL); // no template
495  if (hdl == INVALID_HANDLE_VALUE)
496  {
497  return File();
498  }
499 
500  filePath = filename.get();
501  return File(hdl);
502 }
503 
504 File
506 {
508  {
509  return g_fileSystemMockObject->createTempFile(dir);
510  }
511 
512  int rc = 0 ;
513  String sfname;
514  if (dir.empty())
515  {
516  char envtmp[MAX_PATH];
517  rc = ::GetTempPath(MAX_PATH, envtmp);
518  if (rc == 0)
519  {
520  sfname = "c:/tmp/";
521  }
522  else
523  {
524  sfname = envtmp;
525  if (!sfname.endsWith('/'))
526  sfname += '/';
527  }
528  }
529  else
530  {
531  sfname = (dir.endsWith('/')) ? dir : dir+"/";
532  }
533 
534  char szTempName[MAX_PATH];
535  // Create a temporary file name.
536  rc = ::GetTempFileName(sfname.c_str(), // directory for tmp files
537  "blocxxtmpfile", // temp file name prefix - The function uses the first three characters of this string as the prefix of the file name.
538  0, // create unique name
539  szTempName); // buffer for name
540  if (rc == 0)
541  {
542  return File();
543  }
544 
545  sfname = szTempName;
546  size_t len = sfname.length();
547  AutoPtrVec<char> filename(new char[len + 1]);
548  SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str());
549  MutexLock tmpfileML(tmpfileMutex);
550 
551  // Create the new file to write the upper-case version to.
552  FileHandle hdl = ::CreateFile((LPTSTR)sfname.c_str(), // file name
553  GENERIC_READ | GENERIC_WRITE, // open r-w
554  0, // do not share
555  NULL, // default security
556  CREATE_ALWAYS, // overwrite existing
557  FILE_ATTRIBUTE_NORMAL,// normal file
558  NULL); // no template
559  if (hdl == INVALID_HANDLE_VALUE)
560  {
561  return File();
562  }
563  else
564  {
565  if (::unlink(filename.get()) != 0)
566  {
567  Logger lgr(COMPONENT_NAME);
568  BLOCXX_LOG_ERROR(lgr, Format("PosixFileSystem::createTempFile: unlink failed: %1", errno));
569  }
570  }
571  return File(hdl);
572 }
573 #endif
574 
576 bool
577 exists(const String& path)
578 {
580  {
581  return g_fileSystemMockObject->exists(path);
582  }
583  return _ACCESS(path.c_str(), F_OK) == 0;
584 }
585 
587 #ifndef BLOCXX_WIN32
588 bool
589 isExecutable(const String& path)
590 {
592  {
593  return g_fileSystemMockObject->isExecutable(path);
594  }
595  return _ACCESS(path.c_str(), X_OK) == 0;
596 }
597 #endif
598 
600 bool
601 canRead(const String& path)
602 {
604  {
605  return g_fileSystemMockObject->canRead(path);
606  }
607  return _ACCESS(path.c_str(), R_OK) == 0;
608 }
610 bool
611 canWrite(const String& path)
612 {
614  {
615  return g_fileSystemMockObject->canWrite(path);
616  }
617  return _ACCESS(path.c_str(), W_OK) == 0;
618 }
620 #ifndef BLOCXX_WIN32
621 bool
622 isLink(const String& path)
623 {
625  {
626  return g_fileSystemMockObject->isLink(path);
627  }
628  struct stat st;
629  if (lstat(path.c_str(), &st) != 0)
630  {
631  return false;
632  }
633  return S_ISLNK(st.st_mode);
634 }
635 #endif
636 
637 bool
638 isDirectory(const String& path)
639 {
641  {
642  return g_fileSystemMockObject->isDirectory(path);
643  }
644 #ifdef BLOCXX_WIN32
645  struct _stat st;
646  if (_stat(path.c_str(), &st) != 0)
647  {
648  return false;
649  }
650  return ((st.st_mode & _S_IFDIR) != 0);
651 #else
652  struct stat st;
653  if (stat(path.c_str(), &st) != 0)
654  {
655  return false;
656  }
657  return S_ISDIR(st.st_mode);
658 #endif
659 }
661 bool
663 {
665  {
666  return g_fileSystemMockObject->changeDirectory(path);
667  }
668  return _CHDIR(path.c_str()) == 0;
669 }
671 bool
672 makeDirectory(const String& path, int mode)
673 {
675  {
676  return g_fileSystemMockObject->makeDirectory(path, mode);
677  }
678  return _MKDIR(path.c_str(), mode) == 0;
679 }
681 bool
682 getFileSize(const String& path, Int64& size)
683 {
685  {
686  return g_fileSystemMockObject->getFileSize(path, size);
687  }
688 #ifdef BLOCXX_WIN32
689  struct _stat st;
690  if (_stat(path.c_str(), &st) != 0)
691  {
692  return false;
693  }
694 #else
695  struct stat st;
696  if (stat(path.c_str(), &st) != 0)
697  {
698  return false;
699  }
700 #endif
701  size = st.st_size;
702  return true;
703 }
705 bool
707 {
709  {
710  return g_fileSystemMockObject->removeDirectory(path);
711  }
712  return _RMDIR(path.c_str()) == 0;
713 }
715 bool
716 removeFile(const String& path)
717 {
719  {
720  return g_fileSystemMockObject->removeFile(path);
721  }
722  return _UNLINK(path.c_str()) == 0;
723 }
725 bool
727  StringArray& dirEntries)
728 {
730  {
731  return g_fileSystemMockObject->getDirectoryContents(path, dirEntries);
732  }
733  static Mutex readdirGuard;
734  MutexLock lock(readdirGuard);
735 
736 #ifdef BLOCXX_WIN32
737  struct _finddata_t dentry;
738  long hFile;
739  String _path = path;
740 
741  // Find first directory entry
743  {
744  _path += BLOCXX_FILENAME_SEPARATOR;
745  }
746  _path += "*";
747  if ((hFile = _findfirst( _path.c_str(), &dentry)) == -1L)
748  {
749  return false;
750  }
751  dirEntries.clear();
752  while (_findnext(hFile, &dentry) == 0)
753  {
754  dirEntries.append(String(dentry.name));
755  }
756  _findclose(hFile);
757 #else
758  DIR* dp(0);
759  struct dirent* dentry(0);
760  if ((dp = opendir(path.c_str())) == NULL)
761  {
762  return false;
763  }
764  dirEntries.clear();
765  while ((dentry = readdir(dp)) != NULL)
766  {
767  dirEntries.append(String(dentry->d_name));
768  }
769  closedir(dp);
770 #endif
771  return true;
772 }
774 bool
775 renameFile(const String& oldFileName,
776  const String& newFileName)
777 {
779  {
780  return g_fileSystemMockObject->renameFile(oldFileName, newFileName);
781  }
782  return ::rename(oldFileName.c_str(), newFileName.c_str()) == 0;
783 }
785 size_t
786 read(const FileHandle& hdl, void* bfr, size_t numberOfBytes,
787  Int64 offset)
788 {
790  {
791  return g_fileSystemMockObject->read(hdl, bfr, numberOfBytes, offset);
792  }
793 #ifdef BLOCXX_WIN32
794  OVERLAPPED ov = { 0, 0, 0, 0, NULL };
795  OVERLAPPED *pov = NULL;
796  if(offset != -1L)
797  {
798  ov.Offset = (DWORD) offset;
799  // check for truncation
800  if (ov.Offset != offset)
801  {
802  BLOCXX_THROW(FileSystemException, "offset out of range");
803  }
804  pov = &ov;
805  }
806 
807  DWORD bytesRead;
808  size_t cc = (size_t)-1;
809  if(::ReadFile(hdl, bfr, (DWORD)numberOfBytes, &bytesRead, pov))
810  {
811  cc = (size_t)bytesRead;
812  }
813 
814  return cc;
815 #else
816  if (offset != -1L)
817  {
818  ::off_t offset2 = static_cast< ::off_t>(offset);
819  // check for truncation
820  if (offset2 != offset)
821  {
822  BLOCXX_THROW(FileSystemException, "offset out of range");
823  }
824 
825  ::lseek(hdl, offset2, SEEK_SET);
826  }
827  return ::read(hdl, bfr, numberOfBytes);
828 #endif
829 }
831 size_t
832 write(FileHandle hdl, const void* bfr, size_t numberOfBytes,
833  Int64 offset)
834 {
836  {
837  return g_fileSystemMockObject->write(hdl, bfr, numberOfBytes, offset);
838  }
839 #ifdef BLOCXX_WIN32
840  OVERLAPPED ov = { 0, 0, 0, 0, NULL };
841  OVERLAPPED *pov = NULL;
842  if(offset != -1L)
843  {
844  ov.Offset = (DWORD) offset;
845  // check for truncation
846  if (ov.Offset != offset)
847  {
848  BLOCXX_THROW(FileSystemException, "offset out of range");
849  }
850  pov = &ov;
851  }
852 
853  DWORD bytesWritten;
854  size_t cc = (size_t)-1;
855  if(::WriteFile(hdl, bfr, (DWORD)numberOfBytes, &bytesWritten, pov))
856  {
857  cc = (size_t)bytesWritten;
858  }
859  return cc;
860 #else
861 
862  if (offset != -1L)
863  {
864  ::off_t offset2 = static_cast< ::off_t>(offset);
865  // check for truncation
866  if (offset2 != offset)
867  {
868  BLOCXX_THROW(FileSystemException, "offset out of range");
869  }
870  ::lseek(hdl, offset2, SEEK_SET);
871  }
872  return ::write(hdl, bfr, numberOfBytes);
873 #endif
874 }
875 
877 Int64
878 seek(const FileHandle& hdl, Int64 offset, int whence)
879 {
881  {
882  return g_fileSystemMockObject->seek(hdl, offset, whence);
883  }
884 #ifdef BLOCXX_WIN32
885  DWORD moveMethod;
886  switch(whence)
887  {
888  case SEEK_END: moveMethod = FILE_END; break;
889  case SEEK_CUR: moveMethod = FILE_CURRENT; break;
890  default: moveMethod = FILE_BEGIN; break;
891  }
892 
893  LARGE_INTEGER li;
894  li.QuadPart = offset;
895  li.LowPart = SetFilePointer(hdl, li.LowPart, &li.HighPart, moveMethod);
896 
897  if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
898  {
899  li.QuadPart = -1;
900  }
901 
902  return li.QuadPart;
903 #else
904  ::off_t offset2 = static_cast< ::off_t>(offset);
905  // check for truncation
906  if (offset2 != offset)
907  {
908  BLOCXX_THROW(FileSystemException, "offset out of range");
909  }
910  return ::lseek(hdl, offset2, whence);
911 #endif
912 }
914 Int64
915 tell(const FileHandle& hdl)
916 {
918  {
919  return g_fileSystemMockObject->tell(hdl);
920  }
921 #ifdef BLOCXX_WIN32
922  LARGE_INTEGER li;
923  li.QuadPart = 0;
924  li.LowPart = SetFilePointer(hdl, li.LowPart, &li.HighPart, FILE_CURRENT);
925 
926  if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
927  {
928  li.QuadPart = -1;
929  }
930 
931  return li.QuadPart;
932 #else
933  return ::lseek(hdl, 0, SEEK_CUR);
934 #endif
935 }
938 {
940  {
941  return g_fileSystemMockObject->fileSize(fh);
942  }
943 
944 #ifndef BLOCXX_WIN32
945 
946  struct stat st;
947  int rc = ::fstat(fh, &st);
948  if (rc != 0)
949  {
950  BLOCXX_THROW_ERRNO_MSG(FileSystemException, "Could not stat file handle: ");
951  }
952  return st.st_size;
953 
954 #else
955  LARGE_INTEGER FileSize;
956  BOOL rc = GetFileSizeEx(fh, &FileSize);
957  if(!rc)
958  {
959  BLOCXX_THROW_ERRNO_MSG(FileSystemException, "Could not GetFileSizeEx() for file handle: ");
960  }
961 
962  UInt64 tmp = FileSize.QuadPart;
963  return tmp;
964 
965 #endif
966 }
968 void
969 rewind(const FileHandle& hdl)
970 {
972  {
973  return g_fileSystemMockObject->rewind(hdl);
974  }
975 #ifdef BLOCXX_WIN32
976  ::SetFilePointer(hdl, 0L, NULL, FILE_BEGIN);
977 #else
978  ::lseek(hdl, 0, SEEK_SET);
979 #endif
980 }
982 int
983 close(const FileHandle& hdl)
984 {
986  {
987  return g_fileSystemMockObject->close(hdl);
988  }
989 #ifdef BLOCXX_WIN32
990  return (::CloseHandle(hdl)) ? 0 : -1;
991 #else
992  return ::close(hdl);
993 #endif
994 }
996 int
998 {
1000  {
1001  return g_fileSystemMockObject->flush(hdl);
1002  }
1003 #ifdef BLOCXX_WIN32
1004  return (::FlushFileBuffers(hdl)) ? 0 : -1;
1005 #else
1006  return ::fsync(hdl);
1007 #endif
1008 }
1011 {
1013  {
1014  return g_fileSystemMockObject->getFileContents(filename);
1015  }
1016  std::ifstream in(filename.c_str());
1017  if (!in)
1018  {
1019  BLOCXX_THROW(FileSystemException, Format("Failed to open file %1", filename).c_str());
1020  }
1021  OStringStream ss;
1022  ss << in.rdbuf();
1023  return ss.toString();
1024 }
1025 
1028 {
1030  {
1031  return g_fileSystemMockObject->getFileLines(filename);
1032  }
1033  return getFileContents(filename).tokenize("\r\n");
1034 }
1035 
1038 {
1040  {
1041  return g_fileSystemMockObject->readSymbolicLink(path);
1042  }
1043 #ifdef BLOCXX_WIN32
1044  return Path::realPath(path);
1045 #else
1046  std::vector<char> buf(MAXPATHLEN + 1);
1047  int rc;
1048  while (true)
1049  {
1050  rc = ::readlink(path.c_str(), &buf[0], buf.size());
1051  // If the link value is too big to fit into buf, but
1052  // there is no other error, then rc == buf.size(); in particular,
1053  // we do NOT get rc < 0 with errno == ENAMETOOLONG (this indicates
1054  // a problem with the input path, not the link value returned).
1055  if (rc < 0)
1056  {
1058  }
1059  else if (static_cast<unsigned>(rc) == buf.size())
1060  {
1061  buf.resize(buf.size() * 2);
1062  }
1063  else
1064  {
1065  buf.resize(rc);
1066  buf.push_back('\0');
1067  return String(&buf[0]);
1068  }
1069  }
1070 #endif
1071  // Not reachable.
1072  return String();
1073 }
1074 
1076 namespace Path
1077 {
1078 
1080 String realPath(const String& path)
1081 {
1083  {
1084  return g_fileSystemMockObject->realPath(path);
1085  }
1086 #ifdef BLOCXX_WIN32
1087  char c, *bfr, *pname;
1088  const char *pathcstr;
1089  DWORD cc;
1090 
1091  pathcstr = path.c_str();
1092  while (*pathcstr == '/' || *pathcstr == '\\')
1093  {
1094  ++pathcstr;
1095  }
1096 
1097  // if we ate some '\' or '/' chars, the back up to
1098  // allow for 1
1099  if(pathcstr != path.c_str())
1100  {
1101  --pathcstr;
1102  }
1103 
1104  cc = GetFullPathName(path.c_str(), 1, &c, &pname);
1105  if(!cc)
1106  {
1107  BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
1108  }
1109  bfr = new char[cc];
1110  cc = GetFullPathName(path.c_str(), cc, bfr, &pname);
1111  if(!cc)
1112  {
1113  delete [] bfr;
1114  BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
1115  }
1116  String rstr(bfr);
1117  delete [] bfr;
1118  return rstr;
1119 #else
1120  if (path.startsWith("/"))
1121  {
1122  return security(path, 0).second;
1123  }
1124  else
1125  {
1126  return security(getCurrentWorkingDirectory(), path, 0).second;
1127  }
1128 #endif
1129 }
1130 
1132 String dirname(const String& filename)
1133 {
1135  {
1136  return g_fileSystemMockObject->dirname(filename);
1137  }
1138  // skip over trailing slashes
1139  if (filename.length() == 0)
1140  {
1141  return ".";
1142  }
1143  size_t lastSlash = filename.length() - 1;
1144  while (lastSlash > 0
1145  && filename[lastSlash] == BLOCXX_FILENAME_SEPARATOR_C)
1146  {
1147  --lastSlash;
1148  }
1149 
1150  lastSlash = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, lastSlash);
1151 
1152  if (lastSlash == String::npos)
1153  {
1154  return ".";
1155  }
1156 
1157  while (lastSlash > 0 && filename[lastSlash - 1] == BLOCXX_FILENAME_SEPARATOR_C)
1158  {
1159  --lastSlash;
1160  }
1161 
1162  if (lastSlash == 0)
1163  {
1165  }
1166 
1167  return filename.substring(0, lastSlash);
1168 }
1169 
1171 String basename(const String& filename)
1172 {
1174  {
1175  return g_fileSystemMockObject->basename(filename);
1176  }
1177  if (filename.length() == 0)
1178  {
1179  return filename;
1180  }
1181  size_t end = filename.length() -1;
1182  while (end > 0
1183  && filename[end] == BLOCXX_FILENAME_SEPARATOR_C)
1184  {
1185  --end;
1186  }
1187  if (end == 0 && filename[0] == BLOCXX_FILENAME_SEPARATOR_C)
1188  {
1189  return BLOCXX_FILENAME_SEPARATOR;
1190  }
1191  if (end == filename.length() -1)
1192  {
1193  end = String::npos;
1194  }
1195  size_t beg = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, end);
1196  if (beg == String::npos)
1197  {
1198  beg = 0;
1199  }
1200  else
1201  {
1202  ++beg;
1203  }
1204  size_t len = end == String::npos? end : ++end - beg;
1205  return filename.substring(beg, len);
1206 }
1207 
1210 {
1212  {
1213  return g_fileSystemMockObject->getCurrentWorkingDirectory();
1214  }
1215  std::vector<char> buf(MAXPATHLEN);
1216  char* p;
1217  do
1218  {
1219  p = ::getcwd(&buf[0], buf.size());
1220  if (p != 0)
1221  {
1222  return p;
1223  }
1224  buf.resize(buf.size() * 2);
1225  } while (p == 0 && errno == ERANGE);
1226 
1228 }
1229 
1230 } // end namespace Path
1231 } // end namespace FileSystem
1232 } // end namespace BLOCXX_NAMESPACE
1233