1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-30 02:50:05 +00:00

Files: Added support for creating and reading relative or special path symbolic link files

This commit is contained in:
hogliux 2018-04-09 14:02:53 +01:00
parent 71f281257b
commit 06d20b20c0
6 changed files with 185 additions and 48 deletions

View file

@ -941,7 +941,9 @@ File File::createTempFile (StringRef fileNameEnding)
return tempFile;
}
bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const
bool File::createSymbolicLink (const File& linkFileToCreate,
const String& nativePathOfTarget,
bool overwriteExisting)
{
if (linkFileToCreate.exists())
{
@ -959,7 +961,7 @@ bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExist
#if JUCE_MAC || JUCE_LINUX
// one common reason for getting an error here is that the file already exists
if (symlink (fullPath.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1)
if (symlink (nativePathOfTarget.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1)
{
jassertfalse;
return false;
@ -967,15 +969,32 @@ bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExist
return true;
#elif JUCE_MSVC
File targetFile (linkFileToCreate.getSiblingFile (nativePathOfTarget));
return CreateSymbolicLink (linkFileToCreate.getFullPathName().toWideCharPointer(),
fullPath.toWideCharPointer(),
isDirectory() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != FALSE;
nativePathOfTarget.toWideCharPointer(),
targetFile.isDirectory() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != FALSE;
#else
jassertfalse; // symbolic links not supported on this platform!
return false;
#endif
}
bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const
{
return createSymbolicLink (linkFileToCreate, getFullPathName(), overwriteExisting);
}
#if ! JUCE_WINDOWS
File File::getLinkedTarget() const
{
if (isSymbolicLink())
return getSiblingFile (getNativeLinkedTarget());
return *this;
}
#endif
//==============================================================================
MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode, bool exclusive)
: range (0, file.getSize())

View file

@ -987,12 +987,27 @@ public:
*/
File getLinkedTarget() const;
/** Create a symbolic link to a native path and return a boolean to indicate success.
Use this method if you want to create a link to a relative path or a special native
file path (such as a device file on Windows).
*/
static bool createSymbolicLink (const File& linkFileToCreate,
const String& nativePathOfTarget,
bool overwriteExisting);
/** This returns the native path that the symbolic link points to. The returned path
is a native path of the current OS and can be a relative, absolute or special path. */
String getNativeLinkedTarget() const;
#if JUCE_WINDOWS || DOXYGEN
/** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */
bool createShortcut (const String& description, const File& linkFileToCreate) const;
/** Windows ONLY - Returns true if this is a win32 .LNK file. */
bool isShortcut() const;
#else
#endif
//==============================================================================

View file

@ -143,6 +143,7 @@
#include <shlobj.h>
#include <shlwapi.h>
#include <mmsystem.h>
#include <winioctl.h>
#if JUCE_MINGW
#include <basetyps.h>

View file

@ -55,26 +55,16 @@ bool File::isHidden() const
return getFileName().startsWithChar ('.');
}
static String getLinkedFile (const String& file)
{
HeapBlock<char> buffer (8194);
const int numBytes = (int) readlink (file.toRawUTF8(), buffer, 8192);
return String::fromUTF8 (buffer, jmax (0, numBytes));
}
bool File::isSymbolicLink() const
{
return getLinkedFile (getFullPathName()).isNotEmpty();
return getNativeLinkedTarget().isNotEmpty();
}
File File::getLinkedTarget() const
String File::getNativeLinkedTarget() const
{
String f (getLinkedFile (getFullPathName()));
if (f.isNotEmpty())
return getSiblingFile (f);
return *this;
HeapBlock<char> buffer (8194);
const int numBytes = (int) readlink (getFullPathName().toRawUTF8(), buffer, 8192);
return String::fromUTF8 (buffer, jmax (0, numBytes));
}
//==============================================================================

View file

@ -281,12 +281,12 @@ bool File::isSymbolicLink() const
return getFileLink (fullPath) != nil;
}
File File::getLinkedTarget() const
String File::getNativeLinkedTarget() const
{
if (NSString* dest = getFileLink (fullPath))
return getSiblingFile (nsStringToJuce (dest));
return nsStringToJuce (dest);
return *this;
return {};
}
//==============================================================================

View file

@ -30,6 +30,36 @@ namespace juce
//==============================================================================
namespace WindowsFileHelpers
{
//==============================================================================
#if JUCE_WINDOWS
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;
} DUMMYUNIONNAME;
} *PREPARSE_DATA_BUFFER, REPARSE_DATA_BUFFER;
#endif
//==============================================================================
DWORD getAtts (const String& path) noexcept
{
return GetFileAttributes (path.toWideCharPointer());
@ -666,9 +696,104 @@ bool File::isShortcut() const
return hasFileExtension (".lnk");
}
File File::getLinkedTarget() const
static String readWindowsLnkFile (File lnkFile, bool wantsAbsolutePath)
{
if (! lnkFile.exists())
lnkFile = File (lnkFile.getFullPathName() + ".lnk");
if (lnkFile.exists())
{
ComSmartPtr<IShellLink> shellLink;
ComSmartPtr<IPersistFile> persistFile;
if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink))
&& SUCCEEDED (shellLink.QueryInterface (persistFile))
&& SUCCEEDED (persistFile->Load (lnkFile.getFullPathName().toWideCharPointer(), STGM_READ))
&& (! wantsAbsolutePath || SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))))
{
WIN32_FIND_DATA winFindData;
WCHAR resolvedPath [MAX_PATH];
DWORD flags = SLGP_UNCPRIORITY;
if (! wantsAbsolutePath)
flags |= SLGP_RAWPATH;
if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, flags)))
return resolvedPath;
}
}
return {};
}
static String readWindowsShortcutOrLink (const File& shortcut, bool wantsAbsolutePath)
{
#if JUCE_WINDOWS
if (! wantsAbsolutePath)
{
HANDLE h = CreateFile (shortcut.getFullPathName().toWideCharPointer(),
GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
0);
if (h != INVALID_HANDLE_VALUE)
{
HeapBlock<WindowsFileHelpers::REPARSE_DATA_BUFFER> reparseData;
reparseData.calloc (1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
DWORD bytesReturned = 0;
bool success = DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, nullptr, 0,
reparseData.getData(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
&bytesReturned, nullptr) != 0;
CloseHandle (h);
if (success)
{
if (IsReparseTagMicrosoft (reparseData->ReparseTag))
{
String targetPath;
switch (reparseData->ReparseTag)
{
case IO_REPARSE_TAG_SYMLINK:
{
auto& symlinkData = reparseData->SymbolicLinkReparseBuffer;
targetPath = {symlinkData.PathBuffer + (symlinkData.SubstituteNameOffset / sizeof (WCHAR)),
symlinkData.SubstituteNameLength / sizeof (WCHAR)};
}
break;
case IO_REPARSE_TAG_MOUNT_POINT:
{
auto& mountData = reparseData->MountPointReparseBuffer;
targetPath = {mountData.PathBuffer + (mountData.SubstituteNameOffset / sizeof (WCHAR)),
mountData.SubstituteNameLength / sizeof (WCHAR)};
}
break;
default:
break;
}
if (targetPath.isNotEmpty())
{
const StringRef prefix ("\\??\\");
if (targetPath.startsWith (prefix))
targetPath = targetPath.substring (prefix.length());
return targetPath;
}
}
}
}
}
if (! wantsAbsolutePath)
return readWindowsLnkFile (shortcut, false);
typedef DWORD (WINAPI* GetFinalPathNameByHandleFunc) (HANDLE, LPTSTR, DWORD, DWORD);
static GetFinalPathNameByHandleFunc getFinalPathNameByHandle
@ -676,7 +801,7 @@ File File::getLinkedTarget() const
if (getFinalPathNameByHandle != nullptr)
{
HANDLE h = CreateFile (getFullPathName().toWideCharPointer(),
HANDLE h = CreateFile (shortcut.getFullPathName().toWideCharPointer(),
GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
@ -695,8 +820,7 @@ File File::getLinkedTarget() const
// It turns out that GetFinalPathNameByHandleW prepends \\?\ to the path.
// This is not a bug, it's feature. See MSDN for more information.
return File (path.startsWith (prefix) ? path.substring (prefix.length())
: path);
return path.startsWith (prefix) ? path.substring (prefix.length()) : path;
}
}
@ -705,30 +829,18 @@ File File::getLinkedTarget() const
}
#endif
File result (*this);
String p (getFullPathName());
// as last resort try the resolve method of the ShellLink
return readWindowsLnkFile (shortcut, true);
}
if (! exists())
p += ".lnk";
else if (! hasFileExtension (".lnk"))
return result;
String File::getNativeLinkedTarget() const
{
return readWindowsShortcutOrLink (*this, false);
}
ComSmartPtr<IShellLink> shellLink;
ComSmartPtr<IPersistFile> persistFile;
if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink))
&& SUCCEEDED (shellLink.QueryInterface (persistFile))
&& SUCCEEDED (persistFile->Load (p.toWideCharPointer(), STGM_READ))
&& SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI)))
{
WIN32_FIND_DATA winFindData;
WCHAR resolvedPath [MAX_PATH];
if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, SLGP_UNCPRIORITY)))
result = File (resolvedPath);
}
return result;
File File::getLinkedTarget() const
{
return readWindowsShortcutOrLink (*this, true);
}
bool File::createShortcut (const String& description, const File& linkFileToCreate) const