/// <summary>Initializes a new instance of the <see cref="T:AlternateDataStreamInfo"/> class.</summary> /// <param name="stream">The <see cref="T:NativeMethods.Win32StreamId"/> stream ID.</param> /// <param name="transaction"></param> /// <param name="path">The path to an existing file or directory.</param> /// <param name="name">The originalName of the stream.</param> /// <param name="originalName">The alternative data stream name originally specified by the user.</param> /// <param name="isFolder">Specifies that <paramref name="path"/> is a file or directory. <c>null</c> to retrieve automatically.</param> /// <param name="isFullPath"> /// <para><c>true</c> <paramref name="path"/> is an absolute path. Unicode prefix is applied.</para> /// <para><c>false</c> <paramref name="path"/> will be checked and resolved to an absolute path. Unicode prefix is applied.</para> /// <para><c>null</c> <paramref name="path"/> is already an absolute path with Unicode prefix. Use as is.</para> /// </param> private AlternateDataStreamInfo(NativeMethods.Win32StreamId stream, KernelTransaction transaction, string path, string name, string originalName, bool?isFolder, bool?isFullPath) { if (Utils.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException("path"); } _isFullPath = isFullPath; Transaction = transaction; OriginalName = originalName; Name = name ?? string.Empty; Attributes = stream.StreamAttributes; Type = stream.StreamType; Size = (long)stream.StreamSize; FullName = path + Name; if (isFolder == null) { FileAttributes attrs = File.GetAttributesExInternal <FileAttributes>(transaction, LongFullName, null); IsDirectory = (attrs & FileAttributes.Directory) == FileAttributes.Directory; } else { IsDirectory = (bool)isFolder; } }
internal static IEnumerable <AlternateDataStreamInfo> EnumerateStreamsInternal(bool?isFolder, KernelTransaction transaction, SafeFileHandle safeHandle, string path, string originalName, StreamType?streamType, bool?isFullPath) { string pathLp = null; bool callerHandle = safeHandle != null; if (!callerHandle) { pathLp = isFullPath == null ? path : (bool)isFullPath ? Path.GetLongPathInternal(path, false, false, false, false) : Path.GetFullPathInternal(transaction, path, true, false, false, true, false, true, false); if (isFolder == null) { FileAttributes attrs = File.GetAttributesExInternal <FileAttributes>(transaction, pathLp, null); isFolder = (attrs & FileAttributes.Directory) == FileAttributes.Directory; } safeHandle = File.CreateFileInternal(transaction, pathLp, (bool)isFolder ? ExtendedFileAttributes.BackupSemantics : ExtendedFileAttributes.Normal, null, FileMode.Open, FileSystemRights.Read, FileShare.ReadWrite, false, null); } else { NativeMethods.IsValidHandle(safeHandle); } try { using (new PrivilegeEnabler(Privilege.Backup)) using (SafeGlobalMemoryBufferHandle safeBuffer = new SafeGlobalMemoryBufferHandle(NativeMethods.DefaultFileBufferSize)) { Type typeWin32Stream = typeof(NativeMethods.Win32StreamId); uint sizeOfType = (uint)Marshal.SizeOf(typeWin32Stream); uint numberOfBytesRead; IntPtr context; bool doLoop = true; while (doLoop) { if (!NativeMethods.BackupRead(safeHandle, safeBuffer, sizeOfType, out numberOfBytesRead, false, true, out context)) { // Throws IOException. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp, true); } doLoop = numberOfBytesRead == sizeOfType; if (doLoop) { string streamName = null; string streamSearchName = null; // CA2001:AvoidCallingProblematicMethods IntPtr buffer = IntPtr.Zero; bool successRef = false; safeBuffer.DangerousAddRef(ref successRef); // MSDN: The DangerousGetHandle method poses a security risk because it can return a handle that is not valid. if (successRef) { buffer = safeBuffer.DangerousGetHandle(); } safeBuffer.DangerousRelease(); if (buffer == IntPtr.Zero) { NativeError.ThrowException(Resources.HandleDangerousRef); } // CA2001:AvoidCallingProblematicMethods NativeMethods.Win32StreamId stream = Utils.MarshalPtrToStructure <NativeMethods.Win32StreamId>(0, buffer); if (streamType == null || stream.StreamType == streamType) { if (stream.StreamNameSize > 0) { if (!NativeMethods.BackupRead(safeHandle, safeBuffer, stream.StreamNameSize, out numberOfBytesRead, false, true, out context)) { // Throws IOException. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp, true); } // CA2001:AvoidCallingProblematicMethods buffer = IntPtr.Zero; successRef = false; safeBuffer.DangerousAddRef(ref successRef); // MSDN: The DangerousGetHandle method poses a security risk because it can return a handle that is not valid. if (successRef) { buffer = safeBuffer.DangerousGetHandle(); } safeBuffer.DangerousRelease(); if (buffer == IntPtr.Zero) { NativeError.ThrowException(Resources.HandleDangerousRef); } // CA2001:AvoidCallingProblematicMethods streamName = Marshal.PtrToStringUni(buffer, (int)numberOfBytesRead / 2); // Returned stream name format: ":streamName:$DATA" streamSearchName = streamName.TrimStart(Path.StreamSeparatorChar).Replace(Path.StreamSeparator + "$DATA", string.Empty); } if (originalName == null || (streamSearchName != null && streamSearchName.Equals(originalName, StringComparison.OrdinalIgnoreCase))) { yield return(new AlternateDataStreamInfo(stream, transaction, pathLp ?? path, streamName, originalName ?? streamSearchName, isFolder, isFullPath)); } } uint lo, hi; doLoop = !NativeMethods.BackupSeek(safeHandle, uint.MinValue, uint.MaxValue, out lo, out hi, out context); } } // MSDN: To release the memory used by the data structure, // call BackupRead with the bAbort parameter set to TRUE when the backup operation is complete. if (!NativeMethods.BackupRead(safeHandle, safeBuffer, 0, out numberOfBytesRead, true, false, out context)) { // Throws IOException. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp, true); } } } finally { // Handle is ours, dispose. if (!callerHandle && safeHandle != null) { safeHandle.Close(); } } }