xref: /aosp_15_r20/external/lzma/CPP/7zip/Common/MultiOutStream.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // MultiOutStream.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define DEBUG_VOLUMES
6 
7 #ifdef DEBUG_VOLUMES
8 #include <stdio.h>
9   #define PRF(x) x;
10 #else
11   #define PRF(x)
12 #endif
13 
14 #include "../../Common/ComTry.h"
15 
16 #include "../../Windows/FileDir.h"
17 #include "../../Windows/FileFind.h"
18 #include "../../Windows/System.h"
19 
20 #include "MultiOutStream.h"
21 
22 using namespace NWindows;
23 using namespace NFile;
24 using namespace NDir;
25 
26 static const unsigned k_NumVols_MAX = k_VectorSizeMax - 1;
27       // 2; // for debug
28 
29 /*
30 #define UPDATE_HRES(hres, x) \
31   { const HRESULT res2 = (x); if (hres == SZ_OK) hres = res2; }
32 */
33 
Destruct()34 HRESULT CMultiOutStream::Destruct()
35 {
36   COM_TRY_BEGIN
37   HRESULT hres = S_OK;
38   HRESULT hres3 = S_OK;
39 
40   while (!Streams.IsEmpty())
41   {
42     try
43     {
44       HRESULT hres2;
45       if (NeedDelete)
46       {
47         /* we could call OptReOpen_and_SetSize() to test that we try to delete correct file,
48            but we cannot guarantee that (RealSize) will be correct after Write() or another failures.
49            And we still want to delete files even for such cases.
50            So we don't check for OptReOpen_and_SetSize() here: */
51         // if (OptReOpen_and_SetSize(Streams.Size() - 1, 0) == S_OK)
52         hres2 = CloseStream_and_DeleteFile(Streams.Size() - 1);
53       }
54       else
55       {
56         hres2 = CloseStream(Streams.Size() - 1);
57       }
58       if (hres == S_OK)
59         hres = hres2;
60     }
61     catch(...)
62     {
63       hres3 = E_OUTOFMEMORY;
64     }
65 
66     {
67       /* Stream was released in CloseStream_*() above already, and it was removed from linked list
68          it's some unexpected case, if Stream is still attached here.
69          So the following code is optional: */
70       CVolStream &s = Streams.Back();
71       if (s.Stream)
72       {
73         if (hres3 == S_OK)
74           hres3 = E_FAIL;
75         s.Stream.Detach();
76         /* it will be not failure, even if we call RemoveFromLinkedList()
77            twice for same CVolStream in this Destruct() function */
78         RemoveFromLinkedList(Streams.Size() - 1);
79       }
80     }
81     Streams.DeleteBack();
82     // Delete_LastStream_Records();
83   }
84 
85   if (hres == S_OK)
86     hres = hres3;
87   if (hres == S_OK && NumListItems != 0)
88     hres = E_FAIL;
89   return hres;
90   COM_TRY_END
91 }
92 
93 
~CMultiOutStream()94 CMultiOutStream::~CMultiOutStream()
95 {
96   // we try to avoid exception in destructors
97   Destruct();
98 }
99 
100 
Init(const CRecordVector<UInt64> & sizes)101 void CMultiOutStream::Init(const CRecordVector<UInt64> &sizes)
102 {
103   Streams.Clear();
104   InitLinkedList();
105   Sizes = sizes;
106   NeedDelete = true;
107   MTime_Defined = false;
108   FinalVol_WasReopen = false;
109   NumOpenFiles_AllowedMax = NSystem::Get_File_OPEN_MAX_Reduced_for_3_tasks();
110 
111   _streamIndex = 0;
112   _offsetPos = 0;
113   _absPos = 0;
114   _length = 0;
115   _absLimit = (UInt64)(Int64)-1;
116 
117   _restrict_Begin = 0;
118   _restrict_End = (UInt64)(Int64)-1;
119   _restrict_Global = 0;
120 
121   UInt64 sum = 0;
122   unsigned i = 0;
123   for (i = 0; i < Sizes.Size(); i++)
124   {
125     if (i >= k_NumVols_MAX)
126     {
127       _absLimit = sum;
128       break;
129     }
130     const UInt64 size = Sizes[i];
131     const UInt64 next = sum + size;
132     if (next < sum)
133       break;
134     sum = next;
135   }
136 
137   // if (Sizes.IsEmpty()) throw "no volume sizes";
138   const UInt64 size = Sizes.Back();
139   if (size == 0)
140     throw "zero size last volume";
141 
142   if (i == Sizes.Size())
143     if ((_absLimit - sum) / size >= (k_NumVols_MAX - i))
144       _absLimit = sum + (k_NumVols_MAX - i) * size;
145 }
146 
147 
148 /* IsRestricted():
149    we must call only if volume is full (s.RealSize==VolSize) or finished.
150    the function doesn't use VolSize and it uses s.RealSize instead.
151    it returns true  : if stream is restricted, and we can't close that stream
152    it returns false : if there is no restriction, and we can close that stream
153  Note: (RealSize == 0) (empty volume) on restriction bounds are supposed as non-restricted
154 */
IsRestricted(const CVolStream & s) const155 bool CMultiOutStream::IsRestricted(const CVolStream &s) const
156 {
157   if (s.Start < _restrict_Global)
158     return true;
159   if (_restrict_Begin == _restrict_End)
160     return false;
161   if (_restrict_Begin <= s.Start)
162     return _restrict_End > s.Start;
163   return _restrict_Begin < s.Start + s.RealSize;
164 }
165 
166 /*
167 // this function check also _length and volSize
168 bool CMultiOutStream::IsRestricted_for_Close(unsigned index) const
169 {
170   const CVolStream &s = Streams[index];
171   if (_length <= s.Start) // we don't close streams after the end, because we still can write them later
172     return true;
173   // (_length > s.Start)
174   const UInt64 volSize = GetVolSize_for_Stream(index);
175   if (volSize == 0)
176     return IsRestricted_Empty(s);
177   if (_length - s.Start < volSize)
178     return true;
179   return IsRestricted(s);
180 }
181 */
182 
GetFilePath(unsigned index)183 FString CMultiOutStream::GetFilePath(unsigned index)
184 {
185   FString name;
186   name.Add_UInt32((UInt32)(index + 1));
187   while (name.Len() < 3)
188     name.InsertAtFront(FTEXT('0'));
189   name.Insert(0, Prefix);
190   return name;
191 }
192 
193 
194 // we close stream, but we still keep item in Streams[] vector
CloseStream(unsigned index)195 HRESULT CMultiOutStream::CloseStream(unsigned index)
196 {
197   CVolStream &s = Streams[index];
198   if (s.Stream)
199   {
200     RINOK(s.StreamSpec->Close())
201     // the following two commands must be called together:
202     s.Stream.Release();
203     RemoveFromLinkedList(index);
204   }
205   return S_OK;
206 }
207 
208 
209 // we close stream and delete file, but we still keep item in Streams[] vector
CloseStream_and_DeleteFile(unsigned index)210 HRESULT CMultiOutStream::CloseStream_and_DeleteFile(unsigned index)
211 {
212   PRF(printf("\n====== %u, CloseStream_AndDelete \n", index))
213   RINOK(CloseStream(index))
214   FString path = GetFilePath(index);
215   path += Streams[index].Postfix;
216   // we can checki that file exist
217   // if (NFind::DoesFileExist_Raw(path))
218   if (!DeleteFileAlways(path))
219     return GetLastError_noZero_HRESULT();
220   return S_OK;
221 }
222 
223 
CloseStream_and_FinalRename(unsigned index)224 HRESULT CMultiOutStream::CloseStream_and_FinalRename(unsigned index)
225 {
226   PRF(printf("\n====== %u, CloseStream_and_FinalRename \n", index))
227   CVolStream &s = Streams[index];
228   // HRESULT res = S_OK;
229   bool mtime_WasSet = false;
230   if (MTime_Defined && s.Stream)
231   {
232     if (s.StreamSpec->SetMTime(&MTime))
233       mtime_WasSet = true;
234     // else res = GetLastError_noZero_HRESULT();
235   }
236 
237   RINOK(CloseStream(index))
238   if (s.Postfix.IsEmpty()) // if Postfix is empty, the path is already final
239     return S_OK;
240   const FString path = GetFilePath(index);
241   FString tempPath = path;
242   tempPath += s.Postfix;
243 
244   if (MTime_Defined && !mtime_WasSet)
245   {
246     if (!SetDirTime(tempPath, NULL, NULL, &MTime))
247     {
248       // res = GetLastError_noZero_HRESULT();
249     }
250   }
251   if (!MyMoveFile(tempPath, path))
252     return GetLastError_noZero_HRESULT();
253   /* we clear CVolStream::Postfix. So we will not use Temp path
254      anymore for this stream, and we will work only with final path */
255   s.Postfix.Empty();
256   // we can ignore set_mtime error or we can return it
257   return S_OK;
258   // return res;
259 }
260 
261 
PrepareToOpenNew()262 HRESULT CMultiOutStream::PrepareToOpenNew()
263 {
264   PRF(printf("PrepareToOpenNew NumListItems =%u,  NumOpenFiles_AllowedMax = %u \n", NumListItems, NumOpenFiles_AllowedMax))
265 
266   if (NumListItems < NumOpenFiles_AllowedMax)
267     return S_OK;
268   /* when we create zip archive: in most cases we need only starting
269      data of restricted region for rewriting zip's local header.
270      So here we close latest created volume (from Head), and we try to
271      keep oldest volumes that will be used for header rewriting later. */
272   const int index = Head;
273   if (index == -1)
274     return E_FAIL;
275   PRF(printf("\n== %u, PrepareToOpenNew::CloseStream, NumListItems =%u \n", index, NumListItems))
276   /* we don't expect non-restricted stream here in normal cases (if _restrict_Global was not changed).
277      if there was non-restricted stream, it should be closed before */
278   // if (!IsRestricted_for_Close(index)) return CloseStream_and_FinalRename(index);
279   return CloseStream((unsigned)index);
280 }
281 
282 
CreateNewStream(UInt64 newSize)283 HRESULT CMultiOutStream::CreateNewStream(UInt64 newSize)
284 {
285   PRF(printf("\n== %u, CreateNewStream, size =%u \n", Streams.Size(), (unsigned)newSize))
286 
287   if (Streams.Size() >= k_NumVols_MAX)
288     return E_INVALIDARG; // E_OUTOFMEMORY
289 
290   RINOK(PrepareToOpenNew())
291   CVolStream s;
292   s.StreamSpec = new COutFileStream;
293   s.Stream = s.StreamSpec;
294   const FString path = GetFilePath(Streams.Size());
295 
296   if (NFind::DoesFileExist_Raw(path))
297     return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
298   if (!CreateTempFile2(path, false, s.Postfix, &s.StreamSpec->File))
299     return GetLastError_noZero_HRESULT();
300 
301   s.Start = GetGlobalOffset_for_NewStream();
302   s.Pos = 0;
303   s.RealSize = 0;
304 
305   const unsigned index = Streams.Add(s);
306   InsertToLinkedList(index);
307 
308   if (newSize != 0)
309     return s.SetSize2(newSize);
310   return S_OK;
311 }
312 
313 
CreateStreams_If_Required(unsigned streamIndex)314 HRESULT CMultiOutStream::CreateStreams_If_Required(unsigned streamIndex)
315 {
316   // UInt64 lastStreamSize = 0;
317   for (;;)
318   {
319     const unsigned numStreamsBefore = Streams.Size();
320     if (streamIndex < numStreamsBefore)
321       return S_OK;
322     UInt64 newSize;
323     if (streamIndex == numStreamsBefore)
324     {
325       // it's final volume that will be used for real writing.
326       /* SetSize(_offsetPos) is not required,
327       because the file Size will be set later by calling Seek() with Write() */
328       newSize = 0; // lastStreamSize;
329     }
330     else
331     {
332       // it's intermediate volume. So we need full volume size
333       newSize = GetVolSize_for_Stream(numStreamsBefore);
334     }
335 
336     RINOK(CreateNewStream(newSize))
337 
338     // optional check
339     if (numStreamsBefore + 1 != Streams.Size()) return E_FAIL;
340 
341     if (streamIndex != numStreamsBefore)
342     {
343       // it's intermediate volume. So we can close it, if it's non-restricted
344       bool isRestricted;
345       {
346         const CVolStream &s = Streams[numStreamsBefore];
347         if (newSize == 0)
348           isRestricted = IsRestricted_Empty(s);
349         else
350           isRestricted = IsRestricted(s);
351       }
352       if (!isRestricted)
353       {
354         RINOK(CloseStream_and_FinalRename(numStreamsBefore))
355       }
356     }
357   }
358 }
359 
360 
ReOpenStream(unsigned streamIndex)361 HRESULT CMultiOutStream::ReOpenStream(unsigned streamIndex)
362 {
363   PRF(printf("\n====== %u, ReOpenStream \n", streamIndex))
364   RINOK(PrepareToOpenNew())
365   CVolStream &s = Streams[streamIndex];
366 
367   FString path = GetFilePath(streamIndex);
368   path += s.Postfix;
369 
370   s.StreamSpec = new COutFileStream;
371   s.Stream = s.StreamSpec;
372   s.Pos = 0;
373 
374   HRESULT hres;
375   if (s.StreamSpec->Open_EXISTING(path))
376   {
377     if (s.Postfix.IsEmpty())
378     {
379       /* it's unexpected case that we open finished volume.
380          It can mean that the code for restriction is incorrect */
381       FinalVol_WasReopen = true;
382     }
383     UInt64 realSize = 0;
384     hres = s.StreamSpec->GetSize(&realSize);
385     if (hres == S_OK)
386     {
387       if (realSize == s.RealSize)
388       {
389         PRF(printf("\n ReOpenStream OK realSize = %u\n", (unsigned)realSize))
390         InsertToLinkedList(streamIndex);
391         return S_OK;
392       }
393       // file size was changed between Close() and ReOpen()
394       // we must release Stream to be consistent with linked list
395       hres = E_FAIL;
396     }
397   }
398   else
399     hres = GetLastError_noZero_HRESULT();
400   s.Stream.Release();
401   s.StreamSpec = NULL;
402   return hres;
403 }
404 
405 
406 /* Sets size of stream, if new size is not equal to old size (RealSize).
407    If stream was closed and size change is required, it reopens the stream. */
408 
OptReOpen_and_SetSize(unsigned index,UInt64 size)409 HRESULT CMultiOutStream::OptReOpen_and_SetSize(unsigned index, UInt64 size)
410 {
411   CVolStream &s = Streams[index];
412   if (size == s.RealSize)
413     return S_OK;
414   if (!s.Stream)
415   {
416     RINOK(ReOpenStream(index))
417   }
418   PRF(printf("\n== %u, OptReOpen_and_SetSize, size =%u RealSize = %u\n", index, (unsigned)size, (unsigned)s.RealSize))
419   // comment it to debug tail after data
420   return s.SetSize2(size);
421 }
422 
423 
424 /*
425 call Normalize_finalMode(false), if _length was changed.
426   for all streams starting after _length:
427     - it sets zero size
428     - it still keeps file open
429   Note: after _length reducing with CMultiOutStream::SetSize() we can
430     have very big number of empty streams at the end of Streams[] list.
431     And Normalize_finalMode() will runs all these empty streams of Streams[] vector.
432     So it can be ineffective, if we call Normalize_finalMode() many
433     times after big reducing of (_length).
434 
435 call Normalize_finalMode(true) to set final presentations of all streams
436   for all streams starting after _length:
437     - it sets zero size
438     - it removes file
439     - it removes CVolStream object from Streams[] vector
440 
441 Note: we don't remove zero sized first volume, if (_length == 0)
442 */
443 
Normalize_finalMode(bool finalMode)444 HRESULT CMultiOutStream::Normalize_finalMode(bool finalMode)
445 {
446   PRF(printf("\n== Normalize_finalMode: _length =%d \n", (unsigned)_length))
447 
448   unsigned i = Streams.Size();
449 
450   UInt64 offset = 0;
451 
452   /* At first we normalize (reduce or increase) the sizes of all existing
453      streams in Streams[] that can be affected by changed _length.
454      And we remove tailing zero-size streams, if (finalMode == true) */
455   while (i != 0)
456   {
457     offset = Streams[--i].Start; // it's last item in Streams[]
458     // we don't want to remove first volume
459     if (offset < _length || i == 0)
460     {
461       const UInt64 volSize = GetVolSize_for_Stream(i);
462       UInt64 size = _length - offset; // (size != 0) here
463       if (size > volSize)
464         size = volSize;
465       RINOK(OptReOpen_and_SetSize(i, size))
466       if (_length - offset <= volSize)
467         return S_OK;
468       // _length - offset > volSize
469       offset += volSize;
470       // _length > offset
471       break;
472       // UPDATE_HRES(res, OptReOpen_and_SetSize(i, size));
473     }
474 
475     /* we Set Size of stream to zero even for (finalMode==true), although
476        that stream will be deleted in next commands */
477     // UPDATE_HRES(res, OptReOpen_and_SetSize(i, 0));
478     RINOK(OptReOpen_and_SetSize(i, 0))
479     if (finalMode)
480     {
481       RINOK(CloseStream_and_DeleteFile(i))
482       /* CVolStream::Stream was released above already, and it was
483          removed from linked list. So we don't need to update linked list
484          structure, when we delete last item in Streams[] */
485       Streams.DeleteBack();
486       // Delete_LastStream_Records();
487     }
488   }
489 
490   /* now we create new zero-filled streams to cover all data up to _length */
491 
492   if (_length == 0)
493     return S_OK;
494 
495   // (offset) is start offset of next stream after existing Streams[]
496 
497   for (;;)
498   {
499     // _length > offset
500     const UInt64 volSize = GetVolSize_for_Stream(Streams.Size());
501     UInt64 size = _length - offset; // (size != 0) here
502     if (size > volSize)
503       size = volSize;
504     RINOK(CreateNewStream(size))
505     if (_length - offset <= volSize)
506       return S_OK;
507     // _length - offset > volSize)
508     offset += volSize;
509     // _length > offset
510   }
511 }
512 
513 
FinalFlush_and_CloseFiles(unsigned & numTotalVolumesRes)514 HRESULT CMultiOutStream::FinalFlush_and_CloseFiles(unsigned &numTotalVolumesRes)
515 {
516   // at first we remove unused zero-sized streams after _length
517   HRESULT res = Normalize_finalMode(true);
518   numTotalVolumesRes = Streams.Size();
519   FOR_VECTOR (i, Streams)
520   {
521     const HRESULT res2 = CloseStream_and_FinalRename(i);
522     if (res == S_OK)
523       res = res2;
524   }
525   if (NumListItems != 0 && res == S_OK)
526     res = E_FAIL;
527   return res;
528 }
529 
530 
SetMTime_Final(const CFiTime & mTime)531 bool CMultiOutStream::SetMTime_Final(const CFiTime &mTime)
532 {
533   // we will set mtime only if new value differs from previous
534   if (!FinalVol_WasReopen && MTime_Defined && Compare_FiTime(&MTime, &mTime) == 0)
535     return true;
536   bool res = true;
537   FOR_VECTOR (i, Streams)
538   {
539     CVolStream &s = Streams[i];
540     if (s.Stream)
541     {
542       if (!s.StreamSpec->SetMTime(&mTime))
543         res = false;
544     }
545     else
546     {
547       if (!SetDirTime(GetFilePath(i), NULL, NULL, &mTime))
548         res = false;
549     }
550   }
551   return res;
552 }
553 
554 
Z7_COM7F_IMF(CMultiOutStream::SetSize (UInt64 newSize))555 Z7_COM7F_IMF(CMultiOutStream::SetSize(UInt64 newSize))
556 {
557   COM_TRY_BEGIN
558   if ((Int64)newSize < 0)
559     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
560   if (newSize > _absLimit)
561   {
562     /* big seek value was sent to SetSize() or to Seek()+Write().
563        It can mean one of two situations:
564          1) some incorrect code called it with big seek value.
565          2) volume size was small, and we have too big number of volumes
566     */
567     /* in Windows SetEndOfFile() can return:
568        ERROR_NEGATIVE_SEEK:     for >= (1 << 63)
569        ERROR_INVALID_PARAMETER: for >  (16 TiB - 64 KiB)
570        ERROR_DISK_FULL:         for <= (16 TiB - 64 KiB)
571     */
572     // return E_FAIL;
573     // return E_OUTOFMEMORY;
574     return E_INVALIDARG;
575   }
576 
577   if (newSize > _length)
578   {
579     // we don't expect such case. So we just define global restriction */
580     _restrict_Global = newSize;
581   }
582   else if (newSize < _restrict_Global)
583     _restrict_Global = newSize;
584 
585   PRF(printf("\n== CMultiOutStream::SetSize, size =%u \n", (unsigned)newSize))
586 
587   _length = newSize;
588   return Normalize_finalMode(false);
589 
590   COM_TRY_END
591 }
592 
593 
Z7_COM7F_IMF(CMultiOutStream::Write (const void * data,UInt32 size,UInt32 * processedSize))594 Z7_COM7F_IMF(CMultiOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize))
595 {
596   COM_TRY_BEGIN
597   if (processedSize)
598     *processedSize = 0;
599   if (size == 0)
600     return S_OK;
601 
602   PRF(printf("\n -- CMultiOutStream::Write() : _absPos = %6u, size =%6u \n",
603       (unsigned)_absPos, (unsigned)size))
604 
605   if (_absPos > _length)
606   {
607     // it create data only up to _absPos.
608     // but we still can need additional new streams, if _absPos at range of volume
609     RINOK(SetSize(_absPos))
610   }
611 
612   while (size != 0)
613   {
614     UInt64 volSize;
615     {
616       if (_streamIndex < Sizes.Size() - 1)
617       {
618         volSize = Sizes[_streamIndex];
619         if (_offsetPos >= volSize)
620         {
621           _offsetPos -= volSize;
622           _streamIndex++;
623           continue;
624         }
625       }
626       else
627       {
628         volSize = Sizes[Sizes.Size() - 1];
629         if (_offsetPos >= volSize)
630         {
631           const UInt64 v = _offsetPos / volSize;
632           if (v >= ((UInt32)(Int32)-1) - _streamIndex)
633             return E_INVALIDARG;
634             // throw 202208;
635           _streamIndex += (unsigned)v;
636           _offsetPos -= (unsigned)v * volSize;
637         }
638         if (_streamIndex >= k_NumVols_MAX)
639           return E_INVALIDARG;
640       }
641     }
642 
643     // (_offsetPos < volSize) here
644 
645     /* we can need to create one or more streams here,
646        vol_size for some streams is allowed to be 0.
647        Also we close some new created streams, if they are non-restricted */
648     // file Size will be set later by calling Seek() with Write()
649 
650     /* the case (_absPos > _length) was processed above with SetSize(_absPos),
651        so here it's expected. that we can create optional zero-size streams and then _streamIndex */
652     RINOK(CreateStreams_If_Required(_streamIndex))
653 
654     CVolStream &s = Streams[_streamIndex];
655 
656     PRF(printf("\n%d, == Write : Pos = %u, RealSize = %u size =%u \n",
657         _streamIndex, (unsigned)s.Pos, (unsigned)s.RealSize, size))
658 
659     if (!s.Stream)
660     {
661       RINOK(ReOpenStream(_streamIndex))
662     }
663     if (_offsetPos != s.Pos)
664     {
665       RINOK(s.Stream->Seek((Int64)_offsetPos, STREAM_SEEK_SET, NULL))
666       s.Pos = _offsetPos;
667     }
668 
669     UInt32 curSize = size;
670     {
671       const UInt64 rem = volSize - _offsetPos;
672       if (curSize > rem)
673         curSize = (UInt32)rem;
674     }
675     // curSize != 0
676     UInt32 realProcessed = 0;
677 
678     HRESULT hres = s.Stream->Write(data, curSize, &realProcessed);
679 
680     data = (const void *)((const Byte *)data + realProcessed);
681     size -= realProcessed;
682     s.Pos += realProcessed;
683     _offsetPos += realProcessed;
684     _absPos += realProcessed;
685     if (_length < _absPos)
686       _length = _absPos;
687     if (s.RealSize < _offsetPos)
688       s.RealSize = _offsetPos;
689     if (processedSize)
690       *processedSize += realProcessed;
691 
692     if (s.Pos == volSize)
693     {
694       bool isRestricted;
695       if (volSize == 0)
696         isRestricted = IsRestricted_Empty(s);
697       else
698         isRestricted = IsRestricted(s);
699       if (!isRestricted)
700       {
701         const HRESULT res2 = CloseStream_and_FinalRename(_streamIndex);
702         if (hres == S_OK)
703           hres = res2;
704       }
705       _streamIndex++;
706       _offsetPos = 0;
707     }
708 
709     RINOK(hres)
710     if (realProcessed == 0 && curSize != 0)
711       return E_FAIL;
712     // break;
713   }
714   return S_OK;
715   COM_TRY_END
716 }
717 
718 
Z7_COM7F_IMF(CMultiOutStream::Seek (Int64 offset,UInt32 seekOrigin,UInt64 * newPosition))719 Z7_COM7F_IMF(CMultiOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
720 {
721   PRF(printf("\n-- CMultiOutStream::Seek seekOrigin=%u Seek =%u\n", seekOrigin, (unsigned)offset))
722 
723   switch (seekOrigin)
724   {
725     case STREAM_SEEK_SET: break;
726     case STREAM_SEEK_CUR: offset += _absPos; break;
727     case STREAM_SEEK_END: offset += _length; break;
728     default: return STG_E_INVALIDFUNCTION;
729   }
730   if (offset < 0)
731     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
732   if ((UInt64)offset != _absPos)
733   {
734     _absPos = (UInt64)offset;
735     _offsetPos = (UInt64)offset;
736     _streamIndex = 0;
737   }
738   if (newPosition)
739     *newPosition = (UInt64)offset;
740   return S_OK;
741 }
742 
743 
744 // result value will be saturated to (UInt32)(Int32)-1
745 
GetStreamIndex_for_Offset(UInt64 offset,UInt64 & relOffset) const746 unsigned CMultiOutStream::GetStreamIndex_for_Offset(UInt64 offset, UInt64 &relOffset) const
747 {
748   const unsigned last = Sizes.Size() - 1;
749   for (unsigned i = 0; i < last; i++)
750   {
751     const UInt64 size = Sizes[i];
752     if (offset < size)
753     {
754       relOffset = offset;
755       return i;
756     }
757     offset -= size;
758   }
759   const UInt64 size = Sizes[last];
760   const UInt64 v = offset / size;
761   if (v >= ((UInt32)(Int32)-1) - last)
762     return (unsigned)(int)-1; // saturation
763   relOffset = offset - (unsigned)v * size;
764   return last + (unsigned)(v);
765 }
766 
767 
Z7_COM7F_IMF(CMultiOutStream::SetRestriction (UInt64 begin,UInt64 end))768 Z7_COM7F_IMF(CMultiOutStream::SetRestriction(UInt64 begin, UInt64 end))
769 {
770   COM_TRY_BEGIN
771 
772   // begin = end = 0; // for debug
773 
774   PRF(printf("\n==================== CMultiOutStream::SetRestriction %u, %u\n", (unsigned)begin, (unsigned)end))
775   if (begin > end)
776   {
777     // these value are FAILED values.
778     return E_FAIL;
779     // return E_INVALIDARG;
780     /*
781     // or we can ignore error with 3 ways: no change, non-restricted, saturation:
782     end = begin;             // non-restricted
783     end = (UInt64)(Int64)-1; // saturation:
784     return S_OK;
785     */
786   }
787   UInt64 b = _restrict_Begin;
788   UInt64 e = _restrict_End;
789   _restrict_Begin = begin;
790   _restrict_End = end;
791 
792   if (b == e)    // if there were no restriction before
793     return S_OK; // no work to derestrict now.
794 
795   /* [b, e) is previous restricted region. So all volumes that
796      intersect that [b, e) region are candidats for derestriction */
797 
798   if (begin != end) // if there is new non-empty restricted region
799   {
800     /* Now we will try to reduce or change (b) and (e) bounds
801        to reduce main loop that checks volumes for derestriction.
802        We still use one big derestriction region in main loop, although
803        in some cases we could have two smaller derestriction regions.
804        Also usually restriction region cannot move back from previous start position,
805        so (b <= begin) is expected here for normal cases */
806     if (b == begin) // if same low bounds
807       b = end;      // we need to derestrict only after the end of new restricted region
808     if (e == end)   // if same high bounds
809       e = begin;    // we need to derestrict only before the begin of new restricted region
810   }
811 
812   if (b > e) //  || b == (UInt64)(Int64)-1
813     return S_OK;
814 
815   /* Here we close finished volumes that are not restricted anymore.
816      We close (low number) volumes at first. */
817 
818   UInt64 offset;
819   unsigned index = GetStreamIndex_for_Offset(b, offset);
820 
821   for (; index < Streams.Size(); index++)
822   {
823     {
824       const CVolStream &s = Streams[index];
825       if (_length <= s.Start)
826         break; // we don't close streams after _length
827       // (_length > s.Start)
828       const UInt64 volSize = GetVolSize_for_Stream(index);
829       if (volSize == 0)
830       {
831         if (e < s.Start)
832           break;
833         // we don't close empty stream, if next byte [s.Start, s.Start] is restricted
834         if (IsRestricted_Empty(s))
835           continue;
836       }
837       else
838       {
839         if (e <= s.Start)
840           break;
841         // we don't close non full streams
842         if (_length - s.Start < volSize)
843           break;
844         // (volSize == s.RealSize) is expected here. So no need to check it
845         // if (volSize != s.RealSize) break;
846         if (IsRestricted(s))
847           continue;
848       }
849     }
850     RINOK(CloseStream_and_FinalRename(index))
851   }
852 
853   return S_OK;
854   COM_TRY_END
855 }
856