diff options
Diffstat (limited to 'src/libs/installer/link.cpp')
-rw-r--r-- | src/libs/installer/link.cpp | 244 |
1 files changed, 244 insertions, 0 deletions
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 <QFileInfo> +#include <QDir> +#include <QDebug> + + +#ifdef Q_OS_WIN +#include <windows.h> +#include <Strsafe.h> + +// 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 +} |