1 // HfsHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "Common/ComTry.h"
6 #include "Windows/PropVariant.h"
7 #include "../../Common/StreamUtils.h"
8 #include "HfsHandler.h"
9 
10 namespace NArchive {
11 namespace NHfs {
12 
13 STATPROPSTG kProps[] =
14 {
15   { NULL, kpidPath, VT_BSTR},
16   { NULL, kpidIsDir, VT_BOOL},
17   { NULL, kpidSize, VT_UI8},
18   { NULL, kpidPackSize, VT_UI8},
19   { NULL, kpidCTime, VT_FILETIME},
20   { NULL, kpidMTime, VT_FILETIME},
21   { NULL, kpidATime, VT_FILETIME}
22 };
23 
24 STATPROPSTG kArcProps[] =
25 {
26   { NULL, kpidMethod, VT_BSTR},
27   { NULL, kpidClusterSize, VT_UI4},
28   { NULL, kpidFreeSpace, VT_UI8},
29   { NULL, kpidCTime, VT_FILETIME},
30   { NULL, kpidMTime, VT_FILETIME}
31 };
32 
33 IMP_IInArchive_Props
34 IMP_IInArchive_ArcProps
35 
HfsTimeToProp(UInt32 hfsTime,NWindows::NCOM::CPropVariant & prop)36 static void HfsTimeToProp(UInt32 hfsTime, NWindows::NCOM::CPropVariant &prop)
37 {
38   FILETIME ft;
39   HfsTimeToFileTime(hfsTime, ft);
40   prop = ft;
41 }
42 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)43 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
44 {
45   COM_TRY_BEGIN
46   NWindows::NCOM::CPropVariant prop;
47   switch(propID)
48   {
49     case kpidMethod: prop = _db.Header.IsHfsX() ? L"HFSX" : L"HFS+"; break;
50     case kpidClusterSize: prop = (UInt32)1 << _db.Header.BlockSizeLog; break;
51     case kpidFreeSpace: prop = (UInt64)_db.Header.NumFreeBlocks << _db.Header.BlockSizeLog; break;
52     case kpidMTime: HfsTimeToProp(_db.Header.MTime, prop); break;
53     case kpidCTime:
54     {
55       FILETIME localFt, ft;
56       HfsTimeToFileTime(_db.Header.CTime, localFt);
57       if (LocalFileTimeToFileTime(&localFt, &ft))
58         prop = ft;
59       break;
60     }
61   }
62   prop.Detach(value);
63   return S_OK;
64   COM_TRY_END
65 }
66 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)67 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
68 {
69   COM_TRY_BEGIN
70   NWindows::NCOM::CPropVariant prop;
71   const CItem &item = _db.Items[index];
72   switch(propID)
73   {
74     case kpidPath: prop = _db.GetItemPath(index); break;
75     case kpidIsDir: prop = item.IsDir(); break;
76 
77     case kpidCTime:  HfsTimeToProp(item.CTime, prop); break;
78     case kpidMTime:  HfsTimeToProp(item.MTime, prop); break;
79     case kpidATime:  HfsTimeToProp(item.ATime, prop); break;
80 
81     case kpidPackSize: if (!item.IsDir()) prop = (UInt64)item.NumBlocks << _db.Header.BlockSizeLog; break;
82     case kpidSize:     if (!item.IsDir()) prop = item.Size; break;
83   }
84   prop.Detach(value);
85   return S_OK;
86   COM_TRY_END
87 }
88 
89 class CProgressImp: public CProgressVirt
90 {
91   CMyComPtr<IArchiveOpenCallback> _callback;
92 public:
93   HRESULT SetTotal(UInt64 numFiles);
94   HRESULT SetCompleted(UInt64 numFiles);
CProgressImp(IArchiveOpenCallback * callback)95   CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {}
96 };
97 
SetTotal(UInt64 numFiles)98 HRESULT CProgressImp::SetTotal(UInt64 numFiles)
99 {
100   if (_callback)
101     return _callback->SetTotal(&numFiles, NULL);
102   return S_OK;
103 }
104 
SetCompleted(UInt64 numFiles)105 HRESULT CProgressImp::SetCompleted(UInt64 numFiles)
106 {
107   if (_callback)
108     return _callback->SetCompleted(&numFiles, NULL);
109   return S_OK;
110 }
111 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)112 STDMETHODIMP CHandler::Open(IInStream *inStream,
113     const UInt64 * /* maxCheckStartPosition */,
114     IArchiveOpenCallback *callback)
115 {
116   COM_TRY_BEGIN
117   Close();
118   try
119   {
120     CProgressImp progressImp(callback);
121     HRESULT res = _db.Open(inStream, &progressImp);
122     if (res == E_ABORT)
123       return res;
124     if (res != S_OK)
125       return S_FALSE;
126     _stream = inStream;
127   }
128   catch(...) { return S_FALSE; }
129   return S_OK;
130   COM_TRY_END
131 }
132 
Close()133 STDMETHODIMP CHandler::Close()
134 {
135   _stream.Release();
136   _db.Clear();
137   return S_OK;
138 }
139 
Extract(const UInt32 * indices,UInt32 numItems,Int32 _aTestMode,IArchiveExtractCallback * extractCallback)140 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
141     Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
142 {
143   COM_TRY_BEGIN
144   bool testMode = (_aTestMode != 0);
145   bool allFilesMode = (numItems == UInt32(-1));
146   if (allFilesMode)
147     numItems = _db.Items.Size();
148   if (numItems == 0)
149     return S_OK;
150   UInt32 i;
151   UInt64 totalSize = 0;
152   for (i = 0; i < numItems; i++)
153   {
154     const CItem &item = _db.Items[allFilesMode ? i : indices[i]];
155     if (!item.IsDir())
156       totalSize += item.Size;
157   }
158   RINOK(extractCallback->SetTotal(totalSize));
159 
160   UInt64 currentTotalSize = 0, currentItemSize = 0;
161 
162   CByteBuffer buf;
163   const UInt32 kBufSize = (1 << 16);
164   buf.SetCapacity(kBufSize);
165 
166   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
167   {
168     RINOK(extractCallback->SetCompleted(&currentTotalSize));
169     Int32 index = allFilesMode ? i : indices[i];
170     const CItem &item = _db.Items[index];
171     currentItemSize = 0;
172     if (!item.IsDir())
173       currentItemSize = item.Size;
174 
175     CMyComPtr<ISequentialOutStream> realOutStream;
176     Int32 askMode = testMode ?
177         NArchive::NExtract::NAskMode::kTest :
178         NArchive::NExtract::NAskMode::kExtract;
179     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
180 
181     if (item.IsDir())
182     {
183       RINOK(extractCallback->PrepareOperation(askMode));
184       RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
185       continue;
186     }
187     if (!testMode && (!realOutStream))
188       continue;
189     RINOK(extractCallback->PrepareOperation(askMode));
190     UInt64 pos = 0;
191     int res = NArchive::NExtract::NOperationResult::kOK;
192     int i;
193     for (i = 0; i < item.Extents.Size(); i++)
194     {
195       if (item.Size == pos)
196         break;
197       if (res != NArchive::NExtract::NOperationResult::kOK)
198         break;
199       const CExtent &e = item.Extents[i];
200       RINOK(_stream->Seek((UInt64)e.Pos << _db.Header.BlockSizeLog, STREAM_SEEK_SET, NULL));
201       UInt64 extentSize = (UInt64)e.NumBlocks << _db.Header.BlockSizeLog;
202       for (;;)
203       {
204         if (extentSize == 0)
205           break;
206         UInt64 rem = item.Size - pos;
207         if (rem == 0)
208         {
209           if (extentSize >= (UInt64)((UInt32)1 << _db.Header.BlockSizeLog))
210             res = NArchive::NExtract::NOperationResult::kDataError;
211           break;
212         }
213         UInt32 curSize = kBufSize;
214         if (curSize > rem)
215           curSize = (UInt32)rem;
216         if (curSize > extentSize)
217           curSize = (UInt32)extentSize;
218         RINOK(ReadStream_FALSE(_stream, buf, curSize));
219         if (realOutStream)
220         {
221           RINOK(WriteStream(realOutStream, buf, curSize));
222         }
223         pos += curSize;
224         extentSize -= curSize;
225         UInt64 processed = currentTotalSize + pos;
226         RINOK(extractCallback->SetCompleted(&processed));
227       }
228     }
229     if (i != item.Extents.Size() || item.Size != pos)
230       res = NArchive::NExtract::NOperationResult::kDataError;
231     realOutStream.Release();
232     RINOK(extractCallback->SetOperationResult(res));
233   }
234   return S_OK;
235   COM_TRY_END
236 }
237 
GetNumberOfItems(UInt32 * numItems)238 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
239 {
240   *numItems = _db.Items.Size();
241   return S_OK;
242 }
243 
244 }}
245