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