☆ドライバのソースを変更した場合、バイナリの何処が変わるか調べてみました

前回の ☆バイナリファイルを書き換えてUSB無線LANクライアントを認識させる は、depmodが調べているセクション、シンボルをとりあえず書き換えてみただけです。
この2箇所の書き換えは、必要ではありますが、必要十分であるかは不明です。
本当はUSB無線LANクラアントのカーネルモジュールが備えるべきインターフェースの仕様がわかればよいのですが、検索してもそのものズバリは見つけられませんでした。

2010/06/01 追記
USB_DEVICEマクロを使ってIdVendor,IdProductの指定をしている
("__mod_usb_device_table"のmatch_flagsが0x03)
場合は前回の方法で問題ないとおもわれますが、match_flagsの値がそれ以外の場合は十分でないようです。
hitoさんによる此方の投稿を参照してください。
https://forums.ubuntulinux.jp/viewtopic.php?pid=60947#p60947

そこでカーネルモジュールのソースの箇所だけUSBデバイス認識用の"idProduct"を書き換え、ビルドしてバイナリファイルの何処が変わったか比較してみることにしました。
RT2870_LinuxSTA_V2.3.0.0.tar.tar.bz2を使用して検証します。(これは私の環境で正攻法で動かなかったものなので、材料としては若干不安なのですが。)
結果を先に書くと、前回のバイナリ書き換えはおそらく正解(必要十分)であろうと思われます。

比較用のモジュールをビルドする際は同一のパスでビルドする方が良いです。
".rodata.str1.4"にはパスを保持している箇所があって、比較の際にそこが差分として出てくるとかなり面倒です。
パスの長さが違うとファイルサイズやシンボルのファイル先頭からのオフセットも違ってくる可能性がありそうです。

●ソースコードの変更箇所(および自動的に変更された箇所)

■common/rtusb_dev_id.c

下記がソースを手で変更する唯一の箇所です。

--- a/common/rtusb_dev_id.c 2009-11-26 15:22:40.000000000 +0900
+++ b/common/rtusb_dev_id.c 2010-05-30 21:14:50.800580320 +0900
@@ -59,7 +59,7 @@
  {USB_DEVICE(0x0DF6,0x002D)}, /* Sitecom */
  {USB_DEVICE(0x14B2,0x3C06)}, /* Conceptronic */
  {USB_DEVICE(0x14B2,0x3C28)}, /* Conceptronic */
- {USB_DEVICE(0x2019,0xED06)}, /* Planex Communications, Inc. */
+ {USB_DEVICE(0x2019,0xED14)}, /* Planex Communications, Inc. */
  {USB_DEVICE(0x07D1,0x3C09)}, /* D-Link */
  {USB_DEVICE(0x07D1,0x3C11)}, /* D-Link */
  {USB_DEVICE(0x14B2,0x3C07)}, /* AL */

■os/linux/rt2870sta.mod.c

RT2870_LinuxSTA_V2.3.0.0.tar.tar.bz2では自動で変更されます。
MODULE_INFOの部分が変わるのは実施前に想定していませんでした。

--- a/os/linux/rt2870sta.mod.c 2010-05-30 21:17:10.616580922 +0900
+++ b/os/linux/rt2870sta.mod.c 2010-05-30 21:19:10.388580922 +0900
@@ -151,7 +151,7 @@
 MODULE_ALIAS("usb:v0DF6p002Dd*dc*dsc*dp*ic*isc*ip*");
 MODULE_ALIAS("usb:v14B2p3C06d*dc*dsc*dp*ic*isc*ip*");
 MODULE_ALIAS("usb:v14B2p3C28d*dc*dsc*dp*ic*isc*ip*");
-MODULE_ALIAS("usb:v2019pED06d*dc*dsc*dp*ic*isc*ip*");
+MODULE_ALIAS("usb:v2019pED14d*dc*dsc*dp*ic*isc*ip*");
 MODULE_ALIAS("usb:v07D1p3C09d*dc*dsc*dp*ic*isc*ip*");
 MODULE_ALIAS("usb:v07D1p3C11d*dc*dsc*dp*ic*isc*ip*");
 MODULE_ALIAS("usb:v14B2p3C07d*dc*dsc*dp*ic*isc*ip*");
@@ -198,4 +198,4 @@
 MODULE_ALIAS("usb:v100Dp9031d*dc*dsc*dp*ic*isc*ip*");
 MODULE_ALIAS("usb:v0DB0p6899d*dc*dsc*dp*ic*isc*ip*");
 
-MODULE_INFO(srcversion, "BFE72125F5BC16053AC2BDE");
+MODULE_INFO(srcversion, "F9CA397B948AAF46EC1F3E8");

●バイナリが変わった箇所

"cmp -l"を使用してカーネルモジュールが変わった位置を調べました。
出力は下記の通りでした。

"cmp -l"はreadelfやodと違いファイルの先頭バイトの位置が"1"になっているので、"cmp -l"で出た差分をodで調べる際はオフセットの値に注意が必要です。
値は8進数でダンプされます。

そのままでは見にくいので、連続する領域の先頭アドレスと長さ、該当するセクションに変換すると下記の通りです。

start   length  Section
    68  20      .note.gnu.build-i
486964   1      .rodata.str1.1
491211  23      .modinfo
494162   2      .modinfo
520852   1      .data

cmp -l の出力
69  53  51
    70 324 343
    71 114 131
    72 302 122
    73 262 135
    74 153 213
    75 225 136
    76 260 221
    77 334 157
    78 243  75
    79 213 135
    80 141 361
    81 154 145
    82  17 350
    83  52 224
    84   6 232
    85 147 104
    86 342  21
    87 215 246
    88 102 253
486965  67  71
491212 102 106
491213 106  71
491214 105 103
491215  67 101
491216  62  63
491217  61  71
491218  62  67
491219  65 102
491220 106  71
491221  65  64
491222 102  70
491223 103 101
491224  61 101
491225  66 106
491226  60  64
491227  65  66
491228  63 105
491229 101 103
491230 103  61
491231  62 106
491232 102  63
491233 104 105
491234 105  70
494163  60  61
494164  66  64
520853   6  24

■ソース修正が反映されるバイナリの箇所
▼".modinfo"セクションのalias

"elfdump -p '.modinfo'"の出力の差分だけ示します。(-uをつけないdiff)
srcversionの変更は予想していなかったものですが、ソースの変更がそのまま反映されています。
下記の2箇所に相当します。

start   length  Section
491211  23      .modinfo
494162   2      .modinfo

9c9
<   [    e0]  srcversion=BFE72125F5BC16053AC2BDE
---
>   [    e0]  srcversion=F9CA397B948AAF46EC1F3E8
55c55
<   [   c60]  alias=usb:v2019pED06d*dc*dsc*dp*ic*isc*ip*
---
>   [   c60]  alias=usb:v2019pED14d*dc*dsc*dp*ic*isc*ip*

▼".data"セクションの"__mod_usb_device_table"シンボル

前回と同じ要領で"__mod_usb_device_table"シンボルをダンプした結果の差分だけ示します。
下記の箇所に相当します。

start   length  Section
520852   1      .data

21c21
< 0520848 0003 2019 ed06 0000 0000 0000 0000 0000 0000 0000
---
> 0520848 0003 2019 ed14 0000 0000 0000 0000 0000 0000 0000

■変わると想定していなかったが変わったバイナリの箇所
▼".note.gnu.build-i"セクション
start   length  Section
    68  20      .note.gnu.build-i

このセクションは こちらに よると
"ファイルを識別する為のユニークなビットフィールド"が埋め込まれているようです。
20Byteの長さにわたって差分が出たので160-bit SHA1 hash だと思われます。
ひょっとしたら改ざんを検出するためにこの部分をチェックするシステムも存在するのかもしれませんが、
前回のエントリで動いていたのでUbuntuはチェックしていないと思われます。

▼".rodata.str1.1"セクションのビルドした時刻
start   length  Section
486964   1      .rodata.str1.1

"readelf -p '.rodata.str1.1'"でセクションの内容を見てみると、主にメッセージに使用される文字列などが格納されているようです。
変更前後のセクションのダンプを比較すると下記の様になっていました。
ビルドしたタイムスタンプが格納されている部分で違っています。
ここはバイナリを直接書き換える際に修正しなくても問題なさそうです。
@@ -968,7 +968,7 @@
 
   [  246b]  ConnStatus is not connected
 
-  [  2488]  21:17:00
+  [  2488]  21:19:00
   [  2491]  May 30 2010
   [  249d]  2.3.0.0
   [  24a5]  Driver version-%s, %s %s

☆バイナリファイルを書き換えてUSB無線LANクライアントを認識させる

このエントリに記載している方法は危険かもしれません。実施される方は自己責任で行ってください。

2010/06/01 追記
USB_DEVICEマクロを使ってIdVendor,IdProductの指定をしている
("__mod_usb_device_table"のmatch_flagsが0x03)
場合はこの方法で問題ないとおもわれますが、match_flagsの値がそれ以外の場合は十分でないようです。
hitoさんによる此方の投稿を参照してください。
https://forums.ubuntulinux.jp/viewtopic.php?pid=60947#p60947

Ubuntuでインストール後にすぐ使用できるコマンドだけでカーネルモジュールを直接書き換えて
USB無線LANクライアントを認識させる方法を下記に記述します。
(readelf,od,strings,perlおよびテキストエディタを使用)

成功例が一つ(rt2870sta)だけであるため、別のモジュールで有効であるか不明ですが、
どうしても有線LANの接続が確保できない場合やビルド環境の導入が困難である場合に
試してみる価値はあると思います。

使用するUSB無線LANクラアント(GW-USMicroN)がLucid,Karmicで
デフォルトで認識されてしまうので例はJauntyで実施しました。
カーネルは2.6.28-11-generic。GW-USMicroNのIDは2019:ed14です。
これだけでは心もとないので、Lucidで2019:ed14を取り除いたモジュールをビルドし、同じ要領を実施しました。
これも認識、接続に成功しています。
実はRalinkのドライバ(RT2870_LinuxSTA_V2.3.0.0.tar.tar.bz2)で試そうと思ったのですが、なぜかソースに2019:ed14を追加してビルドした場合でもGW-USMicroNで接続が出来ませんでした。(Lucid,Karmicとも)

前提条件は対象のUSB無線LANクライアントが使用しているチップを駆動するカーネルモジュールが既知で
あること。(そしてデフォルトでモジュールが存在するが、子機が認識されないこと。)
USB無線LAN子機のidVendor,idProductのペアがmodules.alias,modules.usbmapに
存在しない場合、下記の方法で接続できる可能性があります。

目次

●1.前置き(きっかけ)

●2.readelf,od,stringsで収集する情報

●3.書き換え作業

●4.モジュールの認識



●1.前置き(きっかけ)

きっかけはdepmodコマンドで更新される[/lib/modules/$(uname-r)/]のmodules.* にlsbusbで出力されるidVendor,idProductと同じ値を見つけたことに始まります。

当該の値が含まれているファイルはmodules.aliasとmodules.usbmapで下記のようなものでした。

■modules.aliasの内容
(先頭行と一部抜粋)
# Aliases extracted from modules themselves.
alias rt3070sta rt2870sta
alias usb:v0411p015Dd*dc*dsc*dp*ic*isc*ip* rt2870sta
alias usb:v1737p0077d*dc*dsc*dp*ic*isc*ip* rt2870sta
■modules.usbmapの内容
(先頭行と一部抜粋)
# usb module         match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info
rt2870sta            0x0003      0x148f   0x2770    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0
rt2870sta            0x0003      0x1737   0x0071    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0
rt2870sta            0x0003      0x1737   0x0070    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0
rt2870sta            0x0003      0x148f   0x2870    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0

これらの値はそれぞれdepmodコマンドがモジュールのファイルから読み出したものです。
depmodのソースから調べてみると下記の部分から取得していました。

■modules.aliasの元情報

モジュールの ".modinfo" セクションを元に作成されています。

$ readelf -p .modinfo モジュールファイルのパス
で簡単に見ることができます。

モジュールのソースにおていこの情報は
linux/module.hで定義されているMODULE_ALIASマクロを使って宣言されているようです。

#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)

#define MODULE_DEVICE_TABLE(type,name)  \
  MODULE_GENERIC_TABLE(type##_device,name)

■modules.usbmapの元情報

モジュールの"__mod_usb_device_table"シンボルに格納されています。
バイナリなので容易には見ることができません。
depmodを含むmodule-init-toolsのソースパッケージではtables.hで
その構造とサイズを定義しています。
(usb_device_id構造体、サイズはUSB_DEVICE_SIZE32,USB_DEVICE_SIZE64)

モジュールのソースにおていこの情報は
linux/usb.hで定義されている下記のマクロを使って宣言されているようです。

#define USB_DEVICE(vend,prod) \
 .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
 .idVendor = (vend), \
 .idProduct = (prod)

Ralinkのドライバ(RT2870_LinuxSTA_V2.3.0.0.tar.tar.bz2)では
USB_DEVICEを使用しているソースからMODULE_ALIASを使用するソースを
自動で生成しているようです。

●2.readelf,od,stringsで収集する情報

書き換え対象のidVendor,idProductとそのモジュール上の位置を知る必要があります。 ここでは"__mod_usb_device_table"シンボルの先頭のIDを対象にします。 そして対応する".modinfo"のエントリのオフセットを探します。

■モジュールの退避、バックアップの作成

対象モジュールを作業ディレクトリにコピーし、念のためバックアップを作っておきます。

$ sudo cp -p /lib/modules/$(uname -r)/kernel/drivers/staging/rt2870/rt2870sta.ko ./
$ TRG_MOD=./rt2870sta.ko
$ sudo cp -p $TRG_MOD ${TRG_MOD}.org
■"__mod_usb_device_table"の最初のエントリの位置と値の取得

まずreadelfコマンドで"__mod_usb_device_table"シンボルがが格納されている
セクションとその先頭からのオフセット、サイズを取得します。
コマンドの実行結果から25番目のセクションに格納されていて、
先頭からのオフセットが16進で0x4140、サイズが1220Byteであることが分かります。
(先頭だけを対象にするのでサイズは特に必要ありません。)

$ TRG_MOD=/lib/modules/2.6.28-11-generic/kernel/drivers/staging/rt2870/rt2870sta.ko
$ readelf -s $TRG_MOD | sed -ne '1,3p;/__mod_usb_device_table/p;'

Symbol table '.symtab' contains 1267 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
   555: 00004140  1220 OBJECT  GLOBAL DEFAULT   25 __mod_usb_device_table

25番目のセクションを探します。".data"セクションです。
ファイルの先頭から16進で0x6c980で始まっていることが分かります。
(別のモジュールで試してみると番号[Nr]はモジュールによって違いますが、
セクション名は".data"でした。基本的に".data"セクションを探せばよさそうです。)

$ readelf -S $TRG_MOD | sed -ne '1,4p;/\[25\]/p;'
There are 35 section headers, starting at offset 0x73830:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [25] .data             PROGBITS        00000000 06c980 004694 00  WA  0   0 32

開始アドレスを計算してodコマンドで内容を表示します。
1レコードが20Byteで先頭5項目はunsigned shortであることが分かっているので
(depmodのソースtable.h参照)
odのオプションに -N 20 --width=20 -t x2 を指定します。

ここで -t x1 とすると、x86はリトルエンディアンなのでunsigned shortの
上位バイトと下位バイトが逆になって表示されます。
出力から書き換え箇所はファイル先頭からのオフセットが十進で(461504 + 2) Byte
そこからunsigned shortを2つ分(計4byte)を書き換え対象にします。

$ perl -e 'print 0x4140 + 0x6c980 ,"\n"'
461504
$ od -j 461504 -N 20 -v -A d -t x2 --width=20 $TRG_MOD
0461504 0003 148f 2770 0000 0000 0000 0000 0000 0000 0000
■modules.aliasの元になる情報の書き換え位置

次にmodules.aliasの元になる情報の書き換え位置を調べます。
これは文字列なのでstringsコマンドに-tdオプションを指定することで
比較的簡単に調べることができます。
十進でファイルの先頭からのオフセット438048Byte,長さ20Byteを書き換えます。
("alias=usb:v148Fp2770" を "alias=usb:v2019pED14"へ)

$ strings -td $TRG_MOD | grep -i -e 148f.2770
 438048 alias=usb:v148Fp2770d*dc*dsc*dp*ic*isc*ip*

●3.書き換え作業

perlを使って書き換えを行ないます。

■"__mod_usb_device_table"の先頭書き換え

write_vp.plというスクリプトを書きました。
ネット接続不可の状況でも、その場ですぐにかけるような内容です。
書換え後のidVendor,idProductは直書きです。
引数は対象ファイルとオフセットです。

### スクリプト内容の表示
$ cat ./write_vp.pl
#!/usr/bin/perl

if($#ARGV != 1){
    print STDERR "usage $0 filepath offset\n";
}

my($file) = shift;
my($offset) = shift;

open FH, "+< " . $file or die "$!:$file";
seek(FH, $offset, 0);
my($buf) = pack "SS", 0x2019, 0xed14;
syswrite(FH, $buf, 4) or die "$!";

### 実行
$ sudo ./write_vp.pl $TRG_MOD 461506
### 確認
$ od -j 461504 -N 20 -v -A d -t x2 --width=20 $TRG_MOD
0461504 0003 2019 ed14 0000 0000 0000 0000 0000 0000 0000
■".modinfo"セクションの書き換え

write_alias.plというスクリプトを書きました。
write_vp.plと同じく短いものです。

### スクリプト内容の表示
$ cat ./write_alias.pl
#!/usr/bin/perl

if($#ARGV != 1){
    print STDERR "usage $0 filepath offset\n";
}

my($file) = shift;
my($offset) = shift;

open FH, "+< " . $file or die "$!:$file";
seek(FH, $offset, 0);
syswrite(FH,"alias=usb:v2019pED14",20);
seek(FH, $offset, 0);
sysread(FH,$buf,20);
print $buf,"\n";

### 実行
$ sudo ./write_alias.pl $TRG_MOD 438048
alias=usb:v2019pED14
### 確認
$ strings -td $TRG_MOD | grep -e 2019.ED14
 438048 alias=usb:v2019pED14d*dc*dsc*dp*ic*isc*ip*

●4.モジュールの認識

モジュールを正式な位置に書き戻してdepmodで依存情報を更新し、modprobeでモジュールを読み込みます。

$ sudo cp $TRG_MOD /lib/modules/$(uname -r)/kernel/drivers/staging/rt2870/
$ sudo depmod -a $(uname -r)
$ sudo modprobe rt2870sta

あとはNetworkManagerで認証関係の設定を行えば接続できるはずです。

☆HDDに置いたISOからブートしてUbuntuをインストールする

このエントリに記載している方法は危険かもしれません。実施される方は自己責任で行ってください。

vine_userさんのサイトでHDDに保存したISOイメージをGRUB2で直接起動する方法《Ubuntu 9.10対応》を見ました。
これはとても快適です。

調子に乗って、この方法をつかってブートし、UbiquityでLucidのインストールを試みたのですが、
実際にインストールを開始する段階の最初でアンマウントできない旨のメッセージが出てインストールできません。

悔しいので、気合を入れてUbiquityのソースを見てみたのですが、結構複雑で追いかけるのはかなりきついです。
基本的にPythonなのですが、GUIがある上に裏でシェルを使っていたりします。
そこで、エラーメッセージが出るところまで進めておいて、pstree -alp でそのときに走っているプロセスと
そのオプションを見てみました。関係ありそうな部分は下記です。

pstree -alp の一部分
|-gksudo,3123 --preserve-env --desktop /home/ubuntu/Desktop/ubiquity-gtkui.desktop -- /usr/bin/ubiquity gtk_ui
  |   `-udisks,3125 --inhibit -- /usr/lib/ubiquity/bin/ubiquity gtk_ui
  |       `-ubiquity,3128 /usr/lib/ubiquity/bin/ubiquity gtk_ui
  |           |-debconf-communi,4062 -w /usr/bin/debconf-communicate -fnoninteractive ubiquity
  |           |-ibus-daemon,4041 --xim
  |           |   |-ibus-gconf,4055
  |           |   |-python,4057 /usr/share/ibus/ui/gtk/main.py
  |           |   |-python,4060 /usr/share/ibus-anthy/engine/main.py --ibus
  |           |   `-{ibus-daemon},4056
  |           |-log-output,11685 -t ubiquity --pass-stdout /bin/partman-commit
  |           |   `-partman-commit,11686 /bin/partman-commit
  |           |       `-01unmount_busy,12462 /lib/partman/commit.d/01unmount_busy
  |           `-{ubiquity},11682

名前からして怪しいのが"/lib/partman/commit.d/01unmount_busy"です。
見てみると、確かにumountを実行しています。
GUIのエラーメッセージもここから出しているようで、そのおかげでpstreeの出力にも出てくれたみたいです。
debconfの機能を使ってumoutを実行するかどうかを調べているようです。
debconf経由でubiquity/partman-skip-unmountをtrueにするのがスマートなのでしょうが、
やりかたがわからなかったので下のコードのように処理を始める前に"exit 0"で正常終了するようにしました。
この修正を実施してからインストールを開始すればISOイメージとは別のパーティションにUbuntuをインストールできるようになります。
下図のダイアログが出たら、[Ubuntu 10.04 を試す]のボタンを選択していったんGnomeのデスクトップ環境を表示されて修正を行います。

立ち上がった直後はキーボードレイアウトに英語配列になっているので"Setting"->"Preferences"->"Keybord"で設定を日本語配列にしてから編集したほうが良いでしょう。

"/lib/partman/commit.d/01unmount_busy" の修正内容(わざわざdiffを表示するほどのものではないですが)
@@ -1,13 +1,13 @@
 #! /bin/sh
 
 # This should largely be unnecessary now that partman-base/init.d/parted
 # checks for mounted partitions at startup, but it may still serve as
 # insurance against partitions being mounted while the partitioner is
 # running.
-
+exit 0
 . /lib/partman/lib/base.sh
 
 db_get ubiquity/partman-skip-unmount
 if [ "$RET" = true ]; then
     exit 0
 fi

☆GmrunのキーバインドをBashに少しだけ近づける

私はGmrunが大好きです。
Mihai Bazonさん、すばらしいソフトウェアを作ってくれてありがとうございます。
さて、前回の「☆gmrun で Ctrl+PをUp、Ctrl+NをDownキーと同じように動作させる」に引き続き、
2つだけBashのキーバインドを付け加えてみました。
Ctrl+K、Ctrl+Uです。文字列を選択した状態はBashではありえない状態なので、
どういう仕様にするか迷ったのですがコーディングが楽な使用にしました。
パッチはこれです。
適用の手順は下記の通りです。
/usr/bin/gmrunだけ圧縮したアーカイブも用意しました。gmrun_0.9.1-4_i386.msll-patched.20100505.tar.gz
開発環境を入れたくない方はapt-getでgmrunを導入した後、/usr/bin/gmrunだけ入れ替えればOKです。
(ビルドしたパッケージをdpkgで導入すると更新がなくてもupdate-managerで
更新対象としてリストされるので、自分でビルドした場合でも一旦apt-getでgmrunを導入してから、
/usr/bin/gmrunだけ入れ替える方が良いかもしれません。
ちゃんとパッケージした方法で作れば良いのでしょうが、方法がわからず...)

ソース、パッチの取得とビルド
$ mkdir gmrun
$ apt-get source gmrun
$ sudo apt-get build-dep gmrun
$ wget http://sites.google.com/site/midspeclowload/files/gmrun-0.9.1-4.msll-20100505.patch?attredirects=0&d=1
$ patch -p1 < gmrun-0.9.1-4.msll-20100505.patch
$ cd gmrun-0.9.1/
$ debuild -r
gmrun-0.9.1-4.msll-20100505.patch
--- a/gmrun-0.9.1/src/gtkcompletionline.cc	2003-06-22 08:14:34.000000000 +0900
+++ b/gmrun-0.9.1/src/gtkcompletionline.cc	2010-05-05 22:14:05.296882925 +0900
@@ -975,6 +975,34 @@
       STOP_PRESS;
       return TRUE;
 
+     case GDK_K:
+     case GDK_k:
+      if (event->state & GDK_CONTROL_MASK) {
+        int pos = gtk_editable_get_position(GTK_EDITABLE(cl));
+        int len = gtk_entry_get_text_length(GTK_ENTRY(cl));
+        gtk_editable_delete_text(GTK_EDITABLE(cl), pos, len);
+        gtk_editable_select_region(GTK_EDITABLE(cl), pos, pos);
+        if (MODE_SRC)
+          search_off(cl);
+        return TRUE;
+      } else goto ordinary;
+
+     case GDK_U:
+     case GDK_u:
+      if (event->state & GDK_CONTROL_MASK) {
+        int pos = gtk_editable_get_position(GTK_EDITABLE(cl));
+        gtk_editable_delete_text(GTK_EDITABLE(cl), 0, pos);
+        gtk_editable_select_region(GTK_EDITABLE(cl), 0, 0);
+        if (MODE_SRC)
+          search_off(cl);
+        return TRUE;
+      } else goto ordinary;
+
+     case GDK_P:
+     case GDK_p:
+      if (event->state & GDK_CONTROL_MASK) {
+		;/* fall to 'case GDK_Up:'*/
+      } else goto ordinary;
      case GDK_Up:
       if (cl->win_compl != NULL) {
         int &item = cl->list_compl_items_where;
@@ -1010,6 +1038,11 @@
      }
      return FALSE;
 
+     case GDK_N:
+     case GDK_n:
+      if (event->state & GDK_CONTROL_MASK) {
+		;/* fall to 'case GDK_Down:'*/
+      } else goto ordinary;
      case GDK_Down:
       if (cl->win_compl != NULL) {
         int &item = cl->list_compl_items_where;

☆os-proberでsda10がsda2の前に出てくるのを直す

私はパーティションを制限いっぱいの16まで切っています。
これで困るのは、grub-updateで作成される/boot/grub/grub.cfgのmenuentryの順序です。
grub2のOS選択画面で/dev/sda2のUbuntuより先に/dev/sda10や/dev/sda11が先に表示されてしまいます。
30_os-proberのセクションではos-proberの出力順にしたがってmenuentryが作成されるので、
修正すべきはos-proberの出力順序です。

os-proberの中をのぞいてみるとpartitionsというシェル関数で"/sys/block/*/*[0-9]"という記述があります。
シェルのグロビングのソートは文字列の昇順なので、ここが原因だとわかります。
コマンドの性質上(障害時などは/usrがマウントされない可能性がある)、
スタティックリンクされた/bin,/sbinの下にあるバイナリだけを使用して処理を記述すべきです。
そうするとsortは使えません。もちろんawkやperlもダメです。
ちょっとしたパズル感覚です。

2010/05/22追記
スタティックリンクされたバイナリは/bin/busyboxと/sbin/ldconfig.realだけでした。
しかもbusyboxにはawkやsortも組み込まれています....

幸いsh(dash)のビルトインにprintfがあったので、sed,printf,シェルのグロビングのあわせ技でソートしてみました。
パッチはこれ os-prober.patch です。
tail_num_sortは/usr/share/os-prober/common.shに追加することも考えたのですが、 自分しか使わないと思ったのでos-proberに入れてしまいました。

しかし、Lucidになってからmenuentryの文字列が長すぎです。
OS選択画面でパーティションの部分が見えなくなってしまいました。
仕方がないので"e"を押してどのパーティションか確認してからCtrl+xで起動しています。

修正前のos-proberの出力
/dev/sda10:Ubuntu 10.04 LTS (10.04):Ubuntu:linux
/dev/sda11:Ubuntu 10.04 LTS (10.04):Ubuntu1:linux
/dev/sda2:Ubuntu 9.10 (9.10):Ubuntu2:linux
/dev/sda3:Ubuntu 9.10 (9.10):Ubuntu3:linux
/dev/sda5:Ubuntu 10.04 LTS (10.04):Ubuntu4:linux
/dev/sda6:Ubuntu 10.04 LTS (10.04):Ubuntu5:linux
/dev/sda7:Ubuntu lucid (development branch) (10.04):Ubuntu6:linux
/dev/sda8:Ubuntu 10.04 LTS (10.04):Ubuntu7:linux
/dev/sda9:Ubuntu 10.04 LTS (10.04):Ubuntu8:linux
修正後のos-proberの出力
/dev/sda2:Ubuntu 9.10 (9.10):Ubuntu:linux
/dev/sda3:Ubuntu 9.10 (9.10):Ubuntu1:linux
/dev/sda5:Ubuntu 10.04 LTS (10.04):Ubuntu2:linux
/dev/sda6:Ubuntu 10.04 LTS (10.04):Ubuntu3:linux
/dev/sda7:Ubuntu lucid (development branch) (10.04):Ubuntu4:linux
/dev/sda8:Ubuntu 10.04 LTS (10.04):Ubuntu5:linux
/dev/sda9:Ubuntu 10.04 LTS (10.04):Ubuntu6:linux
/dev/sda10:Ubuntu 10.04 LTS (10.04):Ubuntu7:linux
/dev/sda11:Ubuntu 10.04 LTS (10.04):Ubuntu8:linux
os-prober.patch
diff -ru a/os-prober b/os-prober
--- a/os-prober 2010-02-11 08:06:58.000000000 +0900
+++ b/os-prober 2010-05-04 21:45:47.261264519 +0900
@@ -24,12 +24,32 @@
  return 1
 }
 
+tail_num_sort() {
+ if [ $# -eq 0 ] ; then
+  return 0
+ fi
+
+ if type mktemp >/dev/null 2>&1; then
+  TAILNUMSORT_TMP="$(mktemp -d /tmp/tailnumsort.XXXXXX)"
+ else
+  TAILNUMSORT_TMP=/tmp/tailnumsort.$$
+ fi
+ trap "rm -rf $TAILNUMSORT_TMP" EXIT HUP INT QUIT TERM
+
+ for dev in $@ ; do
+  disk=$(echo $dev | sed -e 's#/##g;s#[0-9]*[0-9]*$##')
+  fname=$(printf '%s%02d' $disk $(echo $dev | sed -e "s#/##g;s#$disk##"))
+  echo $dev > $TAILNUMSORT_TMP/$fname
+ done
+ cat $TAILNUMSORT_TMP/* 2>/dev/null
+}
+
 partitions () {
  # Exclude partitions that have whole_disk sysfs attribute set.
  if [ -d /sys/block ]; then
   # Exclude partitions on physical disks that are part of a
   # Serial ATA RAID disk.
-  for part in /sys/block/*/*[0-9]; do
+  for part in $(tail_num_sort /sys/block/*/*[0-9]); do
    if [ -f "$part/start" ] && \
       [ ! -f "$part/whole_disk" ] && ! on_sataraid $part; then
     name="$(echo "${part##*/}" | sed 's,[!.],/,g')"

☆fsarchiverでパーティションをコピーしてみました

5/2 追記
5/1 当初の手順だと不足がありました。
grub.cfgのsearchの行とlinuxの行でUUIDの齟齬が生じて
結局 linux行が優先され、/dev/sda8をルートとして起動してしまいます。
修正した手順の確認がとれたので修正済みのものを掲載しました。
修正した点は、実行していたにもかかわらず記載していなかった7.update-grubの実行と
本質的に欠けていた6.のところのgrub.cfgの編集です。

Ubuntu 10.04 Lucid Lynxが正式リリースされました。
本格的に弄るのは日本語Rimixが出てからのつもりですが、一応インストールしました。
インストールすれば弄りたくなるのが人情です。
弄っておかしくなった場合や、バグ報告のを考慮すると、あまり弄っていない状態をすぐに作れるようにしておくと便利です。
パーティション丸ごとのバックアップといえばddやpartimageがメジャーですが、ddは敷居が高く、partimageはext4に対応していません。
そこでfsarchiverを使用してバックアップを作成し、これを別パーティションに復元して弄ることにしました。
fsarchiverはLucidからuniverseのリポジトリに入りました。バージョンはまだ0.6.8ですが...
幸いパーティションはMaxの16まで切ってあるので、空きはいっぱいあります。
とりあえず、/dev/sda8にdesktop-i386をインストール。
その後、sda9,sda10,sda11にコピーをしました。
(バックアップからのリストアは1つあたり3分くらいだったので調子に乗って3つもコピーしてしまいました。)

手順の概略
1.とりあえず10.04を/dev/sda8へインストール
2./dev/sda1にインストールしてあったKarmicを起動
3.fsarchiverで/dev/sda8を丸ごとバックアップ
4.fsarchiverで別のパーティション(/dev/sda9)へリストア
5.tune2fsでリストアしたパーティション(/dev/sda9)のUUIDを変更
6.リストアしたパーティション(/dev/sda9)に入っている設定ファイルを修正
7.リストアしたパーティションを起動時に選択可能にするためupdate-grubを実行
8.リブートして/dev/sda9から起動、dpkg-reconfigureでgrub-pcのインストール先を修正

(私の環境では"/home"や"/var"を分けずに"/"の1パーティションだけで運用しています。 "/home"は個別のパーティションにする理由は設定ファイルをシステムごとに別にするためです。 データ用のパーティションは別に用意して$HOMEのしたからシンボリックリンクを張っています。)

以下で3以降の手順を細かく説明します。

3.fsarchiverで/dev/sda8を丸ごとバックアップ

bzip2 -2と同一の圧縮レベルで、圧縮用のスレッドを2つ使用してバックアップ

$ sudo fsarchiver -z5 -j2 savefs sda8.fsa /dev/sda8

4.fsarchiverで別のパーティション(/dev/sda9)へリストア

$ sudo fsarchiver restfs sda8.fsa id=0,dest=/dev/sda9

5.tune2fsでリストアしたパーティション(/dev/sda9)のUUIDを変更

リストアしたパーティションのUUIDはバックアップしたパーティションと同じになっているので変更します。 tune2fsのmanページで見ると-Uのあとにrandomやtimeをしてできそうなのですが、なぜかうまくいきません。 普通にuuidgenの出力を使うことにしました。

$ sudo tune2fs -U $(uuidgen) /dev/sda9
# fstabを書き換えるためにUUIDを確認
$ sudo blkid /dev/sda9
/dev/sda9: UUID="2bd68985-c6cf-4eb5-9c55-aaee3b173656" TYPE="ext4" 

6.リストアしたパーティション(/dev/sda9)に入っている設定ファイルを修正

fstabとgrub.cfgを修正します。
grub.cfgの修正が必要な理由は、update-grubの実行時に走る/etc/grub.d/30_os-proberの中で
/usr/lib/linux-boot-probes/40grub2 が実行されるのですが、これがリストアしたパーティションにある
grug.cfgを参照してるからです。

$ sudo mkdir /mnt/sda9
# fstabのUUIDを書き換える。ついでにduring installationのコメントも/dev/sda9に変更
$ sudo vi /mnt/sda9/etc/fstab
$ sudo vi /mnt/sda9/boot/grub.cfg
fstabの修正内容
@@ -7,4 +7,4 @@
 #                
 proc            /proc           proc    nodev,noexec,nosuid 0       0
 # / was on /dev/sda10 during installation
-UUID=32c50640-99e1-49c1-837f-05701e9e7def /               ext4    errors=remount-ro 0       1
+UUID=2bd68985-c6cf-4eb5-9c55-aaee3b173656 /               ext4    errors=remount-ro 0       1
grub.cfgの修正内容(書込みは:w!で強制書込みをします)
UUIDの書き換えが必要な箇所は"### BEGIN /etc/grub.d/10_linux ###"から"### END /etc/grub.d/10_linux ###"の間にある
"linux"始まる行です。(ほかの部分は8.のdpkg-reconfigureの時に直ります。)
--- sda10_grub.cfg.org 2010-05-02 20:28:18.581936565 +0900
+++ sda10_grub.cfg.new 2010-05-02 20:31:53.980286836 +0900
@@ -63,22 +63,22 @@
 ### BEGIN /etc/grub.d/10_linux ###
 menuentry 'Ubuntu, with Linux 2.6.32-21-generic' --class ubuntu --class gnu-linux --class gnu --class os {
    recordfail
    insmod ext2
    set root='(hd0,8)'
    search --no-floppy --fs-uuid --set 32c50640-99e1-49c1-837f-05701e9e7def
-   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=32c50640-99e1-49c1-837f-05701e9e7def ro   quiet splash
+   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=2bd68985-c6cf-4eb5-9c55-aaee3b173656 ro   quiet splash
    initrd  /boot/initrd.img-2.6.32-21-generic
 }
 menuentry 'Ubuntu, with Linux 2.6.32-21-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os {
    recordfail
    insmod ext2
    set root='(hd0,8)'
    search --no-floppy --fs-uuid --set 32c50640-99e1-49c1-837f-05701e9e7def
    echo    'Loading Linux 2.6.32-21-generic ...'
-   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=32c50640-99e1-49c1-837f-05701e9e7def ro single 
+   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=2bd68985-c6cf-4eb5-9c55-aaee3b173656 ro single 
    echo    'Loading initial ramdisk ...'
    initrd  /boot/initrd.img-2.6.32-21-generic
 }
 ### END /etc/grub.d/10_linux ###
 
 ### BEGIN /etc/grub.d/20_memtest86+ ###

7.リストアしたパーティションを起動時に選択可能にするためupdate-grubを実行

$ sudo update-grub

8.リブートして/dev/sda9から起動、dpkg-reconfigureでgrub-pcのインストール先を修正

$ sudo dpkg-reconfigure grub-pc
2つほど設定を訊かれますがデフォルトで進めると下記のような画面が出るので grubのインストール先を選択しなおします。