// TarHandlerOut.cpp #include "StdAfx.h" // #include #include "../../../Common/ComTry.h" #include "../../../Common/MyLinux.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/TimeUtils.h" #include "../Common/ItemNameUtils.h" #include "TarHandler.h" #include "TarUpdate.h" using namespace NWindows; namespace NArchive { namespace NTar { Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *type)) { UInt32 t = NFileTimeType::kUnix; const UInt32 prec = _handlerTimeOptions.Prec; if (prec != (UInt32)(Int32)-1) { t = NFileTimeType::kWindows; if (prec == k_PropVar_TimePrec_0 || prec == k_PropVar_TimePrec_100ns) t = NFileTimeType::kWindows; else if (prec == k_PropVar_TimePrec_HighPrec) t = k_PropVar_TimePrec_1ns; else if (prec >= k_PropVar_TimePrec_Base) t = prec; } // 7-Zip before 22.00 fails, if unknown typeType. *type = t; return S_OK; } void Get_AString_From_UString(const UString &s, AString &res, UINT codePage, unsigned utfFlags) { if (codePage == CP_UTF8) ConvertUnicodeToUTF8_Flags(s, res, utfFlags); else UnicodeStringToMultiByte2(res, s, codePage); } HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res, UINT codePage, unsigned utfFlags, bool convertSlash) { NCOM::CPropVariant prop; RINOK(callback->GetProperty(index, propId, &prop)) if (prop.vt == VT_BSTR) { UString s = prop.bstrVal; if (convertSlash) NItemName::ReplaceSlashes_OsToUnix(s); Get_AString_From_UString(s, res, codePage, utfFlags); } else if (prop.vt != VT_EMPTY) return E_INVALIDARG; return S_OK; } // sort old files with original order. static int CompareUpdateItems(void *const *p1, void *const *p2, void *) { const CUpdateItem &u1 = *(*((const CUpdateItem *const *)p1)); const CUpdateItem &u2 = *(*((const CUpdateItem *const *)p2)); if (!u1.NewProps) { if (u2.NewProps) return -1; return MyCompare(u1.IndexInArc, u2.IndexInArc); } if (!u2.NewProps) return 1; return MyCompare(u1.IndexInClient, u2.IndexInClient); } static HRESULT GetTime(UInt32 i, UInt32 pid, IArchiveUpdateCallback *callback, CPaxTime &pt) { pt.Clear(); NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, pid, &prop)) return Prop_To_PaxTime(prop, pt); } /* static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i, UInt32 &majo, UInt32 &mino, bool &majo_defined, bool &mino_defined) { NWindows::NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, kpidDevice, &prop)); if (prop.vt == VT_EMPTY) return S_OK; if (prop.vt != VT_UI8) return E_INVALIDARG; { const UInt64 v = prop.uhVal.QuadPart; majo = MY_dev_major(v); mino = MY_dev_minor(v); majo_defined = true; mino_defined = true; } return S_OK; } */ static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i, UInt32 pid, UInt32 &id, bool &defined) { defined = false; NWindows::NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, pid, &prop)) if (prop.vt == VT_EMPTY) return S_OK; if (prop.vt == VT_UI4) { id = prop.ulVal; defined = true; return S_OK; } return E_INVALIDARG; } static HRESULT GetUser(IArchiveUpdateCallback *callback, UInt32 i, UInt32 pidName, UInt32 pidId, AString &name, UInt32 &id, UINT codePage, unsigned utfFlags) { // printf("\ncallback->GetProperty(i, pidId, &prop))\n"); bool isSet = false; { NWindows::NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, pidId, &prop)) if (prop.vt == VT_UI4) { isSet = true; id = prop.ulVal; // printf("\ncallback->GetProperty(i, pidId, &prop)); = %d \n", (unsigned)id); name.Empty(); } else if (prop.vt != VT_EMPTY) return E_INVALIDARG; } { NWindows::NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, pidName, &prop)) if (prop.vt == VT_BSTR) { const UString s = prop.bstrVal; Get_AString_From_UString(s, name, codePage, utfFlags); if (!isSet) id = 0; } else if (prop.vt == VT_UI4) { id = prop.ulVal; name.Empty(); } else if (prop.vt != VT_EMPTY) return E_INVALIDARG; } return S_OK; } Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *callback)) { COM_TRY_BEGIN if ((_stream && (_arc._error != k_ErrorType_OK || _arc._is_Warning /* || _isSparse */ )) || _seqStream) return E_NOTIMPL; CObjectVector updateItems; const UINT codePage = (_forceCodePage ? _specifiedCodePage : _openCodePage); const unsigned utfFlags = g_Unicode_To_UTF8_Flags; /* // for debug only: unsigned utfFlags = 0; utfFlags |= Z7_UTF_FLAG_TO_UTF8_EXTRACT_BMP_ESCAPE; utfFlags |= Z7_UTF_FLAG_TO_UTF8_SURROGATE_ERROR; */ for (UInt32 i = 0; i < numItems; i++) { CUpdateItem ui; Int32 newData; Int32 newProps; UInt32 indexInArc; if (!callback) return E_FAIL; RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArc)) ui.NewProps = IntToBool(newProps); ui.NewData = IntToBool(newData); ui.IndexInArc = (int)indexInArc; ui.IndexInClient = i; if (IntToBool(newProps)) { { NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, kpidIsDir, &prop)) if (prop.vt == VT_EMPTY) ui.IsDir = false; else if (prop.vt != VT_BOOL) return E_INVALIDARG; else ui.IsDir = (prop.boolVal != VARIANT_FALSE); } { NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, kpidPosixAttrib, &prop)) if (prop.vt == VT_EMPTY) ui.Mode = MY_LIN_S_IRWXO | MY_LIN_S_IRWXG | MY_LIN_S_IRWXU | (ui.IsDir ? MY_LIN_S_IFDIR : MY_LIN_S_IFREG); else if (prop.vt != VT_UI4) return E_INVALIDARG; else ui.Mode = prop.ulVal; // 21.07 : we clear high file type bits as GNU TAR. // we will clear it later // ui.Mode &= ~(UInt32)MY_LIN_S_IFMT; } if (_handlerTimeOptions.Write_MTime.Val) RINOK(GetTime(i, kpidMTime, callback, ui.PaxTimes.MTime)) if (_handlerTimeOptions.Write_ATime.Val) RINOK(GetTime(i, kpidATime, callback, ui.PaxTimes.ATime)) if (_handlerTimeOptions.Write_CTime.Val) RINOK(GetTime(i, kpidCTime, callback, ui.PaxTimes.CTime)) RINOK(GetPropString(callback, i, kpidPath, ui.Name, codePage, utfFlags, true)) if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/') ui.Name.Add_Slash(); // ui.Name.Add_Slash(); // for debug if (_posixMode) { RINOK(GetDevice(callback, i, kpidDeviceMajor, ui.DeviceMajor, ui.DeviceMajor_Defined)) RINOK(GetDevice(callback, i, kpidDeviceMinor, ui.DeviceMinor, ui.DeviceMinor_Defined)) } RINOK(GetUser(callback, i, kpidUser, kpidUserId, ui.User, ui.UID, codePage, utfFlags)) RINOK(GetUser(callback, i, kpidGroup, kpidGroupId, ui.Group, ui.GID, codePage, utfFlags)) } if (IntToBool(newData)) { NCOM::CPropVariant prop; RINOK(callback->GetProperty(i, kpidSize, &prop)) if (prop.vt != VT_UI8) return E_INVALIDARG; ui.Size = prop.uhVal.QuadPart; /* // now we support GNU extension for big files if (ui.Size >= ((UInt64)1 << 33)) return E_INVALIDARG; */ } updateItems.Add(ui); } if (_arc._are_Pax_Items) { // we restore original order of files, if there are pax items updateItems.Sort(CompareUpdateItems, NULL); } CUpdateOptions options; options.CodePage = codePage; options.UtfFlags = utfFlags; options.PosixMode = _posixMode; options.Write_MTime = _handlerTimeOptions.Write_MTime; options.Write_ATime = _handlerTimeOptions.Write_ATime; options.Write_CTime = _handlerTimeOptions.Write_CTime; // options.TimeOptions = TimeOptions; const UInt32 prec = _handlerTimeOptions.Prec; if (prec != (UInt32)(Int32)-1) { unsigned numDigits = 0; if (prec == 0) numDigits = 7; else if (prec == k_PropVar_TimePrec_HighPrec || prec >= k_PropVar_TimePrec_1ns) numDigits = 9; else if (prec >= k_PropVar_TimePrec_Base) numDigits = prec - k_PropVar_TimePrec_Base; options.TimeOptions.NumDigitsMax = numDigits; // options.TimeOptions.RemoveZeroMode = // k_PaxTimeMode_DontRemoveZero; // pure for debug // k_PaxTimeMode_RemoveZero_if_PureSecondOnly; // optimized code // k_PaxTimeMode_RemoveZero_Always; // original pax code } return UpdateArchive(_stream, outStream, _items, updateItems, options, callback); COM_TRY_END } }}