Libation/Source/FileManager/FileSystemTest.cs
Michael Bucari-Tovo ae012548bd Smart handling of filename limitations cross platform
Automatically determine if filename lengths in the Books directory are limited to 255 UTF-16 characters (NTFS) or 255 UTF-8 bytes (pretty much every other file system) (#1260)

In non-Windows environments, determine if the Books directory supports filenames containing characters which are illegal in Windows environments (<>|:*?). If it doesn't, then ensure those characters are included in the user's ReplacementCharacters settings (#1258).
2025-07-30 16:04:48 -06:00

75 lines
2.4 KiB
C#

using System;
using System.IO;
namespace FileManager
{
public static class FileSystemTest
{
/// <summary>
/// Additional characters which are illegal for filenames in Windows environments.
/// Double quotes and slashes are already illegal filename characters on all platforms,
/// so they are not included here.
/// </summary>
public static string AdditionalInvalidWindowsFilenameCharacters { get; } = "<>|:*?";
/// <summary>
/// Test if the directory supports filenames with characters that are invalid on Windows (:, *, ?, &lt;, &gt;, |).
/// </summary>
public static bool CanWriteWindowsInvalidChars(LongPath directoryName)
{
var testFile = Path.Combine(directoryName, AdditionalInvalidWindowsFilenameCharacters + Guid.NewGuid().ToString());
return CanWriteFile(testFile);
}
/// <summary>
/// Test if the directory supports filenames with 255 unicode characters.
/// </summary>
public static bool CanWrite255UnicodeChars(LongPath directoryName)
{
const char unicodeChar = 'ü';
var testFileName = new string(unicodeChar, 255);
var testFile = Path.Combine(directoryName, testFileName);
return CanWriteFile(testFile);
}
/// <summary>
/// Test if a directory has write access by attempting to create an empty file in it.
/// <para/>Returns true even if the temporary file can not be deleted.
/// </summary>
public static bool CanWriteDirectory(LongPath directoryName)
{
if (!Directory.Exists(directoryName))
return false;
Serilog.Log.Logger.Debug("Testing write permissions for directory: {@DirectoryName}", directoryName);
var testFilePath = Path.Combine(directoryName, Guid.NewGuid().ToString());
return CanWriteFile(testFilePath);
}
private static bool CanWriteFile(LongPath filename)
{
try
{
Serilog.Log.Logger.Debug("Testing ability to write filename: {@filename}", filename);
File.WriteAllBytes(filename, []);
Serilog.Log.Logger.Debug("Deleting test file after successful write: {@filename}", filename);
try
{
FileUtility.SaferDelete(filename);
}
catch (Exception ex)
{
//An error deleting the file doesn't constitute a write failure.
Serilog.Log.Logger.Debug(ex, "Error deleting test file: {@filename}", filename);
}
return true;
}
catch (Exception ex)
{
Serilog.Log.Logger.Debug(ex, "Error writing test file: {@filename}", filename);
return false;
}
}
}
}