Home | History | Annotate | Download | only in Common
      1 // UpdateCallback.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../Common/ComTry.h"
      6 #include "../../../Common/IntToString.h"
      7 #include "../../../Common/StringConvert.h"
      8 #include "../../../Common/Wildcard.h"
      9 
     10 #include "../../../Windows/FileDir.h"
     11 #include "../../../Windows/FileName.h"
     12 #include "../../../Windows/PropVariant.h"
     13 #include "../../../Windows/Synchronization.h"
     14 
     15 #include "../../Common/FileStreams.h"
     16 #include "../../Common/StreamObjects.h"
     17 
     18 #include "UpdateCallback.h"
     19 
     20 #if defined(_WIN32) && !defined(UNDER_CE)
     21 #define _USE_SECURITY_CODE
     22 #include "../../../Windows/SecurityUtils.h"
     23 #endif
     24 
     25 using namespace NWindows;
     26 using namespace NFile;
     27 
     28 #ifdef _USE_SECURITY_CODE
     29 bool InitLocalPrivileges();
     30 #endif
     31 
     32 CArchiveUpdateCallback::CArchiveUpdateCallback():
     33   Callback(0),
     34   ShareForWrite(false),
     35   StdInMode(false),
     36   DirItems(0),
     37   ArcItems(0),
     38   UpdatePairs(0),
     39   NewNames(0),
     40   KeepOriginalItemNames(false),
     41   ProcessedItemsStatuses(NULL),
     42   ParentDirItem(NULL),
     43   StoreNtSecurity(false),
     44   StoreHardLinks(false),
     45   StoreSymLinks(false),
     46   _hardIndex_From((UInt32)(Int32)-1)
     47 {
     48   #ifdef _USE_SECURITY_CODE
     49   _saclEnabled = InitLocalPrivileges();
     50   #endif
     51 }
     52 
     53 
     54 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
     55 {
     56   COM_TRY_BEGIN
     57   return Callback->SetTotal(size);
     58   COM_TRY_END
     59 }
     60 
     61 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
     62 {
     63   COM_TRY_BEGIN
     64   return Callback->SetCompleted(completeValue);
     65   COM_TRY_END
     66 }
     67 
     68 STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
     69 {
     70   COM_TRY_BEGIN
     71   return Callback->SetRatioInfo(inSize, outSize);
     72   COM_TRY_END
     73 }
     74 
     75 
     76 /*
     77 static const STATPROPSTG kProps[] =
     78 {
     79   { NULL, kpidPath, VT_BSTR},
     80   { NULL, kpidIsDir, VT_BOOL},
     81   { NULL, kpidSize, VT_UI8},
     82   { NULL, kpidCTime, VT_FILETIME},
     83   { NULL, kpidATime, VT_FILETIME},
     84   { NULL, kpidMTime, VT_FILETIME},
     85   { NULL, kpidAttrib, VT_UI4},
     86   { NULL, kpidIsAnti, VT_BOOL}
     87 };
     88 
     89 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
     90 {
     91   return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator);
     92 }
     93 */
     94 
     95 STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
     96       Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)
     97 {
     98   COM_TRY_BEGIN
     99   RINOK(Callback->CheckBreak());
    100   const CUpdatePair2 &up = (*UpdatePairs)[index];
    101   if (newData) *newData = BoolToInt(up.NewData);
    102   if (newProps) *newProps = BoolToInt(up.NewProps);
    103   if (indexInArchive)
    104   {
    105     *indexInArchive = (UInt32)(Int32)-1;
    106     if (up.ExistInArchive())
    107       *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer;
    108   }
    109   return S_OK;
    110   COM_TRY_END
    111 }
    112 
    113 STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value)
    114 {
    115   NCOM::CPropVariant prop;
    116   switch (propID)
    117   {
    118     case kpidIsDir:  prop = true; break;
    119     case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break;
    120     case kpidCTime:  if (ParentDirItem) prop = ParentDirItem->CTime; break;
    121     case kpidATime:  if (ParentDirItem) prop = ParentDirItem->ATime; break;
    122     case kpidMTime:  if (ParentDirItem) prop = ParentDirItem->MTime; break;
    123   }
    124   prop.Detach(value);
    125   return S_OK;
    126 }
    127 
    128 STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType)
    129 {
    130   *parentType = NParentType::kDir;
    131   *parent = (UInt32)(Int32)-1;
    132   return S_OK;
    133 }
    134 
    135 STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps)
    136 {
    137   *numProps = 0;
    138   if (StoreNtSecurity)
    139     *numProps = 1;
    140   return S_OK;
    141 }
    142 
    143 STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
    144 {
    145   *name = NULL;
    146   *propID = kpidNtSecure;
    147   return S_OK;
    148 }
    149 
    150 STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID
    151     #ifdef _USE_SECURITY_CODE
    152     propID
    153     #endif
    154     , const void **data, UInt32 *dataSize, UInt32 *propType)
    155 {
    156   *data = 0;
    157   *dataSize = 0;
    158   *propType = 0;
    159   if (!StoreNtSecurity)
    160     return S_OK;
    161   #ifdef _USE_SECURITY_CODE
    162   if (propID == kpidNtSecure)
    163   {
    164     if (StdInMode)
    165       return S_OK;
    166 
    167     if (ParentDirItem)
    168     {
    169       if (ParentDirItem->SecureIndex < 0)
    170         return S_OK;
    171       const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex];
    172       *data = buf;
    173       *dataSize = (UInt32)buf.Size();
    174       *propType = NPropDataType::kRaw;
    175       return S_OK;
    176     }
    177 
    178     if (GetRootProps)
    179       return GetRootProps->GetRootRawProp(propID, data, dataSize, propType);
    180   }
    181   #endif
    182   return S_OK;
    183 }
    184 
    185 //    #ifdef _USE_SECURITY_CODE
    186 //    #endif
    187 
    188 STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
    189 {
    190   *data = 0;
    191   *dataSize = 0;
    192   *propType = 0;
    193 
    194   if (propID == kpidNtSecure ||
    195       propID == kpidNtReparse)
    196   {
    197     if (StdInMode)
    198       return S_OK;
    199 
    200     const CUpdatePair2 &up = (*UpdatePairs)[index];
    201     if (up.UseArcProps && up.ExistInArchive() && GetRawProps)
    202       return GetRawProps->GetRawProp(
    203           ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex,
    204           propID, data, dataSize, propType);
    205 
    206     {
    207       const CUpdatePair2 &up = (*UpdatePairs)[index];
    208       /*
    209       if (!up.NewData)
    210         return E_FAIL;
    211       */
    212       if (up.IsAnti)
    213         return S_OK;
    214 
    215       #ifndef UNDER_CE
    216       const CDirItem &di = DirItems->Items[up.DirIndex];
    217       #endif
    218 
    219       #ifdef _USE_SECURITY_CODE
    220       if (propID == kpidNtSecure)
    221       {
    222         if (!StoreNtSecurity)
    223           return S_OK;
    224         if (di.SecureIndex < 0)
    225           return S_OK;
    226         const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex];
    227         *data = buf;
    228         *dataSize = (UInt32)buf.Size();
    229         *propType = NPropDataType::kRaw;
    230       }
    231       else
    232       #endif
    233       {
    234         // propID == kpidNtReparse
    235         if (!StoreSymLinks)
    236           return S_OK;
    237         #ifndef UNDER_CE
    238         const CByteBuffer *buf = &di.ReparseData2;
    239         if (buf->Size() == 0)
    240           buf = &di.ReparseData;
    241         if (buf->Size() != 0)
    242         {
    243           *data = *buf;
    244           *dataSize = (UInt32)buf->Size();
    245           *propType = NPropDataType::kRaw;
    246         }
    247         #endif
    248       }
    249 
    250       return S_OK;
    251     }
    252   }
    253 
    254   return S_OK;
    255 }
    256 
    257 #ifndef UNDER_CE
    258 
    259 static UString GetRelativePath(const UString &to, const UString &from)
    260 {
    261   UStringVector partsTo, partsFrom;
    262   SplitPathToParts(to, partsTo);
    263   SplitPathToParts(from, partsFrom);
    264 
    265   unsigned i;
    266   for (i = 0;; i++)
    267   {
    268     if (i + 1 >= partsFrom.Size() ||
    269         i + 1 >= partsTo.Size())
    270       break;
    271     if (CompareFileNames(partsFrom[i], partsTo[i]) != 0)
    272       break;
    273   }
    274 
    275   if (i == 0)
    276   {
    277     #ifdef _WIN32
    278     if (NName::IsDrivePath(to) ||
    279         NName::IsDrivePath(from))
    280       return to;
    281     #endif
    282   }
    283 
    284   UString s;
    285   unsigned k;
    286 
    287   for (k = i + 1; k < partsFrom.Size(); k++)
    288     s += L".." WSTRING_PATH_SEPARATOR;
    289 
    290   for (k = i; k < partsTo.Size(); k++)
    291   {
    292     if (k != i)
    293       s += WCHAR_PATH_SEPARATOR;
    294     s += partsTo[k];
    295   }
    296 
    297   return s;
    298 }
    299 
    300 #endif
    301 
    302 STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
    303 {
    304   COM_TRY_BEGIN
    305   const CUpdatePair2 &up = (*UpdatePairs)[index];
    306   NCOM::CPropVariant prop;
    307 
    308   if (up.NewData)
    309   {
    310     /*
    311     if (propID == kpidIsHardLink)
    312     {
    313       prop = _isHardLink;
    314       prop.Detach(value);
    315       return S_OK;
    316     }
    317     */
    318     if (propID == kpidSymLink)
    319     {
    320       if (index == _hardIndex_From)
    321       {
    322         prop.Detach(value);
    323         return S_OK;
    324       }
    325       if (up.DirIndex >= 0)
    326       {
    327         #ifndef UNDER_CE
    328         const CDirItem &di = DirItems->Items[up.DirIndex];
    329         // if (di.IsDir())
    330         {
    331           CReparseAttr attr;
    332           if (attr.Parse(di.ReparseData, di.ReparseData.Size()))
    333           {
    334             UString simpleName = attr.GetPath();
    335             if (attr.IsRelative())
    336               prop = simpleName;
    337             else
    338             {
    339               const UString phyPath = DirItems->GetPhyPath(up.DirIndex);
    340               FString fullPath;
    341               if (NDir::MyGetFullPathName(us2fs(phyPath), fullPath))
    342               {
    343                 prop = GetRelativePath(simpleName, fs2us(fullPath));
    344               }
    345             }
    346             prop.Detach(value);
    347             return S_OK;
    348           }
    349         }
    350         #endif
    351       }
    352     }
    353     else if (propID == kpidHardLink)
    354     {
    355       if (index == _hardIndex_From)
    356       {
    357         const CKeyKeyValPair &pair = _map[_hardIndex_To];
    358         const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value];
    359         prop = DirItems->GetLogPath(up2.DirIndex);
    360         prop.Detach(value);
    361         return S_OK;
    362       }
    363       if (up.DirIndex >= 0)
    364       {
    365         prop.Detach(value);
    366         return S_OK;
    367       }
    368     }
    369   }
    370 
    371   if (up.IsAnti
    372       && propID != kpidIsDir
    373       && propID != kpidPath
    374       && propID != kpidIsAltStream)
    375   {
    376     switch (propID)
    377     {
    378       case kpidSize:  prop = (UInt64)0; break;
    379       case kpidIsAnti:  prop = true; break;
    380     }
    381   }
    382   else if (propID == kpidPath && up.NewNameIndex >= 0)
    383     prop = (*NewNames)[up.NewNameIndex];
    384   else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem)
    385   {
    386     // we can generate new ShortName here;
    387   }
    388   else if ((up.UseArcProps
    389       || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream)))
    390       && up.ExistInArchive() && Archive)
    391     return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value);
    392   else if (up.ExistOnDisk())
    393   {
    394     const CDirItem &di = DirItems->Items[up.DirIndex];
    395     switch (propID)
    396     {
    397       case kpidPath:  prop = DirItems->GetLogPath(up.DirIndex); break;
    398       case kpidIsDir:  prop = di.IsDir(); break;
    399       case kpidSize:  prop = di.Size; break;
    400       case kpidAttrib:  prop = di.Attrib; break;
    401       case kpidCTime:  prop = di.CTime; break;
    402       case kpidATime:  prop = di.ATime; break;
    403       case kpidMTime:  prop = di.MTime; break;
    404       case kpidIsAltStream:  prop = di.IsAltStream; break;
    405       #if defined(_WIN32) && !defined(UNDER_CE)
    406       // case kpidShortName:  prop = di.ShortName; break;
    407       #endif
    408     }
    409   }
    410   prop.Detach(value);
    411   return S_OK;
    412   COM_TRY_END
    413 }
    414 
    415 static NSynchronization::CCriticalSection CS;
    416 
    417 STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
    418 {
    419   COM_TRY_BEGIN
    420   *inStream = NULL;
    421   const CUpdatePair2 &up = (*UpdatePairs)[index];
    422   if (!up.NewData)
    423     return E_FAIL;
    424 
    425   RINOK(Callback->CheckBreak());
    426   RINOK(Callback->Finilize());
    427 
    428   bool isDir = IsDir(up);
    429 
    430   if (up.IsAnti)
    431   {
    432     UString name;
    433     if (up.ArcIndex >= 0)
    434       name = (*ArcItems)[up.ArcIndex].Name;
    435     else if (up.DirIndex >= 0)
    436       name = DirItems->GetLogPath(up.DirIndex);
    437     RINOK(Callback->GetStream(name, true));
    438 
    439     /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file.
    440        so we return empty stream */
    441 
    442     if (!isDir)
    443     {
    444       CBufInStream *inStreamSpec = new CBufInStream();
    445       CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
    446       inStreamSpec->Init(NULL, 0);
    447       *inStream = inStreamLoc.Detach();
    448     }
    449     return S_OK;
    450   }
    451 
    452   RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false));
    453 
    454   if (isDir)
    455     return S_OK;
    456 
    457   if (StdInMode)
    458   {
    459     CStdInFileStream *inStreamSpec = new CStdInFileStream;
    460     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
    461     *inStream = inStreamLoc.Detach();
    462   }
    463   else
    464   {
    465     CInFileStream *inStreamSpec = new CInFileStream;
    466     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
    467 
    468     inStreamSpec->SupportHardLinks = StoreHardLinks;
    469 
    470     const UString path = DirItems->GetPhyPath(up.DirIndex);
    471 
    472     #if defined(_WIN32) && !defined(UNDER_CE)
    473     if (DirItems->Items[up.DirIndex].AreReparseData())
    474     {
    475       if (!inStreamSpec->File.OpenReparse(us2fs(path)))
    476       {
    477         return Callback->OpenFileError(path, ::GetLastError());
    478       }
    479     }
    480     else
    481     #endif
    482     if (!inStreamSpec->OpenShared(us2fs(path), ShareForWrite))
    483     {
    484       return Callback->OpenFileError(path, ::GetLastError());
    485     }
    486 
    487     if (StoreHardLinks)
    488     {
    489       CStreamFileProps props;
    490       if (inStreamSpec->GetProps2(&props) == S_OK)
    491       {
    492         if (props.NumLinks > 1)
    493         {
    494           CKeyKeyValPair pair;
    495           pair.Key1 = props.VolID;
    496           pair.Key2 = props.FileID_Low;
    497           pair.Value = index;
    498           unsigned numItems = _map.Size();
    499           unsigned pairIndex = _map.AddToUniqueSorted2(pair);
    500           if (numItems == _map.Size())
    501           {
    502             // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex];
    503             _hardIndex_From = index;
    504             _hardIndex_To = pairIndex;
    505             // we could return NULL as stream, but it's better to return real stream
    506             // return S_OK;
    507           }
    508         }
    509       }
    510     }
    511 
    512     if (ProcessedItemsStatuses)
    513     {
    514       NSynchronization::CCriticalSectionLock lock(CS);
    515       ProcessedItemsStatuses[up.DirIndex] = 1;
    516     }
    517     *inStream = inStreamLoc.Detach();
    518   }
    519 
    520   return S_OK;
    521   COM_TRY_END
    522 }
    523 
    524 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult)
    525 {
    526   COM_TRY_BEGIN
    527   return Callback->SetOperationResult(operationResult);
    528   COM_TRY_END
    529 }
    530 
    531 STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
    532 {
    533   if (VolumesSizes.Size() == 0)
    534     return S_FALSE;
    535   if (index >= (UInt32)VolumesSizes.Size())
    536     index = VolumesSizes.Size() - 1;
    537   *size = VolumesSizes[index];
    538   return S_OK;
    539 }
    540 
    541 STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
    542 {
    543   COM_TRY_BEGIN
    544   FChar temp[16];
    545   ConvertUInt32ToString(index + 1, temp);
    546   FString res = temp;
    547   while (res.Len() < 2)
    548     res.InsertAtFront(FTEXT('0'));
    549   FString fileName = VolName;
    550   fileName += L'.';
    551   fileName += res;
    552   fileName += VolExt;
    553   COutFileStream *streamSpec = new COutFileStream;
    554   CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
    555   if (!streamSpec->Create(fileName, false))
    556     return ::GetLastError();
    557   *volumeStream = streamLoc.Detach();
    558   return S_OK;
    559   COM_TRY_END
    560 }
    561 
    562 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
    563 {
    564   COM_TRY_BEGIN
    565   return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
    566   COM_TRY_END
    567 }
    568 
    569 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password)
    570 {
    571   COM_TRY_BEGIN
    572   return Callback->CryptoGetTextPassword(password);
    573   COM_TRY_END
    574 }
    575