• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Windows/FileIO.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifdef SUPPORT_DEVICE_FILE
6 #include "../../C/Alloc.h"
7 #endif
8 
9 // #include <stdio.h>
10 
11 /*
12 #ifndef _WIN32
13 // for ioctl BLKGETSIZE64
14 #include <sys/ioctl.h>
15 #include <linux/fs.h>
16 #endif
17 */
18 
19 #include "FileIO.h"
20 #include "FileName.h"
21 
GetLastError_noZero_HRESULT()22 HRESULT GetLastError_noZero_HRESULT()
23 {
24   DWORD res = ::GetLastError();
25   if (res == 0)
26     return E_FAIL;
27   return HRESULT_FROM_WIN32(res);
28 }
29 
30 #ifdef _WIN32
31 
32 #ifndef _UNICODE
33 extern bool g_IsNT;
34 #endif
35 
36 using namespace NWindows;
37 using namespace NFile;
38 using namespace NName;
39 
40 namespace NWindows {
41 namespace NFile {
42 
43 #ifdef SUPPORT_DEVICE_FILE
44 
45 namespace NSystem
46 {
47 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize);
48 }
49 #endif
50 
51 namespace NIO {
52 
53 /*
54 WinXP-64 CreateFile():
55   ""             -  ERROR_PATH_NOT_FOUND
56   :stream        -  OK
57   .:stream       -  ERROR_PATH_NOT_FOUND
58   .\:stream      -  OK
59 
60   folder\:stream -  ERROR_INVALID_NAME
61   folder:stream  -  OK
62 
63   c:\:stream     -  OK
64 
65   c::stream      -  ERROR_INVALID_NAME, if current dir is NOT ROOT ( c:\dir1 )
66   c::stream      -  OK,                 if current dir is ROOT     ( c:\ )
67 */
68 
Create(CFSTR path,DWORD desiredAccess,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)69 bool CFileBase::Create(CFSTR path, DWORD desiredAccess,
70     DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
71 {
72   if (!Close())
73     return false;
74 
75   #ifdef SUPPORT_DEVICE_FILE
76   IsDeviceFile = false;
77   #endif
78 
79   #ifndef _UNICODE
80   if (!g_IsNT)
81   {
82     _handle = ::CreateFile(fs2fas(path), desiredAccess, shareMode,
83         (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
84   }
85   else
86   #endif
87   {
88     IF_USE_MAIN_PATH
89       _handle = ::CreateFileW(fs2us(path), desiredAccess, shareMode,
90         (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
91     #ifdef WIN_LONG_PATH
92     if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
93     {
94       UString superPath;
95       if (GetSuperPath(path, superPath, USE_MAIN_PATH))
96         _handle = ::CreateFileW(superPath, desiredAccess, shareMode,
97             (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
98     }
99     #endif
100   }
101 
102   /*
103   #ifndef UNDER_CE
104   #ifndef _SFX
105   if (_handle == INVALID_HANDLE_VALUE)
106   {
107     // it's debug hack to open symbolic links in Windows XP and WSL links in Windows 10
108     DWORD lastError = GetLastError();
109     if (lastError == ERROR_CANT_ACCESS_FILE)
110     {
111       CByteBuffer buf;
112       if (NIO::GetReparseData(path, buf, NULL))
113       {
114         CReparseAttr attr;
115         if (attr.Parse(buf, buf.Size()))
116         {
117           FString dirPrefix, fileName;
118           if (NDir::GetFullPathAndSplit(path, dirPrefix, fileName))
119           {
120             FString fullPath;
121             if (GetFullPath(dirPrefix, us2fs(attr.GetPath()), fullPath))
122             {
123               // FIX IT: recursion levels must be restricted
124               return Create(fullPath, desiredAccess,
125                 shareMode, creationDisposition, flagsAndAttributes);
126             }
127           }
128         }
129       }
130       SetLastError(lastError);
131     }
132   }
133   #endif
134   #endif
135   */
136 
137   return (_handle != INVALID_HANDLE_VALUE);
138 }
139 
Close()140 bool CFileBase::Close() throw()
141 {
142   if (_handle == INVALID_HANDLE_VALUE)
143     return true;
144   if (!::CloseHandle(_handle))
145     return false;
146   _handle = INVALID_HANDLE_VALUE;
147   return true;
148 }
149 
GetLength(UInt64 & length) const150 bool CFileBase::GetLength(UInt64 &length) const throw()
151 {
152   #ifdef SUPPORT_DEVICE_FILE
153   if (IsDeviceFile && SizeDefined)
154   {
155     length = Size;
156     return true;
157   }
158   #endif
159 
160   DWORD high = 0;
161   const DWORD low = ::GetFileSize(_handle, &high);
162   if (low == INVALID_FILE_SIZE)
163     if (::GetLastError() != NO_ERROR)
164       return false;
165   length = (((UInt64)high) << 32) + low;
166   return true;
167 
168   /*
169   LARGE_INTEGER fileSize;
170   // GetFileSizeEx() is unsupported in 98/ME/NT, and supported in Win2000+
171   if (!GetFileSizeEx(_handle, &fileSize))
172     return false;
173   length = (UInt64)fileSize.QuadPart;
174   return true;
175   */
176 }
177 
178 
179 /* Specification for SetFilePointer():
180 
181    If a new file pointer is a negative value,
182    {
183      the function fails,
184      the file pointer is not moved,
185      the code returned by GetLastError() is ERROR_NEGATIVE_SEEK.
186    }
187 
188    If the hFile handle is opened with the FILE_FLAG_NO_BUFFERING flag set
189    {
190      an application can move the file pointer only to sector-aligned positions.
191      A sector-aligned position is a position that is a whole number multiple of
192      the volume sector size.
193      An application can obtain a volume sector size by calling the GetDiskFreeSpace.
194    }
195 
196    It is not an error to set a file pointer to a position beyond the end of the file.
197    The size of the file does not increase until you call the SetEndOfFile, WriteFile, or WriteFileEx function.
198 
199    If the return value is INVALID_SET_FILE_POINTER and if lpDistanceToMoveHigh is non-NULL,
200    an application must call GetLastError to determine whether or not the function has succeeded or failed.
201 */
202 
GetPosition(UInt64 & position) const203 bool CFileBase::GetPosition(UInt64 &position) const throw()
204 {
205   LONG high = 0;
206   const DWORD low = ::SetFilePointer(_handle, 0, &high, FILE_CURRENT);
207   if (low == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
208   {
209     // for error case we can set (position)  to (-1) or (0) or leave (position) unchanged.
210     // position = (UInt64)(Int64)-1; // for debug
211     position = 0;
212     return false;
213   }
214   position = (((UInt64)(UInt32)high) << 32) + low;
215   return true;
216   // we don't want recursed GetPosition()
217   // return Seek(0, FILE_CURRENT, position);
218 }
219 
Seek(Int64 distanceToMove,DWORD moveMethod,UInt64 & newPosition) const220 bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const throw()
221 {
222   #ifdef SUPPORT_DEVICE_FILE
223   if (IsDeviceFile && SizeDefined && moveMethod == FILE_END)
224   {
225     distanceToMove += Size;
226     moveMethod = FILE_BEGIN;
227   }
228   #endif
229 
230   LONG high = (LONG)(distanceToMove >> 32);
231   const DWORD low = ::SetFilePointer(_handle, (LONG)(distanceToMove & 0xFFFFFFFF), &high, moveMethod);
232   if (low == INVALID_SET_FILE_POINTER)
233   {
234     const DWORD lastError = ::GetLastError();
235     if (lastError != NO_ERROR)
236     {
237       // 21.07: we set (newPosition) to real position even after error.
238       GetPosition(newPosition);
239       SetLastError(lastError); // restore LastError
240       return false;
241     }
242   }
243   newPosition = (((UInt64)(UInt32)high) << 32) + low;
244   return true;
245 }
246 
Seek(UInt64 position,UInt64 & newPosition) const247 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) const throw()
248 {
249   return Seek((Int64)position, FILE_BEGIN, newPosition);
250 }
251 
SeekToBegin() const252 bool CFileBase::SeekToBegin() const throw()
253 {
254   UInt64 newPosition = 0;
255   return Seek(0, newPosition) && (newPosition == 0);
256 }
257 
SeekToEnd(UInt64 & newPosition) const258 bool CFileBase::SeekToEnd(UInt64 &newPosition) const throw()
259 {
260   return Seek(0, FILE_END, newPosition);
261 }
262 
263 // ---------- CInFile ---------
264 
265 #ifdef SUPPORT_DEVICE_FILE
266 
CorrectDeviceSize()267 void CInFile::CorrectDeviceSize()
268 {
269   // maybe we must decrease kClusterSize to 1 << 12, if we want correct size at tail
270   static const UInt32 kClusterSize = 1 << 14;
271   UInt64 pos = Size & ~(UInt64)(kClusterSize - 1);
272   UInt64 realNewPosition;
273   if (!Seek(pos, realNewPosition))
274     return;
275   Byte *buf = (Byte *)MidAlloc(kClusterSize);
276 
277   bool needbackward = true;
278 
279   for (;;)
280   {
281     UInt32 processed = 0;
282     // up test is slow for "PhysicalDrive".
283     // processed size for latest block for "PhysicalDrive0" is 0.
284     if (!Read1(buf, kClusterSize, processed))
285       break;
286     if (processed == 0)
287       break;
288     needbackward = false;
289     Size = pos + processed;
290     if (processed != kClusterSize)
291       break;
292     pos += kClusterSize;
293   }
294 
295   if (needbackward && pos != 0)
296   {
297     pos -= kClusterSize;
298     for (;;)
299     {
300       // break;
301       if (!Seek(pos, realNewPosition))
302         break;
303       if (!buf)
304       {
305         buf = (Byte *)MidAlloc(kClusterSize);
306         if (!buf)
307           break;
308       }
309       UInt32 processed = 0;
310       // that code doesn't work for "PhysicalDrive0"
311       if (!Read1(buf, kClusterSize, processed))
312         break;
313       if (processed != 0)
314       {
315         Size = pos + processed;
316         break;
317       }
318       if (pos == 0)
319         break;
320       pos -= kClusterSize;
321     }
322   }
323   MidFree(buf);
324 }
325 
326 
CalcDeviceSize(CFSTR s)327 void CInFile::CalcDeviceSize(CFSTR s)
328 {
329   SizeDefined = false;
330   Size = 0;
331   if (_handle == INVALID_HANDLE_VALUE || !IsDeviceFile)
332     return;
333   #ifdef UNDER_CE
334 
335   SizeDefined = true;
336   Size = 128 << 20;
337 
338   #else
339 
340   PARTITION_INFORMATION partInfo;
341   bool needCorrectSize = true;
342 
343   /*
344     WinXP 64-bit:
345 
346     HDD \\.\PhysicalDrive0 (MBR):
347       GetPartitionInfo == GeometryEx :  corrrect size? (includes tail)
348       Geometry   :  smaller than GeometryEx (no tail, maybe correct too?)
349       MyGetDiskFreeSpace : FAIL
350       Size correction is slow and block size (kClusterSize) must be small?
351 
352     HDD partition \\.\N: (NTFS):
353       MyGetDiskFreeSpace   :  Size of NTFS clusters. Same size can be calculated after correction
354       GetPartitionInfo     :  size of partition data: NTFS clusters + TAIL; TAIL contains extra empty sectors and copy of first sector of NTFS
355       Geometry / CdRomGeometry / GeometryEx :  size of HDD (not that partition)
356 
357     CD-ROM drive (ISO):
358       MyGetDiskFreeSpace   :  correct size. Same size can be calculated after correction
359       Geometry == CdRomGeometry  :  smaller than corrrect size
360       GetPartitionInfo == GeometryEx :  larger than corrrect size
361 
362     Floppy \\.\a: (FAT):
363       Geometry :  correct size.
364       CdRomGeometry / GeometryEx / GetPartitionInfo / MyGetDiskFreeSpace - FAIL
365       correction works OK for FAT.
366       correction works OK for non-FAT, if kClusterSize = 512.
367   */
368 
369   if (GetPartitionInfo(&partInfo))
370   {
371     Size = (UInt64)partInfo.PartitionLength.QuadPart;
372     SizeDefined = true;
373     needCorrectSize = false;
374     if ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\' && (s)[5] == ':' && (s)[6] == 0)
375     {
376       FChar path[4] = { s[4], ':', '\\', 0 };
377       UInt64 clusterSize, totalSize, freeSize;
378       if (NSystem::MyGetDiskFreeSpace(path, clusterSize, totalSize, freeSize))
379         Size = totalSize;
380       else
381         needCorrectSize = true;
382     }
383   }
384 
385   if (!SizeDefined)
386   {
387     my_DISK_GEOMETRY_EX geomEx;
388     SizeDefined = GetGeometryEx(&geomEx);
389     if (SizeDefined)
390       Size = (UInt64)geomEx.DiskSize.QuadPart;
391     else
392     {
393       DISK_GEOMETRY geom;
394       SizeDefined = GetGeometry(&geom);
395       if (!SizeDefined)
396         SizeDefined = GetCdRomGeometry(&geom);
397       if (SizeDefined)
398         Size = (UInt64)geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector;
399     }
400   }
401 
402   if (needCorrectSize && SizeDefined && Size != 0)
403   {
404     CorrectDeviceSize();
405     SeekToBegin();
406   }
407 
408   // SeekToBegin();
409   #endif
410 }
411 
412 // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 &&
413 
414 #define MY_DEVICE_EXTRA_CODE \
415   IsDeviceFile = IsDevicePath(fileName); \
416   CalcDeviceSize(fileName);
417 #else
418 #define MY_DEVICE_EXTRA_CODE
419 #endif
420 
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)421 bool CInFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
422 {
423   DWORD desiredAccess = GENERIC_READ;
424 
425   #ifdef _WIN32
426   if (PreserveATime)
427     desiredAccess |= FILE_WRITE_ATTRIBUTES;
428   #endif
429 
430   bool res = Create(fileName, desiredAccess, shareMode, creationDisposition, flagsAndAttributes);
431 
432   #ifdef _WIN32
433   if (res && PreserveATime)
434   {
435     FILETIME ft;
436     ft.dwHighDateTime = ft.dwLowDateTime = 0xFFFFFFFF;
437     ::SetFileTime(_handle, NULL, &ft, NULL);
438   }
439   #endif
440 
441   MY_DEVICE_EXTRA_CODE
442   return res;
443 }
444 
OpenShared(CFSTR fileName,bool shareForWrite)445 bool CInFile::OpenShared(CFSTR fileName, bool shareForWrite)
446 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
447 
Open(CFSTR fileName)448 bool CInFile::Open(CFSTR fileName)
449   { return OpenShared(fileName, false); }
450 
451 // ReadFile and WriteFile functions in Windows have BUG:
452 // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
453 // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
454 // (Insufficient system resources exist to complete the requested service).
455 
456 // Probably in some version of Windows there are problems with other sizes:
457 // for 32 MB (maybe also for 16 MB).
458 // And message can be "Network connection was lost"
459 
460 static const UInt32 kChunkSizeMax = (1 << 22);
461 
Read1(void * data,UInt32 size,UInt32 & processedSize)462 bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize) throw()
463 {
464   DWORD processedLoc = 0;
465   bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL));
466   processedSize = (UInt32)processedLoc;
467   return res;
468 }
469 
ReadPart(void * data,UInt32 size,UInt32 & processedSize)470 bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) throw()
471 {
472   if (size > kChunkSizeMax)
473     size = kChunkSizeMax;
474   return Read1(data, size, processedSize);
475 }
476 
Read(void * data,UInt32 size,UInt32 & processedSize)477 bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) throw()
478 {
479   processedSize = 0;
480   do
481   {
482     UInt32 processedLoc = 0;
483     bool res = ReadPart(data, size, processedLoc);
484     processedSize += processedLoc;
485     if (!res)
486       return false;
487     if (processedLoc == 0)
488       return true;
489     data = (void *)((unsigned char *)data + processedLoc);
490     size -= processedLoc;
491   }
492   while (size > 0);
493   return true;
494 }
495 
ReadFull(void * data,size_t size,size_t & processedSize)496 bool CInFile::ReadFull(void *data, size_t size, size_t &processedSize) throw()
497 {
498   processedSize = 0;
499   do
500   {
501     UInt32 processedLoc = 0;
502     const UInt32 sizeLoc = (size > kChunkSizeMax ? (UInt32)kChunkSizeMax : (UInt32)size);
503     const bool res = Read1(data, sizeLoc, processedLoc);
504     processedSize += processedLoc;
505     if (!res)
506       return false;
507     if (processedLoc == 0)
508       return true;
509     data = (void *)((unsigned char *)data + processedLoc);
510     size -= processedLoc;
511   }
512   while (size > 0);
513   return true;
514 }
515 
516 // ---------- COutFile ---------
517 
GetCreationDisposition(bool createAlways)518 static inline DWORD GetCreationDisposition(bool createAlways)
519   { return createAlways? CREATE_ALWAYS: CREATE_NEW; }
520 
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)521 bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
522   { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
523 
Open(CFSTR fileName,DWORD creationDisposition)524 bool COutFile::Open(CFSTR fileName, DWORD creationDisposition)
525   { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
526 
Create(CFSTR fileName,bool createAlways)527 bool COutFile::Create(CFSTR fileName, bool createAlways)
528   { return Open(fileName, GetCreationDisposition(createAlways)); }
529 
CreateAlways(CFSTR fileName,DWORD flagsAndAttributes)530 bool COutFile::CreateAlways(CFSTR fileName, DWORD flagsAndAttributes)
531   { return Open(fileName, FILE_SHARE_READ, GetCreationDisposition(true), flagsAndAttributes); }
532 
SetTime(const FILETIME * cTime,const FILETIME * aTime,const FILETIME * mTime)533 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw()
534   { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); }
535 
SetMTime(const FILETIME * mTime)536 bool COutFile::SetMTime(const FILETIME *mTime) throw() {  return SetTime(NULL, NULL, mTime); }
537 
WritePart(const void * data,UInt32 size,UInt32 & processedSize)538 bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw()
539 {
540   if (size > kChunkSizeMax)
541     size = kChunkSizeMax;
542   DWORD processedLoc = 0;
543   bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL));
544   processedSize = (UInt32)processedLoc;
545   return res;
546 }
547 
Write(const void * data,UInt32 size,UInt32 & processedSize)548 bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) throw()
549 {
550   processedSize = 0;
551   do
552   {
553     UInt32 processedLoc = 0;
554     bool res = WritePart(data, size, processedLoc);
555     processedSize += processedLoc;
556     if (!res)
557       return false;
558     if (processedLoc == 0)
559       return true;
560     data = (const void *)((const unsigned char *)data + processedLoc);
561     size -= processedLoc;
562   }
563   while (size != 0);
564   return true;
565 }
566 
WriteFull(const void * data,size_t size)567 bool COutFile::WriteFull(const void *data, size_t size) throw()
568 {
569   do
570   {
571     UInt32 processedLoc = 0;
572     const UInt32 sizeCur = (size > kChunkSizeMax ? kChunkSizeMax : (UInt32)size);
573     if (!WritePart(data, sizeCur, processedLoc))
574       return false;
575     if (processedLoc == 0)
576       return (size == 0);
577     data = (const void *)((const unsigned char *)data + processedLoc);
578     size -= processedLoc;
579   }
580   while (size != 0);
581   return true;
582 }
583 
SetEndOfFile()584 bool COutFile::SetEndOfFile() throw() { return BOOLToBool(::SetEndOfFile(_handle)); }
585 
SetLength(UInt64 length)586 bool COutFile::SetLength(UInt64 length) throw()
587 {
588   UInt64 newPosition;
589   if (!Seek(length, newPosition))
590     return false;
591   if (newPosition != length)
592     return false;
593   return SetEndOfFile();
594 }
595 
SetLength_KeepPosition(UInt64 length)596 bool COutFile::SetLength_KeepPosition(UInt64 length) throw()
597 {
598   UInt64 currentPos = 0;
599   if (!GetPosition(currentPos))
600     return false;
601   DWORD lastError = 0;
602   const bool result = SetLength(length);
603   if (!result)
604     lastError = GetLastError();
605   UInt64 currentPos2;
606   const bool result2 = Seek(currentPos, currentPos2);
607   if (lastError != 0)
608     SetLastError(lastError);
609   return (result && result2);
610 }
611 
612 }}}
613 
614 #else // _WIN32
615 
616 
617 // POSIX
618 
619 #include <fcntl.h>
620 #include <unistd.h>
621 
622 namespace NWindows {
623 namespace NFile {
624 
625 namespace NDir {
626 bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
627 }
628 
629 namespace NIO {
630 
OpenBinary(const char * name,int flags)631 bool CFileBase::OpenBinary(const char *name, int flags)
632 {
633   #ifdef O_BINARY
634   flags |= O_BINARY;
635   #endif
636 
637   Close();
638   _handle = ::open(name, flags, 0666);
639   return _handle != -1;
640 
641   /*
642   if (_handle == -1)
643     return false;
644   if (IsString1PrefixedByString2(name, "/dev/"))
645   {
646     // /dev/sda
647     // IsDeviceFile = true; // for debug
648     // SizeDefined = false;
649     // SizeDefined = (GetDeviceSize_InBytes(Size) == 0);
650   }
651   return true;
652   */
653 }
654 
Close()655 bool CFileBase::Close()
656 {
657   if (_handle == -1)
658     return true;
659   if (close(_handle) != 0)
660     return false;
661   _handle = -1;
662   /*
663   IsDeviceFile = false;
664   SizeDefined = false;
665   */
666   return true;
667 }
668 
GetLength(UInt64 & length) const669 bool CFileBase::GetLength(UInt64 &length) const
670 {
671   length = 0;
672   // length = (UInt64)(Int64)-1; // for debug
673   const off_t curPos = seekToCur();
674   if (curPos == -1)
675     return false;
676   const off_t lengthTemp = seek(0, SEEK_END);
677   seek(curPos, SEEK_SET);
678   length = (UInt64)lengthTemp;
679 
680   /*
681   // 22.00:
682   if (lengthTemp == 1)
683   if (IsDeviceFile && SizeDefined)
684   {
685     length = Size;
686     return true;
687   }
688   */
689 
690   return (lengthTemp != -1);
691 }
692 
seek(off_t distanceToMove,int moveMethod) const693 off_t CFileBase::seek(off_t distanceToMove, int moveMethod) const
694 {
695   /*
696   if (IsDeviceFile && SizeDefined && moveMethod == SEEK_END)
697   {
698     printf("\n seek : IsDeviceFile moveMethod = %d distanceToMove = %ld\n", moveMethod, distanceToMove);
699     distanceToMove += Size;
700     moveMethod = SEEK_SET;
701   }
702   */
703 
704   // printf("\nCFileBase::seek() moveMethod = %d, distanceToMove = %lld", moveMethod, (long long)distanceToMove);
705   // off_t res = ::lseek(_handle, distanceToMove, moveMethod);
706   // printf("\n lseek : moveMethod = %d distanceToMove = %ld\n", moveMethod, distanceToMove);
707   return ::lseek(_handle, distanceToMove, moveMethod);
708   // return res;
709 }
710 
seekToBegin() const711 off_t CFileBase::seekToBegin() const throw()
712 {
713   return seek(0, SEEK_SET);
714 }
715 
seekToCur() const716 off_t CFileBase::seekToCur() const throw()
717 {
718   return seek(0, SEEK_CUR);
719 }
720 
721 /*
722 bool CFileBase::SeekToBegin() const throw()
723 {
724   return (::seek(0, SEEK_SET) != -1);
725 }
726 */
727 
728 
729 /////////////////////////
730 // CInFile
731 
Open(const char * name)732 bool CInFile::Open(const char *name)
733 {
734   return CFileBase::OpenBinary(name, O_RDONLY);
735 }
736 
OpenShared(const char * name,bool)737 bool CInFile::OpenShared(const char *name, bool)
738 {
739   return Open(name);
740 }
741 
742 
743 /*
744 int CFileBase::my_ioctl_BLKGETSIZE64(unsigned long long *numBlocks)
745 {
746   // we can read "/sys/block/sda/size" "/sys/block/sda/sda1/size" - partition
747   // #include <linux/fs.h>
748   return ioctl(_handle, BLKGETSIZE64, numBlocks);
749   // in block size
750 }
751 
752 int CFileBase::GetDeviceSize_InBytes(UInt64 &size)
753 {
754   size = 0;
755   unsigned long long numBlocks;
756   int res = my_ioctl_BLKGETSIZE64(&numBlocks);
757   if (res == 0)
758     size = numBlocks; // another blockSize s possible?
759   printf("\nGetDeviceSize_InBytes res = %d, size = %lld\n", res, (long long)size);
760   return res;
761 }
762 */
763 
764 /*
765 On Linux (32-bit and 64-bit):
766 read(), write() (and similar system calls) will transfer at most
767 0x7ffff000 = (2GiB - 4 KiB) bytes, returning the number of bytes actually transferred.
768 */
769 
770 static const size_t kChunkSizeMax = ((size_t)1 << 22);
771 
read_part(void * data,size_t size)772 ssize_t CInFile::read_part(void *data, size_t size) throw()
773 {
774   if (size > kChunkSizeMax)
775     size = kChunkSizeMax;
776   return ::read(_handle, data, size);
777 }
778 
ReadFull(void * data,size_t size,size_t & processed)779 bool CInFile::ReadFull(void *data, size_t size, size_t &processed) throw()
780 {
781   processed = 0;
782   do
783   {
784     const ssize_t res = read_part(data, size);
785     if (res < 0)
786       return false;
787     if (res == 0)
788       break;
789     data = (void *)((unsigned char *)data + (size_t)res);
790     size -= (size_t)res;
791     processed += (size_t)res;
792   }
793   while (size > 0);
794   return true;
795 }
796 
797 
798 /////////////////////////
799 // COutFile
800 
Create(const char * name,bool createAlways)801 bool COutFile::Create(const char *name, bool createAlways)
802 {
803   Path = name; // change it : set it only if open is success.
804   if (createAlways)
805   {
806     Close();
807     _handle = ::creat(name, 0666);
808     return _handle != -1;
809   }
810   return OpenBinary(name, O_CREAT | O_EXCL | O_WRONLY);
811 }
812 
Open(const char * name,DWORD creationDisposition)813 bool COutFile::Open(const char *name, DWORD creationDisposition)
814 {
815   UNUSED_VAR(creationDisposition) // FIXME
816   return Create(name, false);
817 }
818 
write_part(const void * data,size_t size)819 ssize_t COutFile::write_part(const void *data, size_t size) throw()
820 {
821   if (size > kChunkSizeMax)
822     size = kChunkSizeMax;
823   return ::write(_handle, data, size);
824 }
825 
write_full(const void * data,size_t size,size_t & processed)826 ssize_t COutFile::write_full(const void *data, size_t size, size_t &processed) throw()
827 {
828   processed = 0;
829   do
830   {
831     const ssize_t res = write_part(data, size);
832     if (res < 0)
833       return res;
834     if (res == 0)
835       break;
836     data = (const void *)((const unsigned char *)data + (size_t)res);
837     size -= (size_t)res;
838     processed += (size_t)res;
839   }
840   while (size > 0);
841   return (ssize_t)processed;
842 }
843 
SetLength(UInt64 length)844 bool COutFile::SetLength(UInt64 length) throw()
845 {
846   const off_t len2 = (off_t)length;
847   if ((Int64)length != len2)
848   {
849     SetLastError(EFBIG);
850     return false;
851   }
852   // The value of the seek pointer shall not be modified by a call to ftruncate().
853   int iret = ftruncate(_handle, len2);
854   return (iret == 0);
855 }
856 
Close()857 bool COutFile::Close()
858 {
859   bool res = CFileBase::Close();
860   if (!res)
861     return res;
862   if (CTime_defined || ATime_defined || MTime_defined)
863   {
864     /* bool res2 = */ NWindows::NFile::NDir::SetDirTime(Path,
865         CTime_defined ? &CTime : NULL,
866         ATime_defined ? &ATime : NULL,
867         MTime_defined ? &MTime : NULL);
868   }
869   return res;
870 }
871 
SetTime(const CFiTime * cTime,const CFiTime * aTime,const CFiTime * mTime)872 bool COutFile::SetTime(const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) throw()
873 {
874   // On some OS (cygwin, MacOSX ...), you must close the file before updating times
875   // return true;
876 
877   if (cTime) { CTime = *cTime; CTime_defined = true; } else CTime_defined = false;
878   if (aTime) { ATime = *aTime; ATime_defined = true; } else ATime_defined = false;
879   if (mTime) { MTime = *mTime; MTime_defined = true; } else MTime_defined = false;
880   return true;
881 
882   /*
883   struct timespec times[2];
884   UNUSED_VAR(cTime)
885   if (!aTime && !mTime)
886     return true;
887   bool needChange;
888   needChange  = FiTime_To_timespec(aTime, times[0]);
889   needChange |= FiTime_To_timespec(mTime, times[1]);
890   if (!needChange)
891     return true;
892   return futimens(_handle, times) == 0;
893   */
894 }
895 
SetMTime(const CFiTime * mTime)896 bool COutFile::SetMTime(const CFiTime *mTime) throw()
897 {
898   if (mTime) { MTime = *mTime; MTime_defined = true; } else MTime_defined = false;
899   return true;
900 }
901 
902 }}}
903 
904 
905 #endif
906