Coverage Report

Created: 2025-04-11 06:56

/src/unrar/filefn.cpp
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
}