using System.Collections.Concurrent; using System.Runtime.InteropServices; namespace LightlessSync.Utils { public static class FileSystemHelper { public enum FilesystemType { Unknown = 0, NTFS, // Compressable on file level Btrfs, // Compressable on file level Ext4, // Uncompressable Xfs, // Uncompressable Apfs, // Compressable on OS HfsPlus, // Compressable on OS Fat, // Uncompressable Exfat, // Uncompressable Zfs // Compressable, not on file level } private const string _mountPath = "/proc/mounts"; private static readonly ConcurrentDictionary _filesystemTypeCache = new(StringComparer.OrdinalIgnoreCase); public static FilesystemType GetFilesystemType(string filePath, bool isWine = false) { try { string rootPath; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (!IsProbablyWine() || !isWine)) { var info = new FileInfo(filePath); var dir = info.Directory ?? new DirectoryInfo(filePath); rootPath = dir.Root.FullName; } else { rootPath = GetMountPoint(filePath); if (string.IsNullOrEmpty(rootPath)) rootPath = "/"; } if (_filesystemTypeCache.TryGetValue(rootPath, out var cachedType)) return cachedType; FilesystemType detected; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (!IsProbablyWine() || !isWine)) { var root = new DriveInfo(rootPath); var format = root.DriveFormat?.ToUpperInvariant() ?? string.Empty; detected = format switch { "NTFS" => FilesystemType.NTFS, "FAT32" => FilesystemType.Fat, "EXFAT" => FilesystemType.Exfat, _ => FilesystemType.Unknown }; } else { detected = GetLinuxFilesystemType(filePath); } _filesystemTypeCache[rootPath] = detected; return detected; } catch (Exception ex) { return FilesystemType.Unknown; } } private static string GetMountPoint(string filePath) { try { var path = Path.GetFullPath(filePath); if (!File.Exists(_mountPath)) return "/"; var mounts = File.ReadAllLines(_mountPath); string bestMount = "/"; foreach (var line in mounts) { var parts = line.Split(' '); if (parts.Length < 3) continue; var mountPoint = parts[1].Replace("\\040", " ", StringComparison.Ordinal); string normalizedMount; try { normalizedMount = Path.GetFullPath(mountPoint); } catch { normalizedMount = mountPoint; } if (path.StartsWith(normalizedMount, StringComparison.Ordinal) && normalizedMount.Length > bestMount.Length) { bestMount = normalizedMount; } } return bestMount; } catch { return "/"; } } public static string GetMountOptionsForPath(string path) { try { var fullPath = Path.GetFullPath(path); var mounts = File.ReadAllLines("/proc/mounts"); string bestMount = string.Empty; string mountOptions = string.Empty; foreach (var line in mounts) { var parts = line.Split(' '); if (parts.Length < 4) continue; var mountPoint = parts[1].Replace("\\040", " ", StringComparison.Ordinal); string normalized; try { normalized = Path.GetFullPath(mountPoint); } catch { normalized = mountPoint; } if (fullPath.StartsWith(normalized, StringComparison.Ordinal) && normalized.Length > bestMount.Length) { bestMount = normalized; mountOptions = parts[3]; } } return mountOptions; } catch (Exception ex) { return string.Empty; } } private static FilesystemType GetLinuxFilesystemType(string filePath) { try { var mountPoint = GetMountPoint(filePath); var mounts = File.ReadAllLines(_mountPath); foreach (var line in mounts) { var parts = line.Split(' '); if (parts.Length < 3) continue; var mount = parts[1].Replace("\\040", " ", StringComparison.Ordinal); if (string.Equals(mount, mountPoint, StringComparison.Ordinal)) { var fstype = parts[2].ToLowerInvariant(); return fstype switch { "btrfs" => FilesystemType.Btrfs, "ext4" => FilesystemType.Ext4, "xfs" => FilesystemType.Xfs, "zfs" => FilesystemType.Zfs, "apfs" => FilesystemType.Apfs, "hfsplus" => FilesystemType.HfsPlus, _ => FilesystemType.Unknown }; } } return FilesystemType.Unknown; } catch { return FilesystemType.Unknown; } } //Extra check on private static bool IsProbablyWine() => Environment.GetEnvironmentVariable("WINELOADERNOEXEC") != null || Environment.GetEnvironmentVariable("WINEDLLPATH") != null || Directory.Exists("/proc/self") && File.Exists("/proc/mounts"); } }