/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 |