1 // TarHandlerOut.cpp
2
3 #include "StdAfx.h"
4
5 // #include <stdio.h>
6
7 #include "../../../Common/ComTry.h"
8 #include "../../../Common/MyLinux.h"
9 #include "../../../Common/StringConvert.h"
10
11 #include "../../../Windows/TimeUtils.h"
12
13 #include "../Common/ItemNameUtils.h"
14
15 #include "TarHandler.h"
16 #include "TarUpdate.h"
17
18 using namespace NWindows;
19
20 namespace NArchive {
21 namespace NTar {
22
Z7_COM7F_IMF(CHandler::GetFileTimeType (UInt32 * type))23 Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *type))
24 {
25 UInt32 t = NFileTimeType::kUnix;
26 const UInt32 prec = _handlerTimeOptions.Prec;
27 if (prec != (UInt32)(Int32)-1)
28 {
29 t = NFileTimeType::kWindows;
30 if (prec == k_PropVar_TimePrec_0 ||
31 prec == k_PropVar_TimePrec_100ns)
32 t = NFileTimeType::kWindows;
33 else if (prec == k_PropVar_TimePrec_HighPrec)
34 t = k_PropVar_TimePrec_1ns;
35 else if (prec >= k_PropVar_TimePrec_Base)
36 t = prec;
37 }
38 // 7-Zip before 22.00 fails, if unknown typeType.
39 *type = t;
40 return S_OK;
41 }
42
43
Get_AString_From_UString(const UString & s,AString & res,UINT codePage,unsigned utfFlags)44 void Get_AString_From_UString(const UString &s, AString &res,
45 UINT codePage, unsigned utfFlags)
46 {
47 if (codePage == CP_UTF8)
48 ConvertUnicodeToUTF8_Flags(s, res, utfFlags);
49 else
50 UnicodeStringToMultiByte2(res, s, codePage);
51 }
52
53
GetPropString(IArchiveUpdateCallback * callback,UInt32 index,PROPID propId,AString & res,UINT codePage,unsigned utfFlags,bool convertSlash)54 HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res,
55 UINT codePage, unsigned utfFlags, bool convertSlash)
56 {
57 NCOM::CPropVariant prop;
58 RINOK(callback->GetProperty(index, propId, &prop))
59
60 if (prop.vt == VT_BSTR)
61 {
62 UString s = prop.bstrVal;
63 if (convertSlash)
64 NItemName::ReplaceSlashes_OsToUnix(s);
65 Get_AString_From_UString(s, res, codePage, utfFlags);
66 }
67 else if (prop.vt != VT_EMPTY)
68 return E_INVALIDARG;
69
70 return S_OK;
71 }
72
73
74 // sort old files with original order.
75
CompareUpdateItems(void * const * p1,void * const * p2,void *)76 static int CompareUpdateItems(void *const *p1, void *const *p2, void *)
77 {
78 const CUpdateItem &u1 = *(*((const CUpdateItem *const *)p1));
79 const CUpdateItem &u2 = *(*((const CUpdateItem *const *)p2));
80 if (!u1.NewProps)
81 {
82 if (u2.NewProps)
83 return -1;
84 return MyCompare(u1.IndexInArc, u2.IndexInArc);
85 }
86 if (!u2.NewProps)
87 return 1;
88 return MyCompare(u1.IndexInClient, u2.IndexInClient);
89 }
90
91
GetTime(UInt32 i,UInt32 pid,IArchiveUpdateCallback * callback,CPaxTime & pt)92 static HRESULT GetTime(UInt32 i, UInt32 pid, IArchiveUpdateCallback *callback,
93 CPaxTime &pt)
94 {
95 pt.Clear();
96 NCOM::CPropVariant prop;
97 RINOK(callback->GetProperty(i, pid, &prop))
98 return Prop_To_PaxTime(prop, pt);
99 }
100
101
102 /*
103 static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i,
104 UInt32 &majo, UInt32 &mino, bool &majo_defined, bool &mino_defined)
105 {
106 NWindows::NCOM::CPropVariant prop;
107 RINOK(callback->GetProperty(i, kpidDevice, &prop));
108 if (prop.vt == VT_EMPTY)
109 return S_OK;
110 if (prop.vt != VT_UI8)
111 return E_INVALIDARG;
112 {
113 const UInt64 v = prop.uhVal.QuadPart;
114 majo = MY_dev_major(v);
115 mino = MY_dev_minor(v);
116 majo_defined = true;
117 mino_defined = true;
118 }
119 return S_OK;
120 }
121 */
122
GetDevice(IArchiveUpdateCallback * callback,UInt32 i,UInt32 pid,UInt32 & id,bool & defined)123 static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i,
124 UInt32 pid, UInt32 &id, bool &defined)
125 {
126 defined = false;
127 NWindows::NCOM::CPropVariant prop;
128 RINOK(callback->GetProperty(i, pid, &prop))
129 if (prop.vt == VT_EMPTY)
130 return S_OK;
131 if (prop.vt == VT_UI4)
132 {
133 id = prop.ulVal;
134 defined = true;
135 return S_OK;
136 }
137 return E_INVALIDARG;
138 }
139
140
GetUser(IArchiveUpdateCallback * callback,UInt32 i,UInt32 pidName,UInt32 pidId,AString & name,UInt32 & id,UINT codePage,unsigned utfFlags)141 static HRESULT GetUser(IArchiveUpdateCallback *callback, UInt32 i,
142 UInt32 pidName, UInt32 pidId, AString &name, UInt32 &id,
143 UINT codePage, unsigned utfFlags)
144 {
145 // printf("\ncallback->GetProperty(i, pidId, &prop))\n");
146
147 bool isSet = false;
148 {
149 NWindows::NCOM::CPropVariant prop;
150 RINOK(callback->GetProperty(i, pidId, &prop))
151 if (prop.vt == VT_UI4)
152 {
153 isSet = true;
154 id = prop.ulVal;
155 // printf("\ncallback->GetProperty(i, pidId, &prop)); = %d \n", (unsigned)id);
156 name.Empty();
157 }
158 else if (prop.vt != VT_EMPTY)
159 return E_INVALIDARG;
160 }
161 {
162 NWindows::NCOM::CPropVariant prop;
163 RINOK(callback->GetProperty(i, pidName, &prop))
164 if (prop.vt == VT_BSTR)
165 {
166 const UString s = prop.bstrVal;
167 Get_AString_From_UString(s, name, codePage, utfFlags);
168 if (!isSet)
169 id = 0;
170 }
171 else if (prop.vt == VT_UI4)
172 {
173 id = prop.ulVal;
174 name.Empty();
175 }
176 else if (prop.vt != VT_EMPTY)
177 return E_INVALIDARG;
178 }
179 return S_OK;
180 }
181
182
183
Z7_COM7F_IMF(CHandler::UpdateItems (ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * callback))184 Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
185 IArchiveUpdateCallback *callback))
186 {
187 COM_TRY_BEGIN
188
189 if ((_stream && (_arc._error != k_ErrorType_OK || _arc._is_Warning
190 /* || _isSparse */
191 )) || _seqStream)
192 return E_NOTIMPL;
193 CObjectVector<CUpdateItem> updateItems;
194 const UINT codePage = (_forceCodePage ? _specifiedCodePage : _openCodePage);
195 const unsigned utfFlags = g_Unicode_To_UTF8_Flags;
196 /*
197 // for debug only:
198 unsigned utfFlags = 0;
199 utfFlags |= Z7_UTF_FLAG_TO_UTF8_EXTRACT_BMP_ESCAPE;
200 utfFlags |= Z7_UTF_FLAG_TO_UTF8_SURROGATE_ERROR;
201 */
202
203 for (UInt32 i = 0; i < numItems; i++)
204 {
205 CUpdateItem ui;
206 Int32 newData;
207 Int32 newProps;
208 UInt32 indexInArc;
209
210 if (!callback)
211 return E_FAIL;
212
213 RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArc))
214
215 ui.NewProps = IntToBool(newProps);
216 ui.NewData = IntToBool(newData);
217 ui.IndexInArc = (int)indexInArc;
218 ui.IndexInClient = i;
219
220 if (IntToBool(newProps))
221 {
222 {
223 NCOM::CPropVariant prop;
224 RINOK(callback->GetProperty(i, kpidIsDir, &prop))
225 if (prop.vt == VT_EMPTY)
226 ui.IsDir = false;
227 else if (prop.vt != VT_BOOL)
228 return E_INVALIDARG;
229 else
230 ui.IsDir = (prop.boolVal != VARIANT_FALSE);
231 }
232
233 {
234 NCOM::CPropVariant prop;
235 RINOK(callback->GetProperty(i, kpidPosixAttrib, &prop))
236 if (prop.vt == VT_EMPTY)
237 ui.Mode =
238 MY_LIN_S_IRWXO
239 | MY_LIN_S_IRWXG
240 | MY_LIN_S_IRWXU
241 | (ui.IsDir ? MY_LIN_S_IFDIR : MY_LIN_S_IFREG);
242 else if (prop.vt != VT_UI4)
243 return E_INVALIDARG;
244 else
245 ui.Mode = prop.ulVal;
246 // 21.07 : we clear high file type bits as GNU TAR.
247 // we will clear it later
248 // ui.Mode &= ~(UInt32)MY_LIN_S_IFMT;
249 }
250
251 if (_handlerTimeOptions.Write_MTime.Val)
252 RINOK(GetTime(i, kpidMTime, callback, ui.PaxTimes.MTime))
253 if (_handlerTimeOptions.Write_ATime.Val)
254 RINOK(GetTime(i, kpidATime, callback, ui.PaxTimes.ATime))
255 if (_handlerTimeOptions.Write_CTime.Val)
256 RINOK(GetTime(i, kpidCTime, callback, ui.PaxTimes.CTime))
257
258 RINOK(GetPropString(callback, i, kpidPath, ui.Name, codePage, utfFlags, true))
259 if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/')
260 ui.Name.Add_Slash();
261 // ui.Name.Add_Slash(); // for debug
262
263 if (_posixMode)
264 {
265 RINOK(GetDevice(callback, i, kpidDeviceMajor, ui.DeviceMajor, ui.DeviceMajor_Defined))
266 RINOK(GetDevice(callback, i, kpidDeviceMinor, ui.DeviceMinor, ui.DeviceMinor_Defined))
267 }
268
269 RINOK(GetUser(callback, i, kpidUser, kpidUserId, ui.User, ui.UID, codePage, utfFlags))
270 RINOK(GetUser(callback, i, kpidGroup, kpidGroupId, ui.Group, ui.GID, codePage, utfFlags))
271 }
272
273 if (IntToBool(newData))
274 {
275 NCOM::CPropVariant prop;
276 RINOK(callback->GetProperty(i, kpidSize, &prop))
277 if (prop.vt != VT_UI8)
278 return E_INVALIDARG;
279 ui.Size = prop.uhVal.QuadPart;
280 /*
281 // now we support GNU extension for big files
282 if (ui.Size >= ((UInt64)1 << 33))
283 return E_INVALIDARG;
284 */
285 }
286
287 updateItems.Add(ui);
288 }
289
290 if (_arc._are_Pax_Items)
291 {
292 // we restore original order of files, if there are pax items
293 updateItems.Sort(CompareUpdateItems, NULL);
294 }
295
296 CUpdateOptions options;
297
298 options.CodePage = codePage;
299 options.UtfFlags = utfFlags;
300 options.PosixMode = _posixMode;
301
302 options.Write_MTime = _handlerTimeOptions.Write_MTime;
303 options.Write_ATime = _handlerTimeOptions.Write_ATime;
304 options.Write_CTime = _handlerTimeOptions.Write_CTime;
305
306 // options.TimeOptions = TimeOptions;
307
308 const UInt32 prec = _handlerTimeOptions.Prec;
309 if (prec != (UInt32)(Int32)-1)
310 {
311 unsigned numDigits = 0;
312 if (prec == 0)
313 numDigits = 7;
314 else if (prec == k_PropVar_TimePrec_HighPrec
315 || prec >= k_PropVar_TimePrec_1ns)
316 numDigits = 9;
317 else if (prec >= k_PropVar_TimePrec_Base)
318 numDigits = prec - k_PropVar_TimePrec_Base;
319 options.TimeOptions.NumDigitsMax = numDigits;
320 // options.TimeOptions.RemoveZeroMode =
321 // k_PaxTimeMode_DontRemoveZero; // pure for debug
322 // k_PaxTimeMode_RemoveZero_if_PureSecondOnly; // optimized code
323 // k_PaxTimeMode_RemoveZero_Always; // original pax code
324 }
325
326 return UpdateArchive(_stream, outStream, _items, updateItems,
327 options, callback);
328
329 COM_TRY_END
330 }
331
332 }}
333