1 // UdfHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "Common/ComTry.h"
6 #include "Common/NewHandler.h"
7
8 #include "Windows/PropVariant.h"
9 #include "Windows/Time.h"
10
11 #include "../../Common/LimitedStreams.h"
12 #include "../../Common/ProgressUtils.h"
13
14 #include "../../Compress/CopyCoder.h"
15
16 #include "UdfHandler.h"
17
18 namespace NArchive {
19 namespace NUdf {
20
UdfTimeToFileTime(const CTime & t,NWindows::NCOM::CPropVariant & prop)21 void UdfTimeToFileTime(const CTime &t, NWindows::NCOM::CPropVariant &prop)
22 {
23 UInt64 numSecs;
24 const Byte *d = t.Data;
25 if (!NWindows::NTime::GetSecondsSince1601(t.GetYear(), d[4], d[5], d[6], d[7], d[8], numSecs))
26 return;
27 if (t.IsLocal())
28 numSecs -= t.GetMinutesOffset() * 60;
29 FILETIME ft;
30 UInt64 v = (((numSecs * 100 + d[9]) * 100 + d[10]) * 100 + d[11]) * 10;
31 ft.dwLowDateTime = (UInt32)v;
32 ft.dwHighDateTime = (UInt32)(v >> 32);
33 prop = ft;
34 }
35
36 STATPROPSTG kProps[] =
37 {
38 { NULL, kpidPath, VT_BSTR},
39 { NULL, kpidIsDir, VT_BOOL},
40 { NULL, kpidSize, VT_UI8},
41 { NULL, kpidPackSize, VT_UI8},
42 { NULL, kpidMTime, VT_FILETIME},
43 { NULL, kpidATime, VT_FILETIME}
44 };
45
46 STATPROPSTG kArcProps[] =
47 {
48 { NULL, kpidComment, VT_BSTR},
49 { NULL, kpidClusterSize, VT_UI4},
50 { NULL, kpidCTime, VT_FILETIME}
51 };
52
53 IMP_IInArchive_Props
54 IMP_IInArchive_ArcProps
55
GetArchiveProperty(PROPID propID,PROPVARIANT * value)56 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
57 {
58 COM_TRY_BEGIN
59 NWindows::NCOM::CPropVariant prop;
60 switch(propID)
61 {
62 case kpidComment:
63 {
64 UString comment = _archive.GetComment();
65 if (!comment.IsEmpty())
66 prop = comment;
67 break;
68 }
69
70 case kpidClusterSize:
71 if (_archive.LogVols.Size() > 0)
72 {
73 UInt32 blockSize = _archive.LogVols[0].BlockSize;
74 int i;
75 for (i = 1; i < _archive.LogVols.Size(); i++)
76 if (_archive.LogVols[i].BlockSize != blockSize)
77 break;
78 if (i == _archive.LogVols.Size())
79 prop = blockSize;
80 }
81 break;
82
83 case kpidCTime:
84 if (_archive.LogVols.Size() == 1)
85 {
86 const CLogVol &vol = _archive.LogVols[0];
87 if (vol.FileSets.Size() >= 1)
88 UdfTimeToFileTime(vol.FileSets[0].RecodringTime, prop);
89 }
90 break;
91 }
92 prop.Detach(value);
93 return S_OK;
94 COM_TRY_END
95 }
96
97 class CProgressImp: public CProgressVirt
98 {
99 CMyComPtr<IArchiveOpenCallback> _callback;
100 UInt64 _numFiles;
101 UInt64 _numBytes;
102 public:
103 HRESULT SetTotal(UInt64 numBytes);
104 HRESULT SetCompleted(UInt64 numFiles, UInt64 numBytes);
105 HRESULT SetCompleted();
CProgressImp(IArchiveOpenCallback * callback)106 CProgressImp(IArchiveOpenCallback *callback): _callback(callback), _numFiles(0), _numBytes(0) {}
107 };
108
SetTotal(UInt64 numBytes)109 HRESULT CProgressImp::SetTotal(UInt64 numBytes)
110 {
111 if (_callback)
112 return _callback->SetTotal(NULL, &numBytes);
113 return S_OK;
114 }
115
SetCompleted(UInt64 numFiles,UInt64 numBytes)116 HRESULT CProgressImp::SetCompleted(UInt64 numFiles, UInt64 numBytes)
117 {
118 _numFiles = numFiles;
119 _numBytes = numBytes;
120 return SetCompleted();
121 }
122
SetCompleted()123 HRESULT CProgressImp::SetCompleted()
124 {
125 if (_callback)
126 return _callback->SetCompleted(&_numFiles, &_numBytes);
127 return S_OK;
128 }
129
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback)130 STDMETHODIMP CHandler::Open(IInStream *stream,
131 const UInt64 * /* maxCheckStartPosition */,
132 IArchiveOpenCallback *callback)
133 {
134 COM_TRY_BEGIN
135 {
136 Close();
137 CProgressImp progressImp(callback);
138 RINOK(_archive.Open(stream, &progressImp));
139 bool showVolName = (_archive.LogVols.Size() > 1);
140 for (int volIndex = 0; volIndex < _archive.LogVols.Size(); volIndex++)
141 {
142 const CLogVol &vol = _archive.LogVols[volIndex];
143 bool showFileSetName = (vol.FileSets.Size() > 1);
144 for (int fsIndex = 0; fsIndex < vol.FileSets.Size(); fsIndex++)
145 {
146 const CFileSet &fs = vol.FileSets[fsIndex];
147 for (int i = ((showVolName || showFileSetName) ? 0 : 1); i < fs.Refs.Size(); i++)
148 {
149 CRef2 ref2;
150 ref2.Vol = volIndex;
151 ref2.Fs = fsIndex;
152 ref2.Ref = i;
153 _refs2.Add(ref2);
154 }
155 }
156 }
157 _inStream = stream;
158 }
159 return S_OK;
160 COM_TRY_END
161 }
162
Close()163 STDMETHODIMP CHandler::Close()
164 {
165 _inStream.Release();
166 _archive.Clear();
167 _refs2.Clear();
168 return S_OK;
169 }
170
GetNumberOfItems(UInt32 * numItems)171 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
172 {
173 *numItems = _refs2.Size();
174 return S_OK;
175 }
176
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)177 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
178 {
179 COM_TRY_BEGIN
180 NWindows::NCOM::CPropVariant prop;
181 {
182 const CRef2 &ref2 = _refs2[index];
183 const CLogVol &vol = _archive.LogVols[ref2.Vol];
184 const CRef &ref = vol.FileSets[ref2.Fs].Refs[ref2.Ref];
185 const CFile &file = _archive.Files[ref.FileIndex];
186 const CItem &item = _archive.Items[file.ItemIndex];
187 switch(propID)
188 {
189 case kpidPath: prop = _archive.GetItemPath(ref2.Vol, ref2.Fs, ref2.Ref,
190 _archive.LogVols.Size() > 1, vol.FileSets.Size() > 1); break;
191 case kpidIsDir: prop = item.IsDir(); break;
192 case kpidSize: if (!item.IsDir()) prop = (UInt64)item.Size; break;
193 case kpidPackSize: if (!item.IsDir()) prop = (UInt64)item.NumLogBlockRecorded * vol.BlockSize; break;
194 case kpidMTime: UdfTimeToFileTime(item.MTime, prop); break;
195 case kpidATime: UdfTimeToFileTime(item.ATime, prop); break;
196 }
197 }
198 prop.Detach(value);
199 return S_OK;
200 COM_TRY_END
201 }
202
203 class CBufInStream:
204 public IInStream,
205 public CMyUnknownImp
206 {
207 CByteBuffer _data;
208 UInt64 _pos;
209
210 public:
Init(const CByteBuffer & data)211 void Init(const CByteBuffer &data)
212 {
213 _data = data;
214 _pos = 0;
215 }
216
217 MY_UNKNOWN_IMP
218
219 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
220 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
221 };
222
223
Read(void * data,UInt32 size,UInt32 * processedSize)224 STDMETHODIMP CBufInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
225 {
226 if (processedSize != NULL)
227 *processedSize = 0;
228 if (_pos > _data.GetCapacity())
229 return E_FAIL;
230 size_t rem = _data.GetCapacity() - (size_t)_pos;
231 if (size < rem)
232 rem = (size_t)size;
233 memcpy(data, (const Byte *)_data + _pos, rem);
234 _pos += rem;
235 if (processedSize != NULL)
236 *processedSize = (UInt32)rem;
237 return S_OK;
238 }
239
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)240 STDMETHODIMP CBufInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
241 {
242 switch(seekOrigin)
243 {
244 case STREAM_SEEK_SET: _pos = offset; break;
245 case STREAM_SEEK_CUR: _pos += offset; break;
246 case STREAM_SEEK_END: _pos = _data.GetCapacity() + offset; break;
247 default: return STG_E_INVALIDFUNCTION;
248 }
249 if (newPosition)
250 *newPosition = _pos;
251 return S_OK;
252 }
253
254 struct CSeekExtent
255 {
256 UInt64 Phy;
257 UInt64 Virt;
258 };
259
260 class CExtentsStream:
261 public IInStream,
262 public CMyUnknownImp
263 {
264 UInt64 _phyPos;
265 UInt64 _virtPos;
266 bool _needStartSeek;
267
SeekToPhys()268 HRESULT SeekToPhys() { return Stream->Seek(_phyPos, STREAM_SEEK_SET, NULL); }
269
270 public:
271 CMyComPtr<IInStream> Stream;
272 CRecordVector<CSeekExtent> Extents;
273
274 MY_UNKNOWN_IMP1(IInStream)
275 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
276 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
ReleaseStream()277 void ReleaseStream() { Stream.Release(); }
278
Init()279 void Init()
280 {
281 _virtPos = 0;
282 _phyPos = 0;
283 _needStartSeek = true;
284 }
285
286 };
287
288
Read(void * data,UInt32 size,UInt32 * processedSize)289 STDMETHODIMP CExtentsStream::Read(void *data, UInt32 size, UInt32 *processedSize)
290 {
291 if (processedSize)
292 *processedSize = 0;
293 if (size > 0)
294 {
295 UInt64 totalSize = Extents.Back().Virt;
296 if (_virtPos >= totalSize)
297 return (_virtPos == totalSize) ? S_OK : E_FAIL;
298 int left = 0, right = Extents.Size() - 1;
299 for (;;)
300 {
301 int mid = (left + right) / 2;
302 if (mid == left)
303 break;
304 if (_virtPos < Extents[mid].Virt)
305 right = mid;
306 else
307 left = mid;
308 }
309
310 const CSeekExtent &extent = Extents[left];
311 UInt64 phyPos = extent.Phy + (_virtPos - extent.Virt);
312 if (_needStartSeek || _phyPos != phyPos)
313 {
314 _needStartSeek = false;
315 _phyPos = phyPos;
316 RINOK(SeekToPhys());
317 }
318
319 UInt64 rem = Extents[left + 1].Virt - _virtPos;
320 if (size > rem)
321 size = (UInt32)rem;
322
323 HRESULT res = Stream->Read(data, size, &size);
324 _phyPos += size;
325 _virtPos += size;
326 if (processedSize)
327 *processedSize = size;
328 return res;
329 }
330 return S_OK;
331 }
332
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)333 STDMETHODIMP CExtentsStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
334 {
335 switch(seekOrigin)
336 {
337 case STREAM_SEEK_SET: _virtPos = offset; break;
338 case STREAM_SEEK_CUR: _virtPos += offset; break;
339 case STREAM_SEEK_END: _virtPos = Extents.Back().Virt + offset; break;
340 default: return STG_E_INVALIDFUNCTION;
341 }
342 if (newPosition)
343 *newPosition = _virtPos;
344 return S_OK;
345 }
346
GetStream(UInt32 index,ISequentialInStream ** stream)347 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
348 {
349 *stream = 0;
350
351 const CRef2 &ref2 = _refs2[index];
352 const CLogVol &vol = _archive.LogVols[ref2.Vol];
353 const CRef &ref = vol.FileSets[ref2.Fs].Refs[ref2.Ref];
354 const CFile &file = _archive.Files[ref.FileIndex];
355 const CItem &item = _archive.Items[file.ItemIndex];
356 UInt64 size = item.Size;
357
358 if (!item.IsRecAndAlloc() || !item.CheckChunkSizes() || ! _archive.CheckItemExtents(ref2.Vol, item))
359 return E_NOTIMPL;
360
361 if (item.IsInline)
362 {
363 CBufInStream *inStreamSpec = new CBufInStream;
364 CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
365 inStreamSpec->Init(item.InlineData);
366 *stream = inStream .Detach();
367 return S_OK;
368 }
369
370 CExtentsStream *extentStreamSpec = new CExtentsStream();
371 CMyComPtr<ISequentialInStream> extentStream = extentStreamSpec;
372
373 extentStreamSpec->Stream = _inStream;
374
375 UInt64 virtOffset = 0;
376 for (int extentIndex = 0; extentIndex < item.Extents.Size(); extentIndex++)
377 {
378 const CMyExtent &extent = item.Extents[extentIndex];
379 UInt32 len = extent.GetLen();
380 if (len == 0)
381 continue;
382 if (size < len)
383 return S_FALSE;
384
385 int partitionIndex = vol.PartitionMaps[extent.PartitionRef].PartitionIndex;
386 UInt32 logBlockNumber = extent.Pos;
387 const CPartition &partition = _archive.Partitions[partitionIndex];
388 UInt64 offset = ((UInt64)partition.Pos << _archive.SecLogSize) +
389 (UInt64)logBlockNumber * vol.BlockSize;
390
391 CSeekExtent se;
392 se.Phy = offset;
393 se.Virt = virtOffset;
394 virtOffset += len;
395 extentStreamSpec->Extents.Add(se);
396
397 size -= len;
398 }
399 if (size != 0)
400 return S_FALSE;
401 CSeekExtent se;
402 se.Phy = 0;
403 se.Virt = virtOffset;
404 extentStreamSpec->Extents.Add(se);
405 extentStreamSpec->Init();
406 *stream = extentStream.Detach();
407 return S_OK;
408 }
409
Extract(const UInt32 * indices,UInt32 numItems,Int32 _aTestMode,IArchiveExtractCallback * extractCallback)410 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
411 Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
412 {
413 COM_TRY_BEGIN
414 bool testMode = (_aTestMode != 0);
415 bool allFilesMode = (numItems == UInt32(-1));
416 if (allFilesMode)
417 numItems = _refs2.Size();
418 if (numItems == 0)
419 return S_OK;
420 UInt64 totalSize = 0;
421 UInt32 i;
422
423 for (i = 0; i < numItems; i++)
424 {
425 UInt32 index = (allFilesMode ? i : indices[i]);
426 const CRef2 &ref2 = _refs2[index];
427 const CRef &ref = _archive.LogVols[ref2.Vol].FileSets[ref2.Fs].Refs[ref2.Ref];
428 const CFile &file = _archive.Files[ref.FileIndex];
429 const CItem &item = _archive.Items[file.ItemIndex];
430 if (!item.IsDir())
431 totalSize += item.Size;
432 }
433 extractCallback->SetTotal(totalSize);
434
435 UInt64 currentTotalSize = 0;
436
437 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
438 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
439
440 CLocalProgress *lps = new CLocalProgress;
441 CMyComPtr<ICompressProgressInfo> progress = lps;
442 lps->Init(extractCallback, false);
443
444 CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
445 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
446
447 for (i = 0; i < numItems; i++)
448 {
449 lps->InSize = lps->OutSize = currentTotalSize;
450 RINOK(lps->SetCur());
451 CMyComPtr<ISequentialOutStream> realOutStream;
452 Int32 askMode = testMode ?
453 NArchive::NExtract::NAskMode::kTest :
454 NArchive::NExtract::NAskMode::kExtract;
455 UInt32 index = allFilesMode ? i : indices[i];
456
457 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
458
459 const CRef2 &ref2 = _refs2[index];
460 const CRef &ref = _archive.LogVols[ref2.Vol].FileSets[ref2.Fs].Refs[ref2.Ref];
461 const CFile &file = _archive.Files[ref.FileIndex];
462 const CItem &item = _archive.Items[file.ItemIndex];
463
464 if (item.IsDir())
465 {
466 RINOK(extractCallback->PrepareOperation(askMode));
467 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
468 continue;
469 }
470 currentTotalSize += item.Size;
471
472 if (!testMode && !realOutStream)
473 continue;
474
475 RINOK(extractCallback->PrepareOperation(askMode));
476 outStreamSpec->SetStream(realOutStream);
477 realOutStream.Release();
478 outStreamSpec->Init(item.Size);
479 Int32 opRes;
480 CMyComPtr<ISequentialInStream> udfInStream;
481 HRESULT res = GetStream(index, &udfInStream);
482 if (res == E_NOTIMPL)
483 opRes = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
484 else if (res != S_OK)
485 opRes = NArchive::NExtract::NOperationResult::kDataError;
486 else
487 {
488 RINOK(copyCoder->Code(udfInStream, outStream, NULL, NULL, progress));
489 opRes = outStreamSpec->IsFinishedOK() ?
490 NArchive::NExtract::NOperationResult::kOK:
491 NArchive::NExtract::NOperationResult::kDataError;
492 }
493 outStreamSpec->ReleaseStream();
494 RINOK(extractCallback->SetOperationResult(opRes));
495 }
496 return S_OK;
497 COM_TRY_END
498 }
499
500 }}
501