// Copyright (C) 2026, The Duplicati Team // https://duplicati.com, hello@duplicati.com // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Versioning; namespace Duplicati.Library.Common.IO; /// /// Helper class for working with file attributes not listed in /// public static class ExtendedFileAttributes { /// /// Comprehensive mapping of Windows FILE_ATTRIBUTE_* constants from winnt.h /// [SupportedOSPlatform("windows")] [Flags] public enum Win32FileAttributes : uint { /// No attributes set. None = 0x00000000, /// FILE_ATTRIBUTE_READONLY: File is read-only. ReadOnly = 0x00000001, /// FILE_ATTRIBUTE_HIDDEN: File or directory is hidden. Hidden = 0x00000002, /// FILE_ATTRIBUTE_SYSTEM: Part of or used exclusively by the OS. System = 0x00000004, /// FILE_ATTRIBUTE_DIRECTORY: Identifies a directory handle. Directory = 0x00000010, /// FILE_ATTRIBUTE_ARCHIVE: Marked for backup or removal. Archive = 0x00000020, /// FILE_ATTRIBUTE_DEVICE: Reserved for system use. Device = 0x00000040, /// FILE_ATTRIBUTE_NORMAL: No other attributes set (only valid when used alone). Normal = 0x00000080, /// FILE_ATTRIBUTE_TEMPORARY: Used for temporary storage; cached heavily. Temporary = 0x00000100, /// FILE_ATTRIBUTE_SPARSE_FILE: File is a sparse file. SparseFile = 0x00000200, /// FILE_ATTRIBUTE_REPARSE_POINT: Has an associated reparse point or symlink. ReparsePoint = 0x00000400, /// FILE_ATTRIBUTE_COMPRESSED: All data in the file/directory is compressed. Compressed = 0x00000800, /// FILE_ATTRIBUTE_OFFLINE: Data physically moved to offline storage. Offline = 0x00001000, /// FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Will not be indexed by content indexing services. NotContentIndexed = 0x00002000, /// FILE_ATTRIBUTE_ENCRYPTED: File or directory is encrypted. Encrypted = 0x00004000, /// FILE_ATTRIBUTE_INTEGRITY_STREAM: ReFS directory/stream configured with integrity. IntegrityStream = 0x00008000, /// FILE_ATTRIBUTE_VIRTUAL: Reserved for internal system use. Virtual = 0x00010000, /// FILE_ATTRIBUTE_NO_SCRUB_DATA: Stream skipped by background data integrity scanner. NoScrubData = 0x00020000, /// FILE_ATTRIBUTE_EA: File/directory has extended attributes (Internal use only). ExtendedAttributes = 0x00040000, /// FILE_ATTRIBUTE_RECALL_ON_OPEN: Completely virtual, has no local physical representation. RecallOnOpen = 0x00040000, /// FILE_ATTRIBUTE_PINNED: Cloud file system user intent to keep fully present locally. Pinned = 0x00080000, /// FILE_ATTRIBUTE_UNPINNED: Cloud file system intent to NOT keep present unless active. Unpinned = 0x00100000, /// FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS: Not fully present locally; reading will fetch from cloud. RecallOnDataAccess = 0x00400000 } /// /// Merge the default C# FileAttributes with the Win32FileAttributes and return the union. /// private static Lazy> _nameToValueMap = new Lazy>(() => { var map = new Dictionary(StringComparer.OrdinalIgnoreCase); var included = new HashSet(); // Add the built-in values foreach (var name in Enum.GetNames()) { map[name] = (uint)Enum.Parse(name); included.Add(map[name]); } if (OperatingSystem.IsWindows()) { // Merge in the Win32FileAttributes, but prefer the built-in values foreach (var name in Enum.GetNames()) { if (map.ContainsKey(name)) continue; var value = (uint)Enum.Parse(name); if (included.Contains(value)) continue; map[name] = value; } } return map; }); /// /// Get the names of all known file attributes, including both the built-in and Win32 values. /// /// The list of known file attribute names. public static IEnumerable GetNames(bool includeNone) => _nameToValueMap.Value.Keys.Where(x => includeNone || x != nameof(FileAttributes.None)); /// /// Get the file attributes that mark files as not local on the system. /// public static FileAttributes NonLocalAttributes => OperatingSystem.IsWindows() ? (FileAttributes)(Win32FileAttributes.Offline | Win32FileAttributes.RecallOnOpen | Win32FileAttributes.RecallOnDataAccess) : FileAttributes.Offline; /// /// Parse a comma-separated list of file attribute names into a FileAttributes value. /// /// The comma-separated list of file attribute names. /// The parsed FileAttributes value. public static FileAttributes Parse(string names) { if (string.IsNullOrWhiteSpace(names)) return FileAttributes.None; var result = 0u; foreach (var name in names.Split([','], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) { if (_nameToValueMap.Value.TryGetValue(name, out var value)) result |= value; } return (FileAttributes)result; } }