Line | Count | Source (jump to first uncovered line) |
1 | | #include "rar.hpp" |
2 | | |
3 | | MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr) |
4 | 139k | { |
5 | | #ifdef _WIN_ALL |
6 | | // Windows automatically removes dots and spaces in the end of directory |
7 | | // name. So we detect such names and process them with \\?\ prefix. |
8 | | wchar LastChar=GetLastChar(Name); |
9 | | bool Special=LastChar=='.' || LastChar==' '; |
10 | | BOOL RetCode=Special ? FALSE : CreateDirectory(Name.c_str(),NULL); |
11 | | if (RetCode==0 && !FileExist(Name)) |
12 | | { |
13 | | std::wstring LongName; |
14 | | if (GetWinLongPath(Name,LongName)) |
15 | | RetCode=CreateDirectory(LongName.c_str(),NULL); |
16 | | } |
17 | | if (RetCode!=0) // Non-zero return code means success for CreateDirectory. |
18 | | { |
19 | | if (SetAttr) |
20 | | SetFileAttr(Name,Attr); |
21 | | return MKDIR_SUCCESS; |
22 | | } |
23 | | int ErrCode=GetLastError(); |
24 | | if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) |
25 | | return MKDIR_BADPATH; |
26 | | return MKDIR_ERROR; |
27 | | #elif defined(_UNIX) |
28 | | std::string NameA; |
29 | 139k | WideToChar(Name,NameA); |
30 | 139k | mode_t uattr=SetAttr ? (mode_t)Attr:0777; |
31 | 139k | int ErrCode=mkdir(NameA.c_str(),uattr); |
32 | 139k | if (ErrCode==-1) |
33 | 106k | return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; |
34 | 32.6k | return MKDIR_SUCCESS; |
35 | | #else |
36 | | return MKDIR_ERROR; |
37 | | #endif |
38 | 139k | } |
39 | | |
40 | | |
41 | | // Simplified version of MakeDir(). |
42 | | bool CreateDir(const std::wstring &Name) |
43 | 0 | { |
44 | 0 | return MakeDir(Name,false,0)==MKDIR_SUCCESS; |
45 | 0 | } |
46 | | |
47 | | |
48 | | bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent) |
49 | 12.0k | { |
50 | 12.0k | if (Path.empty()) |
51 | 0 | return false; |
52 | | |
53 | | #ifdef _WIN_ALL |
54 | | uint DirAttr=0; |
55 | | #else |
56 | 12.0k | uint DirAttr=0777; |
57 | 12.0k | #endif |
58 | | |
59 | 12.0k | bool Success=true; |
60 | | |
61 | 1.47M | for (size_t I=0;I<Path.size();I++) |
62 | 1.46M | { |
63 | | // Process all kinds of path separators, so user can enter Unix style |
64 | | // path in Windows or Windows in Unix. I>0 check avoids attempting |
65 | | // creating an empty directory for paths starting from path separator. |
66 | 1.46M | if (IsPathDiv(Path[I]) && I>0) |
67 | 136k | { |
68 | | #ifdef _WIN_ALL |
69 | | // We must not attempt to create "D:" directory, because first |
70 | | // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine |
71 | | // to create "D:" directory. |
72 | | if (I==2 && Path[1]==':') |
73 | | continue; |
74 | | #endif |
75 | 136k | std::wstring DirName=Path.substr(0,I); |
76 | 136k | Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; |
77 | 136k | if (Success && !Silent) |
78 | 32.3k | { |
79 | 32.3k | mprintf(St(MCreatDir),DirName.c_str()); |
80 | 32.3k | mprintf(L" %s",St(MOk)); |
81 | 32.3k | } |
82 | 136k | } |
83 | 1.46M | } |
84 | 12.0k | if (!SkipLastName && !IsPathDiv(GetLastChar(Path))) |
85 | 0 | Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; |
86 | 12.0k | return Success; |
87 | 12.0k | } |
88 | | |
89 | | |
90 | | void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta) |
91 | 1.01k | { |
92 | | #if defined(_WIN_ALL) |
93 | | bool sm=ftm!=NULL && ftm->IsSet(); |
94 | | bool sc=ftc!=NULL && ftc->IsSet(); |
95 | | bool sa=fta!=NULL && fta->IsSet(); |
96 | | |
97 | | uint DirAttr=GetFileAttr(Name); |
98 | | bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0); |
99 | | if (ResetAttr) |
100 | | SetFileAttr(Name,0); |
101 | | |
102 | | HANDLE hFile=CreateFile(Name.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, |
103 | | NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); |
104 | | if (hFile==INVALID_HANDLE_VALUE) |
105 | | { |
106 | | std::wstring LongName; |
107 | | if (GetWinLongPath(Name,LongName)) |
108 | | hFile=CreateFile(LongName.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, |
109 | | NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); |
110 | | } |
111 | | |
112 | | if (hFile==INVALID_HANDLE_VALUE) |
113 | | return; |
114 | | FILETIME fm,fc,fa; |
115 | | if (sm) |
116 | | ftm->GetWinFT(&fm); |
117 | | if (sc) |
118 | | ftc->GetWinFT(&fc); |
119 | | if (sa) |
120 | | fta->GetWinFT(&fa); |
121 | | SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); |
122 | | CloseHandle(hFile); |
123 | | if (ResetAttr) |
124 | | SetFileAttr(Name,DirAttr); |
125 | | #endif |
126 | 1.01k | #ifdef _UNIX |
127 | 1.01k | File::SetCloseFileTimeByName(Name,ftm,fta); |
128 | 1.01k | #endif |
129 | 1.01k | } |
130 | | |
131 | | |
132 | | |
133 | | |
134 | | bool IsRemovable(const std::wstring &Name) |
135 | 0 | { |
136 | | #if defined(_WIN_ALL) |
137 | | std::wstring Root; |
138 | | GetPathRoot(Name,Root); |
139 | | int Type=GetDriveType(Root.empty() ? nullptr : Root.c_str()); |
140 | | return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; |
141 | | #else |
142 | 0 | return false; |
143 | 0 | #endif |
144 | 0 | } |
145 | | |
146 | | |
147 | | #ifndef SFX_MODULE |
148 | | int64 GetFreeDisk(const std::wstring &Name) |
149 | 0 | { |
150 | | #ifdef _WIN_ALL |
151 | | std::wstring Root; |
152 | | GetPathWithSep(Name,Root); |
153 | | |
154 | | ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; |
155 | | uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; |
156 | | if (GetDiskFreeSpaceEx(Root.empty() ? NULL:Root.c_str(),&uiUserFree,&uiTotalSize,&uiTotalFree) && |
157 | | uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) |
158 | | return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); |
159 | | return 0; |
160 | | #elif defined(_UNIX) |
161 | | std::wstring Root; |
162 | 0 | GetPathWithSep(Name,Root); |
163 | 0 | std::string RootA; |
164 | 0 | WideToChar(Root,RootA); |
165 | 0 | struct statvfs sfs; |
166 | 0 | if (statvfs(RootA.empty() ? ".":RootA.c_str(),&sfs)!=0) |
167 | 0 | return 0; |
168 | 0 | int64 FreeSize=sfs.f_bsize; |
169 | 0 | FreeSize=FreeSize*sfs.f_bavail; |
170 | 0 | return FreeSize; |
171 | | #else |
172 | | return 0; |
173 | | #endif |
174 | 0 | } |
175 | | #endif |
176 | | |
177 | | |
178 | | #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) |
179 | | // Return 'true' for FAT and FAT32, so we can adjust the maximum supported |
180 | | // file size to 4 GB for these file systems. |
181 | | bool IsFAT(const std::wstring &Name) |
182 | | { |
183 | | std::wstring Root; |
184 | | GetPathRoot(Name,Root); |
185 | | wchar FileSystem[MAX_PATH+1]; |
186 | | // Root can be empty, when we create volumes with -v in the current folder. |
187 | | if (GetVolumeInformation(Root.empty() ? NULL:Root.c_str(),NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) |
188 | | return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; |
189 | | return false; |
190 | | } |
191 | | #endif |
192 | | |
193 | | |
194 | | bool FileExist(const std::wstring &Name) |
195 | 29.3k | { |
196 | | #ifdef _WIN_ALL |
197 | | return GetFileAttr(Name)!=0xffffffff; |
198 | | #elif defined(ENABLE_ACCESS) |
199 | | std::string NameA; |
200 | 29.3k | WideToChar(Name,NameA); |
201 | 29.3k | return access(NameA.c_str(),0)==0; |
202 | | #else |
203 | | FindData FD; |
204 | | return FindFile::FastFind(Name,&FD); |
205 | | #endif |
206 | 29.3k | } |
207 | | |
208 | | |
209 | | bool WildFileExist(const std::wstring &Name) |
210 | 0 | { |
211 | 0 | if (IsWildcard(Name)) |
212 | 0 | { |
213 | 0 | FindFile Find; |
214 | 0 | Find.SetMask(Name); |
215 | 0 | FindData fd; |
216 | 0 | return Find.Next(&fd); |
217 | 0 | } |
218 | 0 | return FileExist(Name); |
219 | 0 | } |
220 | | |
221 | | |
222 | | bool IsDir(uint Attr) |
223 | 32.2k | { |
224 | | #ifdef _WIN_ALL |
225 | | return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0; |
226 | | #endif |
227 | 32.2k | #if defined(_UNIX) |
228 | 32.2k | return (Attr & 0xF000)==0x4000; |
229 | 32.2k | #endif |
230 | 32.2k | } |
231 | | |
232 | | |
233 | | bool IsUnreadable(uint Attr) |
234 | 0 | { |
235 | 0 | #if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) |
236 | 0 | return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); |
237 | | #else |
238 | | return false; |
239 | | #endif |
240 | 0 | } |
241 | | |
242 | | |
243 | | bool IsLink(uint Attr) |
244 | 27.7k | { |
245 | 27.7k | #ifdef _UNIX |
246 | 27.7k | return (Attr & 0xF000)==0xA000; |
247 | | #elif defined(_WIN_ALL) |
248 | | return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0; |
249 | | #else |
250 | | return false; |
251 | | #endif |
252 | 27.7k | } |
253 | | |
254 | | |
255 | | |
256 | | |
257 | | |
258 | | |
259 | | bool IsDeleteAllowed(uint FileAttr) |
260 | 0 | { |
261 | | #ifdef _WIN_ALL |
262 | | return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0; |
263 | | #else |
264 | 0 | return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR); |
265 | 0 | #endif |
266 | 0 | } |
267 | | |
268 | | |
269 | | void PrepareToDelete(const std::wstring &Name) |
270 | 12.1k | { |
271 | | #ifdef _WIN_ALL |
272 | | SetFileAttr(Name,0); |
273 | | #endif |
274 | 12.1k | #ifdef _UNIX |
275 | 12.1k | std::string NameA; |
276 | 12.1k | WideToChar(Name,NameA); |
277 | 12.1k | chmod(NameA.c_str(),S_IRUSR|S_IWUSR|S_IXUSR); |
278 | 12.1k | #endif |
279 | 12.1k | } |
280 | | |
281 | | |
282 | | uint GetFileAttr(const std::wstring &Name) |
283 | 4.82k | { |
284 | | #ifdef _WIN_ALL |
285 | | DWORD Attr=GetFileAttributes(Name.c_str()); |
286 | | if (Attr==0xffffffff) |
287 | | { |
288 | | std::wstring LongName; |
289 | | if (GetWinLongPath(Name,LongName)) |
290 | | Attr=GetFileAttributes(LongName.c_str()); |
291 | | } |
292 | | return Attr; |
293 | | #else |
294 | 4.82k | std::string NameA; |
295 | 4.82k | WideToChar(Name,NameA); |
296 | 4.82k | struct stat st; |
297 | 4.82k | if (stat(NameA.c_str(),&st)!=0) |
298 | 0 | return 0; |
299 | 4.82k | return st.st_mode; |
300 | 4.82k | #endif |
301 | 4.82k | } |
302 | | |
303 | | |
304 | | bool SetFileAttr(const std::wstring &Name,uint Attr) |
305 | 11.1k | { |
306 | | #ifdef _WIN_ALL |
307 | | bool Success=SetFileAttributes(Name.c_str(),Attr)!=0; |
308 | | if (!Success) |
309 | | { |
310 | | std::wstring LongName; |
311 | | if (GetWinLongPath(Name,LongName)) |
312 | | Success=SetFileAttributes(LongName.c_str(),Attr)!=0; |
313 | | } |
314 | | return Success; |
315 | | #elif defined(_UNIX) |
316 | | std::string NameA; |
317 | 11.1k | WideToChar(Name,NameA); |
318 | 11.1k | return chmod(NameA.c_str(),(mode_t)Attr)==0; |
319 | | #else |
320 | | return false; |
321 | | #endif |
322 | 11.1k | } |
323 | | |
324 | | |
325 | | // Ext is the extension with the leading dot, like L".bat", or nullptr to use |
326 | | // the default extension. |
327 | | bool MkTemp(std::wstring &Name,const wchar *Ext) |
328 | 0 | { |
329 | 0 | RarTime CurTime; |
330 | 0 | CurTime.SetCurrentTime(); |
331 | | |
332 | | // We cannot use CurTime.GetWin() as is, because its lowest bits can |
333 | | // have low informational value, like being a zero or few fixed numbers. |
334 | 0 | uint Random=(uint)(CurTime.GetWin()/100000); |
335 | | |
336 | | // Using PID we guarantee that different RAR copies use different temp names |
337 | | // even if started in exactly the same time. |
338 | 0 | uint PID=0; |
339 | | #ifdef _WIN_ALL |
340 | | PID=(uint)GetCurrentProcessId(); |
341 | | #elif defined(_UNIX) |
342 | | PID=(uint)getpid(); |
343 | 0 | #endif |
344 | |
|
345 | 0 | for (uint Attempt=0;;Attempt++) |
346 | 0 | { |
347 | 0 | uint RandomExt=Random%50000+Attempt; |
348 | 0 | if (Attempt==1000) |
349 | 0 | return false; |
350 | | |
351 | | // User asked to specify the single extension for all temporary files, |
352 | | // so it can be added to server ransomware protection exceptions. |
353 | | // He wrote, this protection blocks temporary files when adding |
354 | | // a file to RAR archive with drag and drop. So unless a calling code |
355 | | // requires a specific extension, like .bat file when uninstalling, |
356 | | // we set the uniform extension here. |
357 | 0 | if (Ext==nullptr) |
358 | 0 | Ext=L".rartemp"; |
359 | |
|
360 | 0 | std::wstring NewName=Name + std::to_wstring(PID) + L"." + std::to_wstring(RandomExt) + Ext; |
361 | 0 | if (!FileExist(NewName)) |
362 | 0 | { |
363 | 0 | Name=NewName; |
364 | 0 | break; |
365 | 0 | } |
366 | 0 | } |
367 | 0 | return true; |
368 | 0 | } |
369 | | |
370 | | |
371 | | #if !defined(SFX_MODULE) |
372 | | void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) |
373 | 0 | { |
374 | 0 | int64 SavePos=SrcFile->Tell(); |
375 | | #ifndef SILENT |
376 | | int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; |
377 | | #endif |
378 | |
|
379 | 0 | if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0) |
380 | 0 | uiMsg(UIEVENT_FILESUMSTART); |
381 | |
|
382 | 0 | if ((Flags & CALCFSUM_CURPOS)==0) |
383 | 0 | SrcFile->Seek(0,SEEK_SET); |
384 | |
|
385 | 0 | const size_t BufSize=0x100000; |
386 | 0 | std::vector<byte> Data(BufSize); |
387 | |
|
388 | 0 | DataHash HashCRC,HashBlake2; |
389 | 0 | HashCRC.Init(HASH_CRC32,Threads); |
390 | 0 | HashBlake2.Init(HASH_BLAKE2,Threads); |
391 | |
|
392 | 0 | int64 BlockCount=0; |
393 | 0 | int64 TotalRead=0; |
394 | 0 | while (true) |
395 | 0 | { |
396 | 0 | size_t SizeToRead; |
397 | 0 | if (Size==INT64NDF) // If we process the entire file. |
398 | 0 | SizeToRead=BufSize; // Then always attempt to read the entire buffer. |
399 | 0 | else |
400 | 0 | SizeToRead=(size_t)Min((int64)BufSize,Size); |
401 | 0 | int ReadSize=SrcFile->Read(Data.data(),SizeToRead); |
402 | 0 | if (ReadSize==0) |
403 | 0 | break; |
404 | 0 | TotalRead+=ReadSize; |
405 | |
|
406 | 0 | if ((++BlockCount & 0xf)==0) |
407 | 0 | { |
408 | | #ifndef SILENT |
409 | | if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) |
410 | | { |
411 | | // Update only the current file progress in WinRAR, set the total to 0 |
412 | | // to keep it as is. It looks better for WinRAR. |
413 | | uiExtractProgress(TotalRead,FileLength,0,0); |
414 | | } |
415 | | else |
416 | | { |
417 | | if ((Flags & CALCFSUM_SHOWPERCENT)!=0) |
418 | | uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); |
419 | | } |
420 | | #endif |
421 | 0 | Wait(); |
422 | 0 | } |
423 | |
|
424 | 0 | if (CRC32!=NULL) |
425 | 0 | HashCRC.Update(Data.data(),ReadSize); |
426 | 0 | if (Blake2!=NULL) |
427 | 0 | HashBlake2.Update(Data.data(),ReadSize); |
428 | |
|
429 | 0 | if (Size!=INT64NDF) |
430 | 0 | Size-=ReadSize; |
431 | 0 | } |
432 | 0 | SrcFile->Seek(SavePos,SEEK_SET); |
433 | |
|
434 | 0 | if ((Flags & CALCFSUM_SHOWPERCENT)!=0) |
435 | 0 | uiMsg(UIEVENT_FILESUMEND); |
436 | |
|
437 | 0 | if (CRC32!=NULL) |
438 | 0 | *CRC32=HashCRC.GetCRC32(); |
439 | 0 | if (Blake2!=NULL) |
440 | 0 | { |
441 | 0 | HashValue Result; |
442 | 0 | HashBlake2.Result(&Result); |
443 | 0 | memcpy(Blake2,Result.Digest,sizeof(Result.Digest)); |
444 | 0 | } |
445 | 0 | } |
446 | | #endif |
447 | | |
448 | | |
449 | | bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName) |
450 | 0 | { |
451 | | #ifdef _WIN_ALL |
452 | | bool Success=MoveFile(SrcName.c_str(),DestName.c_str())!=0; |
453 | | if (!Success) |
454 | | { |
455 | | std::wstring LongName1,LongName2; |
456 | | if (GetWinLongPath(SrcName,LongName1) && GetWinLongPath(DestName,LongName2)) |
457 | | Success=MoveFile(LongName1.c_str(),LongName2.c_str())!=0; |
458 | | } |
459 | | return Success; |
460 | | #else |
461 | 0 | std::string SrcNameA,DestNameA; |
462 | 0 | WideToChar(SrcName,SrcNameA); |
463 | 0 | WideToChar(DestName,DestNameA); |
464 | 0 | bool Success=rename(SrcNameA.c_str(),DestNameA.c_str())==0; |
465 | 0 | return Success; |
466 | 0 | #endif |
467 | 0 | } |
468 | | |
469 | | |
470 | | bool DelFile(const std::wstring &Name) |
471 | 6.33k | { |
472 | | #ifdef _WIN_ALL |
473 | | bool Success=DeleteFile(Name.c_str())!=0; |
474 | | if (!Success) |
475 | | { |
476 | | std::wstring LongName; |
477 | | if (GetWinLongPath(Name,LongName)) |
478 | | Success=DeleteFile(LongName.c_str())!=0; |
479 | | } |
480 | | return Success; |
481 | | #else |
482 | 6.33k | std::string NameA; |
483 | 6.33k | WideToChar(Name,NameA); |
484 | 6.33k | bool Success=remove(NameA.c_str())==0; |
485 | 6.33k | return Success; |
486 | 6.33k | #endif |
487 | 6.33k | } |
488 | | |
489 | | |
490 | | bool DelDir(const std::wstring &Name) |
491 | 0 | { |
492 | | #ifdef _WIN_ALL |
493 | | bool Success=RemoveDirectory(Name.c_str())!=0; |
494 | | if (!Success) |
495 | | { |
496 | | std::wstring LongName; |
497 | | if (GetWinLongPath(Name,LongName)) |
498 | | Success=RemoveDirectory(LongName.c_str())!=0; |
499 | | } |
500 | | return Success; |
501 | | #else |
502 | 0 | std::string NameA; |
503 | 0 | WideToChar(Name,NameA); |
504 | 0 | bool Success=rmdir(NameA.c_str())==0; |
505 | 0 | return Success; |
506 | 0 | #endif |
507 | 0 | } |
508 | | |
509 | | |
510 | | #if defined(_WIN_ALL) && !defined(SFX_MODULE) |
511 | | bool SetFileCompression(const std::wstring &Name,bool State) |
512 | | { |
513 | | HANDLE hFile=CreateFile(Name.c_str(),FILE_READ_DATA|FILE_WRITE_DATA, |
514 | | FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, |
515 | | FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); |
516 | | if (hFile==INVALID_HANDLE_VALUE) |
517 | | { |
518 | | std::wstring LongName; |
519 | | if (GetWinLongPath(Name,LongName)) |
520 | | hFile=CreateFile(LongName.c_str(),FILE_READ_DATA|FILE_WRITE_DATA, |
521 | | FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, |
522 | | FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); |
523 | | if (hFile==INVALID_HANDLE_VALUE) |
524 | | return false; |
525 | | } |
526 | | bool Success=SetFileCompression(hFile,State); |
527 | | CloseHandle(hFile); |
528 | | return Success; |
529 | | } |
530 | | |
531 | | |
532 | | bool SetFileCompression(HANDLE hFile,bool State) |
533 | | { |
534 | | SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; |
535 | | DWORD Result; |
536 | | int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, |
537 | | sizeof(NewState),NULL,0,&Result,NULL); |
538 | | return RetCode!=0; |
539 | | } |
540 | | |
541 | | |
542 | | void ResetFileCache(const std::wstring &Name) |
543 | | { |
544 | | // To reset file cache in Windows it is enough to open it with |
545 | | // FILE_FLAG_NO_BUFFERING and then close it. |
546 | | HANDLE hSrc=CreateFile(Name.c_str(),GENERIC_READ, |
547 | | FILE_SHARE_READ|FILE_SHARE_WRITE, |
548 | | NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL); |
549 | | if (hSrc!=INVALID_HANDLE_VALUE) |
550 | | CloseHandle(hSrc); |
551 | | } |
552 | | #endif |
553 | | |
554 | | |
555 | | |
556 | | |
557 | | |
558 | | |
559 | | |
560 | | |
561 | | |
562 | | |
563 | | |
564 | | |
565 | | |
566 | | |
567 | | // Delete symbolic links in file path, if any, and replace them by directories. |
568 | | // Prevents extracting files outside of destination folder with symlink chains. |
569 | | bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked) |
570 | 24.0k | { |
571 | | // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like |
572 | | // "lnk1/../dir", but converts the path to "dir". In Unix we need to call |
573 | | // this function to prevent placing unpacked files outside of destination |
574 | | // folder if previously we unpacked "dir/lnk1" -> "..", |
575 | | // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt". |
576 | | // We may still need this function to prevent abusing symlink chains |
577 | | // in link source path if we remove detection of such chains |
578 | | // in IsRelativeSymlinkSafe. This function seems to make other symlink |
579 | | // related safety checks redundant, but for now we prefer to keep them too. |
580 | | // |
581 | | // 2022.12.01: the performance impact is minimized after adding the check |
582 | | // against the previous path and enabling this verification only after |
583 | | // extracting a symlink with ".." in target. So we enabled it for Windows |
584 | | // as well for extra safety. |
585 | | //#ifdef _UNIX |
586 | 24.0k | std::wstring Path=SrcName; |
587 | | |
588 | 24.0k | size_t SkipLength=SkipPart.size(); |
589 | | |
590 | 24.0k | if (SkipLength>0 && Path.rfind(SkipPart,0)!=0) |
591 | 0 | SkipLength=0; // Parameter validation, not really needed now. |
592 | | |
593 | | // Do not check parts already checked in previous path to improve performance. |
594 | 214k | for (size_t I=0;I<Path.size() && I<LastChecked.size() && Path[I]==LastChecked[I];I++) |
595 | 190k | if (IsPathDiv(Path[I]) && I>SkipLength) |
596 | 718 | SkipLength=I; |
597 | | |
598 | | // Avoid converting symlinks in destination path part specified by user. |
599 | 24.3k | while (SkipLength<Path.size() && IsPathDiv(Path[SkipLength])) |
600 | 292 | SkipLength++; |
601 | | |
602 | 24.0k | if (Path.size()>0) |
603 | 1.32M | for (size_t I=Path.size()-1;I>SkipLength;I--) |
604 | 1.29M | if (IsPathDiv(Path[I])) |
605 | 94.8k | { |
606 | 94.8k | Path.erase(I); |
607 | 94.8k | FindData FD; |
608 | 94.8k | if (FindFile::FastFind(Path,&FD,true) && FD.IsLink) |
609 | 1 | { |
610 | | #ifdef _WIN_ALL |
611 | | // Normally Windows symlinks to directory look like a directory |
612 | | // and are deleted with DelDir(). It is possible to create |
613 | | // a file-like symlink pointing at directory, which can be deleted |
614 | | // only with && DelFile, but such symlink isn't really functional. |
615 | | // Here we prefer to fail deleting such symlink and skip extracting |
616 | | // a file. |
617 | | if (!DelDir(Path)) |
618 | | #else |
619 | 1 | if (!DelFile(Path)) |
620 | 0 | #endif |
621 | 0 | { |
622 | 0 | ErrHandler.CreateErrorMsg(SrcName); // Extraction command will skip this file or directory. |
623 | 0 | return false; // Couldn't delete the symlink to replace it with directory. |
624 | 0 | } |
625 | 1 | } |
626 | 94.8k | } |
627 | 24.0k | LastChecked=SrcName; |
628 | | //#endif |
629 | 24.0k | return true; |
630 | 24.0k | } |