How to manage folders permissions on Mac OS with ACL in .NET Core 3.1.0 - c#

I have the following code in C#:
if (!Directory.Exists(inputFolder))
{
return true;
}
else
{
var me = new DirectoryInfo(inputFolder);
AuthorizationRuleCollection rules = null;
WindowsIdentity identity = null;
try
{
rules = me.GetAccessControl().GetAccessRules(true, true, typeof(SecurityIdentifier));
identity = WindowsIdentity.GetCurrent();
}
catch (UnauthorizedAccessException uae)
{
return false;
}
string userSID = identity.User.Value;
foreach (FileSystemAccessRule rule in rules)
{
if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) ||
rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) ||
rule.FileSystemRights.HasFlag(FileSystemRights.ReadData) ||
rule.FileSystemRights.HasFlag(FileSystemRights.ReadAndExecute)) && rule.AccessControlType == AccessControlType.Deny)
{
return false;
}
}
return true;
}
And it works correctly in Windows, however, when I try to run this on mac OS X (Visual Studio for Mac) it throws an exception when trying to get the information from the method "GetAccessRules" or even the "GetAccessControl" saying:
"Access Control List (ACL) APIs are part of resource management on Windows and are not supported on this platform."
So here is my question:
Is there an alternative to do this in .net core? Or how should i manage these kind of permissions in .net core but in mac OS X?

I am assuming you are using System.IO.FileSystem.AccessControl to make GetAccessControl() work on .NET Core? Unfortunately these APIs are closely tied to Windows low level APIs and won't work on Unix/Linux systems.
However, there is an alternative. You could give Mono.Posix.NETStandard a try. Here is the Nuget link.
You can call UnixDirectoryInfo() and get or set FileAccessPermissions that way.

Updated
I found the solution and it was the same provided by Ruv in the answer above.
So, installing the Nuget Package Mono.Posix.NETStandard works. Just get the information of the directory/file:
var unixDirInfo = new Mono.Unix.UnixDirectoryInfo('path');
Then you can check the permissions like this:
unixDirInfo.canAccess(AccessModes.F_OK) // is a file or directory
unixDirInfo.canAccess(AccessModes.R_OK) // accessible for reading
unixDirInfo.canAccess(AccessModes.W_OK) // accessible for writing
unixDirInfo.canAccess(AccessModes.X_OK) // accessible for executing
or from the FileAccessPermissions:
FileAccessPermissions permissions = unixDirInfo.FileAccessPermissions;
permissions.HasFlag(FileAccessPermissions.UserRead);
permissions.HasFlag(FileAccessPermissions.UserWrite);
permissions.HasFlag(FileAccessPermissions.UserExecute);
permissions.HasFlag(FileAccessPermissions.UserExecute .UserReadWriteExecute);
You can check the FileAccessPermission enum for more information.

Related

ServerManager fails with 0x80040154 in c# Winforms app creating simple web application in IIS

I am writing a small app that installs IIS and configures a website before deploying the files to it.
On a freshly reset Windows 10, the first attempt always fails with the 0x80040154 COM+ component failure as documented in This question
I looked at the version I am using and it is the latest and correct one for .net standard (4.8) and not the one meant for .net core
When I press the button to rerun the function it always finishes correctly. I tried using a retry routine, and it fails on each retry, yet runs fine again when the button is pressed. The reason for this I assume is that the server manager object isn't disposed when it hits the catch block since its in a using statement.
I can work around that, but I really want to understand the issue and make a permanent fix.
My routine simply creates a website in IIS and creates an app pool to assign to it.
And it is running with elevated privileges
For reference:
Machine is Windows 10 latest from the downloadable media creator.
Microsoft.Web.Administrator version is 7.0.0.0
App is .net 4.8 standard windows forms
using (var serverManager = new ServerManager())
{
string iisrootdir = drive;
//Check for inetpub/wwwroot
if (!Directory.Exists(iisrootdir)) //Check for Drive D
{
iisrootdir = #"C:\";
}
string iiscmsdir = Path.Combine(iisrootdir, "webdir", "appdir");
if (!Directory.Exists(iiscmsdir))
Directory.CreateDirectory(iiscmsdir);
var settings = new ApplicationSettings();
settings.ReadFromFile();
settings.CMSPATH = iiscmsdir;
settings.SaveToFile();
try
{
string poolName = "DefaultAppPool";
if (serverManager.Sites.Count > 0)
{
Site myDefualtWebsite = serverManager.Sites[0];
if (myDefualtWebsite != null)
{
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Remove Default Website"));
serverManager.Sites.Remove(myDefualtWebsite);
serverManager.CommitChanges();
}
}
if (!WebsiteExists("sitename"))
{
mySite.ServerAutoStart = true;
}
Site site = serverManager.Sites["sitename"];
if (!AppPoolExists(poolName))
{
serverManager.ApplicationPools.Add(poolName);
}
ApplicationPool apppool = serverManager.ApplicationPools[poolName];
apppool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
apppool.ManagedRuntimeVersion = "";
serverManager.Sites["sitename"].ApplicationDefaults.ApplicationPoolName = poolName;
foreach (var item in serverManager.Sites["sitename"].Applications)
{
item.ApplicationPoolName = poolName;
}
serverManager.CommitChanges();
apppool.Recycle();
serverManager.CommitChanges();
}
catch (Exception ex)
{
if (ex.Message.Contains("80040154") && errorCount < 4)
{
if (serverManager != null)
serverManager.Dispose();
errorCount++;
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Error encountered with COM+ object, trying again: " + errorCount));
CreateWebsite(#"D:\");
}
else
{
if (serverManager != null)
serverManager.Dispose();
errorCount = 0;
OnRaiseErrorEvent(new InstallErrorEventArgs("CreateWebsite", ProcessState.Error, ex));
return false;
}
}
finally
{
serverManager?.Dispose();
}
Thanks for the help Guys. I found the problem.
DISM was running in its own thread. The Process object exited the moment it launched. My function was then attempting to configure IIS before it had finished installing.

In what versions of Windows are Storage Management API Classes such as 'MSFT_PhysicalDisk' implemented?

I am trying pull metrics such as 'MediaType' from MSFT_PhysicalDisk. I'm successful on a Windows 10 machine, but not on a Windows 7 machine.
On what type of machines is MSFT_PhysicalDisk available?
The reference for Storage Management API Classes:
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/storage-management-api-classes
See code below for an example of what I'm trying to do:
bool isSsd;
try
{
var physDiskQuery =
$"SELECT MediaType FROM MSFT_PhysicalDisk WHERE DeviceID='{driveNumber.Value}'";
var wmiScope = #"\\.\root\microsoft\windows\storage";
using (var physicalDiskSearcher = new ManagementObjectSearcher(wmiScope, physDiskQuery))
{
var objectCollection = physicalDiskSearcher.Get();
var physicalDisk = objectCollection.Cast<ManagementBaseObject>().SingleOrDefault();
if (physicalDisk == null)
return null;
isSsd = (ushort)physicalDisk["MediaType"] == 4;
}
}
catch (Exception exception)
{
Debug.WriteLine($"Error while checking for SSD drive. Details: {exception.GetBaseException()}");
return null;
}
return isSsd;
MSDN documentation lists requirements way at the bottom of the page. For the MSFT_PhysicalDisk class it says...
Minimum supported client: Windows 8 [desktop apps only]
Minimum supported server: Windows Server 2012 [desktop apps only]
In other words, you need at least Windows version 6.2.

FileNotFoundException when using one-click install

What Happens?
I have been having an issue on this now for the past week and I feel like i get closer and closer each day but can't put my finger on what it is.
I have purchased a license of a Third-Party program called 'Streamcoders' (lets you encoder a live stream). When installing this program it gives you (as far as i know) an x86 and x64 version of two .dll's 'MediaBase' and 'MediaSuite'. I have added the x86 version into my references as this is the one I need.
The properties of these dll's are :
Embed Interop Types = false.
Copy Local = true.
Aliases = global.
Specific Version = false.
So i go to publish my application. I specify the location, and then specify that i want the user to install the application from a Web Site. This all works and the application is installed.
I go to the clients machine and install the application and I get an error:
System.IO.FileNotFoundException: Could not load file or assembly 'MediaSuite.dll' or one of its dependencies. The specified module could not be found.
My Tests + Code
So when looking through my code I tried to add MessageBox.Show(""); to pin-point where the error occurs.
So on my Login page constructor, i call a method CheckUserRegistry():
try
{
if (Registry.GetValue("HKEY_CURRENT_USER", "URL", "").ToString() == string.Empty)
{
//User has not used the application before.
MessageBox.Show("NO URL FOUND");
return false;
}
else
{
MessageBox.Show("GENERATE");
//User has used the application before and is generating that user a session GUID.
Global.podiaClient = new podiaPublish.PublishClient();
Global.podiaClient.Endpoint.Address = new System.ServiceModel.EndpointAddress(string.Format(Registry.GetValue("HKEY_CURRENT_USER", "URL", "").ToString() + "/wcf/publish.svc"));
//Global.podiaSession = Global.podiaClient.Login(Registry.GetValue("HKEY_CURRENT_USER", "UID", "").ToString(), Global.Decrypt(Registry.GetValue("HKEY_CURRENT_USER", "PWD", "").ToString()));
Global.podiaSession = Global.podiaClient.Login(Registry.GetValue("HKEY_CURRENT_USER", "UID", "").ToString(), Registry.GetValue("HKEY_CURRENT_USER", "PWD", "").ToString());
return true;
}
}
catch (Exception ex)
{
//Global.HandleError(ex);
MessageBox.Show("here");
MessageBox.Show(ex.InnerException.ToString());
return false;
}
It successfully hits the MessageBox.Show("GENERATE"); and then after that seems to hit the catch and displays the error.
If i comment out the lines below the code does not error. podiaPublish is a Service Reference in my project.
Global.podiaClient = new podiaPublish.PublishClient();
Global.podiaClient.Endpoint.Address = new System.ServiceModel.EndpointAddress(string.Format(Registry.GetValue("HKEY_CURRENT_USER", "URL", "").ToString() + "/wcf/publish.svc"));
Global.podiaSession = Global.podiaClient.Login(Registry.GetValue("HKEY_CURRENT_USER", "UID", "").ToString(), Registry.GetValue("HKEY_CURRENT_USER", "PWD", "").ToString());
Does anyone have any clue as to what is going on?

SharpShell server .dll NOT signed

I need to develop a Shell Context Menu extension that references some other custom assemblies... I don't want to assign a Strong Name Key to those custom assemblies!
The guide I followed to do this uses the SharpShell project and illustrates how to sign (but does not expalins why) the assembly... and this is my problem: if I sign my final .dll then I have many errors during my project's building phase, because some assemblies my project references are not strongly named ("Referenced assembly does not have a strong name").
In general, googling about the C# Shell Extension implementation, all best tutorials I found sign the final assembly... is it mandatory?
Without signing the assembly ServerManager.exe returns this error: "The file 'XYZ.dll' is not a SharpShell Server".
Finally I've solved my troubles... the SharpShell.dll file obtained through NuGet was a different version of the ServerManager.exe ones.
Uninstalling the SharpShell NuGet package and directly referencing the SharpShell.dll you find inside the ServerManager folder was my solution!
Moreover, I was looking between the article comments... please read this question.
You don't need to use old DLL.
Please use this code directly, without using ServerManager.exe.
private static ServerEntry serverEntry = null;
public static ServerEntry SelectedServerEntry
{
get
{
if (serverEntry == null)
serverEntry = ServerManagerApi.LoadServer("xxx.dll");
return serverEntry;
}
}
public static ServerEntry LoadServer(string path)
{
try
{
// Create a server entry for the server.
var serverEntry = new ServerEntry();
// Set the data.
serverEntry.ServerName = Path.GetFileNameWithoutExtension(path);
serverEntry.ServerPath = path;
// Create an assembly catalog for the assembly and a container from it.
var catalog = new AssemblyCatalog(Path.GetFullPath(path));
var container = new CompositionContainer(catalog);
// Get the exported server.
var server = container.GetExport<ISharpShellServer>().Value;
serverEntry.ServerType = server.ServerType;
serverEntry.ClassId = server.GetType().GUID;
serverEntry.Server = server;
return serverEntry;
}
catch (Exception)
{
// It's almost certainly not a COM server.
MessageBox.Show("The file '" + Path.GetFileName(path) + "' is not a SharpShell Server.", "Warning");
return null;
}
}
Install code:
ServerRegistrationManager.InstallServer(SelectedServerEntry.Server, RegistrationType.OS64Bit, true);
Register code:
ServerRegistrationManager.RegisterServer(SelectedServerEntry.Server, RegistrationType.OS64Bit);

check whether microsoft components are present or not

I want to check whether certain microsoft components like wmencoder, directx or wmplayer
are installed or not. If it is installed, can I also get its version number?
How can I do that?
Thanks in advance.
I use the below to determine if other applications are installed, however you will need to know the "unique" product code (from the setup project in Visual Studio) that the application is installed with in the registry.
Include
using System.Diagnostics;
using Microsoft.Win32;
Usage:
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F03A-0000-0000-C000-000000000046} << This is outlook 2003
String retval = "";
// Look to see if Outlook 2003 is installed and if it is...
if ((checkComServerExists("{0006F03A-0000-0000-C000-000000000046}", out retval)))
{
// Update boolean flag if we get this far so we don't have to check again
Console.WriteLine("Office CSLID exists - Version: " + retval);
}
Function:
// Checks to see if the given CLSID is registerd and exists on the system
private static Boolean checkComServerExists(String CLSID, out String retval)
{
RegistryKey myRegKey = Registry.LocalMachine;
Object val;
try
{
// get the pathname to the COM server DLL/EXE if the key exists
myRegKey = myRegKey.OpenSubKey("SOFTWARE\\Classes\\CLSID\\" + CLSID + "\\LocalServer32");
val = myRegKey.GetValue(null); // the null gets default
}
catch
{
retval = "CLSID not registered";
return false;
}
FileVersionInfo myFileVersionInfo = null;
try
{
// parse out the version number embedded in the resource
// in the DLL
myFileVersionInfo = FileVersionInfo.GetVersionInfo(val.ToString());
}
catch
{
retval = String.Format("DLL {0} not found", val.ToString());
return false;
}
retval = myFileVersionInfo.FileVersion;
return true;
}
My first thought would be WMI. Class Win32_SoftwareElement (on MSDN)
But likely to take some work to get the right classes and queries. Start with the WMI tools for WMI CIM Studio.
Using PowerShell, something like:
gwmi win32_softwareelement -filter "name like '%play%'" | ft
will allow finding the right ids. (Warning: this is extremely slow.)
Possible that the MS Installer (MSI) API has something quicker.
I use RegShot to determine registry setting that can be used to check if a softwre is installed ..
Here is also a small code snippet that uses, among others, Type.GetTypeFromProgID and registry access.

Categories