Coverage Report

Created: 2024-05-20 06:31

/src/clamav/libclamunrar/filcreat.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "rar.hpp"
2
3
// If NewFile==NULL, we delete created file after user confirmation.
4
// It is useful if we need to overwrite an existing folder or file,
5
// but need user confirmation for that.
6
bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
7
                bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
8
489k
{
9
489k
  if (UserReject!=NULL)
10
489k
    *UserReject=false;
11
#ifdef _WIN_ALL
12
  bool ShortNameChanged=false;
13
#endif
14
489k
  while (FileExist(Name))
15
0
  {
16
#if defined(_WIN_ALL)
17
    if (!ShortNameChanged)
18
    {
19
      // Avoid the infinite loop if UpdateExistingShortName returns
20
      // the same name.
21
      ShortNameChanged=true;
22
23
      // Maybe our long name matches the short name of existing file.
24
      // Let's check if we can change the short name.
25
      if (UpdateExistingShortName(Name))
26
        continue;
27
    }
28
    // Allow short name check again. It is necessary, because rename and
29
    // autorename below can change the name, so we need to check it again.
30
    ShortNameChanged=false;
31
#endif
32
0
    UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
33
34
0
    if (Choice==UIASKREP_R_REPLACE)
35
0
      break;
36
0
    if (Choice==UIASKREP_R_SKIP)
37
0
    {
38
0
      if (UserReject!=NULL)
39
0
        *UserReject=true;
40
0
      return false;
41
0
    }
42
0
    if (Choice==UIASKREP_R_CANCEL)
43
0
      ErrHandler.Exit(RARX_USERBREAK);
44
0
  }
45
46
  // Try to truncate the existing file first instead of delete,
47
  // so we preserve existing file permissions, such as NTFS permissions,
48
  // also as "Compressed" attribute and hard links. In GUI version we avoid
49
  // deleting an existing file for non-.rar archive formats as well.
50
489k
  uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
51
489k
  if (NewFile!=NULL && NewFile->Create(Name,FileMode))
52
489k
    return true;
53
54
0
  CreatePath(Name,true,Cmd->DisableNames);
55
0
  return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
56
489k
}
57
58
59
bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
60
0
{
61
0
  wchar NewName[NM];
62
0
  size_t NameLength=wcslen(Name);
63
0
  wchar *Ext=GetExt(Name);
64
0
  if (Ext==NULL)
65
0
    Ext=Name+NameLength;
66
0
  for (uint FileVer=1;;FileVer++)
67
0
  {
68
0
    swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
69
0
    if (!FileExist(NewName))
70
0
    {
71
0
      wcsncpyz(Name,NewName,MaxNameSize);
72
0
      break;
73
0
    }
74
0
    if (FileVer>=1000000)
75
0
      return false;
76
0
  }
77
0
  return true;
78
0
}
79
80
81
#if defined(_WIN_ALL)
82
// If we find a file, which short name is equal to 'Name', we try to change
83
// its short name, while preserving the long name. It helps when unpacking
84
// an archived file, which long name is equal to short name of already
85
// existing file. Otherwise we would overwrite the already existing file,
86
// even though its long name does not match the name of unpacking file.
87
bool UpdateExistingShortName(const wchar *Name)
88
{
89
  wchar LongPathName[NM];
90
  DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
91
  if (Res==0 || Res>=ASIZE(LongPathName))
92
    return false;
93
  wchar ShortPathName[NM];
94
  Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
95
  if (Res==0 || Res>=ASIZE(ShortPathName))
96
    return false;
97
  wchar *LongName=PointToName(LongPathName);
98
  wchar *ShortName=PointToName(ShortPathName);
99
100
  // We continue only if file has a short name, which does not match its
101
  // long name, and this short name is equal to name of file which we need
102
  // to create.
103
  if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 ||
104
      wcsicomp(PointToName(Name),ShortName)!=0)
105
    return false;
106
107
  // Generate the temporary new name for existing file.
108
  wchar NewName[NM];
109
  *NewName=0;
110
  for (int I=0;I<10000 && *NewName==0;I+=123)
111
  {
112
    // Here we copy the path part of file to create. We'll make the temporary
113
    // file in the same folder.
114
    wcsncpyz(NewName,Name,ASIZE(NewName));
115
116
    // Here we set the random name part.
117
    swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
118
    
119
    // If such file is already exist, try next random name.
120
    if (FileExist(NewName))
121
      *NewName=0;
122
  }
123
124
  // If we could not generate the name not used by any other file, we return.
125
  if (*NewName==0)
126
    return false;
127
  
128
  // FastFind returns the name without path, but we need the fully qualified
129
  // name for renaming, so we use the path from file to create and long name
130
  // from existing file.
131
  wchar FullName[NM];
132
  wcsncpyz(FullName,Name,ASIZE(FullName));
133
  SetName(FullName,LongName,ASIZE(FullName));
134
  
135
  // Rename the existing file to randomly generated name. Normally it changes
136
  // the short name too.
137
  if (!MoveFile(FullName,NewName))
138
    return false;
139
140
  // Now we need to create the temporary empty file with same name as
141
  // short name of our already existing file. We do it to occupy its previous
142
  // short name and not allow to use it again when renaming the file back to
143
  // its original long name.
144
  File KeepShortFile;
145
  bool Created=false;
146
  if (!FileExist(Name))
147
    Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);
148
149
  // Now we rename the existing file from temporary name to original long name.
150
  // Since its previous short name is occupied by another file, it should
151
  // get another short name.
152
  MoveFile(NewName,FullName);
153
154
  if (Created)
155
  {
156
    // Delete the temporary zero length file occupying the short name,
157
    KeepShortFile.Close();
158
    KeepShortFile.Delete();
159
  }
160
  // We successfully changed the short name. Maybe sometimes we'll simplify
161
  // this function by use of SetFileShortName Windows API call.
162
  // But SetFileShortName is not available in older Windows.
163
  return true;
164
}
165
#endif