summaryrefslogtreecommitdiffstats
path: root/src/libs/installer/link.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer/link.cpp')
-rw-r--r--src/libs/installer/link.cpp244
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
+}