tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HashCalc.cpp (8180B)


      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:
     21  CHashMidBuf(): _data(0) {}
     22  operator void *() { return _data; }
     23  bool Alloc(size_t size)
     24  {
     25    if (_data != 0)
     26      return false;
     27    _data = ::MidAlloc(size);
     28    return _data != 0;
     29  }
     30  ~CHashMidBuf() { ::MidFree(_data); }
     31 };
     32 
     33 static const char * const k_DefaultHashMethod = "CRC32";
     34 
     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 
    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 
    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 
    125 void CHashBundle::SetSize(UInt64 size)
    126 {
    127  CurSize = size;
    128 }
    129 
    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 
    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 
    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 
    313 static inline char GetHex(unsigned v)
    314 {
    315  return (char)((v < 10) ? ('0' + v) : ('A' + (v - 10)));
    316 }
    317 
    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 }