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
40
41#include "blocxx/BLOCXX_config.h"
42#include "blocxx/FileSystem.hpp"
43#include "blocxx/Mutex.hpp"
44#include "blocxx/MutexLock.hpp"
46#include "blocxx/File.hpp"
47#include "blocxx/Array.hpp"
48#include "blocxx/Format.hpp"
50#include "blocxx/Assertion.hpp"
51#include "blocxx/GlobalPtr.hpp"
53#include "blocxx/AutoPtr.hpp"
55#include "blocxx/Logger.hpp"
57
58extern "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>
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
228namespace BLOCXX_NAMESPACE
229{
230
232
233namespace FileSystem
234{
235
238
240
242// STATIC
243int
244changeFileOwner(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}
253
254// STATIC
255File
256openFile(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}
272
273// STATIC
274File
275createFile(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}
296
297// STATIC
298File
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
317File
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
337namespace
338{
340}
341
342#ifndef BLOCXX_WIN32
344File
345createTempFile(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
389File
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 {
432 BLOCXX_LOG_ERROR(lgr, Format("PosixFileSystem::createTempFile: unlink failed: %1", errno));
433 }
434 }
435 return File(hdl);
436}
437#else
439File
440createTempFile(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
504File
505createAutoDeleteTempFile(const String& dir)
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 {
568 BLOCXX_LOG_ERROR(lgr, Format("PosixFileSystem::createTempFile: unlink failed: %1", errno));
569 }
570 }
571 return File(hdl);
572}
573#endif
574
576bool
577exists(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
588bool
590{
592 {
593 return g_fileSystemMockObject->isExecutable(path);
594 }
595 return _ACCESS(path.c_str(), X_OK) == 0;
596}
597#endif
598
600bool
601canRead(const String& path)
602{
604 {
605 return g_fileSystemMockObject->canRead(path);
606 }
607 return _ACCESS(path.c_str(), R_OK) == 0;
608}
609
610bool
611canWrite(const String& path)
612{
614 {
615 return g_fileSystemMockObject->canWrite(path);
616 }
617 return _ACCESS(path.c_str(), W_OK) == 0;
618}
619
620#ifndef BLOCXX_WIN32
621bool
622isLink(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
637bool
638isDirectory(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}
660
661bool
663{
665 {
666 return g_fileSystemMockObject->changeDirectory(path);
667 }
668 return _CHDIR(path.c_str()) == 0;
669}
670
671bool
672makeDirectory(const String& path, int mode)
673{
675 {
676 return g_fileSystemMockObject->makeDirectory(path, mode);
677 }
678 return _MKDIR(path.c_str(), mode) == 0;
679}
680
681bool
682getFileSize(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}
704
705bool
707{
709 {
710 return g_fileSystemMockObject->removeDirectory(path);
711 }
712 return _RMDIR(path.c_str()) == 0;
713}
714
715bool
716removeFile(const String& path)
717{
719 {
720 return g_fileSystemMockObject->removeFile(path);
721 }
722 return _UNLINK(path.c_str()) == 0;
723}
724
725bool
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 {
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}
773
774bool
775renameFile(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}
784
785size_t
786read(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}
830
831size_t
832write(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
877Int64
878seek(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}
913
914Int64
915tell(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}
936
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}
967
968void
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}
981
982int
983close(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}
995
996int
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}
1009
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
1076namespace Path
1077{
1078
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
1132String 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
1171String 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 {
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
#define BLOCXX_DEFINE_EXCEPTION_WITH_ID(NAME)
Define a new exception class named <NAME>Exception that derives from Exception.
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO(exType)
Throw an exception using FILE, LINE, errno and strerror(errno)
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
#define MAXPATHLEN
#define BLOCXX_GLOBAL_MUTEX_INIT()
#define BLOCXX_GLOBAL_PTR_INIT
This macro is provided to abstract the details of GlobalPtr.
#define BLOCXX_GLOBAL_STRING_INIT(str)
#define BLOCXX_LOG_ERROR(logger, message)
Log message to logger with the Error level.
Definition Logger.hpp:433
#define _UNLINK
#define _CHDIR
#define _MKDIR(a, b)
#define _RMDIR
#define _ACCESS
#define BLOCXX_FILENAME_SEPARATOR
Definition Types.hpp:153
#define BLOCXX_FILENAME_SEPARATOR_C
Definition Types.hpp:154
void append(const T &x)
Append an element to the end of the Array.
void clear()
Remove all items from the Array.
The AutoPtrVec class provides a simple class for smart pointers to a dynamically allocated array of o...
Definition AutoPtr.hpp:185
The purpose of the File class is to provide an abstraction layer over the platform dependant function...
Definition File.hpp:55
virtual File createTempFile(const String &dir)
This class can be used to store a global pointer.
Definition GlobalPtr.hpp:84
Logging interface.
Definition Logger.hpp:87
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
const char * c_str() const
Definition String.cpp:905
String & erase()
Delete all the characters of the string.
Definition String.cpp:824
size_t length() const
Definition String.cpp:354
String substring(size_t beginIndex, size_t length=npos) const
Create another String object that is comprised of a substring of this String object.
Definition String.cpp:698
StringArray tokenize(const char *delims=" \n\r\t\v", EReturnDelimitersFlag returnDelimitersAsTokens=E_DISCARD_DELIMITERS, EEmptyTokenReturnFlag returnEmptyTokens=E_SKIP_EMPTY_TOKENS) const
Tokenize this String object using the given delimeters.
Definition String.cpp:1147
bool startsWith(const char *arg, EIgnoreCaseFlag ignoreCase=E_CASE_SENSITIVE) const
Determine if this String object starts with a given substring.
Definition String.cpp:672
static const size_t npos
Definition String.hpp:742
size_t lastIndexOf(char ch, size_t fromIndex=npos) const
Find the last occurence of a character in this String object.
Definition String.cpp:603
bool endsWith(const char *arg, EIgnoreCaseFlag ignoreCase=E_CASE_SENSITIVE) const
Determine if this String object ends with given C string.
Definition String.cpp:487
BLOCXX_COMMON_API String basename(const String &filename)
Take a string that contains a pathname, and return a string that is the filename with the path remove...
BLOCXX_COMMON_API String realPath(const String &path)
BLOCXX_COMMON_API std::pair< ESecurity, String > security(String const &path, UserId uid)
BLOCXX_COMMON_API String dirname(const String &filename)
Take a string that contains a pathname, and return a string that is a pathname of the parent director...
BLOCXX_COMMON_API String getCurrentWorkingDirectory()
Get the process's current working directory.
The purpose of the FileSystem class is to provide an abstraction layer over the platform dependant fu...
BLOCXX_COMMON_API int changeFileOwner(const String &filename, const UserId &userId)
Change the given file ownership.
BLOCXX_COMMON_API void rewind(const FileHandle &hdl)
Position the file pointer associated with the given file handle to the beginning of the file.
BLOCXX_COMMON_API File createFile(const String &path)
Create the file for the given name.
BLOCXX_COMMON_API File openForAppendOrCreateFile(const String &path)
Opens the file for the given name to append data or create if it does not exist.
BLOCXX_COMMON_API size_t read(const FileHandle &hdl, void *bfr, size_t numberOfBytes, Int64 offset=-1L)
Read data from file.
BLOCXX_COMMON_API bool canRead(const String &path)
BLOCXX_COMMON_API bool isLink(const String &path)
Tests if a file is a symbolic link.
BLOCXX_COMMON_API bool exists(const String &path)
BLOCXX_COMMON_API bool removeFile(const String &path)
Remove the given file.
BLOCXX_COMMON_API bool getFileSize(const String &path, Int64 &size)
Get the size of the file in bytes.
BLOCXX_COMMON_API bool getDirectoryContents(const String &path, StringArray &dirEntries)
Get the names of the files (and directories) in the given directory.
BLOCXX_COMMON_API File createAutoDeleteTempFile(const String &dir=String())
Create a tempororary file that will be removed when the returned File object is closed.
BLOCXX_COMMON_API String readSymbolicLink(const String &path)
Read the value of a symbolic link.
BLOCXX_COMMON_API Int64 seek(const FileHandle &hdl, Int64 offset, int whence)
Seek to a given offset within the file.
BLOCXX_COMMON_API int close(const FileHandle &hdl)
Close file handle.
BLOCXX_COMMON_API size_t write(FileHandle hdl, const void *bfr, size_t numberOfBytes, Int64 offset=-1L)
Write data to a file.
BLOCXX_COMMON_API bool renameFile(const String &oldFileName, const String &newFileName)
Rename the given file to the new name.
BLOCXX_COMMON_API bool isExecutable(const String &path)
Tests if a file is executable.
BLOCXX_COMMON_API bool canWrite(const String &path)
BLOCXX_COMMON_API File openOrCreateFile(const String &path)
Opens or creates the file for the given name.
FileSystemMockObject_t g_fileSystemMockObject
BLOCXX_COMMON_API File createTempFile(String &filePath, const String &dir=String())
Create a tempororary file in an optional directory.
BLOCXX_COMMON_API Int64 tell(const FileHandle &hdl)
BLOCXX_COMMON_API UInt64 fileSize(FileHandle fh)
Get the size of a file from the file handle.
BLOCXX_COMMON_API bool isDirectory(const String &path)
BLOCXX_COMMON_API int flush(FileHandle &hdl)
Flush any buffered data to the file if buffering supported.
BLOCXX_COMMON_API StringArray getFileLines(const String &filename)
Read and return the lines of a test file.
GlobalPtr< FileSystemMockObject, NullFactory > FileSystemMockObject_t
BLOCXX_COMMON_API bool changeDirectory(const String &path)
Change to the given directory.
BLOCXX_COMMON_API File openFile(const String &path)
Open a file for read/write and return an File object that can be used for reading and writing.
BLOCXX_COMMON_API String getFileContents(const String &filename)
Read and return the contents of a text file.
BLOCXX_COMMON_API bool removeDirectory(const String &path)
Remove the given directory.
BLOCXX_COMMON_API bool makeDirectory(const String &path, int mode=0777)
Create a directory.
char * strcpy_check(char *dst, std::size_t dstsize, char const *src)
PROMISE: copies the first n = min(strlen(src), dstsize - 1) chars of the C-string src to dst and appe...
Taken from RFC 1321.
LazyGlobal< String, char const *const > GlobalString
Array< String > StringArray
Definition CommonFwd.hpp:73
class BLOCXX_COMMON_API Logger
Definition CommonFwd.hpp:63
LazyGlobal< Mutex, int, GlobalMutexFactory > GlobalMutex