xref: /aosp_15_r20/external/lzma/CPP/Windows/FileIO.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // Windows/FileIO.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifdef Z7_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   const 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 Z7_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 Z7_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 Z7_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 Z7_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 0
145   if (!IsStdStream)
146 #endif
147   {
148     if (!::CloseHandle(_handle))
149       return false;
150   }
151 #if 0
152   IsStdStream = false;
153   IsStdPipeStream = false;
154 #endif
155   _handle = INVALID_HANDLE_VALUE;
156   return true;
157 }
158 
GetLength(UInt64 & length) const159 bool CFileBase::GetLength(UInt64 &length) const throw()
160 {
161   #ifdef Z7_DEVICE_FILE
162   if (IsDeviceFile && SizeDefined)
163   {
164     length = Size;
165     return true;
166   }
167   #endif
168 
169   DWORD high = 0;
170   const DWORD low = ::GetFileSize(_handle, &high);
171   if (low == INVALID_FILE_SIZE)
172     if (::GetLastError() != NO_ERROR)
173       return false;
174   length = (((UInt64)high) << 32) + low;
175   return true;
176 
177   /*
178   LARGE_INTEGER fileSize;
179   // GetFileSizeEx() is unsupported in 98/ME/NT, and supported in Win2000+
180   if (!GetFileSizeEx(_handle, &fileSize))
181     return false;
182   length = (UInt64)fileSize.QuadPart;
183   return true;
184   */
185 }
186 
187 
188 /* Specification for SetFilePointer():
189 
190    If a new file pointer is a negative value,
191    {
192      the function fails,
193      the file pointer is not moved,
194      the code returned by GetLastError() is ERROR_NEGATIVE_SEEK.
195    }
196 
197    If the hFile handle is opened with the FILE_FLAG_NO_BUFFERING flag set
198    {
199      an application can move the file pointer only to sector-aligned positions.
200      A sector-aligned position is a position that is a whole number multiple of
201      the volume sector size.
202      An application can obtain a volume sector size by calling the GetDiskFreeSpace.
203    }
204 
205    It is not an error to set a file pointer to a position beyond the end of the file.
206    The size of the file does not increase until you call the SetEndOfFile, WriteFile, or WriteFileEx function.
207 
208    If the return value is INVALID_SET_FILE_POINTER and if lpDistanceToMoveHigh is non-NULL,
209    an application must call GetLastError to determine whether or not the function has succeeded or failed.
210 */
211 
GetPosition(UInt64 & position) const212 bool CFileBase::GetPosition(UInt64 &position) const throw()
213 {
214   LONG high = 0;
215   const DWORD low = ::SetFilePointer(_handle, 0, &high, FILE_CURRENT);
216   if (low == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
217   {
218     // for error case we can set (position)  to (-1) or (0) or leave (position) unchanged.
219     // position = (UInt64)(Int64)-1; // for debug
220     position = 0;
221     return false;
222   }
223   position = (((UInt64)(UInt32)high) << 32) + low;
224   return true;
225   // we don't want recursed GetPosition()
226   // return Seek(0, FILE_CURRENT, position);
227 }
228 
Seek(Int64 distanceToMove,DWORD moveMethod,UInt64 & newPosition) const229 bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const throw()
230 {
231   #ifdef Z7_DEVICE_FILE
232   if (IsDeviceFile && SizeDefined && moveMethod == FILE_END)
233   {
234     distanceToMove += Size;
235     moveMethod = FILE_BEGIN;
236   }
237   #endif
238 
239   LONG high = (LONG)(distanceToMove >> 32);
240   const DWORD low = ::SetFilePointer(_handle, (LONG)(distanceToMove & 0xFFFFFFFF), &high, moveMethod);
241   if (low == INVALID_SET_FILE_POINTER)
242   {
243     const DWORD lastError = ::GetLastError();
244     if (lastError != NO_ERROR)
245     {
246       // 21.07: we set (newPosition) to real position even after error.
247       GetPosition(newPosition);
248       SetLastError(lastError); // restore LastError
249       return false;
250     }
251   }
252   newPosition = (((UInt64)(UInt32)high) << 32) + low;
253   return true;
254 }
255 
Seek(UInt64 position,UInt64 & newPosition) const256 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) const throw()
257 {
258   return Seek((Int64)position, FILE_BEGIN, newPosition);
259 }
260 
SeekToBegin() const261 bool CFileBase::SeekToBegin() const throw()
262 {
263   UInt64 newPosition = 0;
264   return Seek(0, newPosition) && (newPosition == 0);
265 }
266 
SeekToEnd(UInt64 & newPosition) const267 bool CFileBase::SeekToEnd(UInt64 &newPosition) const throw()
268 {
269   return Seek(0, FILE_END, newPosition);
270 }
271 
272 // ---------- CInFile ---------
273 
274 #ifdef Z7_DEVICE_FILE
275 
CorrectDeviceSize()276 void CInFile::CorrectDeviceSize()
277 {
278   // maybe we must decrease kClusterSize to 1 << 12, if we want correct size at tail
279   const UInt32 kClusterSize = 1 << 14;
280   UInt64 pos = Size & ~(UInt64)(kClusterSize - 1);
281   UInt64 realNewPosition;
282   if (!Seek(pos, realNewPosition))
283     return;
284   Byte *buf = (Byte *)MidAlloc(kClusterSize);
285 
286   bool needbackward = true;
287 
288   for (;;)
289   {
290     UInt32 processed = 0;
291     // up test is slow for "PhysicalDrive".
292     // processed size for latest block for "PhysicalDrive0" is 0.
293     if (!Read1(buf, kClusterSize, processed))
294       break;
295     if (processed == 0)
296       break;
297     needbackward = false;
298     Size = pos + processed;
299     if (processed != kClusterSize)
300       break;
301     pos += kClusterSize;
302   }
303 
304   if (needbackward && pos != 0)
305   {
306     pos -= kClusterSize;
307     for (;;)
308     {
309       // break;
310       if (!Seek(pos, realNewPosition))
311         break;
312       if (!buf)
313       {
314         buf = (Byte *)MidAlloc(kClusterSize);
315         if (!buf)
316           break;
317       }
318       UInt32 processed = 0;
319       // that code doesn't work for "PhysicalDrive0"
320       if (!Read1(buf, kClusterSize, processed))
321         break;
322       if (processed != 0)
323       {
324         Size = pos + processed;
325         break;
326       }
327       if (pos == 0)
328         break;
329       pos -= kClusterSize;
330     }
331   }
332   MidFree(buf);
333 }
334 
335 
CalcDeviceSize(CFSTR s)336 void CInFile::CalcDeviceSize(CFSTR s)
337 {
338   SizeDefined = false;
339   Size = 0;
340   if (_handle == INVALID_HANDLE_VALUE || !IsDeviceFile)
341     return;
342   #ifdef UNDER_CE
343 
344   SizeDefined = true;
345   Size = 128 << 20;
346 
347   #else
348 
349   PARTITION_INFORMATION partInfo;
350   bool needCorrectSize = true;
351 
352   /*
353     WinXP 64-bit:
354 
355     HDD \\.\PhysicalDrive0 (MBR):
356       GetPartitionInfo == GeometryEx :  corrrect size? (includes tail)
357       Geometry   :  smaller than GeometryEx (no tail, maybe correct too?)
358       MyGetDiskFreeSpace : FAIL
359       Size correction is slow and block size (kClusterSize) must be small?
360 
361     HDD partition \\.\N: (NTFS):
362       MyGetDiskFreeSpace   :  Size of NTFS clusters. Same size can be calculated after correction
363       GetPartitionInfo     :  size of partition data: NTFS clusters + TAIL; TAIL contains extra empty sectors and copy of first sector of NTFS
364       Geometry / CdRomGeometry / GeometryEx :  size of HDD (not that partition)
365 
366     CD-ROM drive (ISO):
367       MyGetDiskFreeSpace   :  correct size. Same size can be calculated after correction
368       Geometry == CdRomGeometry  :  smaller than corrrect size
369       GetPartitionInfo == GeometryEx :  larger than corrrect size
370 
371     Floppy \\.\a: (FAT):
372       Geometry :  correct size.
373       CdRomGeometry / GeometryEx / GetPartitionInfo / MyGetDiskFreeSpace - FAIL
374       correction works OK for FAT.
375       correction works OK for non-FAT, if kClusterSize = 512.
376   */
377 
378   if (GetPartitionInfo(&partInfo))
379   {
380     Size = (UInt64)partInfo.PartitionLength.QuadPart;
381     SizeDefined = true;
382     needCorrectSize = false;
383     if ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\' && (s)[5] == ':' && (s)[6] == 0)
384     {
385       FChar path[4] = { s[4], ':', '\\', 0 };
386       UInt64 clusterSize, totalSize, freeSize;
387       if (NSystem::MyGetDiskFreeSpace(path, clusterSize, totalSize, freeSize))
388         Size = totalSize;
389       else
390         needCorrectSize = true;
391     }
392   }
393 
394   if (!SizeDefined)
395   {
396     my_DISK_GEOMETRY_EX geomEx;
397     SizeDefined = GetGeometryEx(&geomEx);
398     if (SizeDefined)
399       Size = (UInt64)geomEx.DiskSize.QuadPart;
400     else
401     {
402       DISK_GEOMETRY geom;
403       SizeDefined = GetGeometry(&geom);
404       if (!SizeDefined)
405         SizeDefined = GetCdRomGeometry(&geom);
406       if (SizeDefined)
407         Size = (UInt64)geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector;
408     }
409   }
410 
411   if (needCorrectSize && SizeDefined && Size != 0)
412   {
413     CorrectDeviceSize();
414     SeekToBegin();
415   }
416 
417   // SeekToBegin();
418   #endif
419 }
420 
421 // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 &&
422 
423 #define MY_DEVICE_EXTRA_CODE \
424   IsDeviceFile = IsDevicePath(fileName); \
425   CalcDeviceSize(fileName);
426 #else
427 #define MY_DEVICE_EXTRA_CODE
428 #endif
429 
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)430 bool CInFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
431 {
432   DWORD desiredAccess = GENERIC_READ;
433 
434   #ifdef _WIN32
435   if (PreserveATime)
436     desiredAccess |= FILE_WRITE_ATTRIBUTES;
437   #endif
438 
439   bool res = Create(fileName, desiredAccess, shareMode, creationDisposition, flagsAndAttributes);
440 
441   #ifdef _WIN32
442   if (res && PreserveATime)
443   {
444     FILETIME ft;
445     ft.dwHighDateTime = ft.dwLowDateTime = 0xFFFFFFFF;
446     ::SetFileTime(_handle, NULL, &ft, NULL);
447   }
448   #endif
449 
450   MY_DEVICE_EXTRA_CODE
451   return res;
452 }
453 
OpenShared(CFSTR fileName,bool shareForWrite)454 bool CInFile::OpenShared(CFSTR fileName, bool shareForWrite)
455 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
456 
Open(CFSTR fileName)457 bool CInFile::Open(CFSTR fileName)
458   { return OpenShared(fileName, false); }
459 
460 // ReadFile and WriteFile functions in Windows have BUG:
461 // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
462 // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
463 // (Insufficient system resources exist to complete the requested service).
464 
465 // Probably in some version of Windows there are problems with other sizes:
466 // for 32 MB (maybe also for 16 MB).
467 // And message can be "Network connection was lost"
468 
469 static const UInt32 kChunkSizeMax = 1 << 22;
470 
Read1(void * data,UInt32 size,UInt32 & processedSize)471 bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize) throw()
472 {
473   DWORD processedLoc = 0;
474   const bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL));
475   processedSize = (UInt32)processedLoc;
476   return res;
477 }
478 
ReadPart(void * data,UInt32 size,UInt32 & processedSize)479 bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) throw()
480 {
481 #if 0
482   const UInt32 chunkSizeMax = (0 || IsStdStream) ? (1 << 20) : kChunkSizeMax;
483   if (size > chunkSizeMax)
484       size = chunkSizeMax;
485 #else
486   if (size > kChunkSizeMax)
487       size = kChunkSizeMax;
488 #endif
489   return Read1(data, size, processedSize);
490 }
491 
Read(void * data,UInt32 size,UInt32 & processedSize)492 bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) throw()
493 {
494   processedSize = 0;
495   do
496   {
497     UInt32 processedLoc = 0;
498     const bool res = ReadPart(data, size, processedLoc);
499     processedSize += processedLoc;
500     if (!res)
501       return false;
502     if (processedLoc == 0)
503       return true;
504     data = (void *)((Byte *)data + processedLoc);
505     size -= processedLoc;
506   }
507   while (size);
508   return true;
509 }
510 
ReadFull(void * data,size_t size,size_t & processedSize)511 bool CInFile::ReadFull(void *data, size_t size, size_t &processedSize) throw()
512 {
513   processedSize = 0;
514   do
515   {
516     UInt32 processedLoc = 0;
517     const UInt32 sizeLoc = (size > kChunkSizeMax ? (UInt32)kChunkSizeMax : (UInt32)size);
518     const bool res = Read1(data, sizeLoc, processedLoc);
519     processedSize += processedLoc;
520     if (!res)
521       return false;
522     if (processedLoc == 0)
523       return true;
524     data = (void *)((Byte *)data + processedLoc);
525     size -= processedLoc;
526   }
527   while (size);
528   return true;
529 }
530 
531 // ---------- COutFile ---------
532 
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)533 bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
534   { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
535 
Open_Disposition(CFSTR fileName,DWORD creationDisposition)536 bool COutFile::Open_Disposition(CFSTR fileName, DWORD creationDisposition)
537   { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
538 
Create_ALWAYS_with_Attribs(CFSTR fileName,DWORD flagsAndAttributes)539 bool COutFile::Create_ALWAYS_with_Attribs(CFSTR fileName, DWORD flagsAndAttributes)
540   { return Open(fileName, FILE_SHARE_READ, CREATE_ALWAYS, flagsAndAttributes); }
541 
SetTime(const FILETIME * cTime,const FILETIME * aTime,const FILETIME * mTime)542 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw()
543   { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); }
544 
SetMTime(const FILETIME * mTime)545 bool COutFile::SetMTime(const FILETIME *mTime) throw() {  return SetTime(NULL, NULL, mTime); }
546 
WritePart(const void * data,UInt32 size,UInt32 & processedSize)547 bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw()
548 {
549   if (size > kChunkSizeMax)
550     size = kChunkSizeMax;
551   DWORD processedLoc = 0;
552   bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL));
553   processedSize = (UInt32)processedLoc;
554   return res;
555 }
556 
Write(const void * data,UInt32 size,UInt32 & processedSize)557 bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) throw()
558 {
559   processedSize = 0;
560   do
561   {
562     UInt32 processedLoc = 0;
563     const bool res = WritePart(data, size, processedLoc);
564     processedSize += processedLoc;
565     if (!res)
566       return false;
567     if (processedLoc == 0)
568       return true;
569     data = (const void *)((const Byte *)data + processedLoc);
570     size -= processedLoc;
571   }
572   while (size);
573   return true;
574 }
575 
WriteFull(const void * data,size_t size)576 bool COutFile::WriteFull(const void *data, size_t size) throw()
577 {
578   do
579   {
580     UInt32 processedLoc = 0;
581     const UInt32 sizeCur = (size > kChunkSizeMax ? kChunkSizeMax : (UInt32)size);
582     if (!WritePart(data, sizeCur, processedLoc))
583       return false;
584     if (processedLoc == 0)
585       return (size == 0);
586     data = (const void *)((const Byte *)data + processedLoc);
587     size -= processedLoc;
588   }
589   while (size);
590   return true;
591 }
592 
SetEndOfFile()593 bool COutFile::SetEndOfFile() throw() { return BOOLToBool(::SetEndOfFile(_handle)); }
594 
SetLength(UInt64 length)595 bool COutFile::SetLength(UInt64 length) throw()
596 {
597   UInt64 newPosition;
598   if (!Seek(length, newPosition))
599     return false;
600   if (newPosition != length)
601     return false;
602   return SetEndOfFile();
603 }
604 
SetLength_KeepPosition(UInt64 length)605 bool COutFile::SetLength_KeepPosition(UInt64 length) throw()
606 {
607   UInt64 currentPos = 0;
608   if (!GetPosition(currentPos))
609     return false;
610   DWORD lastError = 0;
611   const bool result = SetLength(length);
612   if (!result)
613     lastError = GetLastError();
614   UInt64 currentPos2;
615   const bool result2 = Seek(currentPos, currentPos2);
616   if (lastError != 0)
617     SetLastError(lastError);
618   return (result && result2);
619 }
620 
621 }}}
622 
623 #else // _WIN32
624 
625 
626 // POSIX
627 
628 #include <fcntl.h>
629 #include <unistd.h>
630 
631 namespace NWindows {
632 namespace NFile {
633 
634 namespace NDir {
635 bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
636 }
637 
638 namespace NIO {
639 
OpenBinary(const char * name,int flags,mode_t mode)640 bool CFileBase::OpenBinary(const char *name, int flags, mode_t mode)
641 {
642   #ifdef O_BINARY
643   flags |= O_BINARY;
644   #endif
645 
646   Close();
647   _handle = ::open(name, flags, mode);
648   return _handle != -1;
649 
650   /*
651   if (_handle == -1)
652     return false;
653   if (IsString1PrefixedByString2(name, "/dev/"))
654   {
655     // /dev/sda
656     // IsDeviceFile = true; // for debug
657     // SizeDefined = false;
658     // SizeDefined = (GetDeviceSize_InBytes(Size) == 0);
659   }
660   return true;
661   */
662 }
663 
Close()664 bool CFileBase::Close()
665 {
666   if (_handle == -1)
667     return true;
668   if (close(_handle) != 0)
669     return false;
670   _handle = -1;
671   /*
672   IsDeviceFile = false;
673   SizeDefined = false;
674   */
675   return true;
676 }
677 
GetLength(UInt64 & length) const678 bool CFileBase::GetLength(UInt64 &length) const
679 {
680   length = 0;
681   // length = (UInt64)(Int64)-1; // for debug
682   const off_t curPos = seekToCur();
683   if (curPos == -1)
684     return false;
685   const off_t lengthTemp = seek(0, SEEK_END);
686   seek(curPos, SEEK_SET);
687   length = (UInt64)lengthTemp;
688 
689   /*
690   // 22.00:
691   if (lengthTemp == 1)
692   if (IsDeviceFile && SizeDefined)
693   {
694     length = Size;
695     return true;
696   }
697   */
698 
699   return (lengthTemp != -1);
700 }
701 
seek(off_t distanceToMove,int moveMethod) const702 off_t CFileBase::seek(off_t distanceToMove, int moveMethod) const
703 {
704   /*
705   if (IsDeviceFile && SizeDefined && moveMethod == SEEK_END)
706   {
707     printf("\n seek : IsDeviceFile moveMethod = %d distanceToMove = %ld\n", moveMethod, distanceToMove);
708     distanceToMove += Size;
709     moveMethod = SEEK_SET;
710   }
711   */
712 
713   // printf("\nCFileBase::seek() moveMethod = %d, distanceToMove = %lld", moveMethod, (long long)distanceToMove);
714   // off_t res = ::lseek(_handle, distanceToMove, moveMethod);
715   // printf("\n lseek : moveMethod = %d distanceToMove = %ld\n", moveMethod, distanceToMove);
716   return ::lseek(_handle, distanceToMove, moveMethod);
717   // return res;
718 }
719 
seekToBegin() const720 off_t CFileBase::seekToBegin() const throw()
721 {
722   return seek(0, SEEK_SET);
723 }
724 
seekToCur() const725 off_t CFileBase::seekToCur() const throw()
726 {
727   return seek(0, SEEK_CUR);
728 }
729 
730 /*
731 bool CFileBase::SeekToBegin() const throw()
732 {
733   return (::seek(0, SEEK_SET) != -1);
734 }
735 */
736 
737 
738 /////////////////////////
739 // CInFile
740 
Open(const char * name)741 bool CInFile::Open(const char *name)
742 {
743   return CFileBase::OpenBinary(name, O_RDONLY);
744 }
745 
OpenShared(const char * name,bool)746 bool CInFile::OpenShared(const char *name, bool)
747 {
748   return Open(name);
749 }
750 
751 
752 /*
753 int CFileBase::my_ioctl_BLKGETSIZE64(unsigned long long *numBlocks)
754 {
755   // we can read "/sys/block/sda/size" "/sys/block/sda/sda1/size" - partition
756   // #include <linux/fs.h>
757   return ioctl(_handle, BLKGETSIZE64, numBlocks);
758   // in block size
759 }
760 
761 int CFileBase::GetDeviceSize_InBytes(UInt64 &size)
762 {
763   size = 0;
764   unsigned long long numBlocks;
765   int res = my_ioctl_BLKGETSIZE64(&numBlocks);
766   if (res == 0)
767     size = numBlocks; // another blockSize s possible?
768   printf("\nGetDeviceSize_InBytes res = %d, size = %lld\n", res, (long long)size);
769   return res;
770 }
771 */
772 
773 /*
774 On Linux (32-bit and 64-bit):
775 read(), write() (and similar system calls) will transfer at most
776 0x7ffff000 = (2GiB - 4 KiB) bytes, returning the number of bytes actually transferred.
777 */
778 
779 static const size_t kChunkSizeMax = ((size_t)1 << 22);
780 
read_part(void * data,size_t size)781 ssize_t CInFile::read_part(void *data, size_t size) throw()
782 {
783   if (size > kChunkSizeMax)
784     size = kChunkSizeMax;
785   return ::read(_handle, data, size);
786 }
787 
ReadFull(void * data,size_t size,size_t & processed)788 bool CInFile::ReadFull(void *data, size_t size, size_t &processed) throw()
789 {
790   processed = 0;
791   do
792   {
793     const ssize_t res = read_part(data, size);
794     if (res < 0)
795       return false;
796     if (res == 0)
797       break;
798     data = (void *)((Byte *)data + (size_t)res);
799     processed += (size_t)res;
800     size -= (size_t)res;
801   }
802   while (size);
803   return true;
804 }
805 
806 
807 /////////////////////////
808 // COutFile
809 
OpenBinary_forWrite_oflag(const char * name,int oflag)810 bool COutFile::OpenBinary_forWrite_oflag(const char *name, int oflag)
811 {
812   Path = name; // change it : set it only if open is success.
813   return OpenBinary(name, oflag, mode_for_Create);
814 }
815 
816 
817 /*
818   windows           exist  non-exist  posix
819   CREATE_NEW        Fail   Create     O_CREAT | O_EXCL
820   CREATE_ALWAYS     Trunc  Create     O_CREAT | O_TRUNC
821   OPEN_ALWAYS       Open   Create     O_CREAT
822   OPEN_EXISTING     Open   Fail       0
823   TRUNCATE_EXISTING Trunc  Fail       O_TRUNC ???
824 
825   // O_CREAT = If the file exists, this flag has no effect except as noted under O_EXCL below.
826   // If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
827   // O_TRUNC : If the file exists and the file is successfully opened, its length shall be truncated to 0.
828 */
Open_EXISTING(const char * name)829 bool COutFile::Open_EXISTING(const char *name)
830   { return OpenBinary_forWrite_oflag(name, O_WRONLY); }
Create_ALWAYS(const char * name)831 bool COutFile::Create_ALWAYS(const char *name)
832   { return OpenBinary_forWrite_oflag(name, O_WRONLY | O_CREAT | O_TRUNC); }
Create_NEW(const char * name)833 bool COutFile::Create_NEW(const char *name)
834   { return OpenBinary_forWrite_oflag(name, O_WRONLY | O_CREAT | O_EXCL);  }
Create_ALWAYS_or_Open_ALWAYS(const char * name,bool createAlways)835 bool COutFile::Create_ALWAYS_or_Open_ALWAYS(const char *name, bool createAlways)
836 {
837   return OpenBinary_forWrite_oflag(name,
838       createAlways ?
839         O_WRONLY | O_CREAT | O_TRUNC :
840         O_WRONLY | O_CREAT);
841 }
842 /*
843 bool COutFile::Create_ALWAYS_or_NEW(const char *name, bool createAlways)
844 {
845   return OpenBinary_forWrite_oflag(name,
846       createAlways ?
847         O_WRONLY | O_CREAT | O_TRUNC :
848         O_WRONLY | O_CREAT | O_EXCL);
849 }
850 bool COutFile::Open_Disposition(const char *name, DWORD creationDisposition)
851 {
852   int flag;
853   switch (creationDisposition)
854   {
855     case CREATE_NEW:        flag = O_WRONLY | O_CREAT | O_EXCL;  break;
856     case CREATE_ALWAYS:     flag = O_WRONLY | O_CREAT | O_TRUNC;  break;
857     case OPEN_ALWAYS:       flag = O_WRONLY | O_CREAT;  break;
858     case OPEN_EXISTING:     flag = O_WRONLY;  break;
859     case TRUNCATE_EXISTING: flag = O_WRONLY | O_TRUNC; break;
860     default:
861       SetLastError(EINVAL);
862       return false;
863   }
864   return OpenBinary_forWrite_oflag(name, flag);
865 }
866 */
867 
write_part(const void * data,size_t size)868 ssize_t COutFile::write_part(const void *data, size_t size) throw()
869 {
870   if (size > kChunkSizeMax)
871     size = kChunkSizeMax;
872   return ::write(_handle, data, size);
873 }
874 
write_full(const void * data,size_t size,size_t & processed)875 ssize_t COutFile::write_full(const void *data, size_t size, size_t &processed) throw()
876 {
877   processed = 0;
878   do
879   {
880     const ssize_t res = write_part(data, size);
881     if (res < 0)
882       return res;
883     if (res == 0)
884       break;
885     data = (const void *)((const Byte *)data + (size_t)res);
886     processed += (size_t)res;
887     size -= (size_t)res;
888   }
889   while (size);
890   return (ssize_t)processed;
891 }
892 
SetLength(UInt64 length)893 bool COutFile::SetLength(UInt64 length) throw()
894 {
895   const off_t len2 = (off_t)length;
896   if ((Int64)length != len2)
897   {
898     SetLastError(EFBIG);
899     return false;
900   }
901   // The value of the seek pointer shall not be modified by a call to ftruncate().
902   const int iret = ftruncate(_handle, len2);
903   return (iret == 0);
904 }
905 
Close()906 bool COutFile::Close()
907 {
908   const bool res = CFileBase::Close();
909   if (!res)
910     return res;
911   if (CTime_defined || ATime_defined || MTime_defined)
912   {
913     /* bool res2 = */ NWindows::NFile::NDir::SetDirTime(Path,
914         CTime_defined ? &CTime : NULL,
915         ATime_defined ? &ATime : NULL,
916         MTime_defined ? &MTime : NULL);
917   }
918   return res;
919 }
920 
SetTime(const CFiTime * cTime,const CFiTime * aTime,const CFiTime * mTime)921 bool COutFile::SetTime(const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) throw()
922 {
923   // On some OS (cygwin, MacOSX ...), you must close the file before updating times
924   // return true;
925 
926   if (cTime) { CTime = *cTime; CTime_defined = true; } else CTime_defined = false;
927   if (aTime) { ATime = *aTime; ATime_defined = true; } else ATime_defined = false;
928   if (mTime) { MTime = *mTime; MTime_defined = true; } else MTime_defined = false;
929   return true;
930 
931   /*
932   struct timespec times[2];
933   UNUSED_VAR(cTime)
934   if (!aTime && !mTime)
935     return true;
936   bool needChange;
937   needChange  = FiTime_To_timespec(aTime, times[0]);
938   needChange |= FiTime_To_timespec(mTime, times[1]);
939   if (!needChange)
940     return true;
941   return futimens(_handle, times) == 0;
942   */
943 }
944 
SetMTime(const CFiTime * mTime)945 bool COutFile::SetMTime(const CFiTime *mTime) throw()
946 {
947   if (mTime) { MTime = *mTime; MTime_defined = true; } else MTime_defined = false;
948   return true;
949 }
950 
951 }}}
952 
953 
954 #endif
955