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.
FileCreate(RAROptions * Cmd,File * NewFile,wchar * Name,size_t MaxNameSize,bool * UserReject,int64 FileSize,RarTime * FileTime,bool WriteOnly)6 bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
7                 bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
8 {
9   if (UserReject!=NULL)
10     *UserReject=false;
11 #ifdef _WIN_ALL
12   bool ShortNameChanged=false;
13 #endif
14   while (FileExist(Name))
15   {
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     UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
33 
34     if (Choice==UIASKREP_R_REPLACE)
35       break;
36     if (Choice==UIASKREP_R_SKIP)
37     {
38       if (UserReject!=NULL)
39         *UserReject=true;
40       return false;
41     }
42     if (Choice==UIASKREP_R_CANCEL)
43       ErrHandler.Exit(RARX_USERBREAK);
44   }
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   uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
51   if (NewFile!=NULL && NewFile->Create(Name,FileMode))
52     return true;
53 
54   CreatePath(Name,true,Cmd->DisableNames);
55   return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
56 }
57 
58 
GetAutoRenamedName(wchar * Name,size_t MaxNameSize)59 bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
60 {
61   wchar NewName[NM];
62   size_t NameLength=wcslen(Name);
63   wchar *Ext=GetExt(Name);
64   if (Ext==NULL)
65     Ext=Name+NameLength;
66   for (uint FileVer=1;;FileVer++)
67   {
68     swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
69     if (!FileExist(NewName))
70     {
71       wcsncpyz(Name,NewName,MaxNameSize);
72       break;
73     }
74     if (FileVer>=1000000)
75       return false;
76   }
77   return true;
78 }
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.
UpdateExistingShortName(const wchar * Name)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
166