1 // LinkDialog.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Windows/ErrorMsg.h"
6 #include "../../../Windows/FileDir.h"
7 #include "../../../Windows/FileFind.h"
8 #include "../../../Windows/FileIO.h"
9 #include "../../../Windows/FileName.h"
10
11 #ifdef LANG
12 #include "LangUtils.h"
13 #endif
14
15 #include "BrowseDialog.h"
16 #include "CopyDialogRes.h"
17 #include "LinkDialog.h"
18 #include "resourceGui.h"
19
20 #include "App.h"
21
22 #include "resource.h"
23
24 extern bool g_SymLink_Supported;
25
26 using namespace NWindows;
27 using namespace NFile;
28
29 #ifdef LANG
30 static const UInt32 kLangIDs[] =
31 {
32 IDB_LINK_LINK,
33 IDT_LINK_PATH_FROM,
34 IDT_LINK_PATH_TO,
35 IDG_LINK_TYPE,
36 IDR_LINK_TYPE_HARD,
37 IDR_LINK_TYPE_SYM_FILE,
38 IDR_LINK_TYPE_SYM_DIR,
39 IDR_LINK_TYPE_JUNCTION
40 };
41 #endif
42
GetSymLink(CFSTR path,CReparseAttr & attr)43 static bool GetSymLink(CFSTR path, CReparseAttr &attr)
44 {
45 NIO::CInFile file;
46 if (!file.Open(path,
47 FILE_SHARE_READ,
48 OPEN_EXISTING,
49 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS))
50 return false;
51
52 const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
53 CByteArr buf(kBufSize);
54 DWORD returnedSize;
55 if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize))
56 return false;
57
58 DWORD errorCode = 0;
59 if (!attr.Parse(buf, returnedSize, errorCode))
60 return false;
61
62 CByteBuffer data2;
63 if (!FillLinkData(data2, attr.GetPath(), attr.IsSymLink()))
64 return false;
65
66 if (data2.Size() != returnedSize ||
67 memcmp(data2, buf, returnedSize) != 0)
68 return false;
69
70 return true;
71 }
72
73 static const int k_LinkType_Buttons[] =
74 {
75 IDR_LINK_TYPE_HARD,
76 IDR_LINK_TYPE_SYM_FILE,
77 IDR_LINK_TYPE_SYM_DIR,
78 IDR_LINK_TYPE_JUNCTION
79 };
80
Set_LinkType_Radio(int idb)81 void CLinkDialog::Set_LinkType_Radio(int idb)
82 {
83 CheckRadioButton(k_LinkType_Buttons[0], k_LinkType_Buttons[ARRAY_SIZE(k_LinkType_Buttons) - 1], idb);
84 }
85
OnInit()86 bool CLinkDialog::OnInit()
87 {
88 #ifdef LANG
89 LangSetWindowText(*this, IDD_LINK);
90 LangSetDlgItems(*this, kLangIDs, ARRAY_SIZE(kLangIDs));
91 #endif
92
93 _pathFromCombo.Attach(GetItem(IDC_LINK_PATH_FROM));
94 _pathToCombo.Attach(GetItem(IDC_LINK_PATH_TO));
95
96 if (!FilePath.IsEmpty())
97 {
98 NFind::CFileInfo fi;
99 int linkType = 0;
100 if (!fi.Find(us2fs(FilePath)))
101 linkType = IDR_LINK_TYPE_SYM_FILE;
102 else
103 {
104 if (fi.HasReparsePoint())
105 {
106 CReparseAttr attr;
107 bool res = GetSymLink(us2fs(FilePath), attr);
108
109 UString s = attr.PrintName;
110 if (!attr.IsOkNamePair())
111 {
112 s += " : ";
113 s += attr.SubsName;
114 }
115 if (!res)
116 s.Insert(0, L"ERROR: ");
117
118 SetItemText(IDT_LINK_PATH_TO_CUR, s);
119
120 UString destPath = attr.GetPath();
121 _pathFromCombo.SetText(FilePath);
122 _pathToCombo.SetText(destPath);
123
124 if (res)
125 {
126 if (attr.IsMountPoint())
127 linkType = IDR_LINK_TYPE_JUNCTION;
128 if (attr.IsSymLink())
129 {
130 linkType =
131 fi.IsDir() ?
132 IDR_LINK_TYPE_SYM_DIR :
133 IDR_LINK_TYPE_SYM_FILE;
134 // if (attr.IsRelative()) linkType = IDR_LINK_TYPE_SYM_RELATIVE;
135 }
136
137 if (linkType != 0)
138 Set_LinkType_Radio(linkType);
139 }
140 }
141 else
142 {
143 _pathFromCombo.SetText(AnotherPath);
144 _pathToCombo.SetText(FilePath);
145 if (fi.IsDir())
146 linkType = g_SymLink_Supported ?
147 IDR_LINK_TYPE_SYM_DIR :
148 IDR_LINK_TYPE_JUNCTION;
149 else
150 linkType = IDR_LINK_TYPE_HARD;
151 }
152 }
153 if (linkType != 0)
154 Set_LinkType_Radio(linkType);
155 }
156
157 NormalizeSize();
158 return CModalDialog::OnInit();
159 }
160
OnSize(WPARAM,int xSize,int ySize)161 bool CLinkDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize)
162 {
163 int mx, my;
164 GetMargins(8, mx, my);
165 int bx1, bx2, by;
166 GetItemSizes(IDCANCEL, bx1, by);
167 GetItemSizes(IDB_LINK_LINK, bx2, by);
168 int yPos = ySize - my - by;
169 int xPos = xSize - mx - bx1;
170
171 InvalidateRect(NULL);
172
173 {
174 RECT r, r2;
175 GetClientRectOfItem(IDB_LINK_PATH_FROM, r);
176 GetClientRectOfItem(IDB_LINK_PATH_TO, r2);
177 int bx = RECT_SIZE_X(r);
178 int newButtonXpos = xSize - mx - bx;
179
180 MoveItem(IDB_LINK_PATH_FROM, newButtonXpos, r.top, bx, RECT_SIZE_Y(r));
181 MoveItem(IDB_LINK_PATH_TO, newButtonXpos, r2.top, bx, RECT_SIZE_Y(r2));
182
183 int newComboXsize = newButtonXpos - mx - mx;
184 ChangeSubWindowSizeX(_pathFromCombo, newComboXsize);
185 ChangeSubWindowSizeX(_pathToCombo, newComboXsize);
186 }
187
188 MoveItem(IDCANCEL, xPos, yPos, bx1, by);
189 MoveItem(IDB_LINK_LINK, xPos - mx - bx2, yPos, bx2, by);
190
191 return false;
192 }
193
OnButtonClicked(int buttonID,HWND buttonHWND)194 bool CLinkDialog::OnButtonClicked(int buttonID, HWND buttonHWND)
195 {
196 switch (buttonID)
197 {
198 case IDB_LINK_PATH_FROM:
199 OnButton_SetPath(false);
200 return true;
201 case IDB_LINK_PATH_TO:
202 OnButton_SetPath(true);
203 return true;
204 case IDB_LINK_LINK:
205 OnButton_Link();
206 return true;
207 }
208 return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
209 }
210
OnButton_SetPath(bool to)211 void CLinkDialog::OnButton_SetPath(bool to)
212 {
213 UString currentPath;
214 NWindows::NControl::CComboBox &combo = to ?
215 _pathToCombo :
216 _pathFromCombo;
217 combo.GetText(currentPath);
218 // UString title = "Specify a location for output folder";
219 UString title = LangString(IDS_SET_FOLDER);
220
221 UString resultPath;
222 if (!MyBrowseForFolder(*this, title, currentPath, resultPath))
223 return;
224 NName::NormalizeDirPathPrefix(resultPath);
225 combo.SetCurSel(-1);
226 combo.SetText(resultPath);
227 }
228
ShowError(const wchar_t * s)229 void CLinkDialog::ShowError(const wchar_t *s)
230 {
231 ::MessageBoxW(*this, s, L"7-Zip", MB_ICONERROR);
232 }
233
ShowLastErrorMessage()234 void CLinkDialog::ShowLastErrorMessage()
235 {
236 ShowError(NError::MyFormatMessage(GetLastError()));
237 }
238
OnButton_Link()239 void CLinkDialog::OnButton_Link()
240 {
241 UString from, to;
242 _pathFromCombo.GetText(from);
243 _pathToCombo.GetText(to);
244
245 if (from.IsEmpty())
246 return;
247 if (!NName::IsAbsolutePath(from))
248 from.Insert(0, CurDirPrefix);
249
250 int idb = -1;
251 for (unsigned i = 0;; i++)
252 {
253 if (i >= ARRAY_SIZE(k_LinkType_Buttons))
254 return;
255 idb = k_LinkType_Buttons[i];
256 if (IsButtonCheckedBool(idb))
257 break;
258 }
259
260 NFind::CFileInfo info1, info2;
261 bool finded1 = info1.Find(us2fs(from));
262 bool finded2 = info2.Find(us2fs(to));
263
264 bool isDirLink = (
265 idb == IDR_LINK_TYPE_SYM_DIR ||
266 idb == IDR_LINK_TYPE_JUNCTION);
267
268 if (finded1 && info1.IsDir() != isDirLink ||
269 finded2 && info2.IsDir() != isDirLink)
270 {
271 ShowError(L"Incorrect link type");
272 return;
273 }
274
275 if (idb == IDR_LINK_TYPE_HARD)
276 {
277 if (!NDir::MyCreateHardLink(us2fs(from), us2fs(to)))
278 {
279 ShowLastErrorMessage();
280 return;
281 }
282 }
283 else
284 {
285 bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION);
286
287 CByteBuffer data;
288 if (!FillLinkData(data, to, isSymLink))
289 {
290 ShowError(L"Incorrect link");
291 return;
292 }
293
294 CReparseAttr attr;
295 DWORD errorCode = 0;
296 if (!attr.Parse(data, data.Size(), errorCode))
297 {
298 ShowError(L"Internal conversion error");
299 return;
300 }
301
302
303 if (!NIO::SetReparseData(us2fs(from), isDirLink, data, (DWORD)data.Size()))
304 {
305 ShowLastErrorMessage();
306 return;
307 }
308 }
309
310 End(IDOK);
311 }
312
Link()313 void CApp::Link()
314 {
315 unsigned srcPanelIndex = GetFocusedPanelIndex();
316 CPanel &srcPanel = Panels[srcPanelIndex];
317 if (!srcPanel.IsFSFolder())
318 {
319 srcPanel.MessageBox_Error_UnsupportOperation();
320 return;
321 }
322 CRecordVector<UInt32> indices;
323 srcPanel.GetOperatedItemIndices(indices);
324 if (indices.IsEmpty())
325 return;
326 if (indices.Size() != 1)
327 {
328 srcPanel.MessageBox_Error_LangID(IDS_SELECT_ONE_FILE);
329 return;
330 }
331 int index = indices[0];
332 const UString itemName = srcPanel.GetItemName(index);
333
334 const UString fsPrefix = srcPanel.GetFsPath();
335 const UString srcPath = fsPrefix + srcPanel.GetItemPrefix(index);
336 UString path = srcPath;
337 {
338 unsigned destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex);
339 CPanel &destPanel = Panels[destPanelIndex];
340 if (NumPanels > 1)
341 if (destPanel.IsFSFolder())
342 path = destPanel.GetFsPath();
343 }
344
345 CLinkDialog dlg;
346 dlg.CurDirPrefix = fsPrefix;
347 dlg.FilePath = srcPath + itemName;
348 dlg.AnotherPath = path;
349
350 if (dlg.Create(srcPanel.GetParent()) != IDOK)
351 return;
352
353 RefreshTitleAlways();
354 }
355