Coverage Report

Created: 2023-06-07 06:02

/src/unrar/filefn.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "rar.hpp"
2
3
MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
4
0
{
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
0
  char NameA[NM];
29
0
  WideToChar(Name,NameA,ASIZE(NameA));
30
0
  mode_t uattr=SetAttr ? (mode_t)Attr:0777;
31
0
  int ErrCode=mkdir(NameA,uattr);
32
0
  if (ErrCode==-1)
33
0
    return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
34
0
  return MKDIR_SUCCESS;
35
#else
36
  return MKDIR_ERROR;
37
#endif
38
0
}
39
40
41
bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent)
42
0
{
43
0
  if (Path==NULL || *Path==0)
44
0
    return false;
45
46
#if defined(_WIN_ALL) || defined(_EMX)
47
  uint DirAttr=0;
48
#else
49
0
  uint DirAttr=0777;
50
0
#endif
51
  
52
0
  bool Success=true;
53
54
0
  for (const wchar *s=Path;*s!=0;s++)
55
0
  {
56
0
    wchar DirName[NM];
57
0
    if (s-Path>=ASIZE(DirName))
58
0
      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
0
    if (IsPathDiv(*s) && s>Path)
64
0
    {
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
0
      wcsncpy(DirName,Path,s-Path);
73
0
      DirName[s-Path]=0;
74
75
0
      Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
76
0
      if (Success && !Silent)
77
0
      {
78
0
        mprintf(St(MCreatDir),DirName);
79
0
        mprintf(L" %s",St(MOk));
80
0
      }
81
0
    }
82
0
  }
83
0
  if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path)))
84
0
    Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
85
0
  return Success;
86
0
}
87
88
89
void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
90
0
{
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
0
#if defined(_UNIX) || defined(_EMX)
126
0
  File::SetCloseFileTimeByName(Name,ftm,fta);
127
0
#endif
128
0
}
129
130
131
bool IsRemovable(const wchar *Name)
132
0
{
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
0
  return false;
140
0
#endif
141
0
}
142
143
144
#ifndef SFX_MODULE
145
int64 GetFreeDisk(const wchar *Name)
146
0
{
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
0
  wchar Root[NM];
159
0
  GetFilePath(Name,Root,ASIZE(Root));
160
0
  char RootA[NM];
161
0
  WideToChar(Root,RootA,ASIZE(RootA));
162
0
  struct statvfs sfs;
163
0
  if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
164
0
    return 0;
165
0
  int64 FreeSize=sfs.f_bsize;
166
0
  FreeSize=FreeSize*sfs.f_bavail;
167
0
  return FreeSize;
168
#else
169
  return 0;
170
#endif
171
0
}
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.
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
190
bool FileExist(const wchar *Name)
191
1
{
192
#ifdef _WIN_ALL
193
  return GetFileAttr(Name)!=0xffffffff;
194
#elif defined(ENABLE_ACCESS)
195
1
  char NameA[NM];
196
1
  WideToChar(Name,NameA,ASIZE(NameA));
197
1
  return access(NameA,0)==0;
198
#else
199
  FindData FD;
200
  return FindFile::FastFind(Name,&FD);
201
#endif
202
1
}
203
 
204
205
bool WildFileExist(const wchar *Name)
206
0
{
207
0
  if (IsWildcard(Name))
208
0
  {
209
0
    FindFile Find;
210
0
    Find.SetMask(Name);
211
0
    FindData fd;
212
0
    return Find.Next(&fd);
213
0
  }
214
0
  return FileExist(Name);
215
0
}
216
217
218
bool IsDir(uint Attr)
219
2
{
220
#ifdef _WIN_ALL
221
  return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
222
#endif
223
2
#if defined(_UNIX)
224
2
  return (Attr & 0xF000)==0x4000;
225
2
#endif
226
2
}
227
228
229
bool IsUnreadable(uint Attr)
230
0
{
231
0
#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
232
0
  return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
233
0
#endif
234
0
  return false;
235
0
}
236
237
238
bool IsLink(uint Attr)
239
2
{
240
2
#ifdef _UNIX
241
2
  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
2
}
248
249
250
251
252
253
254
bool IsDeleteAllowed(uint FileAttr)
255
0
{
256
#ifdef _WIN_ALL
257
  return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
258
#else
259
0
  return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
260
0
#endif
261
0
}
262
263
264
void PrepareToDelete(const wchar *Name)
265
0
{
266
#if defined(_WIN_ALL) || defined(_EMX)
267
  SetFileAttr(Name,0);
268
#endif
269
0
#ifdef _UNIX
270
0
  if (Name!=NULL)
271
0
  {
272
0
    char NameA[NM];
273
0
    WideToChar(Name,NameA,ASIZE(NameA));
274
0
    chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
275
0
  }
276
0
#endif
277
0
}
278
279
280
uint GetFileAttr(const wchar *Name)
281
0
{
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
0
  char NameA[NM];
293
0
  WideToChar(Name,NameA,ASIZE(NameA));
294
0
  struct stat st;
295
0
  if (stat(NameA,&st)!=0)
296
0
    return 0;
297
0
  return st.st_mode;
298
0
#endif
299
0
}
300
301
302
bool SetFileAttr(const wchar *Name,uint Attr)
303
0
{
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
0
  char NameA[NM];
315
0
  WideToChar(Name,NameA,ASIZE(NameA));
316
0
  return chmod(NameA,(mode_t)Attr)==0;
317
#else
318
  return false;
319
#endif
320
0
}
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)
361
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
362
0
{
363
0
  int64 SavePos=SrcFile->Tell();
364
#ifndef SILENT
365
  int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size;
366
#endif
367
368
0
  if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0)
369
0
    uiMsg(UIEVENT_FILESUMSTART);
370
371
0
  if ((Flags & CALCFSUM_CURPOS)==0)
372
0
    SrcFile->Seek(0,SEEK_SET);
373
374
0
  const size_t BufSize=0x100000;
375
0
  Array<byte> Data(BufSize);
376
377
378
0
  DataHash HashCRC,HashBlake2;
379
0
  HashCRC.Init(HASH_CRC32,Threads);
380
0
  HashBlake2.Init(HASH_BLAKE2,Threads);
381
382
0
  int64 BlockCount=0;
383
0
  int64 TotalRead=0;
384
0
  while (true)
385
0
  {
386
0
    size_t SizeToRead;
387
0
    if (Size==INT64NDF)   // If we process the entire file.
388
0
      SizeToRead=BufSize; // Then always attempt to read the entire buffer.
389
0
    else
390
0
      SizeToRead=(size_t)Min((int64)BufSize,Size);
391
0
    int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
392
0
    if (ReadSize==0)
393
0
      break;
394
0
    TotalRead+=ReadSize;
395
396
0
    if ((++BlockCount & 0xf)==0)
397
0
    {
398
#ifndef SILENT
399
      if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
400
      {
401
        // Update only the current file progress in WinRAR, set the total to 0
402
        // to keep it as is. It looks better for WinRAR,
403
        uiExtractProgress(TotalRead,FileLength,0,0);
404
      }
405
      else
406
      {
407
        if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
408
          uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength));
409
      }
410
#endif
411
0
      Wait();
412
0
    }
413
414
0
    if (CRC32!=NULL)
415
0
      HashCRC.Update(&Data[0],ReadSize);
416
0
    if (Blake2!=NULL)
417
0
      HashBlake2.Update(&Data[0],ReadSize);
418
419
0
    if (Size!=INT64NDF)
420
0
      Size-=ReadSize;
421
0
  }
422
0
  SrcFile->Seek(SavePos,SEEK_SET);
423
424
0
  if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
425
0
    uiMsg(UIEVENT_FILESUMEND);
426
427
0
  if (CRC32!=NULL)
428
0
    *CRC32=HashCRC.GetCRC32();
429
0
  if (Blake2!=NULL)
430
0
  {
431
0
    HashValue Result;
432
0
    HashBlake2.Result(&Result);
433
0
    memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
434
0
  }
435
0
}
436
#endif
437
438
439
bool RenameFile(const wchar *SrcName,const wchar *DestName)
440
0
{
441
#ifdef _WIN_ALL
442
  bool Success=MoveFile(SrcName,DestName)!=0;
443
  if (!Success)
444
  {
445
    wchar LongName1[NM],LongName2[NM];
446
    if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
447
        GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
448
      Success=MoveFile(LongName1,LongName2)!=0;
449
  }
450
  return Success;
451
#else
452
0
  char SrcNameA[NM],DestNameA[NM];
453
0
  WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
454
0
  WideToChar(DestName,DestNameA,ASIZE(DestNameA));
455
0
  bool Success=rename(SrcNameA,DestNameA)==0;
456
0
  return Success;
457
0
#endif
458
0
}
459
460
461
bool DelFile(const wchar *Name)
462
0
{
463
#ifdef _WIN_ALL
464
  bool Success=DeleteFile(Name)!=0;
465
  if (!Success)
466
  {
467
    wchar LongName[NM];
468
    if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
469
      Success=DeleteFile(LongName)!=0;
470
  }
471
  return Success;
472
#else
473
0
  char NameA[NM];
474
0
  WideToChar(Name,NameA,ASIZE(NameA));
475
0
  bool Success=remove(NameA)==0;
476
0
  return Success;
477
0
#endif
478
0
}
479
480
481
bool DelDir(const wchar *Name)
482
0
{
483
#ifdef _WIN_ALL
484
  bool Success=RemoveDirectory(Name)!=0;
485
  if (!Success)
486
  {
487
    wchar LongName[NM];
488
    if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
489
      Success=RemoveDirectory(LongName)!=0;
490
  }
491
  return Success;
492
#else
493
0
  char NameA[NM];
494
0
  WideToChar(Name,NameA,ASIZE(NameA));
495
0
  bool Success=rmdir(NameA)==0;
496
0
  return Success;
497
0
#endif
498
0
}
499
500
501
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
502
bool SetFileCompression(const wchar *Name,bool State)
503
{
504
  HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA,
505
                 FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
506
                 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
507
  if (hFile==INVALID_HANDLE_VALUE)
508
  {
509
    wchar LongName[NM];
510
    if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
511
      hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA,
512
                 FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
513
                 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
514
  }
515
  if (hFile==INVALID_HANDLE_VALUE)
516
    return false;
517
  SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
518
  DWORD Result;
519
  int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
520
                              sizeof(NewState),NULL,0,&Result,NULL);
521
  CloseHandle(hFile);
522
  return RetCode!=0;
523
}
524
525
526
void ResetFileCache(const wchar *Name)
527
{
528
  // To reset file cache in Windows it is enough to open it with
529
  // FILE_FLAG_NO_BUFFERING and then close it.
530
  HANDLE hSrc=CreateFile(Name,GENERIC_READ,
531
                         FILE_SHARE_READ|FILE_SHARE_WRITE,
532
                         NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL);
533
  if (hSrc!=INVALID_HANDLE_VALUE)
534
    CloseHandle(hSrc);
535
}
536
#endif
537
538
539
540
541
542
543
544
545
546