C# BackupRead, BackupWrite - System.AccessViolatio

2019-08-27 01:16发布

I am trying to get BackupRead() and BackupWrite() to work using http://pinvoke.net as my reference. Most answers I find in google are referencing C++ or even Delphi... I want to backup a file/folder with their ACL.

EDIT:

This is my current code (I am setting privileges for SE_BACKUP_NAME and SE_RESTORE_NAME and SE_TAKE_OWNERSHIP_NAME):

            var bReadHandle = ArchiveWinApi.CreateFile(sourcePath, (uint)(ArchiveWinApi.ACCESS_MASK.GENERIC_READ | ArchiveWinApi.ACCESS_MASK.READ_CONTROL | ArchiveWinApi.ACCESS_MASK.ACCESS_SYSTEM_SECURITY), 0, IntPtr.Zero, FileMode.Open, ArchiveWinApi.ExtendedFileAttributes.BackupSemantics, IntPtr.Zero);
            if (!IsValid(bReadHandle))
            {
                var error = GetLastWin32Error();
                throw new Exception("InvalidFileHandle");
            }

            var bWriteHandle = ArchiveWinApi.CreateFile(destPath, (uint)(ArchiveWinApi.ACCESS_MASK.GENERIC_WRITE | ArchiveWinApi.ACCESS_MASK.WRITE_OWNER | ArchiveWinApi.ACCESS_MASK.WRITE_DAC | ArchiveWinApi.ACCESS_MASK.ACCESS_SYSTEM_SECURITY), 0, IntPtr.Zero, FileMode.Create, ArchiveWinApi.ExtendedFileAttributes.BackupSemantics, IntPtr.Zero);
            if (!IsValid(bWriteHandle))
            {
                var error = GetLastWin32Error();
                throw new Exception("InvalidFileHandle");
            }


            var bufferSize = 4096;
            var buffer = Marshal.AllocHGlobal(bufferSize);

            var backupReadContext = IntPtr.Zero;
            var backupWriteContext = IntPtr.Zero;

            uint lpNumberOfBytesRead = 0;
            uint lpNumberOfBytesWritten = 0;

            while (true)
            {
                var result = ArchiveWinApi.BackupRead(bReadHandle, out buffer, (uint) bufferSize, out lpNumberOfBytesRead, false, true, ref backupReadContext);
                if (!result)
                {
                    var error = GetLastWin32Error();
                    throw new Exception("BackupRead failed");
                }

                if (lpNumberOfBytesRead == 0) break;

                result = ArchiveWinApi.BackupWrite(bWriteHandle, buffer, lpNumberOfBytesRead, out lpNumberOfBytesWritten, false, true, ref backupWriteContext);
                if (!result)
                {
                    var error = GetLastWin32Error();
                    throw new Exception("BackupWrite failed");
                }
            }

            ArchiveWinApi.BackupRead(bReadHandle, out buffer, 0, out lpNumberOfBytesRead, true, true, ref backupReadContext);
            ArchiveWinApi.CloseHandle(bReadHandle);

            ArchiveWinApi.BackupWrite(bWriteHandle, buffer, 0, out lpNumberOfBytesWritten, true, true, ref backupWriteContext);
            ArchiveWinApi.CloseHandle(bWriteHandle);

I get a System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

EDIT 2:

  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  internal static extern bool BackupRead(IntPtr hFile, out IntPtr lpBuffer, [MarshalAs(UnmanagedType.U4)] uint nNumberOfBytesToRead, [MarshalAs(UnmanagedType.U4)] out uint lpNumberOfBytesRead, [MarshalAs(UnmanagedType.Bool)] bool bAbort, [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext);

  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  internal static extern bool BackupWrite(IntPtr hFile, IntPtr lpBuffer, [MarshalAs(UnmanagedType.U4)] uint nNumberOfBytesToWrite, [MarshalAs(UnmanagedType.U4)] out uint lpNumberOfBytesWritten, [MarshalAs(UnmanagedType.Bool)] bool bAbort, [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext);

 [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
      internal static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPWStr)] string lpFileName, [MarshalAs(UnmanagedType.U4)] uint dwDesiredAccess, [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, IntPtr lpSecurityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, [MarshalAs(UnmanagedType.U4)] ExtendedFileAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);

public enum ACCESS_MASK : uint
  {
     READ_CONTROL = 0x00020000,
     WRITE_DAC = 0x00040000,
     WRITE_OWNER = 0x00080000,

     ACCESS_SYSTEM_SECURITY = 0x01000000,

     GENERIC_READ = 0x80000000,
     GENERIC_WRITE = 0x40000000,
     GENERIC_ALL = 0x10000000,
  }

  [Flags]
  public enum ExtendedFileAttributes
  {
     BackupSemantics = 33554432,
  }

2条回答
Ridiculous、
2楼-- · 2019-08-27 02:04

It is a bit difficult to check for the problem / debug, since you did not provide your P/Invoke class (and http://pinvoke.net may not necessarily be 100% correct).

Yes, I know it would be a good idea to help you for this specific question, but I want to show you some alternative way:

Look for https://github.com/alphaleonis/AlphaFS

This is mainly known as a class for long paths (paths with length > 259 chars), but also has a class for BackupRead. We use the AlphaFS lib for production code for years.

I wrote a small sample, quick and dirty without error checks :-)

using System.Security.AccessControl;
using Alphaleonis.Win32.Filesystem;
using Alphaleonis.Win32.Security;
...
...

var sourceFile = @"c:\temp\a.txt";
var destinationFile = @"c:\temp\b.txt";

var buffer = new byte[4096];

using (var privilegeEnabler = new PrivilegeEnabler(Privilege.Backup, Privilege.Restore))
{
    using (var readStream = new BackupFileStream(sourceFile,
                 System.IO.FileMode.Open, FileSystemRights.Read))
    using (var writeStream = new BackupFileStream(destinationFile,
                 System.IO.FileMode.Create, FileSystemRights.FullControl))
    {
        int count;

        while ((count = readStream.Read(buffer, 0, buffer.Length, true)) > 0)
        {
            writeStream.Write(buffer, 0, count, true);
        }
    }
}

This worked for me (copying the file with NTFS alternate data streams and security).

I recommend testing this further for stability, I have no experience with using AlphaFS for this purpose.

查看更多
Bombasti
3楼-- · 2019-08-27 02:09

In P/Invoke declaration

... BackupRead(IntPtr hFile, out IntPtr lpBuffer, ...

remove the out. And during call of course too.

For both BackupRead and BackupWrite API, a pointer to a caller-provided buffer is given. This pointer will be passed by value, no out parameter should be placed here.

查看更多
登录 后发表回答