Files
EonaCat.LogStack/EonaCat.LogStack/Helpers/DirectoryPermissionHelper.cs
T
2026-07-21 11:23:19 +02:00

222 lines
7.4 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
namespace EonaCat.LogStack.Helpers
{
/// <summary>
/// Utility class for managing directory permissions and ensuring write access.
/// Handles both Windows and Unix-like systems gracefully.
/// </summary>
public static class DirectoryPermissionHelper
{
/// <summary>
/// Checks if the current process has write access to the specified directory.
/// Returns true if writable, false otherwise. Never throws.
/// </summary>
public static bool CanWrite(string dirPath)
{
if (string.IsNullOrEmpty(dirPath))
{
return false;
}
try
{
if (!Directory.Exists(dirPath))
{
// Check if we can create it
var parentPath = Path.GetDirectoryName(dirPath);
if (string.IsNullOrEmpty(parentPath) || parentPath == dirPath)
{
return false;
}
return CanWrite(parentPath);
}
// Try to create a temporary file to verify write access
var testFile = Path.Combine(dirPath, ".permission_test_" + Guid.NewGuid().ToString("."));
try
{
using (var fs = File.Create(testFile, 1, FileOptions.DeleteOnClose))
{
fs.WriteByte(0);
}
return true;
}
catch
{
return false;
}
}
catch
{
return false;
}
}
/// <summary>
/// Attempts to create a directory and fix permissions if needed.
/// Never throws - logs errors and returns false if it cannot fix permissions.
/// </summary>
public static bool EnsureDirectory(string dirPath)
{
if (string.IsNullOrEmpty(dirPath))
{
return false;
}
try
{
// Create directory if it doesn't exist
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
// Verify we have write access
if (CanWrite(dirPath))
{
return true;
}
// Try to fix permissions (Windows-specific via attribute clearing)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
TryFixWindowsPermissions(dirPath);
return CanWrite(dirPath);
}
catch
{
// Permission fixing failed, but that's okay - we tried
}
}
// For Unix-like systems, we can't easily fix permissions without external tools
// Just return whether we can write
return CanWrite(dirPath);
}
catch
{
return false;
}
}
/// <summary>
/// Attempts to ensure all parent directories have write access.
/// Creates missing directories.
/// </summary>
public static bool EnsureDirectoryHierarchy(string dirPath)
{
if (string.IsNullOrEmpty(dirPath))
{
return false;
}
try
{
var current = Path.GetDirectoryName(dirPath);
var stack = new System.Collections.Generic.Stack<string>();
// Build directory hierarchy
while (!string.IsNullOrEmpty(current) && current != Path.GetPathRoot(current))
{
if (Directory.Exists(current))
{
break;
}
stack.Push(current);
current = Path.GetDirectoryName(current);
}
// Create missing directories from root to target
while (stack.Count > 0)
{
var dir = stack.Pop();
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
}
// Ensure final target directory
return EnsureDirectory(dirPath);
}
catch
{
return false;
}
}
/// <summary>
/// Attempts to grant write permissions on Windows by clearing the read-only attribute.
/// This is a best-effort operation and may fail on restricted systems.
/// </summary>
private static void TryFixWindowsPermissions(string dirPath)
{
try
{
var dirInfo = new DirectoryInfo(dirPath);
// Clear read-only attribute if set
if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
{
dirInfo.Attributes &= ~FileAttributes.ReadOnly;
}
// Note: Full ACL manipulation would require System.Security.AccessControl
// which is a separate NuGet package. We're using the basic attribute approach
// which covers most common permission issues.
}
catch
{
// Silently fail - we tried our best
throw;
}
}
/// <summary>
/// Gets a safe description of why directory access failed.
/// </summary>
public static string GetAccessIssueDiagnosis(string dirPath)
{
try
{
if (string.IsNullOrEmpty(dirPath))
{
return "Directory path is null or empty";
}
if (!Directory.Exists(dirPath))
{
var parent = Path.GetDirectoryName(dirPath);
if (string.IsNullOrEmpty(parent))
{
return $"Cannot determine parent directory: {dirPath}";
}
if (!Directory.Exists(parent))
{
return $"Parent directory does not exist: {parent}";
}
return $"Directory does not exist and parent is not writable: {dirPath}";
}
var dirInfo = new DirectoryInfo(dirPath);
if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
{
return $"Directory is read-only: {dirPath}";
}
return $"Directory exists but no write permission: {dirPath}. Check file system permissions.";
}
catch (Exception ex)
{
return $"Error diagnosing directory: {ex.Message}";
}
}
}
}