// LinkDialog.cpp #include "StdAfx.h" #include "../../../Windows/ErrorMsg.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/FileName.h" #ifdef LANG #include "LangUtils.h" #endif #include "BrowseDialog.h" #include "CopyDialogRes.h" #include "LinkDialog.h" #include "resourceGui.h" #include "App.h" #include "resource.h" extern bool g_SymLink_Supported; using namespace NWindows; using namespace NFile; #ifdef LANG static const UInt32 kLangIDs[] = { IDB_LINK_LINK, IDT_LINK_PATH_FROM, IDT_LINK_PATH_TO, IDG_LINK_TYPE, IDR_LINK_TYPE_HARD, IDR_LINK_TYPE_SYM_FILE, IDR_LINK_TYPE_SYM_DIR, IDR_LINK_TYPE_JUNCTION }; #endif static bool GetSymLink(CFSTR path, CReparseAttr &attr) { NIO::CInFile file; if (!file.Open(path, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)) return false; const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; CByteArr buf(kBufSize); DWORD returnedSize; if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize)) return false; if (!attr.Parse(buf, returnedSize)) return false; CByteBuffer data2; if (!FillLinkData(data2, attr.GetPath(), attr.IsSymLink())) return false; if (data2.Size() != returnedSize || memcmp(data2, buf, returnedSize) != 0) return false; return true; } static const int k_LinkType_Buttons[] = { IDR_LINK_TYPE_HARD, IDR_LINK_TYPE_SYM_FILE, IDR_LINK_TYPE_SYM_DIR, IDR_LINK_TYPE_JUNCTION }; void CLinkDialog::Set_LinkType_Radio(int idb) { CheckRadioButton(k_LinkType_Buttons[0], k_LinkType_Buttons[ARRAY_SIZE(k_LinkType_Buttons) - 1], idb); } bool CLinkDialog::OnInit() { #ifdef LANG LangSetWindowText(*this, IDD_LINK); LangSetDlgItems(*this, kLangIDs, ARRAY_SIZE(kLangIDs)); #endif _pathFromCombo.Attach(GetItem(IDC_LINK_PATH_FROM)); _pathToCombo.Attach(GetItem(IDC_LINK_PATH_TO)); if (!FilePath.IsEmpty()) { NFind::CFileInfo fi; int linkType = 0; if (!fi.Find(us2fs(FilePath))) linkType = IDR_LINK_TYPE_SYM_FILE; else { if (fi.HasReparsePoint()) { CReparseAttr attr; bool res = GetSymLink(us2fs(FilePath), attr); UString s = attr.PrintName; if (!attr.IsOkNamePair()) { s += L" : "; s += attr.SubsName; } if (!res) s = L"ERROR: " + s; SetItemText(IDT_LINK_PATH_TO_CUR, s); UString destPath = attr.GetPath(); _pathFromCombo.SetText(FilePath); _pathToCombo.SetText(destPath); if (res) { if (attr.IsMountPoint()) linkType = IDR_LINK_TYPE_JUNCTION; if (attr.IsSymLink()) { linkType = fi.IsDir() ? IDR_LINK_TYPE_SYM_DIR : IDR_LINK_TYPE_SYM_FILE; // if (attr.IsRelative()) linkType = IDR_LINK_TYPE_SYM_RELATIVE; } if (linkType != 0) Set_LinkType_Radio(linkType); } } else { _pathFromCombo.SetText(AnotherPath); _pathToCombo.SetText(FilePath); if (fi.IsDir()) linkType = g_SymLink_Supported ? IDR_LINK_TYPE_SYM_DIR : IDR_LINK_TYPE_JUNCTION; else linkType = IDR_LINK_TYPE_HARD; } } if (linkType != 0) Set_LinkType_Radio(linkType); } NormalizeSize(); return CModalDialog::OnInit(); } bool CLinkDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize) { int mx, my; GetMargins(8, mx, my); int bx1, bx2, by; GetItemSizes(IDCANCEL, bx1, by); GetItemSizes(IDB_LINK_LINK, bx2, by); int yPos = ySize - my - by; int xPos = xSize - mx - bx1; InvalidateRect(NULL); { RECT r, r2; GetClientRectOfItem(IDB_LINK_PATH_FROM, r); GetClientRectOfItem(IDB_LINK_PATH_TO, r2); int bx = RECT_SIZE_X(r); int newButtonXpos = xSize - mx - bx; MoveItem(IDB_LINK_PATH_FROM, newButtonXpos, r.top, bx, RECT_SIZE_Y(r)); MoveItem(IDB_LINK_PATH_TO, newButtonXpos, r2.top, bx, RECT_SIZE_Y(r2)); int newComboXsize = newButtonXpos - mx - mx; ChangeSubWindowSizeX(_pathFromCombo, newComboXsize); ChangeSubWindowSizeX(_pathToCombo, newComboXsize); } MoveItem(IDCANCEL, xPos, yPos, bx1, by); MoveItem(IDB_LINK_LINK, xPos - mx - bx2, yPos, bx2, by); return false; } bool CLinkDialog::OnButtonClicked(int buttonID, HWND buttonHWND) { switch (buttonID) { case IDB_LINK_PATH_FROM: OnButton_SetPath(false); return true; case IDB_LINK_PATH_TO: OnButton_SetPath(true); return true; case IDB_LINK_LINK: OnButton_Link(); return true; } return CModalDialog::OnButtonClicked(buttonID, buttonHWND); } void CLinkDialog::OnButton_SetPath(bool to) { UString currentPath; NWindows::NControl::CComboBox &combo = to ? _pathToCombo : _pathFromCombo; combo.GetText(currentPath); // UString title = L"Specify a location for output folder"; UString title = LangString(IDS_SET_FOLDER); UString resultPath; if (!MyBrowseForFolder(*this, title, currentPath, resultPath)) return; NName::NormalizeDirPathPrefix(resultPath); combo.SetCurSel(-1); combo.SetText(resultPath); } void CLinkDialog::ShowError(const wchar_t *s) { ::MessageBoxW(*this, s, L"7-Zip", MB_ICONERROR); } void CLinkDialog::ShowLastErrorMessage() { ShowError(NError::MyFormatMessage(GetLastError())); } void CLinkDialog::OnButton_Link() { UString from, to; _pathFromCombo.GetText(from); _pathToCombo.GetText(to); if (from.IsEmpty()) return; if (!NName::IsAbsolutePath(from)) from.Insert(0, CurDirPrefix); int idb = -1; for (unsigned i = 0;; i++) { if (i >= ARRAY_SIZE(k_LinkType_Buttons)) return; idb = k_LinkType_Buttons[i]; if (IsButtonCheckedBool(idb)) break; } NFind::CFileInfo info1, info2; bool finded1 = info1.Find(us2fs(from)); bool finded2 = info2.Find(us2fs(to)); bool isDirLink = ( idb == IDR_LINK_TYPE_SYM_DIR || idb == IDR_LINK_TYPE_JUNCTION); if (finded1 && info1.IsDir() != isDirLink || finded2 && info2.IsDir() != isDirLink) { ShowError(L"Incorrect link type"); return; } if (idb == IDR_LINK_TYPE_HARD) { if (!NDir::MyCreateHardLink(us2fs(from), us2fs(to))) { ShowLastErrorMessage(); return; } } else { bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION); CByteBuffer data; if (!FillLinkData(data, to, isSymLink)) { ShowError(L"Incorrect link"); return; } CReparseAttr attr; if (!attr.Parse(data, data.Size())) { ShowError(L"Internal conversion error"); return; } if (!NIO::SetReparseData(us2fs(from), isDirLink, data, (DWORD)data.Size())) { ShowLastErrorMessage(); return; } } End(IDOK); } void CApp::Link() { int srcPanelIndex = GetFocusedPanelIndex(); CPanel &srcPanel = Panels[srcPanelIndex]; if (!srcPanel.IsFSFolder()) { srcPanel.MessageBoxErrorLang(IDS_OPERATION_IS_NOT_SUPPORTED); return; } CRecordVector indices; srcPanel.GetOperatedItemIndices(indices); if (indices.IsEmpty()) return; if (indices.Size() != 1) { srcPanel.MessageBoxErrorLang(IDS_SELECT_ONE_FILE); return; } int index = indices[0]; const UString itemName = srcPanel.GetItemName(index); const UString fsPrefix = srcPanel.GetFsPath(); const UString srcPath = fsPrefix + srcPanel.GetItemPrefix(index); UString path = srcPath; { unsigned destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex); CPanel &destPanel = Panels[destPanelIndex]; if (NumPanels > 1) if (destPanel.IsFSFolder()) path = destPanel.GetFsPath(); } CLinkDialog dlg; dlg.CurDirPrefix = fsPrefix; dlg.FilePath = srcPath + itemName; dlg.AnotherPath = path; if (dlg.Create(srcPanel.GetParent()) != IDOK) return; RefreshTitleAlways(); }