Альтернативные streamи данных NTFS – .NET

Как мне создать / удалить / прочитать / записать / альтернативные streamи данных NTFS из .NET?

Если нет встроенной поддержки .NET, какой Win32 API я бы использовал? Кроме того, как я буду использовать их, поскольку я не думаю, что это документировано?

Не в .NET:

http://support.microsoft.com/kb/105763

#include  #include  void main( ) { HANDLE hFile, hStream; DWORD dwRet; hFile = CreateFile( "testfile", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL ); if( hFile == INVALID_HANDLE_VALUE ) printf( "Cannot open testfile\n" ); else WriteFile( hFile, "This is testfile", 16, &dwRet, NULL ); hStream = CreateFile( "testfile:stream", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL ); if( hStream == INVALID_HANDLE_VALUE ) printf( "Cannot open testfile:stream\n" ); else WriteFile(hStream, "This is testfile:stream", 23, &dwRet, NULL); } 

Вот версия для C #

 using System.Runtime.InteropServices; class Program { static void Main(string[] args) { var mainStream = NativeMethods.CreateFileW( "testfile", NativeConstants.GENERIC_WRITE, NativeConstants.FILE_SHARE_WRITE, IntPtr.Zero, NativeConstants.OPEN_ALWAYS, 0, IntPtr.Zero); var stream = NativeMethods.CreateFileW( "testfile:stream", NativeConstants.GENERIC_WRITE, NativeConstants.FILE_SHARE_WRITE, IntPtr.Zero, NativeConstants.OPEN_ALWAYS, 0, IntPtr.Zero); } } public partial class NativeMethods { /// Return Type: HANDLE->void* ///lpFileName: LPCWSTR->WCHAR* ///dwDesiredAccess: DWORD->unsigned int ///dwShareMode: DWORD->unsigned int ///lpSecurityAttributes: LPSECURITY_ATTRIBUTES->_SECURITY_ATTRIBUTES* ///dwCreationDisposition: DWORD->unsigned int ///dwFlagsAndAttributes: DWORD->unsigned int ///hTemplateFile: HANDLE->void* [DllImportAttribute("kernel32.dll", EntryPoint = "CreateFileW")] public static extern System.IntPtr CreateFileW( [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName, uint dwDesiredAccess, uint dwShareMode, [InAttribute()] System.IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, [InAttribute()] System.IntPtr hTemplateFile ); } public partial class NativeConstants { /// GENERIC_WRITE -> (0x40000000L) public const int GENERIC_WRITE = 1073741824; /// FILE_SHARE_DELETE -> 0x00000004 public const int FILE_SHARE_DELETE = 4; /// FILE_SHARE_WRITE -> 0x00000002 public const int FILE_SHARE_WRITE = 2; /// FILE_SHARE_READ -> 0x00000001 public const int FILE_SHARE_READ = 1; /// OPEN_ALWAYS -> 4 public const int OPEN_ALWAYS = 4; } 

Этот пакет nuget CodeFluent Runtime Client имеет (среди других утилит) class NtfsAlternateStream, который поддерживает операции create / read / update / delete / enumeration.

Для них нет встроенной поддержки .NET. Вы должны использовать P / Invoke для вызова собственных методов Win32.

Чтобы создать их, вызовите CreateFile с таким же filename.txt:streamname как filename.txt:streamname . Если вы используете вызов interop, который возвращает SafeFileHandle, вы можете использовать его для создания FileStream, который вы можете читать и писать.

Чтобы перечислить streamи, существующие в файле, используйте FindFirstStreamW и FindNextStreamW (которые существуют только на сервере 2003 и последующих, а не на XP).

Я не верю, что вы можете удалить stream, за исключением того, что копируете остальную часть файла и оставляете один из streamов. Установка длины на 0 также может работать, но я ее не пробовал.

Вы также можете использовать альтернативные streamи данных в каталоге. Вы C:\some\directory:streamname к ним так же, как и к файлам – C:\some\directory:streamname .

Потоки могут иметь сжатие, шифрование и разреженность, установленные на них независимо от streamа по умолчанию.

Во-первых, ничто в Microsoft® .NET Framework не предоставляет эту функциональность. Если вы хотите этого, просто и просто, вам нужно будет сделать какой-то interop, либо напрямую, либо используя стороннюю библиотеку.

Если вы используете Windows Server ™ 2003 или более поздней версии, Kernel32.dll предоставляет сопоставления FindFirstFile и FindNextFile, которые обеспечивают точную функциональность, которую вы ищете. FindFirstStreamW и FindNextStreamW позволяют вам находить и перечислять все альтернативные streamи данных в определенном файле, получая информацию о каждом, включая его имя и его длину. Код для использования этих функций из управляемого кода очень похож на тот, который я показал в своем декабрьском столбце, и показан на рисунке 1.

Рисунок 1 Использование FindFirstStreamW и FindNextStreamW

 [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] public sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeFindHandle() : base(true) { } protected override bool ReleaseHandle() { return FindClose(this.handle); } [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static extern bool FindClose(IntPtr handle); } public class FileStreamSearcher { private const int ERROR_HANDLE_EOF = 38; private enum StreamInfoLevels { FindStreamInfoStandard = 0 } [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] private static extern SafeFindHandle FindFirstStreamW(string lpFileName, StreamInfoLevels InfoLevel, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_STREAM_DATA lpFindStreamData, uint dwFlags); [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FindNextStreamW(SafeFindHandle hndFindFile, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_STREAM_DATA lpFindStreamData); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private class WIN32_FIND_STREAM_DATA { public long StreamSize; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 296)] public string cStreamName; } public static IEnumerable GetStreams(FileInfo file) { if (file == null) throw new ArgumentNullException("file"); WIN32_FIND_STREAM_DATA findStreamData = new WIN32_FIND_STREAM_DATA(); SafeFindHandle handle = FindFirstStreamW(file.FullName, StreamInfoLevels.FindStreamInfoStandard, findStreamData, 0); if (handle.IsInvalid) throw new Win32Exception(); try { do { yield return findStreamData.cStreamName; } while (FindNextStreamW(handle, findStreamData)); int lastError = Marshal.GetLastWin32Error(); if (lastError != ERROR_HANDLE_EOF) throw new Win32Exception(lastError); } finally { handle.Dispose(); } } } 

Вы просто вызываете FindFirstStreamW, передавая ему полный путь к целевому файлу. Второй параметр FindFirstStreamW определяет уровень детализации, который вы хотите получить в возвращаемых данных; в настоящее время существует только один уровень (FindStreamInfoStandard), который имеет числовое значение 0. Третий параметр функции является указателем на структуру WIN32_FIND_STREAM_DATA (технически, что указывает третий параметр, определяется значением второго параметра подробно описывая уровень информации, но поскольку в настоящее время существует только один уровень, для всех целей и задач это WIN32_FIND_STREAM_DATA). Я объявлял управляемый коллега структуры как class, а в сигнатуре interop я отметил, что она маршалируется как указатель на структуру. Последний параметр зарезервирован для будущего использования и должен быть равен 0. Если верный дескриптор возвращается из FindFirstStreamW, экземпляр WIN32_FIND_STREAM_DATA содержит информацию о найденном streamе, а его значение cStreamName может быть возвращено вызывающему в качестве первого имени streamа. FindNextStreamW принимает дескриптор, возвращаемый из FindFirstStreamW, и заполняет предоставленный WIN32_FIND_STREAM_DATA информацией о следующем доступном streamе, если он существует. FindNextStreamW возвращает true, если доступен другой stream, или false, если нет. В результате я постоянно вызываю FindNextStreamW и даю результирующее имя streamа до тех пор, пока FindNextStreamW не вернет false. Когда это произойдет, я дважды проверю последнее значение ошибки, чтобы убедиться, что итерация остановлена, потому что у FindNextStreamW закончились streamи, а не по какой-то неожиданной причине. К сожалению, если вы используете Windows® XP или Windows 2000 Server, эти функции вам недоступны, но есть несколько альтернатив. Первое решение включает недокументированную функцию, которая в настоящее время экспортируется из Kernel32.dll, NTQueryInformationFile. Однако недокументированные функции недокументированы по какой-либо причине, и их можно изменить или даже удалить в любое время в будущем. Лучше не использовать их. Если вы хотите использовать эту функцию, выполните поиск в Интернете, и вы найдете множество ссылок и образец исходного кода. Но делайте это на свой страх и риск. Другое решение, которое я продемонстрировал на рисунке 2 , опирается на две функции, экспортированные из Kernel32.dll, и они документированы. Поскольку их имена подразумевают, BackupRead и BackupSeek являются частью Win32 API для поддержки резервного копирования:

 BOOL BackupRead(HANDLE hFile, LPBYTE lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, BOOL bAbort, BOOL bProcessSecurity, LPVOID* lpContext); BOOL BackupSeek(HANDLE hFile, DWORD dwLowBytesToSeek, DWORD dwHighBytesToSeek, LPDWORD lpdwLowByteSeeked, LPDWORD lpdwHighByteSeeked, LPVOID* lpContext); 

Рисунок 2 Использование BackupRead и BackupSeek

 public enum StreamType { Data = 1, ExternalData = 2, SecurityData = 3, AlternateData = 4, Link = 5, PropertyData = 6, ObjectID = 7, ReparseData = 8, SparseDock = 9 } public struct StreamInfo { public StreamInfo(string name, StreamType type, long size) { Name = name; Type = type; Size = size; } readonly string Name; public readonly StreamType Type; public readonly long Size; } public class FileStreamSearcher { [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool BackupRead(SafeFileHandle hFile, IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [MarshalAs(UnmanagedType.Bool)] bool bAbort, [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext);[DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool BackupSeek(SafeFileHandle hFile, uint dwLowBytesToSeek, uint dwHighBytesToSeek, out uint lpdwLowByteSeeked, out uint lpdwHighByteSeeked, ref IntPtr lpContext); public static IEnumerable GetStreams(FileInfo file) { const int bufferSize = 4096; using (FileStream fs = file.OpenRead()) { IntPtr context = IntPtr.Zero; IntPtr buffer = Marshal.AllocHGlobal(bufferSize); try { while (true) { uint numRead; if (!BackupRead(fs.SafeFileHandle, buffer, (uint)Marshal.SizeOf(typeof(Win32StreamID)), out numRead, false, true, ref context)) throw new Win32Exception(); if (numRead > 0) { Win32StreamID streamID = (Win32StreamID)Marshal.PtrToStructure(buffer, typeof(Win32StreamID)); string name = null; if (streamID.dwStreamNameSize > 0) { if (!BackupRead(fs.SafeFileHandle, buffer, (uint)Math.Min(bufferSize, streamID.dwStreamNameSize), out numRead, false, true, ref context)) throw new Win32Exception(); name = Marshal.PtrToStringUni(buffer, (int)numRead / 2); } yield return new StreamInfo(name, streamID.dwStreamId, streamID.Size); if (streamID.Size > 0) { uint lo, hi; BackupSeek(fs.SafeFileHandle, uint.MaxValue, int.MaxValue, out lo, out hi, ref context); } } else break; } } finally { Marshal.FreeHGlobal(buffer); uint numRead; if (!BackupRead(fs.SafeFileHandle, IntPtr.Zero, 0, out numRead, true, false, ref context)) throw new Win32Exception(); } } } } 

Идея BackupRead заключается в том, что ее можно использовать для чтения данных из файла в буфер, которые затем могут быть записаны на резервный носитель. Однако BackupRead также очень удобен для получения информации о каждом из альтернативных streamов данных, которые составляют целевой файл. Он обрабатывает все данные в файле как последовательность дискретных streamов байтов (каждый альтернативный stream данных является одним из этих streamов байтов), и каждому из streamов предшествует структура WIN32_STREAM_ID. Таким образом, чтобы перечислить все streamи, вам просто нужно прочитать все эти структуры WIN32_STREAM_ID с начала каждого streamа (здесь BackupSeek становится очень удобным, так как его можно использовать для перехода от streamа к streamу без для чтения всех данных в файле). Для начала вам сначала нужно создать управляемый экземпляр для неуправляемой структуры WIN32_STREAM_ID:

 typedef struct _WIN32_STREAM_ID { DWORD dwStreamId; DWORD dwStreamAttributes; LARGE_INTEGER Size; DWORD dwStreamNameSize; WCHAR cStreamName[ANYSIZE_ARRAY]; } WIN32_STREAM_ID; 

По большей части это похоже на любую другую структуру, которую вы планируете через P / Invoke. Однако есть несколько осложнений. Прежде всего, WIN32_STREAM_ID представляет собой структуру с переменным размером. Его последний член, cStreamName, представляет собой массив с длиной ANYSIZE_ARRAY. В то время как ANYSIZE_ARRAY определяется как 1, cStreamName является просто адресом остальных данных в структуре после предыдущих четырех полей, что означает, что если структура распределена как размер, чем sizeof (WIN32_STREAM_ID), то это дополнительное пространство будет фактически является частью массива cStreamName. Предыдущее поле, dwStreamNameSize, точно определяет, как долго будет массив. Хотя это отлично подходит для разработки Win32, это наносит ущерб маршалу, которому необходимо скопировать эти данные из неуправляемой памяти в управляемую память как часть вызова interop в BackupRead. Как маршалер знает, насколько велика структура WIN32_STREAM_ID на самом деле, учитывая, что она имеет переменный размер? Это не так. Вторая проблема связана с упаковкой и выравниванием. Игнорируя cStreamName на мгновение, рассмотрите следующую возможность для управляемой копии WIN32_STREAM_ID:

 [StructLayout(LayoutKind.Sequential)] public struct Win32StreamID { public int dwStreamId; public int dwStreamAttributes; public long Size; public int dwStreamNameSize; } 

Int32 имеет размер 4 байта, а Int64 – 8 байтов. Таким образом, вы ожидаете, что эта структура будет 20 байтов. Однако, если вы запустите следующий код, вы увидите, что оба значения равны 24, а не 20:

 int size1 = Marshal.SizeOf(typeof(Win32StreamID)); int size2 = sizeof(Win32StreamID); // in an unsafe context 

Проблема в том, что компилятор хочет убедиться, что значения внутри этих структур всегда выровнены на соответствующей границе. Четыре байтовые значения должны быть в адресах, делящихся на 4, 8-байтовые значения, должны быть в границах, делящихся на 8 и т. Д. Теперь представьте, что произойдет, если вы создадите массив структур Win32StreamID. Все поля в первом экземпляре массива будут правильно выровнены. Например, поскольку поле «Размер» следует за двумя 32-битными целыми числами, это будет 8 байтов от начала массива, идеально подходящее для 8-байтового значения. Однако, если структура была размером 20 байтов, второй экземпляр в массиве не имел бы всех элементов, которые были бы правильно выровнены. Целочисленные значения будут все в порядке, но длинное значение будет 28 байт от начала массива, значение не равномерно делится на 8. Чтобы исправить это, компилятор накладывает структуру на размер 24, так что все поля всегда будут правильно выровнены (если предположить, что сам массив). Если компилятор делает правильные вещи, вам может быть интересно, почему я беспокоюсь об этом. Вы поймете, почему, если вы посмотрите на код на рисунке 2. Чтобы обойти первую проблему маршалинга, которую я описал, я фактически оставляю cStreamName из структуры Win32StreamID. Я использую BackupRead для чтения в достаточном количестве байтов, чтобы заполнить мою структуру Win32StreamID, а затем рассмотрю поле dwStreamNameSize структуры. Теперь, когда я знаю, как долго это имя, я могу снова использовать BackupRead, чтобы читать значение строки из файла. Это все хорошо и dandy, но если Marshal.SizeOf возвращает 24 для моей структуры Win32StreamID вместо 20, я буду пытаться читать слишком много данных. Чтобы этого избежать, мне нужно убедиться, что размер Win32StreamID на самом деле равен 20, а не 24. Это можно выполнить двумя разными способами, используя поля в StructLayoutAttribute, который украшает структуру. Во-первых, это использование поля «Размер», которое определяет время выполнения как можно больше:

 [StructLayout(LayoutKind.Sequential, Size = 20)] 

Второй вариант – использовать поле «Пакет». Pack указывает размер упаковки, который следует использовать, когда задано значение LayoutKind.Sequential и контролирует выравнивание полей внутри структуры. Размер упаковки по умолчанию для управляемой структуры – 8. Если я изменю это на 4, я получаю 20-байтовую структуру, которую я ищу (и поскольку я не использую это в массиве, я не теряю эффективность или стабильности, которые могут возникнуть в результате такого изменения упаковки):

 [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct Win32StreamID { public StreamType dwStreamId; public int dwStreamAttributes; public long Size; public int dwStreamNameSize; // WCHAR cStreamName[1]; } 

С помощью этого кода я могу теперь перечислять все streamи в файле, как показано ниже:

 static void Main(string[] args) { foreach (string path in args) { Console.WriteLine(path + ":"); foreach (StreamInfo stream in FileStreamSearcher.GetStreams(new FileInfo(path))) { Console.WriteLine("\t{0}\t{1}\t{2}", stream.Name != null ? stream.Name : "(unnamed)", stream.Type, stream.Size); } } } 

Вы заметите, что эта версия FileStreamSearcher возвращает больше информации, чем версия, использующая FindFirstStreamW и FindNextStreamW. BackupRead может предоставлять данные не только для основного streamа и альтернативных streamов данных, но также и для streamов, содержащих информацию о безопасности, повторную обработку данных и т. Д. Если вы хотите видеть альтернативные streamи данных, вы можете фильтровать на основе свойства Type StreamInfo, которое будет StreamType.AlternateData для альтернативных streamов данных. Чтобы протестировать этот код, вы можете создать файл с альтернативными streamами данных, используя команду echo в командной строке:

 > echo ".NET Matters" > C:\test.txt > echo "MSDN Magazine" > C:\test.txt:magStream > StreamEnumerator.exe C:\test.txt test.txt: (unnamed) SecurityData 164 (unnamed) Data 17 :magStream:$DATA AlternateData 18 > type C:\test.txt ".NET Matters" > more < C:\test.txt:magStream "MSDN Magazine" 

Итак, теперь вы можете получить имена всех альтернативных streamов данных, хранящихся в файле. Отлично. Но что, если вы хотите фактически манипулировать данными в одном из этих streamов? К сожалению, если вы попытаетесь передать путь для альтернативного streamа данных одному из конструкторов FileStream, будет выведено NotSupportedException: «Формат данного пути не поддерживается». Чтобы обойти это, вы можете обойти проверку canonicalization пути FileStream путем прямого доступа к функции CreateFile, открытой из файла kernel32.dll (см. Рис. 3 ). Я использовал функцию P / Invoke для CreateFile для открытия и получения SafeFileHandle для указанного пути без выполнения каких-либо проверок управляемого разрешения на пути, поэтому он может включать в себя альтернативные идентификаторы streamа данных. Этот SafeFileHandle затем используется для создания нового управляемого FileStream, обеспечивающего необходимый доступ. Благодаря этому легко манипулировать содержимым альтернативного streamа данных, используя функциональность пространства имен System.IO. В следующем примере читается и выводится содержимое C: \ test.txt: magStream, созданного в предыдущем примере:

 string path = @"C:\test.txt:magStream"; using (StreamReader reader = new StreamReader(CreateFileStream(path, FileAccess.Read, FileMode.Open, FileShare.Read))) { Console.WriteLine(reader.ReadToEnd()); } 

Рисунок 3 Использование P / Invoke для CreateFile

 private static FileStream CreateFileStream(string path, FileAccess access, FileMode mode, FileShare share) { if (mode == FileMode.Append) mode = FileMode.OpenOrCreate; SafeFileHandle handle = CreateFile(path, access, share, IntPtr.Zero, mode, 0, IntPtr.Zero); if (handle.IsInvalid) throw new IOException("Could not open file stream.", new Win32Exception()); return new FileStream(handle, access); } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern SafeFileHandle CreateFile(string lpFileName, FileAccess dwDesiredAccess, FileShare dwShareMode, IntPtr lpSecurityAttributes, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); 

Стивен Туб в журнале MSDN с января 2006 года .

  • Скопируйте на другой диск NTFS и сохраните жесткие ссылки
  • Почему параметр сжатия NTFS игнорируется некоторыми приложениями?
  • Почему я спрашиваю «Вы уверены, что хотите скопировать этот файл без его свойств?» При копировании файлов с NTFS на диск FAT?
  • Является ли NTFS на Ubuntu стабильным?
  • Как подключить раздел NTFS для чтения только в Windows?
  • Создание и создание раздела с использованием сценария bash
  • Как я могу проверить фактический размер, используемый в каталоге NTFS со многими hardlinks?
  • Подключите раздел NTFS Windows на Live CD на Ubuntu
  • При удалении диска при доступе файловая система повреждена, можно ли ее устранить?
  • Как восстановить удаленные разделы NTFS?
  • Получение через EFS (зашифрованная файловая система)
  • Interesting Posts

    Сбой файлового браузера в Windows 10

    Ошибка внутреннего сервера проверки Woocomerce

    Какой протокол должен быть принят типом для общей функции, чтобы использовать любой тип номера в качестве аргумента в Swift?

    Как конкатенировать значения int в java?

    Инъекция конструктора с Quartz.NET и простым инжектором

    Имя метода должно начинаться с findBy или findOneBy. Неопределенный метод Symfony?

    Как настроить громкость звука каждый раз, когда я нажимаю клавиши изменения громкости?

    Как обновить ui из asynctask

    Как отменить геокод без Google?

    Файл поступает с другого компьютера и может быть заблокирован

    Перенос файлов PST Outlook 2010 в новое место

    использование атрибута XmlArrayItem без XmlArray в classе Serializable C #

    Обработчик исключений .NET Global в консольном приложении

    Требования к 64-разрядной версии Windows 10: поддерживает ли мой процессор CMPXCHG16b, PrefetchW и LAHF / SAHF?

    Наследование ООП и конструктор по умолчанию

    Давайте будем гением компьютера.