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);

    }

}

Wording

Illustration

I was in airplane few days ago and we were late (of course). After some twenty minutes of waiting captain told us what happened:

Sorry for wait but airplane in front of us managed to hit some birds during takeoff. We are still waiting for someone to pick up remains.

At that moment you could see difference between frequent travelers and some lady that was there for the first time. Most of travelers made no notice of this announcement, some of them were smiling and few were even laughing (unsurprisingly I was in last group).

Lady was scared. What she probably heard was that we are waiting for someone to pick up remains of airplane in front of us. In any case airplane got rolling and some gentlemen siting next to her took some time explaining exact situation so rest of flight went fine.

This only shows that understanding message highly depends on personal context and that care should be exercised in choosing exact wording. Whether it is airplane announcement or critical error text that users might see.

Cloud Reader

Amazon Cloud Reader is finally available. It does not bring anything new regarding functionality but it does enable to read your books on any platform that supports HTML 5. For me this platform is Chrome.

Biggest loser in this story will be Kindle for PC since there is no more reason why would anybody install it.

VHD Attach 2.10 (Beta 1)

Illustration

VHD Attach in version 2.10 is mostly about fixing some hard to catch bugs. However, one feature sneaked in and justified this beta.

Finally there is an option of read-only attaching a virtual disk file. Really good feature for taking a quick look into virtual disk without risk of changing anything.

Version is available at https://www.medo64.com/vhdattach/beta/.

P.S. Final 2.10 version has no timeline since I do have to pay my bills and that kind of work takes priority. You can always donate some money to speed me up. :)

Mercurial on FreeNAS

Illustration

In search for smallest possible Mercurial installation I remembered my old friend FreeNAS. This is small NAS server (at least in 0.7 version) based on FreeBSD 7.3. System requirements are low: 256 MB of RAM and 512 MB system disk is all that is needed. And, of course, you need to have full installation of FreeNAS. Embedded will not suffice.

I will not explain how to setup FreeNAS here since it works out of box with pure defaults. Only thing to configure is where you want your data to be and this should be easy enough to do by yourself (hint: check “Disks” menu). My final goal is to have equivalent for Mercurial under Ubuntu.

First step is to install needed packets. This can be done from web interface (System->Packages) and following packets are needed (given in order of installation):

Now we need to copy some basic files: [bash highlight=“1,2”] $ cp /usr/local/share/doc/mercurial/www/hgweb.cgi /mnt/data/hg/hgweb.cgi $ cp /var/etc/lighttpd.conf /var/etc/lighttpd2.conf [/bash]

We also need a new config file that should be situated at “/mnt/data/hg/hgweb.config” and it should have following lines:

[collections]
/mnt/data/hg/ = /mnt/data/hg/

[web]
baseurl=/hg/
allow_push=*
push_ssl=false

We need to edit “/mnt/data/hg/hgweb.cgi” in order to fix config parameter. Just change example config line with:

config = "/mnt/data/hgweb.config"

At the BOTTOM of existing “/var/etc/lighttpd2.conf” (notice that we created this file via cp few steps ago) we need to add:

server.modules += ( "mod_rewrite" )
url.rewrite-once = (
    "^/hg([/?].*)?$" => "/hgweb.cgi/$1"
)
$HTTP["url"] =~ "^/hgweb.cgi([/?].*)?$" {
    server.document-root = "/mnt/data/hg/"
    cgi.assign = ( ".cgi" => "/usr/local/bin/python" )
}
$HTTP["querystring"] =~ "cmd=unbundle" {
    auth.require = (
        "" => (
            "method"  => "basic",
            "realm"   => "Mercurial",
            "require" => "valid-user"
        )
    )
    auth.backend = "htpasswd"
    auth.backend.htpasswd.userfile = "/mnt/data/hg/.htpasswd"
}

This config will ensure that everybody can pull repositories while only users in .htpasswd file can push. If you want authorization for pull also, just delete two highlighted files. Notice that users are defined in .htpasswd file that needs to be created with htpasswd command-line tool. Since that tool is not available in FreeNAS easiest way to get user lines is to use online tool.

In order to test how everything works, just go to shell and restart lighttpd with new config:

kill -9 `ps auxw | grep lighttpd | grep -v grep | awk '{print $2}'`
/usr/local/sbin/lighttpd -f /var/etc/lighttpd2.conf start

If everything goes fine, you should be able to access repositories at “http://192.168.1.250/hg/” (or whatever you machine ip/host name is).

As last step we need to add those two commands above (kill and lighttpd) to System->Advanced->Command scripts. Both lines get into their own PostInit command. This ensures that, after every reboot, we start lighttpd with our “enhanced” config file.

P.S. Do not even try to edit “/var/etc/lighttpd.conf”. It gets overwritten after every restart.

P.P.S. This post is just about enabling Mercurial under FreeNAS and because of this it is simplified. It is mere starting point. For further reading check official Publishing Repositories with hgwebdir.cgi guide and my guide on setting-up Mercurial under Ubuntu.