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