Skip to content

Commit 29b1920

Browse files
committed
(#840) SDK.FileSystemUtil: simplify the executable check, call access()
This restores the macOS compatibility.
1 parent 0d5ea2e commit 29b1920

File tree

7 files changed

+34
-190
lines changed

7 files changed

+34
-190
lines changed

Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ SPDX-License-Identifier: MIT
1818
<ItemGroup>
1919
<PackageReference Include="MedallionShell" />
2020
<PackageReference Include="Microsoft.NET.Test.Sdk" />
21+
<PackageReference Include="TruePath.SystemIo" />
2122
<PackageReference Include="xunit" />
2223
<PackageReference Include="xunit.runner.visualstudio" >
2324
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

Cesium.Sdk.Tests/FileUtilTests.cs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,31 @@
22
//
33
// SPDX-License-Identifier: MIT
44

5-
using System.Diagnostics;
65
using System.Runtime.InteropServices;
6+
using TruePath;
7+
using TruePath.SystemIo;
78

89
namespace Cesium.Sdk.Tests;
910

1011
public class FileUtilTests
1112
{
12-
private static void CreateUnixFile(string filePath, string? link = null, UnixFileMode? mode = null)
13+
private static void CreateUnixFile(AbsolutePath file, AbsolutePath? link = null, UnixFileMode? mode = null)
1314
{
14-
File.WriteAllText(filePath, "empty");
15+
file.WriteAllText("empty");
1516

1617
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux) &&
1718
!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return;
1819
if (mode is { } m)
19-
File.SetUnixFileMode(filePath, m);
20-
if (link != null)
21-
File.CreateSymbolicLink(link, filePath);
20+
File.SetUnixFileMode(file.Value, m);
21+
if (link is { } linkFile)
22+
{
23+
linkFile.Delete();
24+
File.CreateSymbolicLink(linkFile.Value, file.Value);
25+
}
2226
}
2327

24-
private string TestFile => $"testfile-{Process.GetCurrentProcess().Id}-{Guid.NewGuid()}";
25-
private string TestLink => $"testlink-{Process.GetCurrentProcess().Id}-{Guid.NewGuid()}";
28+
private AbsolutePath TestFile = Temporary.CreateTempFile();
29+
private AbsolutePath TestLink = Temporary.CreateTempFile();
2630

2731
[Fact]
2832
public void ExecutablePermissionsCheckOnUnix()
@@ -31,10 +35,10 @@ public void ExecutablePermissionsCheckOnUnix()
3135
Assert.True(true);
3236
else
3337
{
34-
var fileName = TestFile;
35-
CreateUnixFile(fileName, mode: UnixFileMode.UserExecute);
38+
var file = TestFile;
39+
CreateUnixFile(file, mode: UnixFileMode.UserExecute);
3640

37-
Assert.True(FileSystemUtil.CheckUnixFilePermissions(fileName, FileSystemUtil.ExecutablePermissions));
41+
Assert.True(FileSystemUtil.IsUnixFileExecutable(file.Value));
3842
}
3943
}
4044

@@ -45,11 +49,11 @@ public void ExecutableLinkPermissionsCheckOnUnix()
4549
Assert.True(true);
4650
else
4751
{
48-
var fileName = TestFile;
49-
var linkName = TestLink;
50-
CreateUnixFile(fileName, link: linkName, mode: UnixFileMode.UserExecute);
52+
var file = TestFile;
53+
var link = TestLink;
54+
CreateUnixFile(file, link: link, mode: UnixFileMode.UserExecute);
5155

52-
Assert.True(FileSystemUtil.CheckUnixFilePermissions(linkName, FileSystemUtil.ExecutablePermissions));
56+
Assert.True(FileSystemUtil.IsUnixFileExecutable(link.Value));
5357
}
5458
}
5559

@@ -60,10 +64,10 @@ public void NoExecutablePermissionsCheckOnUnix()
6064
Assert.True(true);
6165
else
6266
{
63-
var fileName = TestFile;
64-
CreateUnixFile(fileName, mode: UnixFileMode.UserRead | UnixFileMode.UserWrite);
67+
var file = TestFile;
68+
CreateUnixFile(file, mode: UnixFileMode.UserRead | UnixFileMode.UserWrite);
6569

66-
Assert.False(FileSystemUtil.CheckUnixFilePermissions(fileName, FileSystemUtil.ExecutablePermissions));
70+
Assert.False(FileSystemUtil.IsUnixFileExecutable(file.Value));
6771
}
6872
}
6973

@@ -75,7 +79,7 @@ public void InvalidForDirOnUnix()
7579
else
7680
{
7781
var dir = "/etc";
78-
Assert.False(FileSystemUtil.CheckUnixFilePermissions(dir, FileSystemUtil.ExecutablePermissions));
82+
Assert.False(FileSystemUtil.IsUnixFileExecutable(dir));
7983
}
8084
}
8185
}

Cesium.Sdk/Cesium.Sdk.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ SPDX-License-Identifier: MIT
1818
<Content Include="Sdk\Sdk.props" PackagePath="Sdk\Sdk.props" />
1919
<Content Include="Sdk\Sdk.targets" PackagePath="Sdk\Sdk.targets" />
2020
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="tools" Visible="false"/>
21+
<InternalsVisibleTo Include="Cesium.Sdk.Tests" />
2122
</ItemGroup>
2223

2324
<ItemGroup>

Cesium.Sdk/CesiumCompile.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,7 @@ bool IsExecutable(string exePath)
307307
{
308308
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
309309
|| RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
310-
return FileSystemUtil.CheckUnixFilePermissions(exePath,
311-
FileSystemUtil.ExecutablePermissions);
310+
return FileSystemUtil.IsUnixFileExecutable(exePath);
312311

313312
var extension = Path.GetExtension(exePath);
314313
return pathExtWithDot.Value.Contains(extension);

Cesium.Sdk/FileInterop.cs

Lines changed: 0 additions & 65 deletions
This file was deleted.

Cesium.Sdk/FileSystemUtil.cs

Lines changed: 7 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2,118 +2,21 @@
22
//
33
// SPDX-License-Identifier: MIT
44

5-
using System;
65
using System.IO;
76
using System.Runtime.InteropServices;
87

98
namespace Cesium.Sdk;
109

11-
[Flags]
12-
public enum FilePermissions
10+
internal static class FileSystemUtil
1311
{
14-
None = 0,
15-
OtherExecute = 1,
16-
OtherWrite = 2,
17-
OtherRead = 4,
18-
GroupExecute = 8,
19-
GroupWrite = 16,
20-
GroupRead = 32,
21-
UserExecute = 64,
22-
UserWrite = 128,
23-
UserRead = 256,
24-
StickyBit = 512,
25-
SetGroup = 1024,
26-
SetUser = 2048,
27-
}
28-
29-
internal class UnixFileInfo
30-
{
31-
private FileStatus _status;
32-
33-
public UnixFileInfo(string path)
34-
{
35-
LoadFileStatus(path);
36-
}
37-
38-
private void LoadFileStatus(string path)
39-
{
40-
int rv = FileInterop.LStat(path, out _status);
41-
42-
Console.WriteLine($"LStat: {rv}");
43-
if (rv < 0)
44-
{
45-
var error = Marshal.GetLastWin32Error();
46-
47-
throw (Error)error switch
48-
{
49-
Error.ENOENT =>
50-
new ArgumentException("No such file or directory", nameof(path)),
51-
Error.ENOTDIR =>
52-
new ArgumentException("A component of the path is not a directory", nameof(path)),
53-
_ =>
54-
new InvalidOperationException($"lstat failed for {path} with error {error}")
55-
};
56-
}
57-
58-
uint fileType = _status.st_mode & FileTypes.S_IFMT;
59-
if (fileType != FileTypes.S_IFLNK)
60-
return;
12+
[DllImport("libc", EntryPoint = "access", SetLastError = true)]
13+
private static extern int Access(string path, int mode);
6114

62-
// It's a symlink, we need to get the target file's mode
15+
private const int X_OK = 1;
6316

64-
int ret;
65-
FileStatus target;
66-
while ((ret = FileInterop.Stat(path, out target)) < 0);
67-
68-
if (ret == 0)
69-
_status.st_mode = FileTypes.S_IFLNK | (target.st_mode & (int)ValidUnixFileModes);
70-
else
71-
throw new InvalidOperationException($"Stat failed for {path}");
72-
}
73-
74-
private uint FileTypeCode => _status.st_mode & FileTypes.S_IFMT;
75-
76-
public FilePermissions FilePermissions =>
77-
(FilePermissions)(_status.st_mode & (int)ValidUnixFileModes);
78-
79-
public bool IsDirectory => FileTypeCode == FileTypes.S_IFDIR;
80-
81-
internal const FilePermissions ValidUnixFileModes =
82-
FilePermissions.UserRead |
83-
FilePermissions.UserWrite |
84-
FilePermissions.UserExecute |
85-
FilePermissions.GroupRead |
86-
FilePermissions.GroupWrite |
87-
FilePermissions.GroupExecute |
88-
FilePermissions.OtherRead |
89-
FilePermissions.OtherWrite |
90-
FilePermissions.OtherExecute |
91-
FilePermissions.StickyBit |
92-
FilePermissions.SetGroup |
93-
FilePermissions.SetUser;
94-
}
95-
96-
public static class FileSystemUtil
97-
{
98-
public static FilePermissions ExecutablePermissions =>
99-
FilePermissions.UserExecute
100-
| FilePermissions.GroupExecute
101-
| FilePermissions.OtherExecute;
102-
103-
public static bool CheckUnixFilePermissions(string path, FilePermissions permissions)
17+
public static bool IsUnixFileExecutable(string path)
10418
{
105-
try
106-
{
107-
// if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
108-
// return true; // TODO[#840]: Proper executable check for MacOS
109-
110-
var info = new UnixFileInfo(Path.GetFullPath(path));
111-
return (info.FilePermissions & permissions) != 0 && !info.IsDirectory;
112-
}
113-
catch (Exception ex)
114-
{
115-
Console.WriteLine("Error checking file permissions: " + ex.Message);
116-
return false;
117-
}
19+
if (Directory.Exists(path)) return false;
20+
return Access(Path.GetFullPath(path), X_OK) == 0;
11821
}
11922
}

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SPDX-License-Identifier: MIT
1919
<PackageVersion Include="QuikGraph" Version="2.5.0" />
2020
<PackageVersion Include="QuikGraph.Graphviz" Version="2.5.0" />
2121
<PackageVersion Include="TruePath" Version="1.10.0" />
22+
<PackageVersion Include="TruePath.SystemIo" Version="1.10.0" />
2223
<PackageVersion Include="Verify.Xunit" Version="31.0.0" />
2324
<PackageVersion Include="xunit" Version="2.9.3" />
2425
<PackageVersion Include="xunit.assert" Version="2.9.3" />

0 commit comments

Comments
 (0)