| // 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" |
| |
| #include "LangUtils.h" |
| |
| #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 Z7_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, |
| IDR_LINK_TYPE_WSL |
| }; |
| #endif |
| |
| |
| static bool GetSymLink(CFSTR path, CReparseAttr &attr, UString &errorMessage) |
| { |
| CByteBuffer buf; |
| if (!NIO::GetReparseData(path, buf, NULL)) |
| return false; |
| if (!attr.Parse(buf, buf.Size())) |
| { |
| SetLastError(attr.ErrorCode); |
| return false; |
| } |
| CByteBuffer data2; |
| FillLinkData(data2, attr.GetPath(), |
| !attr.IsMountPoint(), attr.IsSymLink_WSL()); |
| if (data2.Size() == 0) |
| { |
| errorMessage = "Cannot reproduce reparse point"; |
| return false; |
| } |
| if (data2 != buf) |
| { |
| errorMessage = "mismatch for reproduced reparse point"; |
| return false; |
| } |
| return true; |
| } |
| |
| |
| static const unsigned k_LinkType_Buttons[] = |
| { |
| IDR_LINK_TYPE_HARD, |
| IDR_LINK_TYPE_SYM_FILE, |
| IDR_LINK_TYPE_SYM_DIR, |
| IDR_LINK_TYPE_JUNCTION, |
| IDR_LINK_TYPE_WSL |
| }; |
| |
| void CLinkDialog::Set_LinkType_Radio(unsigned idb) |
| { |
| CheckRadioButton( |
| k_LinkType_Buttons[0], |
| k_LinkType_Buttons[Z7_ARRAY_SIZE(k_LinkType_Buttons) - 1], |
| idb); |
| } |
| |
| bool CLinkDialog::OnInit() |
| { |
| #ifdef Z7_LANG |
| LangSetWindowText(*this, IDD_LINK); |
| LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs)); |
| #endif |
| |
| _pathFromCombo.Attach(GetItem(IDC_LINK_PATH_FROM)); |
| _pathToCombo.Attach(GetItem(IDC_LINK_PATH_TO)); |
| |
| if (!FilePath.IsEmpty()) |
| { |
| NFind::CFileInfo fi; |
| unsigned linkType = 0; |
| if (!fi.Find(us2fs(FilePath))) |
| linkType = IDR_LINK_TYPE_SYM_FILE; |
| else |
| { |
| if (fi.HasReparsePoint()) |
| { |
| CReparseAttr attr; |
| UString error; |
| const bool res = GetSymLink(us2fs(FilePath), attr, error); |
| if (!res && error.IsEmpty()) |
| { |
| const DWORD lastError = GetLastError(); |
| if (lastError) |
| error = NError::MyFormatMessage(lastError); |
| } |
| |
| UString s = attr.GetPath(); |
| if (!attr.IsSymLink_WSL()) |
| if (!attr.IsOkNamePair()) |
| { |
| s += " : "; |
| s += attr.PrintName; |
| } |
| |
| if (!res) |
| { |
| s.Insert(0, L"ERROR: "); |
| if (!error.IsEmpty()) |
| { |
| s += " : "; |
| s += error; |
| } |
| } |
| |
| |
| SetItemText(IDT_LINK_PATH_TO_CUR, s); |
| |
| const UString destPath = attr.GetPath(); |
| _pathFromCombo.SetText(FilePath); |
| _pathToCombo.SetText(destPath); |
| |
| // if (res) |
| { |
| if (attr.IsMountPoint()) |
| linkType = IDR_LINK_TYPE_JUNCTION; |
| else if (attr.IsSymLink_WSL()) |
| linkType = IDR_LINK_TYPE_WSL; |
| else if (attr.IsSymLink_Win()) |
| { |
| 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 |
| { |
| // no ReparsePoint |
| _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(unsigned 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 = "Specify a location for output folder"; |
| const 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); |
| |
| unsigned idb = 0; |
| for (unsigned i = 0;; i++) |
| { |
| if (i >= Z7_ARRAY_SIZE(k_LinkType_Buttons)) |
| return; |
| idb = k_LinkType_Buttons[i]; |
| if (IsButtonCheckedBool(idb)) |
| break; |
| } |
| |
| NFind::CFileInfo info1, info2; |
| const bool finded1 = info1.Find(us2fs(from)); |
| const bool finded2 = info2.Find(us2fs(to)); |
| |
| const bool isDirLink = ( |
| idb == IDR_LINK_TYPE_SYM_DIR || |
| idb == IDR_LINK_TYPE_JUNCTION); |
| |
| const bool isWSL = (idb == IDR_LINK_TYPE_WSL); |
| |
| if (!isWSL) |
| 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 |
| { |
| if (finded1 && !info1.IsDir() && !info1.HasReparsePoint() && info1.Size != 0) |
| { |
| UString s ("WARNING: reparse point will hide the data of existing file"); |
| s.Add_LF(); |
| s += from; |
| ShowError(s); |
| return; |
| } |
| |
| CByteBuffer data; |
| const bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION); |
| FillLinkData(data, to, isSymLink, isWSL); |
| if (data.Size() == 0) |
| { |
| ShowError(L"Incorrect link"); |
| return; |
| } |
| |
| CReparseAttr attr; |
| if (!attr.Parse(data, data.Size())) |
| { |
| ShowError(L"Internal conversion error"); |
| return; |
| } |
| |
| bool res; |
| if (to.IsEmpty()) |
| { |
| // res = NIO::SetReparseData(us2fs(from), isDirLink, NULL, 0); |
| res = NIO::DeleteReparseData(us2fs(from)); |
| } |
| else |
| res = NIO::SetReparseData(us2fs(from), isDirLink, data, (DWORD)data.Size()); |
| |
| if (!res) |
| { |
| ShowLastErrorMessage(); |
| return; |
| } |
| } |
| |
| End(IDOK); |
| } |
| |
| void CApp::Link() |
| { |
| const unsigned srcPanelIndex = GetFocusedPanelIndex(); |
| CPanel &srcPanel = Panels[srcPanelIndex]; |
| if (!srcPanel.IsFSFolder()) |
| { |
| srcPanel.MessageBox_Error_UnsupportOperation(); |
| return; |
| } |
| CRecordVector<UInt32> indices; |
| srcPanel.Get_ItemIndices_Operated(indices); |
| if (indices.IsEmpty()) |
| return; |
| if (indices.Size() != 1) |
| { |
| srcPanel.MessageBox_Error_LangID(IDS_SELECT_ONE_FILE); |
| return; |
| } |
| const UInt32 index = indices[0]; |
| const UString itemName = srcPanel.GetItemName(index); |
| |
| const UString fsPrefix = srcPanel.GetFsPath(); |
| const UString srcPath = fsPrefix + srcPanel.GetItemPrefix(index); |
| UString path = srcPath; |
| { |
| const unsigned destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex); |
| CPanel &destPanel = Panels[destPanelIndex]; |
| if (NumPanels > 1) |
| if (destPanel.IsFSFolder()) |
| path = destPanel.GetFsPath(); |
| } |
| |
| CSelectedState srcSelState; |
| srcPanel.SaveSelectedState(srcSelState); |
| |
| CLinkDialog dlg; |
| dlg.CurDirPrefix = fsPrefix; |
| dlg.FilePath = srcPath + itemName; |
| dlg.AnotherPath = path; |
| |
| if (dlg.Create(srcPanel.GetParent()) != IDOK) |
| return; |
| |
| // we refresh srcPanel to show changes in "Link" (kpidNtReparse) column. |
| // maybe we should refresh another panel also? |
| if (srcPanel._visibleColumns.FindItem_for_PropID(kpidNtReparse) >= 0) |
| srcPanel.RefreshListCtrl(srcSelState); |
| |
| RefreshTitleAlways(); |
| } |