Monday, August 10, 2015

Clipboard for X11 (debian, ubuntu, etc) under Hyper-V

Introduction

I was unable to get the Hyper-V clipboard to works when the guest os is a linux box.
Follow is a workaround, what you need is an installation of cygwin including packages of xorg-server.

Linux Setup

Enable xdmcp ( gdm )

Assuming you are using the default gnome desktop manager (gdm) go to edit /etc/gdm3/daemon.conf and set xdmcp on with the follow
[xdmcp]
Enable=true

Disable ipv6

To let gdm works with xdmcp you need to disable ipv6, then edit /etc/default/grub and disable the ipv6 setting the follow
GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 quiet"
then update the grub loader
update-grub
and reboot the linux system.

Windows client

Establish a connection to the session manager using
X -query <ip-address-of-linux-box>

Now copy/paste works as expected.


Creative Commons License
Clipboard for X11 (debian, ubuntu, etc) under Hyper-V by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.

Sunday, July 26, 2015

FluentData load references

Introduction

FluentData is an opensource library ( https://fluentdata.codeplex.com/ ) that enables you to access databases using direct query without to have a mapped entity framework, in fact it can work using the C# 4.0 dynamics.

I developed a simple extension method to allow you easily add the navigating properties to gathered data ( https://github.com/devel0/Lib0/tree/master/Lib0.Data.FluentData ).

Test database


The simple database that I used has two tables and a foreign key that let you navigate from the "user_entries" table to the "contact_detail" table through the "id_contact_detail" foreign key.

Test app

The test application is composed by a datagrid that lists the user_entries ids and a form detail which shows the firstname and lastname fields of the related record.






XAML part




<!--#region Master/Detail -->
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            
            <!--#region Master -->
            <DataGrid x:Name="MasterDataGrid" IsReadOnly="True">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="ID" Binding="{Binding id}"/>
                </DataGrid.Columns>
            </DataGrid>
            <!--#endregion-->

            <!--#region Detail -->
            <Grid Grid.Column="1" x:Name="DetailDataGrid" 
                  DataContextChanged="DetailDataGrid_DataContextChanged"
                  DataContext="{Binding ElementName=MasterDataGrid, Path=SelectedItem}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>                    
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                
                <TextBlock Grid.Row="0" Grid.Column="0" Text="Firstname:" VerticalAlignment="Center"/>
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding contact_detail.firstname, Mode=TwoWay}"/>
                
                <TextBlock Grid.Row="1" Grid.Column="0" Text="Lastname:" VerticalAlignment="Center"/>
                <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding contact_detail.lastname, Mode=TwoWay}"/>
            </Grid>
            <!--#endregion-->
        </Grid>
        <!--#endregion-->

Code part

ctx = new DbContext().ConnectionString(ConnectionStringTbox.Text, new PostgreSqlProvider());

var q = ctx.Sql("select * from user_entries").QueryMany<dynamic>();

q.LoadReferences(ctx,                
    "contact_detail",
    k => k.id_contact_detail,                
    k => k.id
    );           

MasterDataGrid.ItemsSource = q.ToList();

The extension method "LoadReferences" extends given dynamic objects that comes from the QueryMany with the property "contact_detail" that is the object gathered from the database using the foreign key.

Considerations

The extension library discussed is mostly an initial act for a deeper investigation about what can be done to make the use of the FluentData library more useful compared to the Entity Framework.

Roadmap :
  • better management of loading references
    • use of custom where to restrict the result set
    • use of join instead of load all records
    • lazy load of records
  • management of changeset
    • listen to dynamics using PropertyChanged

Creative Commons License
FluentData lazy loader by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.

Sunday, July 19, 2015

C# complete operation event handler

Introduction



Fig.1 - Event handling (normal behavior)
Usually when we want to listen an event we have to connect an event handler and then we'll receive calls to the event handler each time the event is raised from the originating source (Fig.1).

In some other scenarios we need to be informed if an event occurred event if its already sent (Fig.2).


Fig. 2 - Listen to past events

Follow is an example :

using System;

namespace MyApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Test x = new Test();           

            x.Fire();

            x.EvTest += (a, b) =>
            {
                Console.WriteLine("Event fired");
            };
        }
    }

    public class Test
    {

        public event EventHandler EvTest;

        public void Fire()
        {
            if (EvTest != null) EvTest(this, null);
        }

    }
}

Running the above code you'll notice that the event is not intercepted because the handler is connected only after the event occurred.

Lib0.Core.EventOperation

To get rid of this problem I built a class to manage such events, called EventOperation.

Source code

Source code for this library is located at follow url https://github.com/devel0/Lib0

Example

/// <summary>
/// Be notified after the event fired.
/// </summary>
[TestMethod]
public void Test4()
{
  IEventOperation op = new EventOperation(EventOperationBehaviorTypes.RemindPastEvents);

  op.Fire();

  Assert.IsTrue(op.FireCount == 1);
  Assert.IsTrue(op.HandledCount == 0);

  op.Event += (a, b) => { }; // This handler function is called right here
                             // cause previous event already fired.
  Assert.IsTrue(op.FireCount == 1);
  Assert.IsTrue(op.HandledCount == 1);
}

EventOperation takes an optional argument to set which behavior between "Normal" or "RemindPastEvent" types.

Multiple listeners

Follow unit test demonstrate how the event operation works using "RemindPastEvent" behavior and multiple listeners :

/// <summary>
/// Be notified after the event fired (multiple listeners).
/// </summary>
[TestMethod]
public void Test5()
{
    IEventOperation op = new EventOperation(EventOperationBehaviorTypes.RemindPastEvents);

    var listener1HitCount = 0;
    var listener2HitCount = 0;

    {
        op.Fire(); // event fired ( no handlers yet connected )
        Assert.IsTrue(op.FireCount == 1 && op.HandledCount == 0);

        op.Event += (a, b) => // listener1 connects and receive 1 event
        {
            ++listener1HitCount;
        };
        Assert.IsTrue(op.FireCount == 1 && op.HandledCount == 1);
    }

    {
        op.Fire(); // event fired ( listener1 will receive its 2-th event )
        Assert.IsTrue(op.FireCount == 2 && op.HandledCount == 2);

        op.Event += (a, b) => // listener2 connected and receive 2 events
        {
            ++listener2HitCount;
        };                
    }

    Assert.IsTrue(listener1HitCount == 2 && listener2HitCount == 2);
    Assert.IsTrue(op.FireCount == 2 && op.HandledCount == listener1HitCount + listener2HitCount);            
}

A final word

RemindPastEvents behavior implies that a list of past event arguments are stored into the memory, so be aware not to use it for massive event handling. The scenario in which this class make sense can be for example the one represented in Fig2 when you need to synchronize many listeners to an event that we don't know if its already fired.


Creative Commons License
C# complete operation event handler by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.

Thursday, June 18, 2015

Overriding Object.GetHashCode

Different objects have different memory address

namespace OverridingGetHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Number() { Value = 1 };
            var b = new Number() { Value = 1 };

            // a,b contains equals semantic values but the Equals returns false
            // cause a and b object addresses are different.
            Debug.Assert(!a.Equals(b));
        }
    }

    public class Number
    {
        public double Value { get; set; }
    }

}

Overriding Object.Equals method

namespace OverridingGetHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Number() { Value = 1 };
            var b = new Number() { Value = 1 };

            // a,b results equals cause the overridden Equals method checks
            // against semantic values of two different objects.
            Debug.Assert(a.Equals(b));
        }
    }

    public class Number
    {
        public double Value { get; set; }

        public override bool Equals(object obj)
        {
            return Value == ((Number)obj).Value;
        }
    }

}

Object.Equals may not enough for collections: use of GetHashCode

namespace OverridingGetHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var hs = new HashSet<Number>();
            var a = new Number() { Value = 1 };
            var b = new Number() { Value = 1 };

            hs.Add(a);

            // the hashset contains a, but not the semantic equals object b
            // cause the GetHashCode of b is different from a by default
            // (different instances)
            Debug.Assert(hs.Contains(a));
            Debug.Assert(!hs.Contains(b));
        }
    }

    public class Number
    {
        public double Value { get; set; }

        public override bool Equals(object obj)
        {
            return Value == ((Number)obj).Value;
        }
    }

}

With the use of GetHashCode two different object instances have a chance to be Object.Equals

namespace OverridingGetHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var hs = new HashSet<Number>();
            var a = new Number() { Value = 1 };
            var b = new Number() { Value = 1 };

            hs.Add(a);

            // hashset contains b from the semantic point of view cause equals a
            Debug.Assert(hs.Contains(a));
            Debug.Assert(hs.Contains(b));
        }
    }

    public class Number
    {
        public double Value { get; set; }

        public override bool Equals(object obj)
        {
            return Value == ((Number)obj).Value;
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }
    }

}

Features and requirements to override GetHashCode method

  1. two semantic equals objects must have the same hash code number or they definitively considered different regardless the Equals result.
  2. two different semantic objects can have the same hash code number (collisions), but
  3. avoid that two different sematic object have the same hash code number, cause
  4. more collision results in more calls to the Equals method
  5. maintain minimal cpu overhead on gethashcode
  6. when composing the xor expression of fields in the hashcode include only those that are used against a simple equality check ( == ) in the Equals 

If not meet the requirements (1) then two semantic equals objects will be considered different regardless the Equals method simply cause the hash code, fast check, states the objects are definitively different.

If two different semantic objects share the same hash code (2) then an additional check using Equals will ensure with a detailed check if these two objects are the same at all. (3) This will results in a slower check cause the detailed check using Equals will span over more objects, so is best to avoid hashcode collision for really different objects.

Xor of GetHashCode members

Computing the xor value of more integers numbers is a fast way to retrieve a combination of these numbers.

When xor works

namespace OverridingGetHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var hs = new HashSet<Number>();
            var a = new Number() { Value = 1, ValueB = 2 };
            var b = new Number() { Value = 1, ValueB = 2 };

            hs.Add(a);

            // hashset contains b from the sematic point of view cause equals a
            Debug.Assert(hs.Contains(a));
            Debug.Assert(hs.Contains(b));
        }
    }

    public class Number
    {
        public double Value { get; set; }
        public double ValueB { get; set; }

        public override bool Equals(object obj)
        {
            return Value == ((Number)obj).Value && ValueB == ((Number)obj).ValueB;
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode() ^ ValueB.GetHashCode();
        }
    }

}

When xor not works

namespace OverridingGetHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var hs = new HashSet<Number>();
            var a = new Number() { Value = 1, Tolerance = .1 };
            var b = new Number() { Value = 1.2, Tolerance = .5 };

            // a and b are object semantically equals
            Debug.Assert(a.Equals(b));

            hs.Add(a);
            
            Debug.Assert(hs.Contains(a));
            Debug.Assert(!hs.Contains(b)); // hash not find b in the set!
        }
    }

    public class Number
    {
        public double Value { get; set; }
        public double Tolerance { get; set; }

        public override bool Equals(object obj)
        {
            var other = (Number)obj;
            var diff = Math.Abs(Value - other.Value);
            var maxtol = Math.Max(Tolerance, other.Tolerance);

            return diff <= maxtol;
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode() ^ Tolerance.GetHashCode(); // wrong
        }
    }

}

The example above shown that using xor of the field without reasoning is not a good practice: the override of GetHashCode is not an obvious task.

Inserting the xor with the Value and Tolerance fields in the GetHashCode does not comply with the first requirement "Two semantic equals objects must have the same hash code number or they definitively considered different regardless the Equals result".

In fact is easy to see that the objects

            var a = new Number() { Value = 1, Tolerance = .1 };
            var b = new Number() { Value = 1.2, Tolerance = .5 };

appears different at a simple view ( they have Value and Tolerance different ).

More it not comply with the 6) rule "when composing the xor expression of fields in the hashcode include only those that are used against a simple equality check ( == ) in the Equals", in fact the Equals contains complex operations over Value and Tolerance fields :

        public override bool Equals(object obj)
        {
            var other = (Number)obj;
            var diff = Math.Abs(Value - other.Value);
            var maxtol = Math.Max(Tolerance, other.Tolerance);

            return diff <= maxtol;
        }

A basic solution to let the HashSet works as expected would be the follow :

...

        public override int GetHashCode()
        {
            return 0;
        }
...

In other words we lost the behavior of an hashset and we are using effectively a List<Number> collection, cause to search an object the worst case is to check against all N objects in the list using the Equals method. After all, not all classes implements complex Equals methods.

Implements IEquatable<T>

When overriding Equals(Object) method is a good practice to mark the class as IEquatable<T> and implements the Equals(T) too. (see C# objects equality).


Creative Commons License
Overriding Object.GetHashCode by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.

Tuesday, June 2, 2015

C# objects equality

Object Equality Schematics

Equals(Object) method override

  • override Equals(Object) method let you to check if two different object instances are the same object from their content point of view, using a.Equals(b).

Equality and inequality operators overload

  • when override Equals(Object) method is a good practice to override the equality == and inequality != operators to avoid mistake if never mind to use the Equals method.

IEquatable<T> interface implementation

  • when override Equals(Object) method is a good practice to implements the IEquatable<T> interface and call the Equals(T) method from the Equals(Object) to increment efficiency avoiding box-unboxing from Object to T type.

Collections HashSet and Dictionary

  • overriding of the Equals method in objects allow to achieve follow results for Equals objects :
    • HashSet avoid double insertion.
    • Dictionary find object key already present using Equals objects not inserted in the dictionary.

GetHashCode

  • when override Equals method for use of objects in collections :
    • must implement GetHashCode or as default it will declare two object different regardless they are equal using Equals method.
    • when two objects have the same hashcode then a second detailed check will ensure they equals using Equals method :
      • two different object can have the same hash code ( this of course decrease performance cause more detailed equals check will be done eg. key collisions ).
      • two equals object must have the same hash code or the equals method check will skipped and they are stated as different.

Sample unit test code

Without Equals(Object)

using System.Diagnostics;

namespace TestHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Test() { value = 10 };
            var b = new Test() { value = 10 };

            Debug.Assert(!a.Equals(b));
        }
    }

    public class Test
    {
        public int value { get; set; }
    }

}

With Equals(Object) and without equality inequality operators

using System.Diagnostics;

namespace TestHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Test() { value = 10 };
            var b = new Test() { value = 10 };

            Debug.Assert(!ReferenceEquals(a, b)); // different instances
            Debug.Assert(a != b); // operator not overloaded
            Debug.Assert(a.Equals(b)); // same equals object            
        }
    }

    public class Test
    {
        public int value { get; set; }
        
        public override bool Equals(object obj)
        {
            return value == ((Test)obj).value;
        }        
    }

}

With Equals(Object) and with equality inequality operators

using System.Diagnostics;

namespace TestHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Test() { value = 10 };
            var b = new Test() { value = 10 };

            Debug.Assert(!ReferenceEquals(a, b)); // different instances
            Debug.Assert(a == b); // operator overloaded            
            Debug.Assert(a.Equals(b)); // same equals object            
        }
    }

    public class Test
    {
        public int value { get; set; }
        
        public override bool Equals(object obj)
        {
            return value == ((Test)obj).value;
        }

        public static bool operator ==(Test x, Test y)
        {
            return x.Equals(y);
        }

        public static bool operator !=(Test x, Test y)
        {
            return !x.Equals(y);
        }
    }

}

With Equals(T)

Test results (in Release mode) :
  • with Equals(Object) : ~5 sec
  • with Equals(T) : ~1 sec

using System;
using System.Diagnostics;

namespace TestHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Test() { value = 10 };
            var b = new Test() { value = 10 };
            var c = 0;

            var sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 1500000000; ++i)
            {
                if (a == b) ++c;
            }
            Console.WriteLine(sw.Elapsed);
        }
    }

    public class Test : IEquatable<Test>
    {
        public int value { get; set; }        
        
        public bool Equals(Test other)
        {
            return value == other.value;
        }
        
        public override bool Equals(object obj)
        {
            return Equals((Test)obj); // recall Equals(T)
        }              
        
        public static bool operator ==(Test x, Test y)
        {
            return x.Equals(y);
        }

        public static bool operator !=(Test x, Test y)
        {
            return !x.Equals(y);
        }
    }

}

Without GetHashCode

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace TestHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Test() { value = 10 };
            var b = new Test() { value = 10 };

            var hs = new HashSet<Test>();
            hs.Add(a);
            // element b will added cause GetHashCode returns different values
            // for different objects as default
hs.Add(b); 
            
            Debug.Assert(hs.Count == 2);

            var dict = new Dictionary<Test, int>();
            dict.Add(a, a.value);
            Debug.Assert(!dict.ContainsKey(b));            
        }
    }

    public class Test : IEquatable<Test>
    {
        public int value { get; set; }        
        
        public bool Equals(Test other)
        {
            return value == other.value;
        }
        
        public override bool Equals(object obj)
        {
            return Equals((Test)obj); // recall Equals(T)
        }              
        
        public static bool operator ==(Test x, Test y)
        {
            return x.Equals(y);
        }

        public static bool operator !=(Test x, Test y)
        {
            return !x.Equals(y);
        }
    }

}

With GetHashCode

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace TestHashCode
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Test() { value = 10 };
            var b = new Test() { value = 10 };

            var hs = new HashSet<Test>();
            hs.Add(a);
            // element b will not added despite its different instance
hs.Add(b); Debug.Assert(hs.Count == 1); var dict = new Dictionary<Test, int>(); dict.Add(a, a.value); Debug.Assert(dict.ContainsKey(b)); } } public class Test : IEquatable<Test> { public int value { get; set; } public bool Equals(Test other) { return value == other.value; } public override bool Equals(object obj) { return Equals((Test)obj); // recall Equals(T) } public static bool operator ==(Test x, Test y) { return x.Equals(y); } public static bool operator !=(Test x, Test y) { return !x.Equals(y); } public override int GetHashCode() { return value; } } }

Reference material


Creative Commons License
C# Objects Equality by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.

Monday, May 25, 2015

Convert index to letter excel like

Follow extension methods allow to convert a number to a string ( mostly used to refer a spreadsheet column name ) and back the string to column index ( first column index = 0 ).


#region Lib0.Spreadsheet, Copyright(C) 2015 Lorenzo Delana, License under Ms-PL
/*
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://www.microsoft.com/en-us/openness/licenses.aspx for details.
 * 
 * Author : Lorenzo Delana <user@searchathing.com>
 * 
 * Contributors :
*/
#endregion

using System.Text;

namespace Lib0.Spreadsheet
{

    public static class Ext
    {

        static readonly string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        /// <summary>
        /// Convert spreadsheet column string to index (first column index = 0).        
        /// Pre: value must non empty uppercase letters.
        /// </summary>                
        public static int LetterToIndex(this string value)
        {
            var sb = new StringBuilder();

            return LetterToIndex(value, 0, sb);
        }

        /// <summary>
        /// LetterToIndex (recursive body).
        /// </summary>        
        static int LetterToIndex(this string value, int curIdx, StringBuilder sb)
        {
            var res = 0;

            int pow = value.Length - curIdx - 1;
            int charpos = value[curIdx] - Alphabet[0];

            if (pow > 0)
            {
                var x = 1; do { x *= 26; --pow; } while (pow != 0);
                res += x * (charpos + 1);

                res += LetterToIndex(value, curIdx + 1, sb);
            }
            else
                res += charpos;

            return res;
        }

        /// <summary>
        /// Convert index to spreadsheet column (first column index = 0).
        /// </summary>        
        public static string IndexToLetter(this int index)
        {
            if (index < 0) return string.Empty;

            var sb = new StringBuilder();

            IndexToLetter(index, sb);

            return sb.ToString();
        }

        /// <summary>
        /// IndexToLetter (recursive body).
        /// </summary>        
        public static void IndexToLetter(this int index, StringBuilder sb)
        {
            do
            {
                var x = index / 26;
                var r = index % 26;

                if (x > 0)
                    IndexToLetter(x - 1, sb);
                else
                    sb.Append(Alphabet[r]);

                if (x == 0) break;

                index -= 26 * x;
            }
            while (index >= 0);
        }

    }

}


Timing considerations

Pow using double

test total time : 00:00:06.9890933


Pow using integer

test total time : 00:00:04.7791627

Considerations about the Math.Pow


  • test timing difference total : about 2 seconds
  • timing difference from the inclusive samples 2118 (using Math.Pow) vs 205 (using custom integer pow)

Why int Math.Pow(int, int) extension method cannot exists

I think, but not sure, that the reason why Math.Pow not have an overloaded method is cause :
  • power of number using integers can easily run overflow
  • introduce such method now will results in an inconsistent behavior of the current code cause the current method upcast integer arguments to double, so that I could have :
    • Math.Pow(20, 100) returns about 1.25e130
    • Math.Pow(20, 100) return 0
A solution to avoid conflict could be to add an Math.IPow(int,int) using different name or create your own extension.


Creative Commons License
Convert index to letter excel like by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.

Sunday, May 24, 2015

Banana-pi encrypted sata disk bootstrap

Follow some annotation how to bootstrap banana-pi with encrypted sata disk.

Update: an extended guide is available here including notes on how to configure firewall, email server, etc.

Prerequisites

  • banana-pi
  • power supply usb micro 5V 2A
  • banana-pi dedicated sata cable
  • hdd 2.5" sata
  • sd card (at least 2gb)
  • hdmi cable (for initial setup)
  • usb keyboard (for decryption password at bootstrap)

Preamble

Pro

  • entire operating system ( except boot that runs from sd card ) is encrypted

Cons

  • sata write speed test shows a slow down from ~40mb/s to ~15mb/s
  • dual threaded kernel worker at 90% during write operation ( this is normal then testing a 500mb continuous file writing )

Cautions

  • backup all your data present in the sata disk before to proceed, encrypting whole disk with this method imply partitioning and formatting

Overview

SD card partition layout

  • /dev/mmcblk0p1 (boot partition ~20mb)
  • /dev/mmcblk0p2 (sd card root partition ~1.8gb)

SATA disk partition layout

  • /dev/sda1 (encrypted root partition)

Bootstrap path

  • /dev/mmcblk0p1 ( uImage [linux kernel] then uInitrd [initial ramdisk] )
  • /dev/sda1 (root filesystem)

Steps

Install operating system in the sd card

Install needed packages

  • install cryptsetup using apt-get install cryptsetup
  • install initial ramdisk util with apt-get install initramfs-tools
  • install Uimage tool apt-get install uboot-mkimage

Install operating system in the sata disk

  • boot from sd card

Create partition

  • fdisk /dev/sda and create 1 primary partition for entire disk

Encrypt partition and create filesystem

Setup crypt table and create initial ramdisk

  • edit /etc/crypttab inserting follow cryptroot  /dev/sda1  none  luks spacing fields using the tab key
  • edit /etc/fstab inserting follow /dev/mapper/cryptroot / ext4 defaults 1 2

Mount encrypted partition and copy operating system

  • mount the unencrypted filesystem using mkdir /x ; mount /dev/mapper/cryptroot /x
  • copy the operating system from the sd card to the sata disk using rsync -arx --exclude="/x*" / /x

Create initial ramdisk and set uInitrd image

  • mount the sd card p0 partition with mkdir /b ; mount /dev/mmcblk0p1 /b
  • create initial ramdisk with mkinitramfs -o /b/initrd
  • create uInitrd image with cd /b ; mkimage -A arm -O linux -T ramdisk -C gzip -a 0 -e 0 -d initrd uInitrd

Adjusting kernel parameters

  • edit /b/uEnv.txt as follow
bootargs=console=ttyS0,115200 console=tty0 sunxi_g2d_mem_reserve=0 sunxi_ve_mem_reserve=0 disp.screen0_output_mode=EDID:1280x720p50 hdmi.audio=EDID:0 console=tty1 cryptdevice=/dev/sda1:cryptroot root=/dev/mapper/cryptroot rootfstype=ext4 elevator=deadline raid=noautodetect rootwait
aload_script=fatload mmc 0 0x43000000 script.bin;
aload_kernel=fatload mmc 0 0x48000000 uImage; fatload mmc 0 0x49000000 uInitrd; bootm 0x48000000 0x49000000;
uenvcmd=run aload_script aload_kernel

Reboot

  • umount /b ; sync ; reboot

Notes

  • the initial ramdisk is a gzipped archive ( you can see the content by follow mkdir /tmp/initrd ; cd /tmp/initrd ; gunzip -c /b/initrd| cpio -i ; ls ). This special post-kernel image contains some routines for bootstrap that will starts withing the init script ( see /tmp/initrd/init ).

Bananian 15.08 ( Jessie )

Replace above sections with follow if using 15.08 bananian.

Install needed packages

  • apt-get install cryptsetup initramfs-tools u-boot-tools

Create initial ramdisk and set uInitrd image

  • mount the sd card p0 partition with mkdir /b ; mount /dev/mmcblk0p1 /b
  • create initial ramdisk with mkinitramfs -o /b/initrd
  • create uInitrd image with cd /b ; mkimage -A arm -T ramdisk -C none -n uInitrd -d initrd uInitrd

Adjusting kernel parameters

  • replace /b/boot.cmd contents with follow
setenv bootargs console=ttyS0,115200 console=tty0 console=tty1 sunxi_g2d_mem_reserve=0 sunxi_ve_mem_reserve=0 hdmi.audio=EDID:0 disp.screen0_output_mode=EDID:1680x1050p60 cryptdevice=/dev/sda1:cryptroot root=/dev/mapper/cryptroot rootfstype=ext4 elevator=deadline rootwait
setenv bootm_boot_mode sec
fatload mmc 0 0x43000000 script.bin
fatload mmc 0 0x41000000 uImage
fatload mmc 0 0x50000000 uInitrd
bootm 0x41000000 0x50000000
  • compile boot.cmd to boot.scr with mkimage -C none -A arm -T script -d boot.cmd boot.scr



Creative Commons License
Banana-pi encrypted sata disk bootstrap by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.