0

I'm looking how to implement the function of linux dd command with C#.Net:

dd if=/dev/sdX of=disk_image_file
dd if=disk_image_file of=/dev/sdX

Creation of a disk image I've already realized with the help of this and this topic and am now trying to similarly write such disk image back to device, SD Card in my case, but am getting the System.UnauthorizedAccessException: Access to the path is denied.

Here is the sample code to reproduce the case:

    class Program
    {
        [DllImport( "Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto )]
        public static extern SafeFileHandle CreateFile( string fileName, [MarshalAs( UnmanagedType.U4 )] FileAccess fileAccess, [MarshalAs( UnmanagedType.U4 )] FileShare fileShare, IntPtr securityAttributes, [MarshalAs( UnmanagedType.U4 )] FileMode creationDisposition, int flags, IntPtr template );

        static void Main( string[] args )
        {
            if( !IsAdministrator() )
            {
                Console.Error.WriteLine( "Not an administrator" );
                return;
            }

            var imageFile = Path.GetTempFileName();
            Console.WriteLine( $"Image File: {imageFile}" );

            try
            {
                var deviceId = GetUsbDevice();
                Console.WriteLine( $"Device ID: {deviceId}" );

                CreateDiskImage( deviceId, imageFile );
                CreateDiskFromImage( deviceId, imageFile );
            }
            catch( Exception exception )
            {
                Console.WriteLine( $"ERROR: {exception}" );
            }
            finally
            {
                File.Delete( imageFile );
            }
        }

        private static string GetUsbDevice()
        {
            var searcher = new ManagementObjectSearcher( "SELECT DeviceID, MediaType FROM Win32_DiskDrive WHERE MediaType='Removable Media' AND InterfaceType='USB'" );

            var drive = searcher.Get().Cast<ManagementObject>().FirstOrDefault();

            return drive?[ "DeviceID" ].ToString();
        }

        private static void CreateDiskImage( string deviceId, string imageFile )
        {
            using( SafeFileHandle device = CreateFile( deviceId, FileAccess.Read, FileShare.Write | FileShare.Read | FileShare.Delete, IntPtr.Zero, FileMode.Open, (int) FileAttributes.System | (int) FileOptions.SequentialScan, IntPtr.Zero ) )
            {
                using( FileStream dest = File.Open( imageFile, FileMode.Create ) )
                {
                    using( FileStream src = new FileStream( device, FileAccess.Read ) )
                    {
                        CopyTo( src, dest, 1048576 ); //read first 1MB for simplifying
                    }
                }
            }
        }

        private static void CreateDiskFromImage( string deviceId, string imageFile )
        {
            using( SafeFileHandle device = CreateFile( deviceId, FileAccess.ReadWrite, FileShare.Write | FileShare.Read, IntPtr.Zero, FileMode.Open, (int) FileAttributes.System, IntPtr.Zero ) )
            {
                using( FileStream src = File.Open( imageFile, FileMode.Open ) )
                {
                    using( FileStream dest = new FileStream( device, FileAccess.Write ) )
                    {
                        src.CopyTo( dest );
                    }
                }
            }
        }

        public static long CopyTo( Stream input, Stream output, long length )
        {
            byte[] buffer = new byte[ 1048576 ];
            int read;
            long totalRead = 0;

            while( totalRead < length && (read = input.Read( buffer, 0, buffer.Length )) > 0 )
            {
                output.Write( buffer, 0, read );
                totalRead += read;
            }

            Console.WriteLine( $"Read first {length} bytes." );
            return totalRead;
        }

        private static bool IsAdministrator()
        {
            using( WindowsIdentity identity = WindowsIdentity.GetCurrent() )
            {
                WindowsPrincipal principal = new WindowsPrincipal( identity );
                return principal.IsInRole( WindowsBuiltInRole.Administrator );
            }
        }
    }

I can't find what's wrong in my code. Maybe it's not possible at all?

Thank you for any hints.

UPDATE 1

Hint from here: The device shall probably be dismounted and locked by application before Write operation. I'll investigate this issue.

UPDATE 2

So the Write action has indeed failed because the disk was locked by the OS because of mounted partition. After deleting the partition, the above code has successfully written the image back to the disk.

The partition can simply be deleted with diskpart:

select disk N - N is the disk number
select part 1 - select the first partition
delete part 

or with PowerShell:

Remove-Partition -DiskNumber N -PartitionNumber 1 -Confirm:$false
igor.br
  • 29
  • 7
  • Are you running your application as an administrator? – ProgrammingLlama May 10 '23 at 08:31
  • [`UnauthorizedAccessException`](https://learn.microsoft.com/en-us/dotnet/api/system.unauthorizedaccessexception): *"The exception that is thrown when the operating system denies access because of an I/O error or a specific type of security error."* The framework even goes as far as providing *additional* information: *"Access to the path is denied."* Equipped with this, you should be at least able to produce a [mcve] (hint: 3 out of the 4 lines of code aren't required to reproduce the issue). – IInspectable May 10 '23 at 08:47
  • Yes, I'm admin. I've added the fully functional code sample to reproduce the case. – igor.br May 10 '23 at 11:22
  • This is far more convoluted than necessary. You are attempting to open a volume, but then move on to perform file system operations on it. That won't work. There is no file system. Please see [Physical Disks and Volumes](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#physical-disks-and-volumes). – IInspectable May 10 '23 at 11:46
  • @IInspectable: The only file system operations I see are related to the image file. Copying between a raw volume and an image file does involve a file on one side of the transfer. Also, the raw volume handle is a "pseudo-file" and the File APIs (`ReadFile`, `WriteFile`) are used with it. – Ben Voigt May 10 '23 at 17:59

0 Answers0