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