1 #include "rar.hpp"
2 
File()3 File::File()
4 {
5   hFile=FILE_BAD_HANDLE;
6   *FileName=0;
7   NewFile=false;
8   LastWrite=false;
9   HandleType=FILE_HANDLENORMAL;
10   SkipClose=false;
11   ErrorType=FILE_SUCCESS;
12   OpenShared=false;
13   AllowDelete=true;
14   AllowExceptions=true;
15   PreserveAtime=false;
16 #ifdef _WIN_ALL
17   NoSequentialRead=false;
18   CreateMode=FMF_UNDEFINED;
19 #endif
20   ReadErrorMode=FREM_ASK;
21   TruncatedAfterReadError=false;
22 }
23 
24 
~File()25 File::~File()
26 {
27   if (hFile!=FILE_BAD_HANDLE && !SkipClose)
28     if (NewFile)
29       Delete();
30     else
31       Close();
32 }
33 
34 
operator =(File & SrcFile)35 void File::operator = (File &SrcFile)
36 {
37   hFile=SrcFile.hFile;
38   NewFile=SrcFile.NewFile;
39   LastWrite=SrcFile.LastWrite;
40   HandleType=SrcFile.HandleType;
41   TruncatedAfterReadError=SrcFile.TruncatedAfterReadError;
42   wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
43   SrcFile.SkipClose=true;
44 }
45 
46 
Open(const wchar * Name,uint Mode)47 bool File::Open(const wchar *Name,uint Mode)
48 {
49   ErrorType=FILE_SUCCESS;
50   FileHandle hNewFile;
51   bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
52   bool UpdateMode=(Mode & FMF_UPDATE)!=0;
53   bool WriteMode=(Mode & FMF_WRITE)!=0;
54 #ifdef _WIN_ALL
55   uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
56   if (UpdateMode)
57     Access|=GENERIC_WRITE;
58   uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
59   if (OpenShared)
60     ShareMode|=FILE_SHARE_WRITE;
61   uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN;
62   FindData FD;
63   if (PreserveAtime)
64     Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime.
65   hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
66 
67   DWORD LastError;
68   if (hNewFile==FILE_BAD_HANDLE)
69   {
70     LastError=GetLastError();
71 
72     wchar LongName[NM];
73     if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
74     {
75       hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
76 
77       // For archive names longer than 260 characters first CreateFile
78       // (without \\?\) fails and sets LastError to 3 (access denied).
79       // We need the correct "file not found" error code to decide
80       // if we create a new archive or quit with "cannot create" error.
81       // So we need to check the error code after \\?\ CreateFile again,
82       // otherwise we'll fail to create new archives with long names.
83       // But we cannot simply assign the new code to LastError,
84       // because it would break "..\arcname.rar" relative names processing.
85       // First CreateFile returns the correct "file not found" code for such
86       // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
87       // dots as a directory name. So we check only for "file not found"
88       // error here and for other errors use the first CreateFile result.
89       if (GetLastError()==ERROR_FILE_NOT_FOUND)
90         LastError=ERROR_FILE_NOT_FOUND;
91     }
92   }
93   if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
94     ErrorType=FILE_NOTFOUND;
95   if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE)
96   {
97     FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification.
98     SetFileTime(hNewFile,NULL,&ft,NULL);
99   }
100 
101 #else
102   int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
103 #ifdef O_BINARY
104   flags|=O_BINARY;
105 #if defined(_AIX) && defined(_LARGE_FILE_API)
106   flags|=O_LARGEFILE;
107 #endif
108 #endif
109   // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+.
110 #if defined(O_NOATIME)
111   if (PreserveAtime)
112     flags|=O_NOATIME;
113 #endif
114   char NameA[NM];
115   WideToChar(Name,NameA,ASIZE(NameA));
116 
117   int handle=open(NameA,flags);
118 #ifdef LOCK_EX
119 
120 #ifdef _OSF_SOURCE
121   extern "C" int flock(int, int);
122 #endif
123   if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
124   {
125     close(handle);
126     return false;
127   }
128 
129 #endif
130   if (handle==-1)
131     hNewFile=FILE_BAD_HANDLE;
132   else
133   {
134 #ifdef FILE_USE_OPEN
135     hNewFile=handle;
136 #else
137     hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
138 #endif
139   }
140   if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
141     ErrorType=FILE_NOTFOUND;
142 #endif
143   NewFile=false;
144   HandleType=FILE_HANDLENORMAL;
145   SkipClose=false;
146   bool Success=hNewFile!=FILE_BAD_HANDLE;
147   if (Success)
148   {
149     hFile=hNewFile;
150     wcsncpyz(FileName,Name,ASIZE(FileName));
151     TruncatedAfterReadError=false;
152   }
153   return Success;
154 }
155 
156 
157 #if !defined(SFX_MODULE)
TOpen(const wchar * Name)158 void File::TOpen(const wchar *Name)
159 {
160   if (!WOpen(Name))
161     ErrHandler.Exit(RARX_OPEN);
162 }
163 #endif
164 
165 
WOpen(const wchar * Name)166 bool File::WOpen(const wchar *Name)
167 {
168   if (Open(Name))
169     return true;
170   ErrHandler.OpenErrorMsg(Name);
171   return false;
172 }
173 
174 
Create(const wchar * Name,uint Mode)175 bool File::Create(const wchar *Name,uint Mode)
176 {
177   // OpenIndiana based NAS and CIFS shares fail to set the file time if file
178   // was created in read+write mode and some data was written and not flushed
179   // before SetFileTime call. So we should use the write only mode if we plan
180   // SetFileTime call and do not need to read from file.
181   bool WriteMode=(Mode & FMF_WRITE)!=0;
182   bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
183 #ifdef _WIN_ALL
184   CreateMode=Mode;
185   uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
186   DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
187 
188   // Windows automatically removes dots and spaces in the end of file name,
189   // So we detect such names and process them with \\?\ prefix.
190   wchar *LastChar=PointToLastChar(Name);
191   bool Special=*LastChar=='.' || *LastChar==' ';
192 
193   if (Special && (Mode & FMF_STANDARDNAMES)==0)
194     hFile=FILE_BAD_HANDLE;
195   else
196     hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
197 
198   if (hFile==FILE_BAD_HANDLE)
199   {
200     wchar LongName[NM];
201     if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
202       hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
203   }
204 
205 #else
206   char NameA[NM];
207   WideToChar(Name,NameA,ASIZE(NameA));
208 #ifdef FILE_USE_OPEN
209   hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
210 #else
211   hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
212 #endif
213 #endif
214   NewFile=true;
215   HandleType=FILE_HANDLENORMAL;
216   SkipClose=false;
217   wcsncpyz(FileName,Name,ASIZE(FileName));
218   return hFile!=FILE_BAD_HANDLE;
219 }
220 
221 
222 #if !defined(SFX_MODULE)
TCreate(const wchar * Name,uint Mode)223 void File::TCreate(const wchar *Name,uint Mode)
224 {
225   if (!WCreate(Name,Mode))
226     ErrHandler.Exit(RARX_FATAL);
227 }
228 #endif
229 
230 
WCreate(const wchar * Name,uint Mode)231 bool File::WCreate(const wchar *Name,uint Mode)
232 {
233   if (Create(Name,Mode))
234     return true;
235   ErrHandler.CreateErrorMsg(Name);
236   return false;
237 }
238 
239 
Close()240 bool File::Close()
241 {
242   bool Success=true;
243 
244   if (hFile!=FILE_BAD_HANDLE)
245   {
246     if (!SkipClose)
247     {
248 #ifdef _WIN_ALL
249       // We use the standard system handle for stdout in Windows
250       // and it must not be closed here.
251       if (HandleType==FILE_HANDLENORMAL)
252         Success=CloseHandle(hFile)==TRUE;
253 #else
254 #ifdef FILE_USE_OPEN
255       Success=close(hFile)!=-1;
256 #else
257       Success=fclose(hFile)!=EOF;
258 #endif
259 #endif
260     }
261     hFile=FILE_BAD_HANDLE;
262   }
263   HandleType=FILE_HANDLENORMAL;
264   if (!Success && AllowExceptions)
265     ErrHandler.CloseError(FileName);
266   return Success;
267 }
268 
269 
Delete()270 bool File::Delete()
271 {
272   if (HandleType!=FILE_HANDLENORMAL)
273     return false;
274   if (hFile!=FILE_BAD_HANDLE)
275     Close();
276   if (!AllowDelete)
277     return false;
278   return DelFile(FileName);
279 }
280 
281 
Rename(const wchar * NewName)282 bool File::Rename(const wchar *NewName)
283 {
284   // No need to rename if names are already same.
285   bool Success=wcscmp(FileName,NewName)==0;
286 
287   if (!Success)
288     Success=RenameFile(FileName,NewName);
289 
290   if (Success)
291     wcsncpyz(FileName,NewName,ASIZE(FileName));
292 
293   return Success;
294 }
295 
296 
Write(const void * Data,size_t Size)297 bool File::Write(const void *Data,size_t Size)
298 {
299   if (Size==0)
300     return true;
301   if (HandleType==FILE_HANDLESTD)
302   {
303 #ifdef _WIN_ALL
304     hFile=GetStdHandle(STD_OUTPUT_HANDLE);
305 #else
306     // Cannot use the standard stdout here, because it already has wide orientation.
307     if (hFile==FILE_BAD_HANDLE)
308     {
309 #ifdef FILE_USE_OPEN
310       hFile=dup(STDOUT_FILENO); // Open new stdout stream.
311 #else
312       hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream.
313 #endif
314     }
315 #endif
316   }
317   bool Success;
318   while (1)
319   {
320     Success=false;
321 #ifdef _WIN_ALL
322     DWORD Written=0;
323     if (HandleType!=FILE_HANDLENORMAL)
324     {
325       // writing to stdout can fail in old Windows if data block is too large
326       const size_t MaxSize=0x4000;
327       for (size_t I=0;I<Size;I+=MaxSize)
328       {
329         Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE;
330         if (!Success)
331           break;
332       }
333     }
334     else
335       Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE;
336 #else
337 #ifdef FILE_USE_OPEN
338     ssize_t Written=write(hFile,Data,Size);
339     Success=Written==Size;
340 #else
341     int Written=fwrite(Data,1,Size,hFile);
342     Success=Written==Size && !ferror(hFile);
343 #endif
344 #endif
345     if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL)
346     {
347 #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
348       int ErrCode=GetLastError();
349       int64 FilePos=Tell();
350       uint64 FreeSize=GetFreeDisk(FileName);
351       SetLastError(ErrCode);
352       if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff)
353         ErrHandler.WriteErrorFAT(FileName);
354 #endif
355       if (ErrHandler.AskRepeatWrite(FileName,false))
356       {
357 #if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN)
358         clearerr(hFile);
359 #endif
360         if (Written<Size && Written>0)
361           Seek(Tell()-Written,SEEK_SET);
362         continue;
363       }
364       ErrHandler.WriteError(NULL,FileName);
365     }
366     break;
367   }
368   LastWrite=true;
369   return Success; // It can return false only if AllowExceptions is disabled.
370 }
371 
372 
Read(void * Data,size_t Size)373 int File::Read(void *Data,size_t Size)
374 {
375   if (TruncatedAfterReadError)
376     return 0;
377 
378   int64 FilePos=0; // Initialized only to suppress some compilers warning.
379 
380   if (ReadErrorMode==FREM_IGNORE)
381     FilePos=Tell();
382   int ReadSize;
383   while (true)
384   {
385     ReadSize=DirectRead(Data,Size);
386     if (ReadSize==-1)
387     {
388       ErrorType=FILE_READERROR;
389       if (AllowExceptions)
390         if (ReadErrorMode==FREM_IGNORE)
391         {
392           ReadSize=0;
393           for (size_t I=0;I<Size;I+=512)
394           {
395             Seek(FilePos+I,SEEK_SET);
396             size_t SizeToRead=Min(Size-I,512);
397             int ReadCode=DirectRead(Data,SizeToRead);
398             ReadSize+=(ReadCode==-1) ? 512:ReadCode;
399           }
400         }
401         else
402         {
403           bool Ignore=false,Retry=false,Quit=false;
404           if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL)
405           {
406             ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit);
407             if (Retry)
408               continue;
409           }
410           if (Ignore || ReadErrorMode==FREM_TRUNCATE)
411           {
412             TruncatedAfterReadError=true;
413             return 0;
414           }
415           ErrHandler.ReadError(FileName);
416         }
417     }
418     break;
419   }
420   return ReadSize; // It can return -1 only if AllowExceptions is disabled.
421 }
422 
423 
424 // Returns -1 in case of error.
DirectRead(void * Data,size_t Size)425 int File::DirectRead(void *Data,size_t Size)
426 {
427 #ifdef _WIN_ALL
428   const size_t MaxDeviceRead=20000;
429   const size_t MaxLockedRead=32768;
430 #endif
431   if (HandleType==FILE_HANDLESTD)
432   {
433 #ifdef _WIN_ALL
434 //    if (Size>MaxDeviceRead)
435 //      Size=MaxDeviceRead;
436     hFile=GetStdHandle(STD_INPUT_HANDLE);
437 #else
438 #ifdef FILE_USE_OPEN
439     hFile=STDIN_FILENO;
440 #else
441     hFile=stdin;
442 #endif
443 #endif
444   }
445 #ifdef _WIN_ALL
446   // For pipes like 'type file.txt | rar -si arcname' ReadFile may return
447   // data in small ~4KB blocks. It may slightly reduce the compression ratio.
448   DWORD Read;
449   if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
450   {
451     if (IsDevice() && Size>MaxDeviceRead)
452       return DirectRead(Data,MaxDeviceRead);
453     if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
454       return 0;
455 
456     // We had a bug report about failure to archive 1C database lock file
457     // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
458     // permanently locked. If our first read request uses too large buffer
459     // and if we are in -dh mode, so we were able to open the file,
460     // we'll fail with "Read error". So now we use try a smaller buffer size
461     // in case of lock error.
462     if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
463         GetLastError()==ERROR_LOCK_VIOLATION)
464       return DirectRead(Data,MaxLockedRead);
465 
466     return -1;
467   }
468   return Read;
469 #else
470 #ifdef FILE_USE_OPEN
471   ssize_t ReadSize=read(hFile,Data,Size);
472   if (ReadSize==-1)
473     return -1;
474   return (int)ReadSize;
475 #else
476   if (LastWrite)
477   {
478     fflush(hFile);
479     LastWrite=false;
480   }
481   clearerr(hFile);
482   size_t ReadSize=fread(Data,1,Size,hFile);
483   if (ferror(hFile))
484     return -1;
485   return (int)ReadSize;
486 #endif
487 #endif
488 }
489 
490 
Seek(int64 Offset,int Method)491 void File::Seek(int64 Offset,int Method)
492 {
493   if (!RawSeek(Offset,Method) && AllowExceptions)
494     ErrHandler.SeekError(FileName);
495 }
496 
497 
RawSeek(int64 Offset,int Method)498 bool File::RawSeek(int64 Offset,int Method)
499 {
500   if (hFile==FILE_BAD_HANDLE)
501     return true;
502   if (Offset<0 && Method!=SEEK_SET)
503   {
504     Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
505     Method=SEEK_SET;
506   }
507 #ifdef _WIN_ALL
508   LONG HighDist=(LONG)(Offset>>32);
509   if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
510       GetLastError()!=NO_ERROR)
511     return false;
512 #else
513   LastWrite=false;
514 #ifdef FILE_USE_OPEN
515   if (lseek(hFile,(off_t)Offset,Method)==-1)
516     return false;
517 #elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
518   if (fseeko(hFile,Offset,Method)!=0)
519     return false;
520 #else
521   if (fseek(hFile,(long)Offset,Method)!=0)
522     return false;
523 #endif
524 #endif
525   return true;
526 }
527 
528 
Tell()529 int64 File::Tell()
530 {
531   if (hFile==FILE_BAD_HANDLE)
532     if (AllowExceptions)
533       ErrHandler.SeekError(FileName);
534     else
535       return -1;
536 #ifdef _WIN_ALL
537   LONG HighDist=0;
538   uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
539   if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
540     if (AllowExceptions)
541       ErrHandler.SeekError(FileName);
542     else
543       return -1;
544   return INT32TO64(HighDist,LowDist);
545 #else
546 #ifdef FILE_USE_OPEN
547   return lseek(hFile,0,SEEK_CUR);
548 #elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
549   return ftello(hFile);
550 #else
551   return ftell(hFile);
552 #endif
553 #endif
554 }
555 
556 
Prealloc(int64 Size)557 void File::Prealloc(int64 Size)
558 {
559 #ifdef _WIN_ALL
560   if (RawSeek(Size,SEEK_SET))
561   {
562     Truncate();
563     Seek(0,SEEK_SET);
564   }
565 #endif
566 
567 #if defined(_UNIX) && defined(USE_FALLOCATE)
568   // fallocate is rather new call. Only latest kernels support it.
569   // So we are not using it by default yet.
570   int fd = GetFD();
571   if (fd >= 0)
572     fallocate(fd, 0, 0, Size);
573 #endif
574 }
575 
576 
GetByte()577 byte File::GetByte()
578 {
579   byte Byte=0;
580   Read(&Byte,1);
581   return Byte;
582 }
583 
584 
PutByte(byte Byte)585 void File::PutByte(byte Byte)
586 {
587   Write(&Byte,1);
588 }
589 
590 
Truncate()591 bool File::Truncate()
592 {
593 #ifdef _WIN_ALL
594   return SetEndOfFile(hFile)==TRUE;
595 #else
596   return ftruncate(GetFD(),(off_t)Tell())==0;
597 #endif
598 }
599 
600 
Flush()601 void File::Flush()
602 {
603 #ifdef _WIN_ALL
604   FlushFileBuffers(hFile);
605 #else
606 #ifndef FILE_USE_OPEN
607   fflush(hFile);
608 #endif
609   fsync(GetFD());
610 #endif
611 }
612 
613 
SetOpenFileTime(RarTime * ftm,RarTime * ftc,RarTime * fta)614 void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
615 {
616 #ifdef _WIN_ALL
617   // Workaround for OpenIndiana NAS time bug. If we cannot create a file
618   // in write only mode, we need to flush the write buffer before calling
619   // SetFileTime or file time will not be changed.
620   if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
621     FlushFileBuffers(hFile);
622 
623   bool sm=ftm!=NULL && ftm->IsSet();
624   bool sc=ftc!=NULL && ftc->IsSet();
625   bool sa=fta!=NULL && fta->IsSet();
626   FILETIME fm,fc,fa;
627   if (sm)
628     ftm->GetWinFT(&fm);
629   if (sc)
630     ftc->GetWinFT(&fc);
631   if (sa)
632     fta->GetWinFT(&fa);
633   SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
634 #endif
635 }
636 
637 
SetCloseFileTime(RarTime * ftm,RarTime * fta)638 void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
639 {
640 // Android APP_PLATFORM := android-14 does not support futimens and futimes.
641 // Newer platforms support futimens, but fail on Android 4.2.
642 // We have to use utime for Android.
643 // Also we noticed futimens fail to set timestamps on NTFS partition
644 // mounted to virtual Linux x86 machine, but utimensat worked correctly.
645 // So we set timestamps for already closed files in Unix.
646 #ifdef _UNIX
647   SetCloseFileTimeByName(FileName,ftm,fta);
648 #endif
649 }
650 
651 
SetCloseFileTimeByName(const wchar * Name,RarTime * ftm,RarTime * fta)652 void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
653 {
654 #ifdef _UNIX
655   bool setm=ftm!=NULL && ftm->IsSet();
656   bool seta=fta!=NULL && fta->IsSet();
657   if (setm || seta)
658   {
659     char NameA[NM];
660     WideToChar(Name,NameA,ASIZE(NameA));
661 
662 #ifdef UNIX_TIME_NS
663     timespec times[2];
664     times[0].tv_sec=seta ? fta->GetUnix() : 0;
665     times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
666     times[1].tv_sec=setm ? ftm->GetUnix() : 0;
667     times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
668     utimensat(AT_FDCWD,NameA,times,0);
669 #else
670     utimbuf ut;
671     if (setm)
672       ut.modtime=ftm->GetUnix();
673     else
674       ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0.
675     if (seta)
676       ut.actime=fta->GetUnix();
677     else
678       ut.actime=ut.modtime; // Need to set something, cannot left it 0.
679     utime(NameA,&ut);
680 #endif
681   }
682 #endif
683 }
684 
685 
GetOpenFileTime(RarTime * ft)686 void File::GetOpenFileTime(RarTime *ft)
687 {
688 #ifdef _WIN_ALL
689   FILETIME FileTime;
690   GetFileTime(hFile,NULL,NULL,&FileTime);
691   ft->SetWinFT(&FileTime);
692 #endif
693 #if defined(_UNIX) || defined(_EMX)
694   struct stat st;
695   fstat(GetFD(),&st);
696   ft->SetUnix(st.st_mtime);
697 #endif
698 }
699 
700 
FileLength()701 int64 File::FileLength()
702 {
703   int64 SavePos=Tell();
704   Seek(0,SEEK_END);
705   int64 Length=Tell();
706   Seek(SavePos,SEEK_SET);
707   return Length;
708 }
709 
710 
IsDevice()711 bool File::IsDevice()
712 {
713   if (hFile==FILE_BAD_HANDLE)
714     return false;
715 #ifdef _WIN_ALL
716   uint Type=GetFileType(hFile);
717   return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
718 #else
719   return isatty(GetFD());
720 #endif
721 }
722 
723 
724 #ifndef SFX_MODULE
Copy(File & Dest,int64 Length)725 int64 File::Copy(File &Dest,int64 Length)
726 {
727   Array<byte> Buffer(File::CopyBufferSize());
728   int64 CopySize=0;
729   bool CopyAll=(Length==INT64NDF);
730 
731   while (CopyAll || Length>0)
732   {
733     Wait();
734     size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
735     byte *Buf=&Buffer[0];
736     int ReadSize=Read(Buf,SizeToRead);
737     if (ReadSize==0)
738       break;
739     size_t WriteSize=ReadSize;
740 #ifdef _WIN_ALL
741     // For FAT32 USB flash drives in Windows if first write is 4 KB or more,
742     // write caching is disabled and "write through" is enabled, resulting
743     // in bad performance, especially for many small files. It happens when
744     // we create SFX archive on USB drive, because SFX module is written first.
745     // So we split the first write to small 1 KB followed by rest of data.
746     if (CopySize==0 && WriteSize>=4096)
747     {
748       const size_t FirstWrite=1024;
749       Dest.Write(Buf,FirstWrite);
750       Buf+=FirstWrite;
751       WriteSize-=FirstWrite;
752     }
753 #endif
754     Dest.Write(Buf,WriteSize);
755     CopySize+=ReadSize;
756     if (!CopyAll)
757       Length-=ReadSize;
758   }
759   return CopySize;
760 }
761 #endif
762