You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

772 lines
19 KiB

// UpdateCallback.cpp
#include "StdAfx.h"
#ifndef _7ZIP_ST
#include "../../../Windows/Synchronization.h"
#endif
#include "../../../Common/ComTry.h"
#include "../../../Common/IntToString.h"
#include "../../../Common/StringConvert.h"
#include "../../../Common/Wildcard.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileName.h"
#include "../../../Windows/PropVariant.h"
#include "../../Common/StreamObjects.h"
#include "UpdateCallback.h"
#if defined(_WIN32) && !defined(UNDER_CE)
#define _USE_SECURITY_CODE
#include "../../../Windows/SecurityUtils.h"
#endif
using namespace NWindows;
using namespace NFile;
#ifndef _7ZIP_ST
static NSynchronization::CCriticalSection g_CriticalSection;
#define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
#else
#define MT_LOCK
#endif
#ifdef _USE_SECURITY_CODE
bool InitLocalPrivileges();
#endif
CArchiveUpdateCallback::CArchiveUpdateCallback():
_hardIndex_From((UInt32)(Int32)-1),
Callback(NULL),
DirItems(NULL),
ParentDirItem(NULL),
Arc(NULL),
ArcItems(NULL),
UpdatePairs(NULL),
NewNames(NULL),
CommentIndex(-1),
Comment(NULL),
ShareForWrite(false),
StopAfterOpenError(false),
StdInMode(false),
KeepOriginalItemNames(false),
StoreNtSecurity(false),
StoreHardLinks(false),
StoreSymLinks(false),
ProcessedItemsStatuses(NULL)
{
#ifdef _USE_SECURITY_CODE
_saclEnabled = InitLocalPrivileges();
#endif
}
STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
{
COM_TRY_BEGIN
return Callback->SetTotal(size);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
{
COM_TRY_BEGIN
return Callback->SetCompleted(completeValue);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
{
COM_TRY_BEGIN
return Callback->SetRatioInfo(inSize, outSize);
COM_TRY_END
}
/*
static const CStatProp kProps[] =
{
{ NULL, kpidPath, VT_BSTR},
{ NULL, kpidIsDir, VT_BOOL},
{ NULL, kpidSize, VT_UI8},
{ NULL, kpidCTime, VT_FILETIME},
{ NULL, kpidATime, VT_FILETIME},
{ NULL, kpidMTime, VT_FILETIME},
{ NULL, kpidAttrib, VT_UI4},
{ NULL, kpidIsAnti, VT_BOOL}
};
STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
{
return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator);
}
*/
STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)
{
COM_TRY_BEGIN
RINOK(Callback->CheckBreak());
const CUpdatePair2 &up = (*UpdatePairs)[index];
if (newData) *newData = BoolToInt(up.NewData);
if (newProps) *newProps = BoolToInt(up.NewProps);
if (indexInArchive)
{
*indexInArchive = (UInt32)(Int32)-1;
if (up.ExistInArchive())
*indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer;
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value)
{
NCOM::CPropVariant prop;
switch (propID)
{
case kpidIsDir: prop = true; break;
case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break;
case kpidCTime: if (ParentDirItem) prop = ParentDirItem->CTime; break;
case kpidATime: if (ParentDirItem) prop = ParentDirItem->ATime; break;
case kpidMTime: if (ParentDirItem) prop = ParentDirItem->MTime; break;
}
prop.Detach(value);
return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType)
{
*parentType = NParentType::kDir;
*parent = (UInt32)(Int32)-1;
return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps)
{
*numProps = 0;
if (StoreNtSecurity)
*numProps = 1;
return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
{
*name = NULL;
*propID = kpidNtSecure;
return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID
#ifdef _USE_SECURITY_CODE
propID
#endif
, const void **data, UInt32 *dataSize, UInt32 *propType)
{
*data = 0;
*dataSize = 0;
*propType = 0;
if (!StoreNtSecurity)
return S_OK;
#ifdef _USE_SECURITY_CODE
if (propID == kpidNtSecure)
{
if (StdInMode)
return S_OK;
if (ParentDirItem)
{
if (ParentDirItem->SecureIndex < 0)
return S_OK;
const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex];
*data = buf;
*dataSize = (UInt32)buf.Size();
*propType = NPropDataType::kRaw;
return S_OK;
}
if (Arc && Arc->GetRootProps)
return Arc->GetRootProps->GetRootRawProp(propID, data, dataSize, propType);
}
#endif
return S_OK;
}
// #ifdef _USE_SECURITY_CODE
// #endif
STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
{
*data = 0;
*dataSize = 0;
*propType = 0;
if (propID == kpidNtSecure ||
propID == kpidNtReparse)
{
if (StdInMode)
return S_OK;
const CUpdatePair2 &up = (*UpdatePairs)[index];
if (up.UseArcProps && up.ExistInArchive() && Arc->GetRawProps)
return Arc->GetRawProps->GetRawProp(
ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex,
propID, data, dataSize, propType);
{
/*
if (!up.NewData)
return E_FAIL;
*/
if (up.IsAnti)
return S_OK;
#ifndef UNDER_CE
const CDirItem &di = DirItems->Items[up.DirIndex];
#endif
#ifdef _USE_SECURITY_CODE
if (propID == kpidNtSecure)
{
if (!StoreNtSecurity)
return S_OK;
if (di.SecureIndex < 0)
return S_OK;
const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex];
*data = buf;
*dataSize = (UInt32)buf.Size();
*propType = NPropDataType::kRaw;
}
else
#endif
{
// propID == kpidNtReparse
if (!StoreSymLinks)
return S_OK;
#ifndef UNDER_CE
const CByteBuffer *buf = &di.ReparseData2;
if (buf->Size() == 0)
buf = &di.ReparseData;
if (buf->Size() != 0)
{
*data = *buf;
*dataSize = (UInt32)buf->Size();
*propType = NPropDataType::kRaw;
}
#endif
}
return S_OK;
}
}
return S_OK;
}
#ifndef UNDER_CE
static UString GetRelativePath(const UString &to, const UString &from)
{
UStringVector partsTo, partsFrom;
SplitPathToParts(to, partsTo);
SplitPathToParts(from, partsFrom);
unsigned i;
for (i = 0;; i++)
{
if (i + 1 >= partsFrom.Size() ||
i + 1 >= partsTo.Size())
break;
if (CompareFileNames(partsFrom[i], partsTo[i]) != 0)
break;
}
if (i == 0)
{
#ifdef _WIN32
if (NName::IsDrivePath(to) ||
NName::IsDrivePath(from))
return to;
#endif
}
UString s;
unsigned k;
for (k = i + 1; k < partsFrom.Size(); k++)
s += ".." STRING_PATH_SEPARATOR;
for (k = i; k < partsTo.Size(); k++)
{
if (k != i)
s.Add_PathSepar();
s += partsTo[k];
}
return s;
}
#endif
STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
const CUpdatePair2 &up = (*UpdatePairs)[index];
NCOM::CPropVariant prop;
if (up.NewData)
{
/*
if (propID == kpidIsHardLink)
{
prop = _isHardLink;
prop.Detach(value);
return S_OK;
}
*/
if (propID == kpidSymLink)
{
if (index == _hardIndex_From)
{
prop.Detach(value);
return S_OK;
}
if (up.DirIndex >= 0)
{
#ifndef UNDER_CE
const CDirItem &di = DirItems->Items[up.DirIndex];
// if (di.IsDir())
{
CReparseAttr attr;
DWORD errorCode = 0;
if (attr.Parse(di.ReparseData, di.ReparseData.Size(), errorCode))
{
UString simpleName = attr.GetPath();
if (attr.IsRelative())
prop = simpleName;
else
{
const FString phyPath = DirItems->GetPhyPath(up.DirIndex);
FString fullPath;
if (NDir::MyGetFullPathName(phyPath, fullPath))
{
prop = GetRelativePath(simpleName, fs2us(fullPath));
}
}
prop.Detach(value);
return S_OK;
}
}
#endif
}
}
else if (propID == kpidHardLink)
{
if (index == _hardIndex_From)
{
const CKeyKeyValPair &pair = _map[_hardIndex_To];
const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value];
prop = DirItems->GetLogPath(up2.DirIndex);
prop.Detach(value);
return S_OK;
}
if (up.DirIndex >= 0)
{
prop.Detach(value);
return S_OK;
}
}
}
if (up.IsAnti
&& propID != kpidIsDir
&& propID != kpidPath
&& propID != kpidIsAltStream)
{
switch (propID)
{
case kpidSize: prop = (UInt64)0; break;
case kpidIsAnti: prop = true; break;
}
}
else if (propID == kpidPath && up.NewNameIndex >= 0)
prop = (*NewNames)[up.NewNameIndex];
else if (propID == kpidComment
&& CommentIndex >= 0
&& (unsigned)CommentIndex == index
&& Comment)
prop = *Comment;
else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem)
{
// we can generate new ShortName here;
}
else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream)))
&& up.ExistInArchive() && Archive)
return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value);
else if (up.ExistOnDisk())
{
const CDirItem &di = DirItems->Items[up.DirIndex];
switch (propID)
{
case kpidPath: prop = DirItems->GetLogPath(up.DirIndex); break;
case kpidIsDir: prop = di.IsDir(); break;
case kpidSize: prop = di.IsDir() ? (UInt64)0 : di.Size; break;
case kpidAttrib: prop = di.Attrib; break;
case kpidCTime: prop = di.CTime; break;
case kpidATime: prop = di.ATime; break;
case kpidMTime: prop = di.MTime; break;
case kpidIsAltStream: prop = di.IsAltStream; break;
#if defined(_WIN32) && !defined(UNDER_CE)
// case kpidShortName: prop = di.ShortName; break;
#endif
}
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
#ifndef _7ZIP_ST
static NSynchronization::CCriticalSection CS;
#endif
STDMETHODIMP CArchiveUpdateCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode)
{
COM_TRY_BEGIN
*inStream = NULL;
const CUpdatePair2 &up = (*UpdatePairs)[index];
if (!up.NewData)
return E_FAIL;
RINOK(Callback->CheckBreak());
// RINOK(Callback->Finalize());
bool isDir = IsDir(up);
if (up.IsAnti)
{
UString name;
if (up.ArcIndex >= 0)
name = (*ArcItems)[up.ArcIndex].Name;
else if (up.DirIndex >= 0)
name = DirItems->GetLogPath(up.DirIndex);
RINOK(Callback->GetStream(name, isDir, true, mode));
/* 9.33: fixed. Handlers expect real stream object for files, even for anti-file.
so we return empty stream */
if (!isDir)
{
CBufInStream *inStreamSpec = new CBufInStream();
CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
inStreamSpec->Init(NULL, 0);
*inStream = inStreamLoc.Detach();
}
return S_OK;
}
RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), isDir, false, mode));
if (isDir)
return S_OK;
if (StdInMode)
{
if (mode != NUpdateNotifyOp::kAdd &&
mode != NUpdateNotifyOp::kUpdate)
return S_OK;
CStdInFileStream *inStreamSpec = new CStdInFileStream;
CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
*inStream = inStreamLoc.Detach();
}
else
{
CInFileStream *inStreamSpec = new CInFileStream;
CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
inStreamSpec->SupportHardLinks = StoreHardLinks;
inStreamSpec->Callback = this;
inStreamSpec->CallbackRef = index;
const FString path = DirItems->GetPhyPath(up.DirIndex);
_openFiles_Indexes.Add(index);
_openFiles_Paths.Add(path);
#if defined(_WIN32) && !defined(UNDER_CE)
if (DirItems->Items[up.DirIndex].AreReparseData())
{
if (!inStreamSpec->File.OpenReparse(path))
{
return Callback->OpenFileError(path, ::GetLastError());
}
}
else
#endif
if (!inStreamSpec->OpenShared(path, ShareForWrite))
{
DWORD error = ::GetLastError();
HRESULT hres = Callback->OpenFileError(path, error);
if (StopAfterOpenError)
if (hres == S_OK || hres == S_FALSE)
return HRESULT_FROM_WIN32(error);
return hres;
}
if (StoreHardLinks)
{
CStreamFileProps props;
if (inStreamSpec->GetProps2(&props) == S_OK)
{
if (props.NumLinks > 1)
{
CKeyKeyValPair pair;
pair.Key1 = props.VolID;
pair.Key2 = props.FileID_Low;
pair.Value = index;
unsigned numItems = _map.Size();
unsigned pairIndex = _map.AddToUniqueSorted2(pair);
if (numItems == _map.Size())
{
// const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex];
_hardIndex_From = index;
_hardIndex_To = pairIndex;
// we could return NULL as stream, but it's better to return real stream
// return S_OK;
}
}
}
}
if (ProcessedItemsStatuses)
{
#ifndef _7ZIP_ST
NSynchronization::CCriticalSectionLock lock(CS);
#endif
ProcessedItemsStatuses[(unsigned)up.DirIndex] = 1;
}
*inStream = inStreamLoc.Detach();
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 opRes)
{
COM_TRY_BEGIN
return Callback->SetOperationResult(opRes);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
{
COM_TRY_BEGIN
return GetStream2(index, inStream,
(*UpdatePairs)[index].ArcIndex < 0 ?
NUpdateNotifyOp::kAdd :
NUpdateNotifyOp::kUpdate);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::ReportOperation(UInt32 indexType, UInt32 index, UInt32 op)
{
COM_TRY_BEGIN
bool isDir = false;
if (indexType == NArchive::NEventIndexType::kOutArcIndex)
{
UString name;
if (index != (UInt32)(Int32)-1)
{
const CUpdatePair2 &up = (*UpdatePairs)[index];
if (up.ExistOnDisk())
{
name = DirItems->GetLogPath(up.DirIndex);
isDir = DirItems->Items[up.DirIndex].IsDir();
}
}
return Callback->ReportUpdateOpeartion(op, name.IsEmpty() ? NULL : name.Ptr(), isDir);
}
wchar_t temp[16];
UString s2;
const wchar_t *s = NULL;
if (indexType == NArchive::NEventIndexType::kInArcIndex)
{
if (index != (UInt32)(Int32)-1)
{
if (ArcItems)
{
const CArcItem &ai = (*ArcItems)[index];
s = ai.Name;
isDir = ai.IsDir;
}
else if (Arc)
{
RINOK(Arc->GetItemPath(index, s2));
s = s2;
RINOK(Archive_IsItem_Dir(Arc->Archive, index, isDir));
}
}
}
else if (indexType == NArchive::NEventIndexType::kBlockIndex)
{
temp[0] = '#';
ConvertUInt32ToString(index, temp + 1);
s = temp;
}
if (!s)
s = L"";
return Callback->ReportUpdateOpeartion(op, s, isDir);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
{
COM_TRY_BEGIN
bool isEncrypted = false;
wchar_t temp[16];
UString s2;
const wchar_t *s = NULL;
if (indexType == NArchive::NEventIndexType::kOutArcIndex)
{
/*
UString name;
if (index != (UInt32)(Int32)-1)
{
const CUpdatePair2 &up = (*UpdatePairs)[index];
if (up.ExistOnDisk())
{
s2 = DirItems->GetLogPath(up.DirIndex);
s = s2;
}
}
*/
return E_FAIL;
}
if (indexType == NArchive::NEventIndexType::kInArcIndex)
{
if (index != (UInt32)(Int32)-1)
{
if (ArcItems)
s = (*ArcItems)[index].Name;
else if (Arc)
{
RINOK(Arc->GetItemPath(index, s2));
s = s2;
}
if (Archive)
{
RINOK(Archive_GetItemBoolProp(Archive, index, kpidEncrypted, isEncrypted));
}
}
}
else if (indexType == NArchive::NEventIndexType::kBlockIndex)
{
temp[0] = '#';
ConvertUInt32ToString(index, temp + 1);
s = temp;
}
return Callback->ReportExtractResult(opRes, BoolToInt(isEncrypted), s);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
{
if (VolumesSizes.Size() == 0)
return S_FALSE;
if (index >= (UInt32)VolumesSizes.Size())
index = VolumesSizes.Size() - 1;
*size = VolumesSizes[index];
return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
{
COM_TRY_BEGIN
char temp[16];
ConvertUInt32ToString(index + 1, temp);
FString res (temp);
while (res.Len() < 2)
res.InsertAtFront(FTEXT('0'));
FString fileName = VolName;
fileName += '.';
fileName += res;
fileName += VolExt;
COutFileStream *streamSpec = new COutFileStream;
CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
if (!streamSpec->Create(fileName, false))
return ::GetLastError();
*volumeStream = streamLoc.Detach();
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
{
COM_TRY_BEGIN
return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password)
{
COM_TRY_BEGIN
return Callback->CryptoGetTextPassword(password);
COM_TRY_END
}
HRESULT CArchiveUpdateCallback::InFileStream_On_Error(UINT_PTR val, DWORD error)
{
if (error == ERROR_LOCK_VIOLATION)
{
MT_LOCK
UInt32 index = (UInt32)val;
FOR_VECTOR(i, _openFiles_Indexes)
{
if (_openFiles_Indexes[i] == index)
{
RINOK(Callback->ReadingFileError(_openFiles_Paths[i], error));
break;
}
}
}
return HRESULT_FROM_WIN32(error);
}
void CArchiveUpdateCallback::InFileStream_On_Destroy(UINT_PTR val)
{
MT_LOCK
UInt32 index = (UInt32)val;
FOR_VECTOR(i, _openFiles_Indexes)
{
if (_openFiles_Indexes[i] == index)
{
_openFiles_Indexes.Delete(i);
_openFiles_Paths.Delete(i);
return;
}
}
throw 20141125;
}