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