From fb396036ccf3a0fd75d57c68e1e301b291245371 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 24 May 2012 15:51:00 +0200 Subject: added new Link class and corresponding operation - replace read junction link target code with already existing Qt code Change-Id: I6642f19ceb9a907791987ced8e9eacc6ca4f2780 Reviewed-by: Karsten Heimrich --- src/libs/installer/createlinkoperation.cpp | 105 +++++++++++++ src/libs/installer/createlinkoperation.h | 54 +++++++ src/libs/installer/init.cpp | 2 + src/libs/installer/installer.pro | 8 +- src/libs/installer/link.cpp | 244 +++++++++++++++++++++++++++++ src/libs/installer/link.h | 52 ++++++ src/libs/kdtools/kdsysinfo_win.cpp | 91 +---------- 7 files changed, 466 insertions(+), 90 deletions(-) create mode 100644 src/libs/installer/createlinkoperation.cpp create mode 100644 src/libs/installer/createlinkoperation.h create mode 100644 src/libs/installer/link.cpp create mode 100644 src/libs/installer/link.h (limited to 'src/libs') diff --git a/src/libs/installer/createlinkoperation.cpp b/src/libs/installer/createlinkoperation.cpp new file mode 100644 index 000000000..b844e179b --- /dev/null +++ b/src/libs/installer/createlinkoperation.cpp @@ -0,0 +1,105 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#include "createlinkoperation.h" + +#include "link.h" + +#include + +using namespace QInstaller; + +/* +TRANSLATOR QInstaller::CreateLinkOperation +*/ + +CreateLinkOperation::CreateLinkOperation() +{ + setName(QLatin1String("CreateLink")); +} + +void CreateLinkOperation::backup() +{ +} + +bool CreateLinkOperation::performOperation() +{ + QStringList args = arguments(); + + if (args.count() != 2) { + setError(InvalidArguments); + setErrorString(QObject::tr("Invalid arguments: %1 arguments given, 2 expected").arg( + args.count())); + return false; + } + + const QString& linkPath = args.at(0); + const QString& targetPath = args.at(1); + Link link = Link::create(linkPath, targetPath); + + if (!link.isValid()) { + setError(UserDefinedError); + setErrorString(QObject::tr("Could not create link from %1 to %2.").arg(linkPath, targetPath)); + return false; + } + + return true; +} + +bool CreateLinkOperation::undoOperation() +{ + QStringList args = arguments(); + + const QString& linkPath = args.at(0); + const QString& targetPath = args.at(1); + + if (!QFileInfo(linkPath).exists()) { + return true; + } + Link link = Link(linkPath); + if (!link.remove()) { + setError(UserDefinedError); + setErrorString(QObject::tr("Could not remove link from %1 to %2.").arg(linkPath, targetPath)); + return false; + } + + return !QFileInfo(linkPath).exists(); +} + +bool CreateLinkOperation::testOperation() +{ + return true; +} + +Operation *CreateLinkOperation::clone() const +{ + return new CreateLinkOperation(); +} diff --git a/src/libs/installer/createlinkoperation.h b/src/libs/installer/createlinkoperation.h new file mode 100644 index 000000000..9e111926f --- /dev/null +++ b/src/libs/installer/createlinkoperation.h @@ -0,0 +1,54 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef CREATELINKOPERATION_H +#define CREATELINKOPERATION_H + +#include "qinstallerglobal.h" + +namespace QInstaller { + +class INSTALLER_EXPORT CreateLinkOperation : public Operation +{ +public: + CreateLinkOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + Operation *clone() const; +}; + +} + +#endif diff --git a/src/libs/installer/init.cpp b/src/libs/installer/init.cpp index 35aa15151..463628c03 100644 --- a/src/libs/installer/init.cpp +++ b/src/libs/installer/init.cpp @@ -65,6 +65,7 @@ #include "registertoolchainoperation.h" #include "registerdefaultdebuggeroperation.h" #include "updatecreatorsettingsfrom21to22operation.h" +#include "createlinkoperation.h" #include "minimumprogressoperation.h" @@ -228,6 +229,7 @@ void QInstaller::init() factory.registerUpdateOperation(QLatin1String("Replace")); factory.registerUpdateOperation(QLatin1String( "LineReplace" ) ); factory.registerUpdateOperation(QLatin1String("UpdateCreatorSettingsFrom21To22")); + factory.registerUpdateOperation(QLatin1String("CreateLink")); factory.registerUpdateOperation(QLatin1String("MinimumProgress")); factory.registerUpdateOperation(QLatin1String("License")); diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index 31ebbaa84..f21394ae2 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -88,7 +88,9 @@ HEADERS += packagemanagercore.h \ constants.h \ packagemanagerproxyfactory.h \ createlocalrepositoryoperation.h \ - lib7z_facade.h + lib7z_facade.h \ + link.h \ + createlinkoperation.h SOURCES += packagemanagercore.cpp \ packagemanagercore_p.cpp \ @@ -158,7 +160,9 @@ SOURCES += packagemanagercore.cpp \ settings.cpp \ packagemanagerproxyfactory.cpp \ createlocalrepositoryoperation.cpp \ - lib7z_facade.cpp + lib7z_facade.cpp \ + link.cpp \ + createlinkoperation.cpp RESOURCES += resources/patch_file_lists.qrc diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp new file mode 100644 index 000000000..b42e87b47 --- /dev/null +++ b/src/libs/installer/link.cpp @@ -0,0 +1,244 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "link.h" + +#include +#include +#include + + +#ifdef Q_OS_WIN +#include +#include + +// REPARSE_DATA_BUFFER structure from msdn help: http://msdn.microsoft.com/en-us/library/ff552012.aspx +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + +#define _REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(_REPARSE_DATA_BUFFER, GenericReparseBuffer) + + +class FileHandleWrapper +{ +public: + FileHandleWrapper(const QString &path) + : m_dirHandle(INVALID_HANDLE_VALUE) + { + m_dirHandle = CreateFile(path.utf16(), GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0); + if (m_dirHandle == INVALID_HANDLE_VALUE) { + qWarning() << QString::fromLatin1("Could not open: '%1'; error: %2\n").arg(path).arg(GetLastError()); + } + } + + ~FileHandleWrapper() { + if (m_dirHandle != INVALID_HANDLE_VALUE) + CloseHandle(m_dirHandle); + } + + HANDLE handle() { + return m_dirHandle; + } + +private: + HANDLE m_dirHandle; +}; + +Link createJunction(const QString &linkPath, const QString &targetPath) +{ + bool junctionDirectoryExists = QFileInfo(linkPath).exists(); + if (!junctionDirectoryExists) + junctionDirectoryExists = QDir().mkpath(linkPath); + if (!junctionDirectoryExists) { + qWarning() << QString::fromLatin1("Could not create the mount directory: %1").arg( + linkPath); + return Link(linkPath); + } + + FileHandleWrapper dirHandle(linkPath); + if (dirHandle.handle() == INVALID_HANDLE_VALUE) { + qWarning() << QString::fromLatin1("Could not open: '%1'; error: %2\n").arg(linkPath).arg( + GetLastError()); + return Link(linkPath); + } + + TCHAR szDestDir[1024] = L"\\??\\"; //you need this to create valid unicode junctions + //now we add the real absolute path + StringCchCat(szDestDir, 1024, targetPath.utf16()); + + // Allocates a block of memory for an array of num elements(1) and initializes all its bits to zero. + _REPARSE_DATA_BUFFER* reparseStructData = (_REPARSE_DATA_BUFFER*)calloc(1, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + const size_t destMountPointBytes = lstrlen(szDestDir) * sizeof(WCHAR); + + reparseStructData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + + // Reserved(USHORT) + SubstituteNameOffset(USHORT) + SubstituteNameLength(USHORT) + // + PrintNameOffset(USHORT) + PrintNameLength(USHORT) + PathBuffer[1](WCHAR) + uint spaceAfterGeneralData = sizeof(USHORT) * 5 + sizeof(WCHAR); //should be 12 + reparseStructData->ReparseDataLength = destMountPointBytes + spaceAfterGeneralData; + reparseStructData->Reserved = 0; + reparseStructData->MountPointReparseBuffer.SubstituteNameOffset = 0; + reparseStructData->MountPointReparseBuffer.SubstituteNameLength = destMountPointBytes; + // + sizeof(WCHAR) means \0 termination at the end of the string + reparseStructData->MountPointReparseBuffer.PrintNameOffset = destMountPointBytes + sizeof(WCHAR); + reparseStructData->MountPointReparseBuffer.PrintNameLength = 0; + StringCchCopy(reparseStructData->MountPointReparseBuffer.PathBuffer, 1024, szDestDir); + + + DWORD bytesReturned; + if (!::DeviceIoControl(dirHandle.handle(), FSCTL_SET_REPARSE_POINT, reparseStructData, + reparseStructData->ReparseDataLength + _REPARSE_DATA_BUFFER_HEADER_SIZE, 0, 0, + &bytesReturned, 0)) { + qWarning() << QString::fromLatin1("Could not set the reparse point: for '%1' to %2; error: %3" + ).arg(linkPath, targetPath).arg(GetLastError()); + } + return Link(linkPath); +} + +bool removeJunction(const QString &path) +{ + // Allocates a block of memory for an array of num elements(1) and initializes all its bits to zero. + _REPARSE_DATA_BUFFER* reparseStructData = (_REPARSE_DATA_BUFFER*)calloc(1, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + + reparseStructData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + + { // extra scope because we need to close the dirHandle before we can remove that directory + FileHandleWrapper dirHandle(path); + + DWORD bytesReturned; + if (!::DeviceIoControl(dirHandle.handle(), FSCTL_DELETE_REPARSE_POINT, reparseStructData, + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, 0, 0, + &bytesReturned, 0)) { + + qWarning() << QString::fromLatin1("Could not remove the reparse point: '%1'; error: %3" + ).arg(path).arg(GetLastError()); + return false; + } + } + + return QDir().rmdir(path); +} +#else +Link createLnSymlink(const QString &linkPath, const QString &targetPath) +{ + int linkedError = symlink(targetPath.toLocal8Bit(), linkPath.toLocal8Bit()); + if (linkedError > 0) { + qWarning() << QString::fromLatin1("Could not create a symlink: from '%1' to %2; error: %3" + ).arg(linkPath, targetPath).arg(linkedError); + } + + + return Link(linkPath); +} + +bool removeLnSymlink(const QString &path) +{ + return QFile::remove(path); +} + +#endif + +Link::Link(const QString &path) : m_path(path) +{ +} + +Link Link::create(const QString &linkPath, const QString &targetPath) +{ + QFileInfo targetFileInfo(targetPath); + if (!targetFileInfo.exists()) { + qWarning() << QString::fromLatin1("\"%1\" does not exist - so nothing was created."); + return Link(linkPath); + } + +#ifdef Q_OS_WIN + if (targetFileInfo.isDir()) + return createJunction(linkPath, targetPath); + + qWarning() << QString::fromLatin1("At the moment the %1 can not create anything else as "\ + "junctions for directories under windows").arg(QLatin1String(Q_FUNC_INFO)); + return Link(linkPath); +#else + return createLnSymlink(linkPath, targetPath); +#endif +} + +QString Link::targetPath() const +{ + return QFileInfo(m_path).readLink(); +} + +bool Link::targetExists() +{ + return QFileInfo(targetPath()).exists(); +} + +bool Link::isValid() +{ + return targetExists() && QFileInfo(m_path).exists(); +} + +bool Link::remove() +{ + if (!isValid()) + return false; +#ifdef Q_OS_WIN + return removeJunction(m_path); +#else + return removeLnSymlink(m_path); +#endif +} diff --git a/src/libs/installer/link.h b/src/libs/installer/link.h new file mode 100644 index 000000000..bb843b62f --- /dev/null +++ b/src/libs/installer/link.h @@ -0,0 +1,52 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LINK_H +#define LINK_H + +#include + +class Link +{ +public: + Link(const QString &path); + static Link create(const QString &linkPath, const QString &targetPath); + QString targetPath() const; + bool targetExists(); + bool isValid(); + bool remove(); + +private: + QString m_path; +}; + +#endif // LINK_H diff --git a/src/libs/kdtools/kdsysinfo_win.cpp b/src/libs/kdtools/kdsysinfo_win.cpp index 3e99e3925..8681c6825 100644 --- a/src/libs/kdtools/kdsysinfo_win.cpp +++ b/src/libs/kdtools/kdsysinfo_win.cpp @@ -22,6 +22,8 @@ #include "kdsysinfo.h" +#include "link.h" + #include #include #include @@ -249,93 +251,6 @@ bool killProcess(const ProcessInfo &process, int msecs) return returnValue; } -// REPARSE_DATA_BUFFER structure from msdn help: http://msdn.microsoft.com/en-us/library/ff552012.aspx -typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - }; -} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; - -QString junctionTargetPath(const QString &path) -{ - HANDLE fileHandle; - fileHandle = CreateFile(path.utf16(), FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | - FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | - FILE_FLAG_OPEN_REPARSE_POINT, NULL); - if (fileHandle == INVALID_HANDLE_VALUE) { - qDebug() << QString::fromLatin1("Could not open: '%1'; error: %2\n").arg(path).arg(GetLastError()); - return path; - } - - REPARSE_DATA_BUFFER* reparseStructData = (REPARSE_DATA_BUFFER*)calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); - - DWORD bytesReturned; - // fill the reparseStructData - BOOL isOk = DeviceIoControl(fileHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseStructData, - MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned, NULL); - if (isOk == FALSE) { - DWORD deviceIOControlError = GetLastError(); - if (deviceIOControlError == ERROR_NOT_A_REPARSE_POINT) - qDebug() << QString::fromLatin1("Could not reparse information (windows symlink) for %1").arg(path); - else { - qDebug() << QString::fromLatin1("Get DeviceIoControl for %1 failed with error: %2").arg( - path).arg(deviceIOControlError); - } - CloseHandle(fileHandle); - return path; - } - CloseHandle(fileHandle); - - QString realPath = path; - if (IsReparseTagMicrosoft(reparseStructData->ReparseTag)) { - size_t realPathLength = 0; - WCHAR *realPathWCHAR = 0; - if (reparseStructData->ReparseTag == IO_REPARSE_TAG_SYMLINK){ - realPathLength = reparseStructData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR); - realPathWCHAR = new WCHAR[realPathLength + 1]; - wcsncpy_s(realPathWCHAR, realPathLength + 1, &reparseStructData->SymbolicLinkReparseBuffer.PathBuffer[ - reparseStructData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], realPathLength); - } else if (reparseStructData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { - realPathLength = reparseStructData->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); - realPathWCHAR = new WCHAR[realPathLength + 1]; - wcsncpy_s(realPathWCHAR, realPathLength + 1, &reparseStructData->MountPointReparseBuffer.PathBuffer[ - reparseStructData->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR)], realPathLength); - } else { - qDebug() << QString::fromLatin1("Path %1 is not a symlink and not a mount point.").arg(path); - } - if (realPathLength != 0) { - realPathWCHAR[realPathLength] = 0; - realPath = QString::fromStdWString(realPathWCHAR); - delete [] realPathWCHAR; - } - - } else { - qDebug() << QString::fromLatin1("Path %1 is not reparse point.").arg(path); - } - free(reparseStructData); - return realPath; -} - bool pathIsOnLocalDevice(const QString &path) { if (!QFileInfo(path).exists()) @@ -348,7 +263,7 @@ bool pathIsOnLocalDevice(const QString &path) do { if (QFileInfo(dir, QString()).isSymLink()) { QString currentPath = QFileInfo(dir, QString()).absoluteFilePath(); - return pathIsOnLocalDevice(junctionTargetPath(currentPath)); + return pathIsOnLocalDevice(Link(currentPath).targetPath()); } } while (dir.cdUp()); -- cgit v1.2.3