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