"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