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