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