|
1 | | -namespace Vanara.PInvoke; |
| 1 | +using System.Collections.Generic; |
| 2 | + |
| 3 | +namespace Vanara.PInvoke; |
2 | 4 |
|
3 | 5 | public static partial class Kernel32 |
4 | 6 | { |
@@ -470,6 +472,105 @@ public enum TAPEMARK_TYPE |
470 | 472 | public static extern bool BackupRead([In] HFILE hFile, IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [MarshalAs(UnmanagedType.Bool)] bool bAbort, |
471 | 473 | [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext); |
472 | 474 |
|
| 475 | + /// <summary> |
| 476 | + /// The <c>BackupRead</c> function can be used to back up a file or directory, including the security information. The function reads |
| 477 | + /// data associated with a specified file or directory into a buffer, which can then be written to the backup medium using the |
| 478 | + /// <c>WriteFile</c> function. |
| 479 | + /// <para>This method appropriately closes the read operation after reading all the streams.</para> |
| 480 | + /// </summary> |
| 481 | + /// <param name="hFile"> |
| 482 | + /// <para> |
| 483 | + /// Handle to the file or directory to be backed up. To obtain the handle, call the <c>CreateFile</c> function. The SACLs are not read |
| 484 | + /// unless the file handle was created with the <c>ACCESS_SYSTEM_SECURITY</c> access right. For more information, see File Security and |
| 485 | + /// Access Rights. |
| 486 | + /// </para> |
| 487 | + /// <para> |
| 488 | + /// The handle must be synchronous (nonoverlapped). This means that the FILE_FLAG_OVERLAPPED flag must not be set when <c>CreateFile</c> |
| 489 | + /// is called. This function does not validate that the handle it receives is synchronous, so it does not return an error code for a |
| 490 | + /// synchronous handle, but calling it with an asynchronous (overlapped) handle can result in subtle errors that are very difficult to debug. |
| 491 | + /// </para> |
| 492 | + /// <para> |
| 493 | + /// The <c>BackupRead</c> function may fail if <c>CreateFile</c> was called with the flag <c>FILE_FLAG_NO_BUFFERING</c>. In this case, |
| 494 | + /// the <c>GetLastError</c> function returns the value <c>ERROR_INVALID_PARAMETER</c>. |
| 495 | + /// </para> |
| 496 | + /// </param> |
| 497 | + /// <param name="bProcessSecurity"> |
| 498 | + /// <para>Indicates whether the function will restore the access-control list (ACL) data for the file or directory.</para> |
| 499 | + /// <para>If bProcessSecurity is <c>TRUE</c>, the ACL data will be backed up.</para> |
| 500 | + /// </param> |
| 501 | + /// <param name="retrieveContents"> |
| 502 | + /// If set to <see langword="true"/>, the contents of each stream will be returned in <paramref name="lpBuffers"/>; otherwise <see |
| 503 | + /// langword="false"/> will return a null buffer. |
| 504 | + /// </param> |
| 505 | + /// <param name="lpBuffers">Returns a list of tuples with each stream's information (as a <see cref="WIN32_STREAM_ID"/> value, and optionally the contents.</param> |
| 506 | + /// <returns> |
| 507 | + /// <para>If the function succeeds, the return value is <see cref="Win32Error.NO_ERROR"/>.</para> |
| 508 | + /// <para>If the function fails, the return value indicates which error occurred.</para> |
| 509 | + /// </returns> |
| 510 | + [PInvokeData("Winbase.h", MSDNShortId = "aa362509")] |
| 511 | + public static Win32Error BackupRead([In] HFILE hFile, [Optional] bool bProcessSecurity, [Optional] bool retrieveContents, out List<(WIN32_STREAM_ID id, SafeAllocatedMemoryHandle? buffer)> lpBuffers) |
| 512 | + { |
| 513 | + lpBuffers = []; |
| 514 | + unsafe |
| 515 | + { |
| 516 | + // Use header to prevent unknown allocation of name value |
| 517 | + WIN32_STREAM_ID_HEADER hdr = new(); |
| 518 | + var hdrSize = (uint)sizeof(WIN32_STREAM_ID_HEADER); |
| 519 | + |
| 520 | + IntPtr lpContext = default; |
| 521 | + try |
| 522 | + { |
| 523 | + while (true) |
| 524 | + { |
| 525 | + // Read the next stream header: |
| 526 | + var ret = BackupRead(hFile, (IntPtr)(void*)&hdr, hdrSize, out var bytesRead, false, bProcessSecurity, ref lpContext); |
| 527 | + if (!ret) |
| 528 | + return GetLastError(); |
| 529 | + // Last stream found, so exit loop |
| 530 | + if (bytesRead == 0) |
| 531 | + break; |
| 532 | + // Unexpected error -- this should always be right |
| 533 | + if (bytesRead != hdrSize) |
| 534 | + return Win32Error.ERROR_INVALID_DATA; |
| 535 | + |
| 536 | + // Get the name if available |
| 537 | + string? name = null; |
| 538 | + if (hdr.dwStreamNameSize > 0) |
| 539 | + { |
| 540 | + using SafeLPWSTR pName = new(((int)hdr.dwStreamNameSize / 2) + 1); |
| 541 | + bool nameRead = BackupRead(hFile, pName, hdr.dwStreamNameSize, out bytesRead, false, bProcessSecurity, ref lpContext); |
| 542 | + if (!nameRead) |
| 543 | + return GetLastError(); |
| 544 | + name = pName; |
| 545 | + } |
| 546 | + |
| 547 | + // Capture the details about the stream and allocated memory with the contents |
| 548 | + (WIN32_STREAM_ID id, SafeAllocatedMemoryHandle? buffer) entry = (hdr, null); |
| 549 | + entry.id.cStreamName = name ?? ""; |
| 550 | + if (hdr.dwStreamId != BACKUP_STREAM_ID.BACKUP_INVALID && retrieveContents) |
| 551 | + { |
| 552 | + var buf = new SafeHGlobalHandle(hdr.Size.LowPart()); |
| 553 | + if (buf.Size > 0 && !BackupRead(hFile, buf, buf.Size, out bytesRead, false, bProcessSecurity, ref lpContext)) |
| 554 | + return GetLastError(); |
| 555 | + entry.buffer = buf; |
| 556 | + } |
| 557 | + else |
| 558 | + { |
| 559 | + if (!BackupSeek(hFile, hdr.Size.LowPart(), (uint)hdr.Size.HighPart(), out _, out _, ref lpContext)) |
| 560 | + return GetLastError(); |
| 561 | + } |
| 562 | + lpBuffers.Add(entry); |
| 563 | + } |
| 564 | + } |
| 565 | + finally |
| 566 | + { |
| 567 | + // Close the backup |
| 568 | + BackupRead(hFile, IntPtr.Zero, 0, out _, true, bProcessSecurity, ref lpContext); |
| 569 | + } |
| 570 | + } |
| 571 | + return 0; |
| 572 | + } |
| 573 | + |
473 | 574 | /// <summary> |
474 | 575 | /// The <c>BackupSeek</c> function seeks forward in a data stream initially accessed by using the <c>BackupRead</c> or <c>BackupWrite</c> function. |
475 | 576 | /// </summary> |
@@ -1881,6 +1982,12 @@ public struct WIN32_STREAM_ID |
1881 | 1982 | /// <summary>Unicode string that specifies the name of the alternative data stream.</summary> |
1882 | 1983 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] |
1883 | 1984 | public string cStreamName; |
| 1985 | + |
| 1986 | + /// <summary>Performs an implicit conversion from <see cref="WIN32_STREAM_ID_HEADER"/> to <see cref="WIN32_STREAM_ID"/>.</summary> |
| 1987 | + /// <param name="h">The header.</param> |
| 1988 | + /// <returns>The result of the conversion.</returns> |
| 1989 | + public static implicit operator WIN32_STREAM_ID(in WIN32_STREAM_ID_HEADER h) => |
| 1990 | + new() { dwStreamId = h.dwStreamId, dwStreamAttributes = h.dwStreamAttributes, Size = h.Size, dwStreamNameSize = h.dwStreamNameSize }; |
1884 | 1991 | } |
1885 | 1992 |
|
1886 | 1993 | /// <summary>The <c>WIN32_STREAM_ID</c> structure contains stream data.</summary> |
|
0 commit comments