1 // HashCalc.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/Alloc.h"
6
7 #include "../../../Common/StringToInt.h"
8
9 #include "../../Common/FileStreams.h"
10 #include "../../Common/StreamUtils.h"
11
12 #include "EnumDirItems.h"
13 #include "HashCalc.h"
14
15 using namespace NWindows;
16
17 class CHashMidBuf
18 {
19 void *_data;
20 public:
CHashMidBuf()21 CHashMidBuf(): _data(0) {}
operator void*()22 operator void *() { return _data; }
Alloc(size_t size)23 bool Alloc(size_t size)
24 {
25 if (_data != 0)
26 return false;
27 _data = ::MidAlloc(size);
28 return _data != 0;
29 }
~CHashMidBuf()30 ~CHashMidBuf() { ::MidFree(_data); }
31 };
32
33 static const char * const k_DefaultHashMethod = "CRC32";
34
SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector & hashMethods)35 HRESULT CHashBundle::SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &hashMethods)
36 {
37 UStringVector names = hashMethods;
38 if (names.IsEmpty())
39 names.Add(UString(k_DefaultHashMethod));
40
41 CRecordVector<CMethodId> ids;
42 CObjectVector<COneMethodInfo> methods;
43
44 unsigned i;
45 for (i = 0; i < names.Size(); i++)
46 {
47 COneMethodInfo m;
48 RINOK(m.ParseMethodFromString(names[i]));
49
50 if (m.MethodName.IsEmpty())
51 m.MethodName = k_DefaultHashMethod;
52
53 if (m.MethodName == "*")
54 {
55 CRecordVector<CMethodId> tempMethods;
56 GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods);
57 methods.Clear();
58 ids.Clear();
59 FOR_VECTOR (t, tempMethods)
60 {
61 unsigned index = ids.AddToUniqueSorted(tempMethods[t]);
62 if (ids.Size() != methods.Size())
63 methods.Insert(index, m);
64 }
65 break;
66 }
67 else
68 {
69 // m.MethodName.RemoveChar(L'-');
70 CMethodId id;
71 if (!FindHashMethod(EXTERNAL_CODECS_LOC_VARS m.MethodName, id))
72 return E_NOTIMPL;
73 unsigned index = ids.AddToUniqueSorted(id);
74 if (ids.Size() != methods.Size())
75 methods.Insert(index, m);
76 }
77 }
78
79 for (i = 0; i < ids.Size(); i++)
80 {
81 CMyComPtr<IHasher> hasher;
82 AString name;
83 RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS ids[i], name, hasher));
84 if (!hasher)
85 throw "Can't create hasher";
86 const COneMethodInfo &m = methods[i];
87 {
88 CMyComPtr<ICompressSetCoderProperties> scp;
89 hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp);
90 if (scp)
91 RINOK(m.SetCoderProps(scp, NULL));
92 }
93 UInt32 digestSize = hasher->GetDigestSize();
94 if (digestSize > k_HashCalc_DigestSize_Max)
95 return E_NOTIMPL;
96 CHasherState &h = Hashers.AddNew();
97 h.Hasher = hasher;
98 h.Name = name;
99 h.DigestSize = digestSize;
100 for (unsigned k = 0; k < k_HashCalc_NumGroups; k++)
101 memset(h.Digests[k], 0, digestSize);
102 }
103
104 return S_OK;
105 }
106
InitForNewFile()107 void CHashBundle::InitForNewFile()
108 {
109 CurSize = 0;
110 FOR_VECTOR (i, Hashers)
111 {
112 CHasherState &h = Hashers[i];
113 h.Hasher->Init();
114 memset(h.Digests[k_HashCalc_Index_Current], 0, h.DigestSize);
115 }
116 }
117
Update(const void * data,UInt32 size)118 void CHashBundle::Update(const void *data, UInt32 size)
119 {
120 CurSize += size;
121 FOR_VECTOR (i, Hashers)
122 Hashers[i].Hasher->Update(data, size);
123 }
124
SetSize(UInt64 size)125 void CHashBundle::SetSize(UInt64 size)
126 {
127 CurSize = size;
128 }
129
AddDigests(Byte * dest,const Byte * src,UInt32 size)130 static void AddDigests(Byte *dest, const Byte *src, UInt32 size)
131 {
132 unsigned next = 0;
133 for (UInt32 i = 0; i < size; i++)
134 {
135 next += (unsigned)dest[i] + (unsigned)src[i];
136 dest[i] = (Byte)next;
137 next >>= 8;
138 }
139 }
140
Final(bool isDir,bool isAltStream,const UString & path)141 void CHashBundle::Final(bool isDir, bool isAltStream, const UString &path)
142 {
143 if (isDir)
144 NumDirs++;
145 else if (isAltStream)
146 {
147 NumAltStreams++;
148 AltStreamsSize += CurSize;
149 }
150 else
151 {
152 NumFiles++;
153 FilesSize += CurSize;
154 }
155
156 Byte pre[16];
157 memset(pre, 0, sizeof(pre));
158 if (isDir)
159 pre[0] = 1;
160
161 FOR_VECTOR (i, Hashers)
162 {
163 CHasherState &h = Hashers[i];
164 if (!isDir)
165 {
166 h.Hasher->Final(h.Digests[0]);
167 if (!isAltStream)
168 AddDigests(h.Digests[k_HashCalc_Index_DataSum], h.Digests[0], h.DigestSize);
169 }
170
171 h.Hasher->Init();
172 h.Hasher->Update(pre, sizeof(pre));
173 h.Hasher->Update(h.Digests[0], h.DigestSize);
174
175 for (unsigned k = 0; k < path.Len(); k++)
176 {
177 wchar_t c = path[k];
178 Byte temp[2] = { (Byte)(c & 0xFF), (Byte)((c >> 8) & 0xFF) };
179 h.Hasher->Update(temp, 2);
180 }
181
182 Byte tempDigest[k_HashCalc_DigestSize_Max];
183 h.Hasher->Final(tempDigest);
184 if (!isAltStream)
185 AddDigests(h.Digests[k_HashCalc_Index_NamesSum], tempDigest, h.DigestSize);
186 AddDigests(h.Digests[k_HashCalc_Index_StreamsSum], tempDigest, h.DigestSize);
187 }
188 }
189
190
HashCalc(DECL_EXTERNAL_CODECS_LOC_VARS const NWildcard::CCensor & censor,const CHashOptions & options,AString & errorInfo,IHashCallbackUI * callback)191 HRESULT HashCalc(
192 DECL_EXTERNAL_CODECS_LOC_VARS
193 const NWildcard::CCensor &censor,
194 const CHashOptions &options,
195 AString &errorInfo,
196 IHashCallbackUI *callback)
197 {
198 CDirItems dirItems;
199 dirItems.Callback = callback;
200
201 if (options.StdInMode)
202 {
203 CDirItem di;
204 di.Size = (UInt64)(Int64)-1;
205 di.Attrib = 0;
206 di.MTime.dwLowDateTime = 0;
207 di.MTime.dwHighDateTime = 0;
208 di.CTime = di.ATime = di.MTime;
209 dirItems.Items.Add(di);
210 }
211 else
212 {
213 RINOK(callback->StartScanning());
214 dirItems.ScanAltStreams = options.AltStreamsMode;
215
216 HRESULT res = EnumerateItems(censor,
217 options.PathMode,
218 UString(),
219 dirItems);
220
221 if (res != S_OK)
222 {
223 if (res != E_ABORT)
224 errorInfo = "Scanning error";
225 return res;
226 }
227 RINOK(callback->FinishScanning(dirItems.Stat));
228 }
229
230 unsigned i;
231 CHashBundle hb;
232 RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS options.Methods));
233 // hb.Init();
234
235 hb.NumErrors = dirItems.Stat.NumErrors;
236
237 if (options.StdInMode)
238 {
239 RINOK(callback->SetNumFiles(1));
240 }
241 else
242 {
243 RINOK(callback->SetTotal(dirItems.Stat.GetTotalBytes()));
244 }
245
246 const UInt32 kBufSize = 1 << 15;
247 CHashMidBuf buf;
248 if (!buf.Alloc(kBufSize))
249 return E_OUTOFMEMORY;
250
251 UInt64 completeValue = 0;
252
253 RINOK(callback->BeforeFirstFile(hb));
254
255 for (i = 0; i < dirItems.Items.Size(); i++)
256 {
257 CMyComPtr<ISequentialInStream> inStream;
258 UString path;
259 bool isDir = false;
260 bool isAltStream = false;
261 if (options.StdInMode)
262 {
263 inStream = new CStdInFileStream;
264 }
265 else
266 {
267 CInFileStream *inStreamSpec = new CInFileStream;
268 inStream = inStreamSpec;
269 const CDirItem &dirItem = dirItems.Items[i];
270 isDir = dirItem.IsDir();
271 isAltStream = dirItem.IsAltStream;
272 path = dirItems.GetLogPath(i);
273 if (!isDir)
274 {
275 FString phyPath = dirItems.GetPhyPath(i);
276 if (!inStreamSpec->OpenShared(phyPath, options.OpenShareForWrite))
277 {
278 HRESULT res = callback->OpenFileError(phyPath, ::GetLastError());
279 hb.NumErrors++;
280 if (res != S_FALSE)
281 return res;
282 continue;
283 }
284 }
285 }
286 RINOK(callback->GetStream(path, isDir));
287 UInt64 fileSize = 0;
288
289 hb.InitForNewFile();
290 if (!isDir)
291 {
292 for (UInt32 step = 0;; step++)
293 {
294 if ((step & 0xFF) == 0)
295 RINOK(callback->SetCompleted(&completeValue));
296 UInt32 size;
297 RINOK(inStream->Read(buf, kBufSize, &size));
298 if (size == 0)
299 break;
300 hb.Update(buf, size);
301 fileSize += size;
302 completeValue += size;
303 }
304 }
305 hb.Final(isDir, isAltStream, path);
306 RINOK(callback->SetOperationResult(fileSize, hb, !isDir));
307 RINOK(callback->SetCompleted(&completeValue));
308 }
309 return callback->AfterLastFile(hb);
310 }
311
312
GetHex(unsigned v)313 static inline char GetHex(unsigned v)
314 {
315 return (char)((v < 10) ? ('0' + v) : ('A' + (v - 10)));
316 }
317
AddHashHexToString(char * dest,const Byte * data,UInt32 size)318 void AddHashHexToString(char *dest, const Byte *data, UInt32 size)
319 {
320 dest[size * 2] = 0;
321
322 if (!data)
323 {
324 for (UInt32 i = 0; i < size; i++)
325 {
326 dest[0] = ' ';
327 dest[1] = ' ';
328 dest += 2;
329 }
330 return;
331 }
332
333 int step = 2;
334 if (size <= 8)
335 {
336 step = -2;
337 dest += size * 2 - 2;
338 }
339
340 for (UInt32 i = 0; i < size; i++)
341 {
342 unsigned b = data[i];
343 dest[0] = GetHex((b >> 4) & 0xF);
344 dest[1] = GetHex(b & 0xF);
345 dest += step;
346 }
347 }
348