1 #include "rar.hpp"
2
MakeDir(const wchar * Name,bool SetAttr,uint Attr)3 MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
4 {
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=PointToLastChar(Name);
9 bool Special=*LastChar=='.' || *LastChar==' ';
10 BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL);
11 if (RetCode==0 && !FileExist(Name))
12 {
13 wchar LongName[NM];
14 if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
15 RetCode=CreateDirectory(LongName,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 char NameA[NM];
29 WideToChar(Name,NameA,ASIZE(NameA));
30 mode_t uattr=SetAttr ? (mode_t)Attr:0777;
31 int ErrCode=mkdir(NameA,uattr);
32 if (ErrCode==-1)
33 return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
34 return MKDIR_SUCCESS;
35 #else
36 return MKDIR_ERROR;
37 #endif
38 }
39
40
CreatePath(const wchar * Path,bool SkipLastName,bool Silent)41 bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent)
42 {
43 if (Path==NULL || *Path==0)
44 return false;
45
46 #if defined(_WIN_ALL) || defined(_EMX)
47 uint DirAttr=0;
48 #else
49 uint DirAttr=0777;
50 #endif
51
52 bool Success=true;
53
54 for (const wchar *s=Path;*s!=0;s++)
55 {
56 wchar DirName[NM];
57 if (s-Path>=ASIZE(DirName))
58 break;
59
60 // Process all kinds of path separators, so user can enter Unix style
61 // path in Windows or Windows in Unix. s>Path check avoids attempting
62 // creating an empty directory for paths starting from path separator.
63 if (IsPathDiv(*s) && s>Path)
64 {
65 #ifdef _WIN_ALL
66 // We must not attempt to create "D:" directory, because first
67 // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine
68 // to create "D:" directory.
69 if (s==Path+2 && Path[1]==':')
70 continue;
71 #endif
72 wcsncpy(DirName,Path,s-Path);
73 DirName[s-Path]=0;
74
75 Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
76 if (Success && !Silent)
77 {
78 mprintf(St(MCreatDir),DirName);
79 mprintf(L" %s",St(MOk));
80 }
81 }
82 }
83 if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path)))
84 Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
85 return Success;
86 }
87
88
SetDirTime(const wchar * Name,RarTime * ftm,RarTime * ftc,RarTime * fta)89 void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
90 {
91 #if defined(_WIN_ALL)
92 bool sm=ftm!=NULL && ftm->IsSet();
93 bool sc=ftc!=NULL && ftc->IsSet();
94 bool sa=fta!=NULL && fta->IsSet();
95
96 uint DirAttr=GetFileAttr(Name);
97 bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0);
98 if (ResetAttr)
99 SetFileAttr(Name,0);
100
101 HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
102 NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
103 if (hFile==INVALID_HANDLE_VALUE)
104 {
105 wchar LongName[NM];
106 if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
107 hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
108 NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
109 }
110
111 if (hFile==INVALID_HANDLE_VALUE)
112 return;
113 FILETIME fm,fc,fa;
114 if (sm)
115 ftm->GetWinFT(&fm);
116 if (sc)
117 ftc->GetWinFT(&fc);
118 if (sa)
119 fta->GetWinFT(&fa);
120 SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
121 CloseHandle(hFile);
122 if (ResetAttr)
123 SetFileAttr(Name,DirAttr);
124 #endif
125 #if defined(_UNIX) || defined(_EMX)
126 File::SetCloseFileTimeByName(Name,ftm,fta);
127 #endif
128 }
129
130
IsRemovable(const wchar * Name)131 bool IsRemovable(const wchar *Name)
132 {
133 #if defined(_WIN_ALL)
134 wchar Root[NM];
135 GetPathRoot(Name,Root,ASIZE(Root));
136 int Type=GetDriveType(*Root!=0 ? Root:NULL);
137 return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
138 #else
139 return false;
140 #endif
141 }
142
143
144 #ifndef SFX_MODULE
GetFreeDisk(const wchar * Name)145 int64 GetFreeDisk(const wchar *Name)
146 {
147 #ifdef _WIN_ALL
148 wchar Root[NM];
149 GetFilePath(Name,Root,ASIZE(Root));
150
151 ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
152 uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
153 if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
154 uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
155 return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
156 return 0;
157 #elif defined(_UNIX)
158 wchar Root[NM];
159 GetFilePath(Name,Root,ASIZE(Root));
160 char RootA[NM];
161 WideToChar(Root,RootA,ASIZE(RootA));
162 struct statvfs sfs;
163 if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
164 return 0;
165 int64 FreeSize=sfs.f_bsize;
166 FreeSize=FreeSize*sfs.f_bavail;
167 return FreeSize;
168 #else
169 return 0;
170 #endif
171 }
172 #endif
173
174
175 #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
176 // Return 'true' for FAT and FAT32, so we can adjust the maximum supported
177 // file size to 4 GB for these file systems.
IsFAT(const wchar * Name)178 bool IsFAT(const wchar *Name)
179 {
180 wchar Root[NM];
181 GetPathRoot(Name,Root,ASIZE(Root));
182 wchar FileSystem[MAX_PATH+1];
183 if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
184 return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
185 return false;
186 }
187 #endif
188
189
FileExist(const wchar * Name)190 bool FileExist(const wchar *Name)
191 {
192 #ifdef _WIN_ALL
193 return GetFileAttr(Name)!=0xffffffff;
194 #elif defined(ENABLE_ACCESS)
195 char NameA[NM];
196 WideToChar(Name,NameA,ASIZE(NameA));
197 return access(NameA,0)==0;
198 #else
199 FindData FD;
200 return FindFile::FastFind(Name,&FD);
201 #endif
202 }
203
204
WildFileExist(const wchar * Name)205 bool WildFileExist(const wchar *Name)
206 {
207 if (IsWildcard(Name))
208 {
209 FindFile Find;
210 Find.SetMask(Name);
211 FindData fd;
212 return Find.Next(&fd);
213 }
214 return FileExist(Name);
215 }
216
217
IsDir(uint Attr)218 bool IsDir(uint Attr)
219 {
220 #ifdef _WIN_ALL
221 return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
222 #endif
223 #if defined(_UNIX)
224 return (Attr & 0xF000)==0x4000;
225 #endif
226 }
227
228
IsUnreadable(uint Attr)229 bool IsUnreadable(uint Attr)
230 {
231 #if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
232 return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
233 #endif
234 return false;
235 }
236
237
IsLink(uint Attr)238 bool IsLink(uint Attr)
239 {
240 #ifdef _UNIX
241 return (Attr & 0xF000)==0xA000;
242 #elif defined(_WIN_ALL)
243 return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0;
244 #else
245 return false;
246 #endif
247 }
248
249
250
251
252
253
IsDeleteAllowed(uint FileAttr)254 bool IsDeleteAllowed(uint FileAttr)
255 {
256 #ifdef _WIN_ALL
257 return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
258 #else
259 return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
260 #endif
261 }
262
263
PrepareToDelete(const wchar * Name)264 void PrepareToDelete(const wchar *Name)
265 {
266 #if defined(_WIN_ALL) || defined(_EMX)
267 SetFileAttr(Name,0);
268 #endif
269 #ifdef _UNIX
270 if (Name!=NULL)
271 {
272 char NameA[NM];
273 WideToChar(Name,NameA,ASIZE(NameA));
274 chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
275 }
276 #endif
277 }
278
279
GetFileAttr(const wchar * Name)280 uint GetFileAttr(const wchar *Name)
281 {
282 #ifdef _WIN_ALL
283 DWORD Attr=GetFileAttributes(Name);
284 if (Attr==0xffffffff)
285 {
286 wchar LongName[NM];
287 if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
288 Attr=GetFileAttributes(LongName);
289 }
290 return Attr;
291 #else
292 char NameA[NM];
293 WideToChar(Name,NameA,ASIZE(NameA));
294 struct stat st;
295 if (stat(NameA,&st)!=0)
296 return 0;
297 return st.st_mode;
298 #endif
299 }
300
301
SetFileAttr(const wchar * Name,uint Attr)302 bool SetFileAttr(const wchar *Name,uint Attr)
303 {
304 #ifdef _WIN_ALL
305 bool Success=SetFileAttributes(Name,Attr)!=0;
306 if (!Success)
307 {
308 wchar LongName[NM];
309 if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
310 Success=SetFileAttributes(LongName,Attr)!=0;
311 }
312 return Success;
313 #elif defined(_UNIX)
314 char NameA[NM];
315 WideToChar(Name,NameA,ASIZE(NameA));
316 return chmod(NameA,(mode_t)Attr)==0;
317 #else
318 return false;
319 #endif
320 }
321
322
323 #if 0
324 wchar *MkTemp(wchar *Name,size_t MaxSize)
325 {
326 size_t Length=wcslen(Name);
327
328 RarTime CurTime;
329 CurTime.SetCurrentTime();
330
331 // We cannot use CurTime.GetWin() as is, because its lowest bits can
332 // have low informational value, like being a zero or few fixed numbers.
333 uint Random=(uint)(CurTime.GetWin()/100000);
334
335 // Using PID we guarantee that different RAR copies use different temp names
336 // even if started in exactly the same time.
337 uint PID=0;
338 #ifdef _WIN_ALL
339 PID=(uint)GetCurrentProcessId();
340 #elif defined(_UNIX)
341 PID=(uint)getpid();
342 #endif
343
344 for (uint Attempt=0;;Attempt++)
345 {
346 uint Ext=Random%50000+Attempt;
347 wchar RndText[50];
348 swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext);
349 if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
350 return NULL;
351 wcsncpyz(Name+Length,RndText,MaxSize-Length);
352 if (!FileExist(Name))
353 break;
354 }
355 return Name;
356 }
357 #endif
358
359
360 #if !defined(SFX_MODULE)
CalcFileSum(File * SrcFile,uint * CRC32,byte * Blake2,uint Threads,int64 Size,uint Flags)361 void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
362 {
363 int64 SavePos=SrcFile->Tell();
364 #ifndef SILENT
365 int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size;
366 #endif
367
368 if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0)
369 uiMsg(UIEVENT_FILESUMSTART);
370
371 if ((Flags & CALCFSUM_CURPOS)==0)
372 SrcFile->Seek(0,SEEK_SET);
373
374 const size_t BufSize=0x100000;
375 Array<byte> Data(BufSize);
376
377
378 DataHash HashCRC,HashBlake2;
379 HashCRC.Init(HASH_CRC32,Threads);
380 HashBlake2.Init(HASH_BLAKE2,Threads);
381
382 int64 BlockCount=0;
383 int64 TotalRead=0;
384 while (true)
385 {
386 size_t SizeToRead;
387 if (Size==INT64NDF) // If we process the entire file.
388 SizeToRead=BufSize; // Then always attempt to read the entire buffer.
389 else
390 SizeToRead=(size_t)Min((int64)BufSize,Size);
391 int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
392 if (ReadSize==0)
393 break;
394 TotalRead+=ReadSize;
395
396 if ((++BlockCount & 0xf)==0)
397 {
398 #ifndef SILENT
399 if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
400 uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength);
401 else
402 {
403 if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
404 uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength));
405 }
406 #endif
407 Wait();
408 }
409
410 if (CRC32!=NULL)
411 HashCRC.Update(&Data[0],ReadSize);
412 if (Blake2!=NULL)
413 HashBlake2.Update(&Data[0],ReadSize);
414
415 if (Size!=INT64NDF)
416 Size-=ReadSize;
417 }
418 SrcFile->Seek(SavePos,SEEK_SET);
419
420 if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
421 uiMsg(UIEVENT_FILESUMEND);
422
423 if (CRC32!=NULL)
424 *CRC32=HashCRC.GetCRC32();
425 if (Blake2!=NULL)
426 {
427 HashValue Result;
428 HashBlake2.Result(&Result);
429 memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
430 }
431 }
432 #endif
433
434
RenameFile(const wchar * SrcName,const wchar * DestName)435 bool RenameFile(const wchar *SrcName,const wchar *DestName)
436 {
437 #ifdef _WIN_ALL
438 bool Success=MoveFile(SrcName,DestName)!=0;
439 if (!Success)
440 {
441 wchar LongName1[NM],LongName2[NM];
442 if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
443 GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
444 Success=MoveFile(LongName1,LongName2)!=0;
445 }
446 return Success;
447 #else
448 char SrcNameA[NM],DestNameA[NM];
449 WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
450 WideToChar(DestName,DestNameA,ASIZE(DestNameA));
451 bool Success=rename(SrcNameA,DestNameA)==0;
452 return Success;
453 #endif
454 }
455
456
DelFile(const wchar * Name)457 bool DelFile(const wchar *Name)
458 {
459 #ifdef _WIN_ALL
460 bool Success=DeleteFile(Name)!=0;
461 if (!Success)
462 {
463 wchar LongName[NM];
464 if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
465 Success=DeleteFile(LongName)!=0;
466 }
467 return Success;
468 #else
469 char NameA[NM];
470 WideToChar(Name,NameA,ASIZE(NameA));
471 bool Success=remove(NameA)==0;
472 return Success;
473 #endif
474 }
475
476
DelDir(const wchar * Name)477 bool DelDir(const wchar *Name)
478 {
479 #ifdef _WIN_ALL
480 bool Success=RemoveDirectory(Name)!=0;
481 if (!Success)
482 {
483 wchar LongName[NM];
484 if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
485 Success=RemoveDirectory(LongName)!=0;
486 }
487 return Success;
488 #else
489 char NameA[NM];
490 WideToChar(Name,NameA,ASIZE(NameA));
491 bool Success=rmdir(NameA)==0;
492 return Success;
493 #endif
494 }
495
496
497 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
SetFileCompression(const wchar * Name,bool State)498 bool SetFileCompression(const wchar *Name,bool State)
499 {
500 HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA,
501 FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
502 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
503 if (hFile==INVALID_HANDLE_VALUE)
504 {
505 wchar LongName[NM];
506 if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
507 hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA,
508 FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
509 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
510 }
511 if (hFile==INVALID_HANDLE_VALUE)
512 return false;
513 SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
514 DWORD Result;
515 int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
516 sizeof(NewState),NULL,0,&Result,NULL);
517 CloseHandle(hFile);
518 return RetCode!=0;
519 }
520 #endif
521
522
523
524
525
526
527
528
529
530
531