Skip to content

Commit 4b92d2d

Browse files
committed
Implement remaining
1 parent 1105784 commit 4b92d2d

File tree

4 files changed

+131
-12
lines changed

4 files changed

+131
-12
lines changed

core/src/main/csharp/NativeMethods.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ ExtractIconEx
1515
FILE_ID_INFO
1616
FOLDERID_Downloads
1717
GetCurrentPackageFullName
18+
GetFileInformationByHandleEx
1819
GetFinalPathNameByHandle
1920
GetTokenInformation
2021
GetWindowLong
@@ -33,6 +34,9 @@ LoadLibrary
3334
LoadString
3435
MESSAGEBOX_RESULT
3536
OpenFileById
37+
PATHCCH_ALLOW_LONG_PATHS
38+
PATHCCH_MAX_CCH
39+
PathCchCanonicalizeEx
3640
PathCchStripPrefix
3741
PathParseIconLocation
3842
PBST_ERROR

core/src/main/csharp/Windows/Win32/CorePInvoke.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Runtime.InteropServices;
3+
using Microsoft.Win32.SafeHandles;
34
using Windows.Win32.Foundation;
5+
using Windows.Win32.Security;
46
using Windows.Win32.Security.Credentials;
57
using Windows.Win32.Storage.FileSystem;
68
using Windows.Win32.UI.Shell;
@@ -22,6 +24,48 @@ public static unsafe int LoadString(SafeHandle hInstance, uint uID, out PCWSTR l
2224
}
2325
}
2426

27+
/// <inheritdoc cref="CreateFile(PCWSTR, uint, FILE_SHARE_MODE, SECURITY_ATTRIBUTES*, FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, HANDLE)"/>
28+
public static unsafe SafeFileHandle CreateFile(
29+
in ReadOnlySpan<char> lpFileName,
30+
uint dwDesiredAccess,
31+
FILE_SHARE_MODE dwShareMode,
32+
SECURITY_ATTRIBUTES? lpSecurityAttributes,
33+
FILE_CREATION_DISPOSITION dwCreationDisposition,
34+
FILE_FLAGS_AND_ATTRIBUTES dwFlagsAndAttributes,
35+
SafeHandle hTemplateFile)
36+
{
37+
bool hTemplateFileAddRef = false;
38+
try
39+
{
40+
fixed (char* lpFileNameLocal = lpFileName)
41+
{
42+
SECURITY_ATTRIBUTES lpSecurityAttributesLocal = lpSecurityAttributes ?? default(SECURITY_ATTRIBUTES);
43+
HANDLE hTemplateFileLocal;
44+
if (hTemplateFile is object)
45+
{
46+
hTemplateFile.DangerousAddRef(ref hTemplateFileAddRef);
47+
hTemplateFileLocal = (HANDLE)hTemplateFile.DangerousGetHandle();
48+
}
49+
else
50+
hTemplateFileLocal = (HANDLE)new IntPtr(0L);
51+
HANDLE __result = CorePInvoke.CreateFile(
52+
lpFileName: lpFileNameLocal,
53+
dwDesiredAccess: dwDesiredAccess,
54+
dwShareMode: dwShareMode,
55+
lpSecurityAttributes: lpSecurityAttributes.HasValue ? &lpSecurityAttributesLocal : null,
56+
dwCreationDisposition: dwCreationDisposition,
57+
dwFlagsAndAttributes: dwFlagsAndAttributes,
58+
hTemplateFile: hTemplateFileLocal);
59+
return new SafeFileHandle(__result, ownsHandle: true);
60+
}
61+
}
62+
finally
63+
{
64+
if (hTemplateFileAddRef)
65+
hTemplateFile.DangerousRelease();
66+
}
67+
}
68+
2569
/// <inheritdoc cref="CredDelete(PCWSTR, CRED_TYPE, uint)" />
2670
public static unsafe bool CredDelete(string TargetName, CRED_TYPE type, CRED_FLAGS flags)
2771
{
@@ -43,6 +87,15 @@ public static unsafe SafeCredentialHandle CredRead(string TargetName, CRED_TYPE
4387
return new((nint)credential, true);
4488
}
4589

90+
/// <inheritdoc cref="GetFileInformationByHandleEx(HANDLE, FILE_INFO_BY_HANDLE_CLASS, void*, uint)"/>
91+
public static unsafe BOOL GetFileInformationByHandleEx<T>(SafeHandle hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, out T value) where T : unmanaged
92+
{
93+
fixed (T* valueLocal = &value)
94+
{
95+
return GetFileInformationByHandleEx(hFile, FileInformationClass, valueLocal, (uint)Marshal.SizeOf<T>());
96+
}
97+
}
98+
4699
/// <inheritdoc cref="GetFinalPathNameByHandle(HANDLE, PWSTR, uint, GETFINALPATHNAMEBYHANDLE_FLAGS)"/>
47100
public static unsafe partial uint GetFinalPathNameByHandle(SafeHandle hFile, Span<char> lpszFilePath, GETFINALPATHNAMEBYHANDLE_FLAGS dwFlags)
48101
{
@@ -52,6 +105,18 @@ public static unsafe partial uint GetFinalPathNameByHandle(SafeHandle hFile, Spa
52105
}
53106
}
54107

108+
/// <inheritdoc cref="PathCchCanonicalizeEx(PWSTR, nuint, PCWSTR, PATHCCH_OPTIONS)"/>
109+
public static unsafe HRESULT PathCchCanonicalizeEx(ref Span<char> pszPathOut, string pszPathIn, PATHCCH_OPTIONS dwFlags)
110+
{
111+
fixed (char* ppszPathOut = pszPathOut)
112+
{
113+
PWSTR wstrpszPathOut = ppszPathOut;
114+
HRESULT __result = CorePInvoke.PathCchCanonicalizeEx(wstrpszPathOut, (nuint)pszPathOut.Length, pszPathIn, dwFlags);
115+
pszPathOut = pszPathOut.Slice(0, wstrpszPathOut.Length);
116+
return __result;
117+
}
118+
}
119+
55120
/// <inheritdoc cref="SHCreateAssociationRegistration(Guid*, object)"/>
56121
public static unsafe HRESULT SHCreateAssociationRegistration<T>(out T ppv) where T : class
57122
{

core/src/main/csharp/ch/cyberduck/core/local/NTFSFilesystemBookmarkResolver.cs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
// GNU General Public License for more details.
1313

1414
using System;
15-
using System.Diagnostics;
1615
using System.Globalization;
16+
using System.Runtime.CompilerServices;
1717
using System.Runtime.InteropServices;
1818
using ch.cyberduck.core;
1919
using ch.cyberduck.core.exception;
@@ -22,36 +22,62 @@
2222
using org.apache.logging.log4j;
2323
using Windows.Win32;
2424
using Windows.Win32.Storage.FileSystem;
25+
using Windows.Win32.UI.Shell;
2526
using CoreLocal = ch.cyberduck.core.Local;
27+
using NetPath = System.IO.Path;
2628

2729
namespace Ch.Cyberduck.Core.Local
2830
{
2931
public class NTFSFilesystemBookmarkResolver(CoreLocal local) : FilesystemBookmarkResolver
3032
{
3133
private static readonly Logger Log = LogManager.getLogger(typeof(NTFSFilesystemBookmarkResolver).FullName);
3234

33-
public NTFSFilesystemBookmarkResolver() : this(null)
34-
{
35-
}
36-
3735
public string create(CoreLocal file) => FilesystemBookmarkResolver.__DefaultMethods.create(this, file);
3836

3937
public string create(CoreLocal file, bool prompt)
4038
{
41-
Debug.Assert(local is null, "Unnecessary usage of Local-constructor.");
39+
Span<char> finalNameBuffer = new char[CorePInvoke.PATHCCH_MAX_CCH];
40+
if (CorePInvoke.PathCchCanonicalizeEx(
41+
ref finalNameBuffer,
42+
NetPath.GetFullPath(file.getAbsolute()),
43+
PATHCCH_OPTIONS.PATHCCH_ALLOW_LONG_PATHS | PATHCCH_OPTIONS.PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS) is
44+
{
45+
Failed: true,
46+
Value: { } error
47+
})
48+
{
49+
goto error;
50+
}
4251

43-
// ToDo: Backport
52+
FILE_ID_INFO info;
53+
using (var handle = CorePInvoke.CreateFile(
54+
lpFileName: finalNameBuffer,
55+
dwDesiredAccess: 0,
56+
dwShareMode: (FILE_SHARE_MODE)7,
57+
lpSecurityAttributes: null,
58+
dwCreationDisposition: FILE_CREATION_DISPOSITION.OPEN_EXISTING,
59+
dwFlagsAndAttributes: FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS,
60+
hTemplateFile: null))
61+
{
62+
if (handle.IsInvalid)
63+
{
64+
goto error;
65+
}
4466

67+
if (!CorePInvoke.GetFileInformationByHandleEx(handle, FILE_INFO_BY_HANDLE_CLASS.FileIdInfo, out info))
68+
{
69+
goto error;
70+
}
71+
}
72+
73+
return Unsafe.As<FILE_ID_128, long>(ref info.FileId).ToString("X16");
74+
75+
error:
4576
return null;
4677
}
4778

4879
public object resolve(string bookmark)
4980
{
50-
if (local is null)
51-
{
52-
throw new LocalAccessDeniedException("Unsupported Interface usage");
53-
}
54-
5581
if (!ToFileId(bookmark, out var fileId))
5682
{
5783
throw new LocalAccessDeniedException(bookmark);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using ch.cyberduck.core.local;
2+
using NUnit.Framework;
3+
using NUnit.Framework.Constraints;
4+
using CoreLocal = ch.cyberduck.core.Local;
5+
using NetPath = System.IO.Path;
6+
7+
namespace Ch.Cyberduck.Core.Local;
8+
9+
[TestFixture]
10+
public class NTFSFilesystemBookmarkResolverTest
11+
{
12+
[Test]
13+
public void EnsureRoundtrip()
14+
{
15+
SystemLocal temp = new(NetPath.GetTempPath());
16+
SystemLocal file = new(temp, NetPath.GetRandomFileName());
17+
new DefaultLocalTouchFeature().touch(file);
18+
NTFSFilesystemBookmarkResolver resolver = new(file);
19+
var bookmark = resolver.create(file);
20+
Assert.That(bookmark, new NotConstraint(new NullConstraint()));
21+
CoreLocal resolved = (CoreLocal)resolver.resolve(bookmark);
22+
Assert.That(resolved, new EqualConstraint(file));
23+
}
24+
}

0 commit comments

Comments
 (0)