1 // UpdatePair.cpp
2
3 #include "StdAfx.h"
4
5 #include <time.h>
6 // #include <stdio.h>
7
8 #include "../../../Common/Wildcard.h"
9
10 #include "../../../Windows/TimeUtils.h"
11
12 #include "SortUtils.h"
13 #include "UpdatePair.h"
14
15 using namespace NWindows;
16 using namespace NTime;
17
18
19 /*
20 a2.Prec =
21 {
22 0 (k_PropVar_TimePrec_0):
23 if GetProperty(kpidMTime) returned 0 and
24 GetProperty(kpidTimeType) did not returned VT_UI4.
25 7z, wim, tar in 7-Zip before v21)
26 in that case we use
27 (prec) that is set by IOutArchive::GetFileTimeType()
28 }
29 */
30
MyCompareTime(unsigned prec,const CFiTime & f1,const CArcTime & a2)31 static int MyCompareTime(unsigned prec, const CFiTime &f1, const CArcTime &a2)
32 {
33 // except of precision, we also have limitation, when timestamp is out of range
34
35 /* if (Prec) in archive item is defined, then use global (prec) */
36 if (a2.Prec != k_PropVar_TimePrec_0)
37 prec = a2.Prec;
38
39 CArcTime a1;
40 a1.Set_From_FiTime(f1);
41 /* Set_From_FiTime() must set full form precision:
42 k_PropVar_TimePrec_Base + numDigits
43 windows: 7 digits, non-windows: 9 digits */
44
45 if (prec == k_PropVar_TimePrec_DOS)
46 {
47 const UInt32 dosTime1 = a1.Get_DosTime();
48 const UInt32 dosTime2 = a2.Get_DosTime();
49 return MyCompare(dosTime1, dosTime2);
50 }
51
52 if (prec == k_PropVar_TimePrec_Unix)
53 {
54 const Int64 u2 = FileTime_To_UnixTime64(a2.FT);
55 if (u2 == 0 || u2 == (UInt32)0xFFFFFFFF)
56 {
57 // timestamp probably was saturated in archive to 32-bit
58 // so we use saturated 32-bit value for disk file too.
59 UInt32 u1;
60 FileTime_To_UnixTime(a1.FT, u1);
61 const UInt32 u2_32 = (UInt32)u2;
62 return MyCompare(u1, u2_32);
63 }
64
65 const Int64 u1 = FileTime_To_UnixTime64(a1.FT);
66 return MyCompare(u1, u2);
67 // prec = k_PropVar_TimePrec_Base; // for debug
68 }
69
70 if (prec == k_PropVar_TimePrec_0)
71 prec = k_PropVar_TimePrec_Base + 7;
72 else if (prec == k_PropVar_TimePrec_HighPrec)
73 prec = k_PropVar_TimePrec_Base + 9;
74 else if (prec < k_PropVar_TimePrec_Base)
75 prec = k_PropVar_TimePrec_Base;
76 else if (prec > k_PropVar_TimePrec_Base + 9)
77 prec = k_PropVar_TimePrec_Base + 7;
78
79 // prec now is full form: k_PropVar_TimePrec_Base + numDigits;
80 if (prec > a1.Prec && a1.Prec >= k_PropVar_TimePrec_Base)
81 prec = a1.Prec;
82
83 const unsigned numDigits = prec - k_PropVar_TimePrec_Base;
84 if (numDigits >= 7)
85 {
86 const int comp = CompareFileTime(&a1.FT, &a2.FT);
87 if (comp != 0 || numDigits == 7)
88 return comp;
89 return MyCompare(a1.Ns100, a2.Ns100);
90 }
91 UInt32 d = 1;
92 for (unsigned k = numDigits; k < 7; k++)
93 d *= 10;
94 const UInt64 v1 = a1.Get_FILETIME_as_UInt64() / d * d;
95 const UInt64 v2 = a2.Get_FILETIME_as_UInt64() / d * d;
96 // printf("\ndelta=%d numDigits=%d\n", (unsigned)(v1- v2), numDigits);
97 return MyCompare(v1, v2);
98 }
99
100
101
102 static const char * const k_Duplicate_inArc_Message = "Duplicate filename in archive:";
103 static const char * const k_Duplicate_inDir_Message = "Duplicate filename on disk:";
104 static const char * const k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):";
105
106 Z7_ATTR_NORETURN
107 static
ThrowError(const char * message,const UString & s1,const UString & s2)108 void ThrowError(const char *message, const UString &s1, const UString &s2)
109 {
110 UString m (message);
111 m.Add_LF(); m += s1;
112 m.Add_LF(); m += s2;
113 throw m;
114 }
115
CompareArcItemsBase(const CArcItem & ai1,const CArcItem & ai2)116 static int CompareArcItemsBase(const CArcItem &ai1, const CArcItem &ai2)
117 {
118 const int res = CompareFileNames(ai1.Name, ai2.Name);
119 if (res != 0)
120 return res;
121 if (ai1.IsDir != ai2.IsDir)
122 return ai1.IsDir ? -1 : 1;
123 return 0;
124 }
125
CompareArcItems(const unsigned * p1,const unsigned * p2,void * param)126 static int CompareArcItems(const unsigned *p1, const unsigned *p2, void *param)
127 {
128 const unsigned i1 = *p1;
129 const unsigned i2 = *p2;
130 const CObjectVector<CArcItem> &arcItems = *(const CObjectVector<CArcItem> *)param;
131 const int res = CompareArcItemsBase(arcItems[i1], arcItems[i2]);
132 if (res != 0)
133 return res;
134 return MyCompare(i1, i2);
135 }
136
GetUpdatePairInfoList(const CDirItems & dirItems,const CObjectVector<CArcItem> & arcItems,NFileTimeType::EEnum fileTimeType,CRecordVector<CUpdatePair> & updatePairs)137 void GetUpdatePairInfoList(
138 const CDirItems &dirItems,
139 const CObjectVector<CArcItem> &arcItems,
140 NFileTimeType::EEnum fileTimeType,
141 CRecordVector<CUpdatePair> &updatePairs)
142 {
143 CUIntVector dirIndices, arcIndices;
144
145 const unsigned numDirItems = dirItems.Items.Size();
146 const unsigned numArcItems = arcItems.Size();
147
148 CIntArr duplicatedArcItem(numArcItems);
149 {
150 int *vals = &duplicatedArcItem[0];
151 for (unsigned i = 0; i < numArcItems; i++)
152 vals[i] = 0;
153 }
154
155 {
156 arcIndices.ClearAndSetSize(numArcItems);
157 if (numArcItems != 0)
158 {
159 unsigned *vals = &arcIndices[0];
160 for (unsigned i = 0; i < numArcItems; i++)
161 vals[i] = i;
162 }
163 arcIndices.Sort(CompareArcItems, (void *)&arcItems);
164 for (unsigned i = 0; i + 1 < numArcItems; i++)
165 if (CompareArcItemsBase(
166 arcItems[arcIndices[i]],
167 arcItems[arcIndices[i + 1]]) == 0)
168 {
169 duplicatedArcItem[i] = 1;
170 duplicatedArcItem[i + 1] = -1;
171 }
172 }
173
174 UStringVector dirNames;
175 {
176 dirNames.ClearAndReserve(numDirItems);
177 unsigned i;
178 for (i = 0; i < numDirItems; i++)
179 dirNames.AddInReserved(dirItems.GetLogPath(i));
180 SortFileNames(dirNames, dirIndices);
181 for (i = 0; i + 1 < numDirItems; i++)
182 {
183 const UString &s1 = dirNames[dirIndices[i]];
184 const UString &s2 = dirNames[dirIndices[i + 1]];
185 if (CompareFileNames(s1, s2) == 0)
186 ThrowError(k_Duplicate_inDir_Message, s1, s2);
187 }
188 }
189
190 unsigned dirIndex = 0;
191 unsigned arcIndex = 0;
192
193 int prevHostFile = -1;
194 const UString *prevHostName = NULL;
195
196 while (dirIndex < numDirItems || arcIndex < numArcItems)
197 {
198 CUpdatePair pair;
199
200 int dirIndex2 = -1;
201 int arcIndex2 = -1;
202 const CDirItem *di = NULL;
203 const CArcItem *ai = NULL;
204
205 int compareResult = -1;
206 const UString *name = NULL;
207
208 if (dirIndex < numDirItems)
209 {
210 dirIndex2 = (int)dirIndices[dirIndex];
211 di = &dirItems.Items[(unsigned)dirIndex2];
212 }
213
214 if (arcIndex < numArcItems)
215 {
216 arcIndex2 = (int)arcIndices[arcIndex];
217 ai = &arcItems[(unsigned)arcIndex2];
218 compareResult = 1;
219 if (dirIndex < numDirItems)
220 {
221 compareResult = CompareFileNames(dirNames[(unsigned)dirIndex2], ai->Name);
222 if (compareResult == 0)
223 {
224 if (di->IsDir() != ai->IsDir)
225 compareResult = (ai->IsDir ? 1 : -1);
226 }
227 }
228 }
229
230 if (compareResult < 0)
231 {
232 name = &dirNames[(unsigned)dirIndex2];
233 pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
234 pair.DirIndex = dirIndex2;
235 dirIndex++;
236 }
237 else if (compareResult > 0)
238 {
239 name = &ai->Name;
240 pair.State = ai->Censored ?
241 NUpdateArchive::NPairState::kOnlyInArchive:
242 NUpdateArchive::NPairState::kNotMasked;
243 pair.ArcIndex = arcIndex2;
244 arcIndex++;
245 }
246 else
247 {
248 const int dupl = duplicatedArcItem[arcIndex];
249 if (dupl != 0)
250 ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[(unsigned)((int)arcIndex + dupl)]].Name);
251
252 name = &dirNames[(unsigned)dirIndex2];
253 if (!ai->Censored)
254 ThrowError(k_NotCensoredCollision_Message, *name, ai->Name);
255
256 pair.DirIndex = dirIndex2;
257 pair.ArcIndex = arcIndex2;
258
259 int compResult = 0;
260 if (ai->MTime.Def)
261 {
262 compResult = MyCompareTime((unsigned)fileTimeType, di->MTime, ai->MTime);
263 }
264 switch (compResult)
265 {
266 case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
267 case 1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
268 default:
269 pair.State = (ai->Size_Defined && di->Size == ai->Size) ?
270 NUpdateArchive::NPairState::kSameFiles :
271 NUpdateArchive::NPairState::kUnknowNewerFiles;
272 }
273
274 dirIndex++;
275 arcIndex++;
276 }
277
278 if (
279 #ifdef _WIN32
280 (di && di->IsAltStream) ||
281 #endif
282 (ai && ai->IsAltStream))
283 {
284 if (prevHostName)
285 {
286 const unsigned hostLen = prevHostName->Len();
287 if (name->Len() > hostLen)
288 if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0)
289 pair.HostIndex = prevHostFile;
290 }
291 }
292 else
293 {
294 prevHostFile = (int)updatePairs.Size();
295 prevHostName = name;
296 }
297
298 updatePairs.Add(pair);
299 }
300
301 updatePairs.ReserveDown();
302 }
303