VHD Attach 2.10 and a Little Bit More

VHD Attach 2.10 (final) is among us. This version brings read-only attaching as one big feature and a LOT of bug fixing as a side dish.

I would definitely recommend update to all who had issues with stability but beta enthusiasts might skip it altogether since first beta of 3.00 makes it’s debut.

This version brings improvements in setup area (you get to choose your context menu items during installation) and you can create virtual disk without trip to dreadful Computer Management.

Whether you decide upon stable version or go adventurous with beta I hope that you will enjoy it. :)

MagiWOL 3.00

Illustration

MagiWOL has moved to version 3.00.

Compared to version 2.21, changes are:

  • No registry writes will be performed if application is not installed.
  • Double-click now wakes computer instead of editing entry.
  • Broadcast address now can be host name (useful for waking over Internet).
  • Works with Citrix virtualization.
  • Brand new icon.
  • A lot of bug fixing.

Special thanks to all those among you who supported me with bug fixing and installed many wrong versions to make one right. :)

Download is available now.

IB-NAS902StUS2-B Kinda Works

Illustration

I bought Icybox IB-NAS902 to serve as cheap backup solution. It seemed like a perfect device for job. It supports SATA 3.5" HDD and it has both USB and Ethernet connectivity. And it is dirt-cheap.

For my particular scenario 100 Mbps network was good enough and, since samba was supported, Windows should connect like a charm. That was partially true since Windows XP did connect but Windows 7 refused to cooperate. Short investigation has shown that device uses ancient version of samba protocol and solution was to lower Windows 7 security a bit. Not an ideal solution, but good enough.

I was little worried about using FAT32 as file system since there is no support for files larger than 4 GB but that proved not to be a problem. When drive got accessed over network it would just split and combine large files seamlessly. When I connected over USB, I would see them as separate files. That seemed like bearable solution since all big files I had were DVD backups and, in worst case scenario, I could just grab originals and recreate them.

Configuration itself was ugly but simple and it would almost always work. It had tendency to turn on DHCP server after every restart but since it was slow to respond, my DHCP server ended up winning all the time. This was not something I liked but, again, I was willing to accept it.

Daily synchronization discovered another fault. As long as there was single computer accessing it everything was going fine. When two computers did synchronization at same time (or there was heavy traffic) everything would break apart. First it would slow down and then it would reboot. To make things even worse, data consistency would go to hell. After doing daily sync (diff-only) for a month I did chkdsk. Over an hour I looked at errors that touched almost every file on disk. This is not something that you can tolerate in your backups.

At the end, I gave up. I just connected this drive to my existing file server as another USB drive. For now it seems that direct SATA-to-USB bridge works just fine.

I cannot recommend this enclosure to anyone. It is probably cheapest 100 Mbps NAS you can find but it is useless in that mode. It does work as USB enclosure but it is both bigger and pricier than anything else out there.

It is a great idea ruined by the lousy firmware.

How to Initialize Disk

Illustration

When new EMPTY disk is added to Windows first thing that needs to be done is initialization. Only after this step Windows will recognize disk as their own and allow you to create partitions.

Part of (bigger) script was automatic disk initialization. Surfing Internet yielded just few results for this particular problem. Most of them were using DISKPART and this was not acceptable. I needed something in Win32 API.

Theory is simple enough. Single function that does it all is DeviceIoControl. Unfortunately this function is ridiculously overloaded. Little bit of investigation will bring us to IOCTL_DISK_CREATE_DISK control code which does disk initialization. Even better - almost all parameters can be ignored or set to default.

Only parameter that we must understand is CREATE_DISK structure so it is only appropriate to have it use unions. As you might know, there is no meaningful support for unions in C#. There is a way around it with FieldOffset(0) but it does not look nice.

Rest of code is fairly straightforward. To call it just use DiskIO.InitializeDisk("\\.\PHYSICALDRIVE4") (substitute number for whichever drive you wish to initialize).

Full code follows:

internal static class DiskIO {

    public static void InitializeDisk(string path) {
        var signature = new byte[4];
        RandomNumberGenerator.Create().GetBytes(signature); //lazy way to generate "unique" signature

        using (SafeFileHandle handle = NativeMethods.CreateFile(path, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero)) {
            if (handle.IsInvalid) { throw new Win32Exception(); }

            var cd = new NativeMethods.CREATE_DISK();
            cd.PartitionStyle = NativeMethods.PARTITION_STYLE.PARTITION_STYLE_MBR;
            cd.MbrGpt.Mbr.Signature = BitConverter.ToInt32(signature, 0);
            Int32 bytesOut = 0;
            if (NativeMethods.DeviceIoControl(handle, NativeMethods.IOCTL_DISK_CREATE_DISK, ref cd, Marshal.SizeOf(cd), IntPtr.Zero, 0, ref bytesOut, IntPtr.Zero) == false) { throw new Win32Exception(); }
        }
    }


    private static class NativeMethods {

        public const int GENERIC_READ = -2147483648;
        public const int GENERIC_WRITE = 1073741824;
        public const int OPEN_EXISTING = 3;

        public const int IOCTL_DISK_CREATE_DISK = 0x7C058;


        public enum PARTITION_STYLE {
            PARTITION_STYLE_MBR = 0,
            PARTITION_STYLE_GPT = 1,
            PARTITION_STYLE_RAW = 2,
        }


        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct CREATE_DISK {
            public PARTITION_STYLE PartitionStyle;
            public CREATE_DISK_UNION_MBR_GPT MbrGpt;
        }

        [StructLayoutAttribute(LayoutKind.Explicit)]
        public struct CREATE_DISK_UNION_MBR_GPT {
            [FieldOffset(0)]
            public CREATE_DISK_MBR Mbr;
            [FieldOffset(0)]
            public CREATE_DISK_GPT Gpt;
        }


        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct CREATE_DISK_MBR {
            public Int32 Signature;
        }

        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct CREATE_DISK_GPT {
            public Guid DiskId;
            public Int32 MaxPartitionCount;
        }


        [DllImportAttribute("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true)]
        public static extern SafeFileHandle CreateFile([MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName, Int32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, IntPtr hTemplateFile);

        [DllImportAttribute("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
        [return: MarshalAsAttribute(UnmanagedType.Bool)]
        public static extern Boolean DeviceIoControl(SafeFileHandle hDevice, Int32 dwIoControlCode, ref CREATE_DISK lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, Int32 nOutBufferSize, ref Int32 lpBytesReturned, IntPtr lpOverlapped);

    }

}