虎之助の徒然記

【Python】numpy 整数の演算結果が float64 に。摩訶不思議なデータ型変換ルール。

【概要】 numpyで扱っていた整数が、いつのまにか float64 の浮動小数になっていました。pyhonでは、被演算子のデータ型の組み合わせによっては、演算結果のデータ型が変更されます。動的型付けの影響です。調べてみると、この変換にはいろいろなパターンがあります。そして、整数の演算同士の演算でも、その結果が浮動小数となる摩訶不思議なデータ型の変換ルールがあることも分かりました。

1. はじめに

 GPTテーブルを読み取るPythonスクリプト(関連記事は、ここ)を書いていて、困ったのが、セクタ数をバイト数に変換する際、numpy.uint64 のつもりの演算結果が float64 の浮動小数になってしまったこと。これでは、ファイルの頭出し ( f.seek() ) ができません。

 とりあえずの解決策は見つけたのですが、動作は良く理解していないままでした。本稿では、uint64の演算がfloat64 へ変換されるケースについて、まず述べます。その後、pythonにおける演算結果のデータ型の割り付け規則についてまとめます。

 なお、本稿は、python3 での動作について述べています。python2 の場合は動作が異なります(付録B参照)。

2. uint64 の演算結果が、float64 になる

2.1 整数が浮動小数になって、f.seek できない

 numpyのfloat32のスカラー値 (numpy.float32) と ( numpy ではない ) 普通のスカラー値の整数 (<class 'int'>) や浮動小数 (<class 'float'>) との演算を行うと、自動的に numpy.float64 の浮動小数になるそうです*1

 このようなデータ型の変更は、numpy.float32に限らず、numpy.unit64の場合にも発生します。

 例えば、セクタ数 (sector=2)をバイト数 (byte=1024) に変換するつもりで以下のように演算すると、byte は numpy.float64 の変数になってしまいます。

>>> import numpy as np
>>>
>>> sector = np.array(2,'uint64')
>>> 
>>> sector
array(2, dtype=uint64)
>>> type(sector)
<class 'numpy.ndarray'>
>>> sector.dtype
dtype('uint64')

この uint64 の numpy の変数 sector を用いて、バイト数に変換して、ファイルを f.seek(sector*512) しようとすると、エラーとなります。

>>> f = open('tmpfile','rb')
>>> f.seek(sector*512)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'numpy.float64' object cannot be interpreted as an integer

 sector*512 は、numpy.float64 の変数になっているようです。

>>> byte = sector*512
>>> byte
1024.0
>>> type(byte)
<class 'numpy.float64'>
>>> byte.dtype
dtype('float64')

 float64 になった値をint() や astype() で整数に変換すればよいのかしれませんが、美しくありません。

2.2 float64 への昇格問題

 これは、型の昇格というpythonの動作によるものです。普通のスカラー値、numpy によるスカラー値 ( 0次元配列)、numpy の配列の3つの場合について、乗算をした結果は、次の表のようになります。

512
<class 'int'>
-
np.array(512,'uint64')
<class 'numpy.ndarray'>
'uint64'
np.array([512],'uint64')
<class 'numpy.ndarray'>
'uint64'
2
<class 'int'>
-
1024
<class 'int'>
-
1024.0
<class 'numpy.float64'>
'float64'
[1024]
<class 'numpy.ndarray'>
'uint64'
np.array(2,'uint64')
<class 'numpy.ndarray'>
'uint64'
1024.0
<class 'numpy.float64'>
'float64'
1024
<class 'numpy.uint64'>
'uint64'
[1024]
<class 'numpy.ndarray'>
'uint64'
np.array([2],'uint64')
<class 'numpy.ndarray'>
'uint64'
[1024]
<class 'numpy.ndarray'>
'uint64'
[1024]
<class 'numpy.ndarray'>
'uint64'
[1024]
<class 'numpy.ndarray'>
'uint64'

乗算結果の上段は、print()の結果、中段はtype()の結果、下段はdtype属性。

 この表から、次のことが分かります。

  • float64 に型変換されるのは、普通のスカラー値 (<class 'int') と numpy のスカラー値との間で演算が行われた場合のみ。
  • それ以外は、すべて整数型のまま。

 整数同士の演算結果が浮動小数に型変換されるのは、奇妙な仕様です。

3. データ型変換のルール

 前節で調べたのは、型の組合せの極一部です。主な整数型、浮動小数型について、プログラムを作って調べました(付録A参照)。

3.1 調べたデータ型

 調査対象は、普通のスカラー値の int ( <class 'int'>)、普通のスカラー値の float( <class 'float'>)、numpy の整数 ( 32ビット、64ビット、符号有無)、浮動小数(32ビット、64ビット )、及び、それらの配列の計14個の型です。

  • 普通のスカラー値のint (i)
  • 普通のスカラー値のfloat (f)
  • numpyのint32 (i4)、uint32 (u4) 、int64 (i8)、uint64 (u8)、及び、それらの配列
  • numpyのfloat32 (f4), float64(f8)、及び、それらの配列

3.2 データ型の変換テーブルと変換ルール

 これらの型の演算結果の組合せは、以下の図のテーブル(マトリックス)に示す通りです。[ ]の場合は、配列です。最上段、最左列がそれぞれの被演算子のデータ型、それぞれの被演算子と演算を行った結果をテーブルにしています。上右三角の部分と左下三角の部分は同じ結果となるので、省略しています。

3.2.1 加算・減算・乗算の場合のデータ型の変換テーブル

 演算は、加算・減算・乗算、いずれの場合も同じ結果です。除算は異なります。

f:id:toranosuke_blog:20180928224221p:plain
 14個のデータ型の演算結果のデータ型。

 i, f は、<class 'int'>、<class 'float'>、それ以外は numpy の型コードによる表記(例えば、i4 であれば int32、[i4] は int32 の配列)。赤は整数が浮動小数に変換される場合、青色は、型の昇格がある場合、オレンジは型の降格がある場合、灰色は符号が変わる場合。赤・青の細線は被演算子のいずれかが昇格・降格の場合、太線は被演算子の両方にとって昇格の場合。スカラー型の int、float は、それぞれ int64、float64 の精度として、昇格・降格を判定。

f:id:toranosuke_blog:20180928193435p:plain

 スカラー値、0次元配列(スカラー値相当)のみ。64ビットの符号なし整数 (uint64) と符号あり整数 (int, int32, int64) の時に64ビットの浮動小数 (float64) に変換される。

f:id:toranosuke_blog:20180928193453p:plain
配列のみ。

3.2.2 加算・減算・乗算の場合のデータ型の変換ルール

 この結果から次の変換ルールがあることが分かります。

  • 型の順位:float64 > float32 > int64, uint64 >int32, uint32
  • スカラー同士の演算
    • 同じ順位の型同士の演算で、型は変わらない。
    • float32、float64との演算 ⇒ float64
    • 符号なし整数と符号あり整数の演算は、昇格する。
      • uint32 と int32 ⇒ int64
      • uint64 とint, int32, int64 ⇒ float64
    • 型の順位が異なる場合は、順位が上位の型に合わせる
      • int32とint64 ⇒ int64
      • uint32とint64 ⇒ int64
      • uint32とuint64 ⇒ uint64
  • 配列とスカラーの演算
    • float32の配列とスカラーの演算 ⇒ float32の配列
    • float64の配列とスカラーの演算 ⇒ float64の配列
    • 整数配列とfloat32, float64の演算 ⇒ float64の配列
    • 整数配列と整数型スカラーの演算 ⇒ 入力配列の型と同じ型の配列
       スカラーの型順位が上位の場合、スカラーのデータ型が降格する。
  • 配列と配列の演算
    • スカラー同士の演算に準じる。

 動的型付けの演算では、何らかの変換ルールで、演算結果のデータ型を決める必要があります。uint64 と符号あり整数型の演算の場合を除けば、概ね理解できる変換ルールです。

 符号なし整数と符号ありの整数は昇格させるという基準で設定されている変換ルールと思いますが、だからと言って、昇格した先のデータ型が浮動小数というのは、理解しがたい仕様です。

3.2.3 除算の場合のデータ型の変換ルール

 除算の場合の変換テーブルは、以下の図の通りです。

f:id:toranosuke_blog:20180928230322p:plain
除算のデータ型。

 変換ルールは、次の通りです。

  • 除算の場合は、基本的にfloat64を出力する。
  • 但し、
    • float32とfloat32の演算 ⇒ float32
    • float32の配列とfloat32の配列 ⇒ float32の配列
    • float32の配列とスカラーの演算 ⇒ float32の配列

3.3 摩訶不思議なデータ型の変換ルール

 pythonは予め出力のデータがを決めない動的型付けのため、被演算子に基づき決めた何らかのデータ変換ルールに従って、演算を行う必要があります。そのデータ変換ルールが、前節で説明したような変換ルールだったわけです。

 概ね、理解できる変換ルールですが、整数同士の演算を浮動小数点に昇格させてしまうのは、直感的に予想できない変換ルールです。また、uint64 の主たる使用目的の一つはアドレス指定と思います。

 そうであれば、uint64を浮動小数へ変換するルールは、奇異なルール、摩訶不思議なルールと言わざるを得ません。

 浮動小数で困る場合には、int() や astype() などで整数型へ変換を行えば済む話かもしれません。それでも、float64 への昇格ではなく、64ビットの整数への変換の方が分かりやすく、適切な仕様ではないでしょうか。

4. 最後に

 pythonのデータ型の変換についてまとめました。pythonでは、uint64型と符号あり整数型の演算が浮動小数に変換されるという摩訶不思議なデータ型の変換ルールがあり、注意が必要です。

 私のような Python 初心者でなくても、はまりそうな点ではないかと思います。

(2018/9/29)

関連記事

付録A:演算結果のデータ型を表示するスクリプト

 python3のスクリプトです。動作確認は、ubuntu18.04で行っています。

 python2のスクリプトも作っています(python3との違いは、print文の関係のみ)。python2のスクリプトと実験結果と合わせて、ここ (Googleドライブ) に置いておきます。

#!/usr/bin/python3
#
# ckeck_types.py
# 
# make tables of data types converted with operation in python. 
#
# copyright (c) 2018 Toranosuke Tenyu

import sys
import numpy as np

def add_bra(cc,bra=False):

    if bra == True:
        return "["+cc+"]"
    else:
        return cc

def cc (dd):

    ti = type(2)
    tf = type(2.0)

    bra = "[" in "{}".format(dd)

    if hasattr(dd,"dtype"):
        code = dd.dtype
        if code == 'int32':
            return add_bra('i4',bra)
        if code == 'uint32':
            return add_bra('u4',bra)
        if code == 'int64':
            return add_bra('i8',bra)
        if code == 'uint64':
            return add_bra('u8',bra)
        if code == 'float32':
            return add_bra('f4',bra)
        if code == 'float64':
            return add_bra('f8',bra)
    else:
        code = type(dd)
        if code == ti :
            return 'i'
        if code == tf :
            return 'f'

def print_dtype(dd,dd_print=False):

    if dd_print:
        print("%5s"%dd,"%5s"%cc(dd),end=" ")
    else:
        print("%5s"%cc(dd),end=" ")
            

def print_conv(data,dd_print=False,dd_trig=True,op="mul"):

    if dd_print:
        print("%10s"%"",end="  ")
    else:
        print("%5s"%"",end=" ")

    for dd in data:
        print_dtype(dd,dd_print)

    print("")

    for dd2 in data:
        print_dtype(dd2,dd_print)

        for dd1 in data:

            if op == "mul":
                val = dd2*dd1
            elif op == "add":
                val = dd2+dd1
            elif op == "sub":
                val = dd2-dd1
            else:
                val = dd2/dd1
                
            print_dtype(val,dd_print)
            if dd_trig :
                if cc(dd1) == cc(dd2):
                    break

        print("")
            

        
if __name__ == '__main__': 

    i2 = 2
    f2 = 2.0

    # print orignal data
    # dd_print = True
    dd_print = False

    # print only lower triangular matrix
    dd_trig = True
    #dd_trig = False

    op = "mul"
    # op = "add"
    # op = "sub"
    # op = "div"

    print(sys.version)

    print("")
    
    data = [
        i2,
        f2,
        np.array(i2  ,'i4'),
        np.array([i2],'i4'),
        np.array(i2  ,'u4'),
        np.array([i2],'u4'),
        np.array(i2  ,'i8'),
        np.array([i2],'i8'),
        np.array(i2  ,'u8'),
        np.array([i2],'u8'),
        np.array(f2  ,'f4'),
        np.array([f2],'f4'),
        np.array(f2  ,'f8'),
        np.array([f2],'f8')
    ]
    print_conv(data,dd_print,dd_trig,op)
    print("")

    data = [
        i2,
        f2,
        np.array(i2  ,'i4'),
        np.array(i2  ,'u4'),
        np.array(i2  ,'i8'),
        np.array(i2  ,'u8'),
        np.array(f2  ,'f4'),
        np.array(f2  ,'f8')
    ]
    print_conv(data,dd_print,dd_trig,op)
    print("")

    data = [
        np.array([i2],'i4'),
        np.array([i2],'u4'),
        np.array([i2],'i8'),
        np.array([i2],'u8'),
        np.array([f2],'f4'),
        np.array([f2],'f8')
    ]
    print_conv(data,dd_print,dd_trig,op)
    print("")

付録B. python2との違い

 python2とpython3で加算・乗算・乗算については、同一動作でした。

 しかし、除算については、python3と動作が異なります。変換テーブルは、以下の通りです。

f:id:toranosuke_blog:20180929153135p:plain
Python2の除算演算における変換規則。

 この変換規則は、加算・減算・乗算の変換規則と同一です。つまり、python2では四則演算の全てで同じ変換規則を使っています。

 一方、python3では、加算・減算・乗算に関しては、python2と同じですが、除算に関しては、基本的に float64 を出力するように変更されています。

 つまり、python2とpython3の違いは、除算結果のデータ型の変換ルールの違いとなります。

GPTテーブルを読み取るプログラムを作って、Windowsディスクを調べた(in Python)

【概要】GPTテーブルを読み取って、テキストで出力する Python プログラムを作りました。gdisk ではテキスト出力できない GPT ヘッダの位置を読み出す目的で作りましたが、GPT テーブル内に入っている他の情報もすべて出力します。このプログラムを使って、Windows でディスクのパーティションテーブルが正常に設定されているか、調査しました。

1. はじめに

 クローンディスクを作っていて気になったのが、Windowsでは、GUIDパーティションテーブル (GUID Partition Talbe; GPT) の第2GPTヘッダを正しい位置、つまり、ディスクの末尾に配置しているか、という点です(関連記事は、ここ)。

 Windowsで作ったディスクを gdisk で覗くと、次のエラーメッセージが出力されます。

The protective MBR's 0xEE partition is oversized! Auto-repairing.

 gdisk のソースコードを読めばいいのかもしれませんが、この原因をよく理解していませんでした。パーティションサイズの認識に何らかの異常があるのですが、このエラー自体は深刻なものではなく、自動修復しても構わない程度のもののようです*1

 最初に疑ったのは、Windows。Windowsでは認識したことになっているディスクサイズが実際のディスクサイズと違うのではないか?その影響が、先のエラーメッセージとなって現れたと推測しました。このことを確認する一つの方法が、第2GPTヘッダの位置が適切な場所、つまり、ディスク末尾に置かれているか、ということです。

 GPTテーブルを読み取るプログラムは、このような背景から生まれました。

 なお、使用した言語は、Pythonです。Python 初心者なので、変なところがあると思います。ご指導・ご鞭撻のほどよろしくお願い致します。

2. Pythonプログラム

2.1 仕様

 GPTヘッダの情報を全てテキストで出力します。入力は、dd ダンプしたハードディスクイメージか、gdisk で保存したGPTデータです。

 gdisk の CUI で取り出せない主なデータは、第1と第2のGPTヘッダの位置情報とCRCの値、MBRぐらいです。それ以外の情報は、gdiskでもほぼすべてテキスト出力できます。

 今回作った read-gpt-table.py では、gdisk でテキスト出力できない GPTヘッダの位置情報を含めて、すべてのデータを出力します。

 目的や入出力が異なりますが、GPTテーブルを読み出すCのプログラムもあります。今回の目的には合致しませんでしたが、参考にさせて頂きました。
 ● GUID Partition Table を読む - jou4のブログ

2.2 コード

 GPTテーブルを読み取るためのプログラム read-gpt-table.py を付録A につけておきます。使い方は、次の通りです。

$ read-gpt-table.py 
Usage: read-gpt-table.py file [option]
 Read GPT table from file.
 Option:
  --dd    : file is a dd-dumped image. (default)
  --gdisk : file is a GPT table saved by gdisk.

 Caution:
     gdisk may repair GPT header automatically.
     To get GPT header as is, use dd-dumped image.

 注にも書きましたが、gdiskでは、自動的にGPTヘッダを修復してしまうことがあるので、変更を加えないそのままのGPTヘッダを読み取りたい場合には、ddダンプしたファイルを使った方がよいです。

 なお、動作確認は、ubuntu/lubuntu 18.04 の pyhonの3.6.6で行っています。

2.3 使い方

● ddダンプしたファイルの場合

$ dd if=/dev/sdX of=foo.img
$ read-gpt-table.py foo.img --dd 

● gdiskのGPTデータの場合

$ gdisk /dev/sdX
 ( command 'b' を実行し、foo.gpt に保存 )
$ read-gpt-table.py foo.gpt --gdisk

3. WindowsのGPTテーブルを調べた

3.1 gdiskで保存されるGPTテーブル

 GPTテーブルのほとんどの情報は、gdiskのCUIから読み出すことができます。しかし、筆者が知りたかったGPTヘッダの位置情報については、CUIからは調べることはできません。

 一方、gdiskのコマンド 'b' によって保存されたバイナリのファイルには、GPTテーブルのすべての情報が保存されますので *2、このバイナリファイルを解析することで、知りたい情報を得ることができます。

 このデータファイルの構造は、下記の計35セクタ ( =17,920 バイト)です。

  • [MBR(1), 第1GPTヘッダ(1), 第2GPTヘッダ(1), パーティションエントリー(32)]
 ddダンプしたファイルでは、ファイルの先頭に [MBR, 第1GPTヘッダ, パーティションエントリー]の計34セクタが配置され、第2GPTヘッダやパーティションエントリーのコピーはディスク末尾に配置されますので、プログラミングの際には、少々、注意を払う必要があります。

 但し、gdiskが保存するGPTデータは、メモリ上に展開されたパーティション情報です。gdiskのマニュアルには、バックアップコマンド b について以下の説明があります。

Save partition data to a backup file. You can back up your current in-memory partition table to a disk file using this option. The resulting file is a binary file consisting of the protective MBR, the main GPT header, the backup GPT header, and one copy of the partition table, in that order. Note that the backup is of the current in-memory data structures, so if you launch the program, make changes, and then use this option, the backup will reflect your changes.

 この説明を読むと、前述の "Auto-repairing" が実施された場合にも、自動修復された状態のGPTテーブルがファイルに出力されそうです。

3.2 WindowsのGPTテーブルに異常なし

 次の二つのディスクについて、GPTテーブルを調査しました。

  • Windowsシステムが入った1TBのHDD
  • Windowsシステムが入った500GBのSSD

 プログラムの出力例を付録Bに示します。

 HDDの場合でも、SSDの場合でも、ddダンプしたファイルから読み取ったGPTテーブルと、gdiskのGPTデータから読み取ったGPTテーブルとの間では、MBRの極一部を除き同一でした。

 一番気になっていた第2GPTの位置ですが、ディスク末尾の最終セクタへのオフセット値を示していて、正常です。また、ddダンプしたディスクイメージの末尾の1セクタ(512バイト)から読み込んだ第2GPTテーブルにも異常は見られませんでした。

 Windowsが作ったパーティションテーブルだからと言って、GPTについては、おかしなことをやっているわけではなさそうです。

 但し、「GPTについては」です。

3.3 WindowsのMBRはいい加減

 gdiskの結果とddの結果で異なる部分は、MBRの部分でした。これは、MBRの第1パーティションの全セクタ数で、例えば、SSDの場合には以下となります。

バイト数ddの値
(修復前)
gdiskの値
(修復後)
ブートフラグ 1 0x 00
最初のセクタ(CHS方式) 3 0x 00 20 00
パーティション識別子 1 0x EE (=GPT)
最後のセクタ(CHS方式) 3 0x FF FF FF
最初のセクタ(LBA方式) 4 0x 00 00 00 01
パーティションの全セクタ数4 0x FF FF FF FF 0x 3A 38 60 2F
(=976,773,167)
MBRの第1パーティションの情報 (500GBのSSDの場合)。

 Windowsの場合、パーティションの最初のセクタ数(LBA)は正しいようですが、パーティションの全セクタ数は実際の数字を反映せず、0xFFFFFFFFが設定されています。

 gdiskは、パーティションサイズが0xFFFFFFFFというのは大きすぎる (The protective MBR's 0xEE partition is oversized!) と判定し、自動修復(Auto-repairing) して、GPTが管理するセクタ数(=ディスクの全セクタ数-1)をMBRの第1パーティションの全セクタ数として設定しているようです。HDDの場合も同様に修正されています。

 Windowsとしては、パーティションサイズが-1 (0xFFFFFFFF) という設定は、サイズ不明ということなのでしょう。パーティションサイズを取得できないわけではないので、手抜き実装としか思えません。

 CHS方式のセクタ数も不正確と思いますが、gdisk は修復していません。CHS方式でのディスク管理は廃れた方式なので、gdisk も相手にしなかったということでしょうね。

 やはり、Windows。MBR については、いい加減な実装をしていました。

4. 結論

 Windowsでは、第2GPTヘッダーが正しく配置されないという疑念がありましたが、特に問題なさそうです。

 但し、WindowsのMBRのパーティションサイズは正しく入力されていません。gdiskでは、Windowsが設定した不適切なのMBRの値を自動的に正しい値に設定し直しているようです。

 モヤモヤしていたことなので、疑念が晴れてスッキリしました。また、Pythonの勉強にもなりました。

(2018/9/24)

関連記事

付録A:GPTテーブルを読み取るPythonプログラム

ソースコードとサンプルデータは、ここ(Googleドライブ)に置いておきます。

#!/usr/bin/python3
#
# read-gpt-table.py
# 
#   Read and print GPT partition table from a file saved with dd or gdisk.
# 
#   Copyright (C) 2018 Toranosuke Tenyu
#
#

### structure of GPT table in dd-dumped image ###
# LBA0       is MBR
# LBA1       is 1st GPT Header
# LBA2-33    is partition entries (128 entries)
# last LBA   is 2nd GPT Header (the end of disk)
#
# see:  https://en.wikipedia.org/wiki/GUID_Partition_Table

### structure of GPT table in GPT data saved with gdisk ###
#
# LBA0       is MBR
# LBA1       is 1st GPT Header
# LBA2       is 2nd GPT Header
# LBA3-LBA34 is partition entries (128 entries)
#
# The size of GPT data by gdisk is 35 sectors (17920 bytes)
# The size of LBA is 1 sector.

import sys
import numpy as np

### gpt header dtype (512 byte)
gpt_dtype = np.dtype([
    ('signature',          'S8'),
    ('revision',           '>u4'),
    ('header_size',        '<u4'),
    ('crc32_1',            '<u4'),
    ('reserved_1',         '<u4'),
    ('loc_1st_gpt',        '<u8'),
    ('loc_2nd_gpt',        '<u8'),
    ('first_usable_sector','<u8'),
    ('last_usable_sector', '<u8'),
    ('disk_guid',          'u1',16),
    ('starting_lba',       '<u8'),
    ('num_of_entry',       '<u4'),
    ('size_of_entry',      '<u4'),
    ('crc32_2',            '<u4'),
    ('reserved_2',         'u1',420)
])

### read GPT Header 
def read_gpt(filename, offset=512):

    # open gpt file
    fp = open(filename,'rb')
    fp.seek(offset)

    # read GPT Header from file
    gpt = np.fromfile(fp,dtype=gpt_dtype,count=1)

    # close file
    fp.close()

    return gpt

### print GPT Header ###
def print_gpt(gpt):
    print('signature             : %s' %gpt['signature'].tostring().decode('ascii'))
    print('revision              : 0x%08x'%gpt['revision'][0])
    print('header size           : %d'%gpt['header_size'])
    print('crc32(1)              : %d'%gpt['crc32_1'])
    print('reserved(2)           : %d'%gpt['reserved_1'])
    print('1st GPT location      : %d'%gpt['loc_1st_gpt'])
    print('2nd GPT location      : %d'%gpt['loc_2nd_gpt'])
    print('first usable sector   : %d'%gpt['first_usable_sector'])
    print('last usable sector    : %d'%gpt['last_usable_sector'])
    print('disk_guid             : %s'%guid_toString(gpt['disk_guid'][0]))
    print('starting LBA          : %d'%gpt['starting_lba'])
    print('number of entry       : %d'%gpt['num_of_entry'])
    print('size of entry         : %d'%gpt['size_of_entry'])
    print('crc32(2)              : %d'%gpt['crc32_2'])

# Convert binary GUID to string
def guid_toString(guid):

    # print(guid)

    guid_ascii =''

    # data1 (ulong)
    guid_ascii += f'%02X'%guid[3]
    guid_ascii += f'%02X'%guid[2]
    guid_ascii += f'%02X'%guid[1]
    guid_ascii += f'%02X'%guid[0]
    guid_ascii += '-'

    # data2 (ushort)
    guid_ascii += f'%02X'%guid[5]
    guid_ascii += f'%02X'%guid[4]
    guid_ascii += '-'

    # data3 (ushort)
    guid_ascii += f'%02X'%guid[7]
    guid_ascii += f'%02X'%guid[6]
    guid_ascii += '-'

    # data4 (uchar[8])
    guid_ascii += f'%02X'%guid[8]
    guid_ascii += f'%02X'%guid[9]
    guid_ascii += '-'
    guid_ascii += f'%02X'%guid[10]
    guid_ascii += f'%02X'%guid[11]
    guid_ascii += f'%02X'%guid[12]
    guid_ascii += f'%02X'%guid[13]
    guid_ascii += f'%02X'%guid[14]
    guid_ascii += f'%02X'%guid[15]

    return guid_ascii

### partition entry (128 byte/entry) ###

entry_dtype = np.dtype([
    ('type_guid',      'u1',16),
    ('unique_guid',    'u1',16),
    ('first_lba',      '<u8'),
    ('last_lba' ,      '<u8'),
    ('attribute_flag', '<u8'),
    ('partition_name', '<u2',36)
])

def read_entry(filename,offset=1024,npart=-1):

    # open file
    fp = open(filename,'rb')
    fp.seek(offset)

    # read partition entry from file
    entry = np.fromfile(fp,dtype=entry_dtype,count=npart)
    
    # close file
    fp.close()

    return entry

def print_entry(entry):

    count = 0
    for item in entry:
        if item['first_lba'] != 0 :
            print('partition type guid   :', guid_toString(item['type_guid']))
            print('unique partition guid :', guid_toString(item['unique_guid']))
            print('first_lba             :', item['first_lba'])
            print('last_lba              :', item['last_lba'])
            print('attribute flags       : 0x%016X'%item['attribute_flag'])
            print('partition name        : \'{}\''.format(item['partition_name'].tostring().decode('utf-16-le').strip('\0')))
            print('')
            count += 1

    print('Number of valid entries = %d\n'%count)

### MBR ###
def read_mbr(filename,offset=0):
    fp = open(filename,'rb')
    fp.seek(offset)
    
    mbr = np.fromfile(fp,dtype='<u1',count=512)
    fp.close()
    return mbr

def print_mbr(mbr):
    count=1
    for item in mbr :
        if item != 0 :
            print('%02X '%item,end='')
        else :
            print('-- ',end='')
        if count%16 == 0 :
            print('')
        count += 1
    print(' ')

def print_usage():
    print('Usage: {} file [option]'.format(__file__))
    print(' Read GPT table from file.')
    print(' Option:')
    print('  --dd    : file is a dd-dumped image. (default)')
    print('  --gdisk : file is a GPT table saved by gdisk.')
    print('')
    print(' Caution:')
    print('     gdisk may repair GPT header automatically.')
    print('     To get GPT header as is, use dd-dumped image.')

def args_parser():
    args = sys.argv

    if (len(args) == 2 ):
        return 'dd'         # read GPT table from dd-dump (default)

    if (len(args) == 3):
        if ( args[2] == '--gdisk' ):
            return 'gdisk'   # read GPT table  saved by gdisk
        if ( args[2] == '--dd' ):
            return 'dd'

    print_usage()
    sys.exit()

if __name__ == '__main__':

    # set file_type = 'dd' or 'gdisk'
    file_type = args_parser()

    filename = sys.argv[1]

    bsize = 512

    ### read & print GPT header

    if ( file_type == 'gdisk' ):

        # read from gdisk file 

        print('### 1st GPT Header ###')
        gpt_header1 = read_gpt(filename,bsize)
        print_gpt(gpt_header1)
        print('')

        print('### 2nd GPT Header ###')
        gpt_header2 = read_gpt(filename,2*bsize)
        print_gpt(gpt_header2)
        print('')

        print('### Partition Entry ###')
        entry_list = read_entry(filename,3*bsize)
        print('Number of entries = %d\n'%entry_list.size)
        print_entry(entry_list)

        print('### MBR ###')
        mbr = read_mbr(filename,0)
        print_mbr(mbr)
    else:
        # read from dd-dump file        

        print('### 1st GPT Header ###')
        gpt_header1 = read_gpt(filename,bsize)
        print_gpt(gpt_header1)
        print('')

       print('### 2nd GPT Header ###')

        # escape from converting into float64
        uint64_512 = np.array([512],dtype=np.uint64)
        uint64_loc = gpt_header1['loc_2nd_gpt'][0]*uint64_512[0]
        
        gpt_header2 = read_gpt(filename,uint64_loc)
        print_gpt(gpt_header2)
        print('')

        print('### Partition Entry ###')
        entry_list = read_entry(filename,2*bsize,128)
        print('Number of entries = %d\n'%entry_list.size)
        print_entry(entry_list)
        
        print('### MBR ###')
        mbr = read_mbr(filename,0)
        print_mbr(mbr)

    sys.exit()

付録B:read-gpt-table.pyの実行結果

### 1st GPT Header ###
signature             : EFI PART
revision              : 0x00000100
header size           : 92
crc32(1)              : 2997411310
reserved(2)           : 0
1st GPT location      : 1
2nd GPT location      : 1953525167
first usable sector   : 34
last usable sector    : 1953525134
disk_guid             : B45D2A5C-F9A4-478D-818B-D8E6280ABEA4
starting LBA          : 2
number of entry       : 128
size of entry         : 128
crc32(2)              : 3723135598

### 2nd GPT Header ###
signature             : EFI PART
revision              : 0x00000100
header size           : 92
crc32(1)              : 288711948
reserved(2)           : 0
1st GPT location      : 1953525167
2nd GPT location      : 1
first usable sector   : 34
last usable sector    : 1953525134
disk_guid             : B45D2A5C-F9A4-478D-818B-D8E6280ABEA4
starting LBA          : 1953525135
number of entry       : 128
size of entry         : 128
crc32(2)              : 3723135598

### Partition Entry ###
Number of entries = 128

partition type guid   : C12A7328-F81F-11D2-BA4B-00A0C93EC93B
unique partition guid : CC2D4D60-A1FB-4D4E-9542-C603A9BA1CCD
first_lba             : 2048
last_lba              : 534527
attribute flags       : 0x8000000000000000
partition name        : 'EFI system partition'

partition type guid   : E3C9E316-0B5C-4DB8-817D-F92DF00215AE
unique partition guid : 91297EAC-45C5-4BDC-8F28-CC0978A7084F
first_lba             : 534528
last_lba              : 567295
attribute flags       : 0x8000000000000000
partition name        : 'Microsoft reserved partition'

partition type guid   : EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
unique partition guid : 79886FBF-71C1-4E37-9922-A2C336CA8720
first_lba             : 567296
last_lba              : 1926947658
attribute flags       : 0x0000000000000000
partition name        : 'Basic data partition'

partition type guid   : DE94BBA4-06D1-4D40-A16A-BFD50179D6AC
unique partition guid : 6ABF2C3A-3420-48A2-968D-5F3FCBA7EF1B
first_lba             : 1926948864
last_lba              : 1929021439
attribute flags       : 0x8000000000000001
partition name        : ''

partition type guid   : DE94BBA4-06D1-4D40-A16A-BFD50179D6AC
unique partition guid : 932D4253-8484-440A-856C-2A8E03037CBE
first_lba             : 1929022863
last_lba              : 1953525134
attribute flags       : 0x8000000000000001
partition name        : 'Basic data partition'

Number of valid entries = 5

### MBR ###
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
02 -- EE FF FF FF 01 -- -- -- FF FF FF FF -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- 55 AA 

*1:"The oversized 0xEE partition ...(snip)... could indicate something strange to do with disk size detection." (Rod Smith, gdiskの開発者)
https://superuser.com/questions/1023343/after-upgrading-from-windows-7-to-windows-10-system-thinks-gpt-partition-is-mbr#comment1424307_1023702

*2: GPTテーブルの構造については、「GUIパーティションテーブル」(wikipedia)やその英語版に解説があります。これを参考にしました。

Win10 1803 が VirtualBox にも悪さした

【概要】 Windows10の1803アップグレードで、CryptSvc が原因で chrome が動作異常を起こすことを報告しましたが、VirtualBox にも悪影響を与えていました。とりあえずの回避方法を紹介します。

1. Windows10 1803アップグレード

 何かと安定しないと言われる1803アップグレード。既にChromeについての異常動作については紹介しました(関連記事は、ここ)。

 この原因は、CryptSvcの暴走ですが、VirtualBoxでも、起動時にCryptSvcが暴走し、正常起動できない状態となりました。

2. ソフトウェアのバージョン

  • Windows 10 Home (x64) 1803、ビルド:17134.254
  • VirtualBox:5.2.18 r124319
  • ゲストOS:Ubuntu18.04

3. 症状

3.1 ゲストOSのUbuntuが起動しない

 VirtualBoxからゲストOSのUbuntuを起動しようとしても、応答せず、起動しません。

f:id:toranosuke_blog:20180922112649p:plain:w400
画面が真っ暗でUbuntuが起動しない。

 このとき、CryptSvc (Cryptographic Services)の負荷が上昇します。

 f:id:toranosuke_blog:20180922113240p:plain:w500
私のdynabookの場合、通常は0%のCPU使用率が数%程度となる。

3.2 CryptSvcを停止しても起動しない

 CyptSvcの暴走が原因のようなので、CryptSvcを停止しましたが、今度は異なるエラーで起動しません。

f:id:toranosuke_blog:20180922113728p:plain:w500
今度は、ubuntuの起動以前の段階で停止しているようです。

3.3 原因の推測

 CryptSvcが起動していれば、ubuntuを起動するフェーズに入れるが、ubuntuを起動するフェーズに入ると、CryptSvcが暴走し起動を阻害するので、一旦、CryptSvcが停止させる必要がある、といったところではないかと思います。

① ubuntuの起動フェーズに入る(CyptSvcが必要)→ CryptSvcが暴走する
② ubuntuの起動中に待ち状態になる(画面が黒)← CryptSvcが起動を阻害する
③ CryptSvcを停止する → 待ち状態が解消し、起動が進行する

4. とりあえずの解決策

 この問題は、以下の方法で回避できます。

  • ubuntuの起動中にCryptSvcを再起動する

 CryptSvcの停止のタイミングにシビアな印象はありませんが、タスクマネージャからの停止だと面倒なので、スクリプトを書いておくと楽です。

net stop CryptSvc
net start CryptSvc

 上記のバッチファイルを管理者特権で実行すれば、CryptSvcが再起動します(ちょっとだけ楽にするために改良したスクリプトは、ここ)。

 なお、根本的な解決方法の一つはクリアインストールです。私の場合、諸事情でWin10をクリアインストールする羽目になって、この問題は根本的に解決しました(笑)。

5. 最後に

 Microsoftさん、CyptSvcの暴走問題を早く解決してくださいませ。

(2018/9/22)

関連記事

Windows10のインストールメモ

【概要】Windows10の筆者用のカスタマイズのための設定についてまとめました。リカバリメディア・バックアップの作成、WindowsOSの設定、アプリのインストール,、機器設定などについて述べます。

1. はじめに

 最近、何回かWindowsを再インストールする羽目になったので、メモしておきます。

  • 対象マシン:dynabook AZ77

 我が家のメインマシンdynabook az77についての記事ですが、サブマシンのVOYO V3も同じような設定です。

VOYO V3は中華製4K対応ミニパソコンですが、SSD128GB搭載で、ハードディスク時代のdynabookよりも快適でした(笑)。関連記事は、ここ

2. Windowsの設定

2.1 Windows Updateとバックアップ

2.1.1 リカバリメディアの作成

 クリアインストールするときに必須なメディアを作成する。復旧にも利用できる。

  • 「アプリ」→「TOSHIBA」→「リカバリメディア作成ツール」
    • DVD3枚にバックアップ
    • USBフラッシュの場合、16GB以上必要

2.1.2 回復ドライブの作成

  • 「アプリ」→「Windows管理ツール」→「回復ドライブ」→「システムファイルを回復ドライブにバックアップします」にチェック
    • メディアに必要な容量は、16GBも必要だったかな?VOYOの場合は、8GBも要らなかった。
※ 回復ドライブを作れば、 システム修復ディスクの作成は不要*1
● 「設定」→「更新とセキュリティ」→「バックアップ」→「[バックアップと復元]に移動(Windows 7)」→「システムイメージの作成」(DVDで1枚)

2.1.3 Windows Update

  • 「設定」→「更新とセキュリティ」→「Windows Update」
    • 数時間を要する。
    • バージョン:1803, ビルド:17134.254へバージョンアップ

2.1.4 東芝からのソフトアップデート

  • 「アプリ」→「TOSHIBA」→「Service Station」
    • TOSHIBA Service Station(東芝サービスステーション)でアップデート

2.1.5 バックアップの作成

  • 「設定」→「更新とセキュリティ」→「バックアップ」→「[バックアップと復元]に移動(Windows 7)」→「システムイメージの作成」
    • 160GBの外付けハードディスクを接続して、バックアップ(約70GB)

2.1.6 復元ポイントの作成

  • 「アプリ」→「Windows システムツール」→「コントロールパネル」→「システムとセキュリティ」→「システム」→「システムの詳細設定」→「システムの保護」タブ→「復元ポイントを今すぐ作成します」→「作成(C)」

2.2 マシン名の変更

  • 「設定」→「システム」→「バージョン情報」→「このPCの名前を変更」
    • 「DESKTOP-47V1BAU」→「kaliwin」→再起動

2.3 設定の同期

 背景などの「個人用設定画面」やMicrosoft Edgeの「お気に入り」などが、Microsoftアカウントの設定と同期する。

  • 「設定」→「アカウント」→「設定の同期」

 「確認」が必要な場合には、Microsoftアカウントのメールアドレスへのセキュリティコードの送信を行う。

2.4 ウィンドウのスナップをオフ

 ウィンドウを画面の端にドラッグしたときに最大化するなど、スナップ機能をオフにする。

  • 「設定」→「システム」→「マルチタスク」→「スナップ」をオフにする。

2.5 電源とスリープ

  • 「設定」→「システム」→「電源とスリープ」
    • 「画面」→「電源接続時」→「3時間」
    • 「スリープ」→「電源接続時」→「なし」
  • 「設定」→「システム」→「電源とスリープ」→「電源の追加設定」
    • 「電源ボタンを押したときの動作」:「スリープ状態」のまま
    • 「カバーを閉じたときの動作」:「スリープ状態」から「何もしない」に変更

2.6 ディスプレイ

  • 「設定」→「システム」→「ディスプレイ」
    • 「複数のディスプレイ」→「2のみに表示する」
      • ディスプレイ2
        • 「サイズを変更する」→「100%」
        • 「解像度」→「3840x2160(推奨)」

2.7 スクリーンセーバー

  • 「設定」→「個人用設定」→「ロック画面」→「スクリーンセーバー設定」

2.8 Wi-Fi

2.8.1 内蔵wifiの無効化

 BuffaloのWifiドングルのみに限定する(内蔵のwifiは不安定なため)。

  • 「設定」→「ネットワークとインターネット」→「Wi-Fi」→「アダプタのオプションを変更する」
    • 「Wi-Fi」を「無効にする」
    • 「Wi-Fi2」の「名前の変更(M)」→「buffalo-wifi」(5GHzに接続すること)
  • 「アプリ」→「TOSHIBA」→「システムセッティング」→「LAN」
    • 「内蔵LAN」:(必要に応じて)無効
    • 「東芝システムセッティング」で起動装置の優先順位などでも設定可能

2.8.2 固定IPアドレス設定

 必要に応じて、IPアドレスを固定する。

  • 「設定」→「ネットワークとインターネット」→「Wi-Fi」→「アダプタのオプションを変更する」(右列にある)
    • 「buffalo-wifi」を右クリック→「プロパティ」→「インターネットプロコル バージョン4(TCP/IPv4)」→「プロパティ」
      • 「次のIPアドレスを使う(S)」にチェック
        • 「IPアドレス(I)」:192.168.1.181
        • 「 サブネットマスク(U)」:255.255.255.0
        • 「デフォルトゲートウェイ(D)」:192.168.1.1
      • 「次のDNSサーバのアドレスを使う(E)」にチェック
        • 「優先DNSサーバ(P)」:192.168.1.1

2.9 Microsoft IMEの設定

  • 「タスクバー」→「Microsoft IME」→「プロパティ」→「Microsoft IMEの設定」
    • 「IME入力モード切替の通知」→「画面中央に表示する」のチェックを外す。
  • あるいは、「設定」→「時刻と言語」→「地域と言語」→「日本語(Windowsの表示言語)」→「オプション」→「Microsoft IME(入力方式エディター)」→「オプション」→「IME 入力モードの通知」→「画面中央に入力モードを表示する」を「オフ」にする。

2.10 CapsLockをCtrlに入れ替える

2.11 完全シャットダウンのショートカット作成

 完全シャットダウンのショートカットを作成する。

  • 「デスクトップ」で右クリック→「新規作成(X)」→「ショートカット(S)」
    • 完全シャットダウン
      • コマンド「shutdown /s /t 0」
      • 名前「full_shutdown」
    • 再起動
      • コマンド「shutdown /r /t 0」
      • 名前「full_reboot」
  • 「シフトキー」+「シャットダウン」でも完全シャットダウンは可能。

2.12 ユーザーフォルダ名の変更

 Windowsが生成したユーザーフォルダ名を「tora」に変更する。

  • ① 現在のMicrosoftアカウントをローカルアカウントに切り替える。
    • 「ローカルアカウントでのサインインに切り替える」
      • このときのユーザー名は、tempなどで、toraにはしない。
      • パスワードは空白で構わない。
  • ② 新規ローカルアカウントを作成する。
    • 「その他のユーザーをこのPCに追加」→「このユーザーのサインイン情報がありません」→「Microsoftアカウントを持たないユーザーを追加する」
      • 「ユーザー名」に「tora」を入力(ユーザーフォルダは、C:\Users\toraとなる)
      • パスワードは空白で構わない。
      • 「アカウントの種類の変更」で「管理者」にする。
    • サインアウトし、toraでログオンする。
  • ③ toraをMicrosoftアカウントでのサインインに切り替える。
    • 「Microsoftアカウントでのサインインに切り替える」
    • Microsoftアカウントでサインインする。
    • 現在のWindowsパスワード(ローカルアカウントtoraのパスワード、つまり空白)を入力
    • 初めてサインインするときの設定画面が現れ、いくつか設定する。
  • ④ C:\Users\以下のディレクトリを確認し、必要なものはコピーする。
  • ⑤ 古いアカウントを削除する。
    • ローカルアカウント「temp」を削除する。
  • ⑥ 古いフォルダを削除する。
    • 不要なC:\Users\xxxを削除する
      • 一回、名前変更して、再起動後に、削除した方が安心

 詳細は、以下を参照。

3. アプリケーション

3.1 アプリの削除

  • 「設定」→「アプリ」
    • 「ウィルスバスタークラウド」のアンインストール→再起動
    • 「WinZip 19.5」のアンインストール

3.2 ブラウザ

3.3 Acrobat Reader DC

3.4 Googleドライブ(バックアップと同期)

  • https://www.google.com/drive/からダウンロード
  • フォルダの場所:「C:\Users\tora\GoogleDrive」に変更
    • スペース・日本語があると、VirtualBoxでの共有フォルダ設定に不便

3.5 VLC media player

3.6 カスペルスキーインターネットセキュリティ

3.6.1 インストール

3.6.2 スキャン

  • メイン画面で「推奨項目:1件」→「詳細」→Internet Explorerにプラグインをインストール
  • 「便利ツール」→「脆弱性スキャン」
    • ハードディスク、ネットワークドライブ、リムーバブルドライブの自動実行を無効にする

3.6.3 定期的なスキャンの設定

「スキャン」→「スキャンスケジュール」でスキャンのスケジュールを設定する。

  • 完全スキャン
    • 「毎月」「1日」「午前9時」「翌日実行」で設定
  • 簡易スキャンスケジュール
    • 「毎月」「15日」「午前9時」「翌日実行」で設定
  • 脆弱性スキャン
    • 「毎月」「7日」「午前9時」「翌日実行」で設定
  • Windows Defenderのスキャンを併用
    • 「設定」→「更新とセキュリティ」→「Windowsセキュリティ」→「Windows Defenderセキュリティセンター」→「ウイルスと脅威の防止」→「Windows Defenderウイルス対策のオプション」→「定期的なスキャン」を「ON」

3.7 VirtualBox

3.7.1 VirtualBoxのインストール

  • https://www.virtualbox.org/からダウンロード
    • VirtualBox 5.2.18 platform packages (5.2.18 r124319)
    • VirtualBox 5.2.18 Oracle VM VirtualBox Extension Pack
  • VirtualBoxのインストール後、拡張パックをインストールする。
    • 「ファイル(F)」→「環境設定」→「拡張機能」→「+」
    • 「Oracle_VM_VirtualBox_Extension_Pack-5.2.18.vbox-extpack」を選択し、インストール
      • エラーコードVERR_ACCESS_DENIEDでインストールに失敗。
      • VirtualBoxの起動し直し、3度目で成功した(謎)

3.7.2 仮想マシン(ubuntu)を復旧する

  • バックアップした「VirtualBox VMs」を元の場所(C:\Users\tora)に戻す。
    • 「仮想マシン(M)」→「追加(A)」→「VirtualBox VMs\ubuntu16.04\ubuntu1604.vbox」を開く。
  • 「設定(S)」を確認する。

3.7.3 ゲストOS(ubuntu)の設定

  • Guest Additionのインストール
    • 仮想マシンのストレージ(光学ドライブ)にVBoxGuestAdditions.isoを設定
      • パスは、「C:\Program Files\Oracle\VirtualBox\VBoxGuestAdditions.iso」
  • Ubuntu側からファイルマネージャ(Caja)で、「VBox_GAs_5.2.18」にアクセスしようとしても、「VBox_GAs_5.2.18をマウントできません」とエラーとなる場合がある。この場合、コマンドラインでマウントする。
    • sudo mount -r /dev/sr0 /mnt/tmp
    • 「cd /mnt/tmp/」「 ./autorun.sh」を実行する(VBoxLinuxAdditions.runでも可)
  • Ubuntuのアップデート:「apt-get update」「apt-get upgrade」を実施

3.7.4 仮想ディスクのサイズの縮小

 500GBへのSSDに入れたために、SSDの空き容量が80GBと余裕がなくなった。ゲストOS上の不要ファイルを削除して、仮想ディスクのサイズを縮小する。

  • ① ゲストOS(ubuntu)の不要ファイルを削除する。
    • 170GB程度を削除して、使用量は90GB程度。この他にswap領域として4GBが確保されている。
$ df
Filesystem                 1K-blocks       Used Available Use% Mounted on
udev                         2012376          0   2012376   0% /dev
tmpfs                         404612       6500    398112   2% /run
/dev/mapper/kali--vg-root  475595856   85649708 365764192  19% /
tmpfs                        2023044        152   2022892   1% /dev/shm
tmpfs                           5120          0      5120   0% /run/lock
tmpfs                        2023044          0   2023044   0% /sys/fs/cgroup
/dev/sda1                     240972     197906     30625  87% /boot
cgmfs                            100          0       100   0% /run/cgmanager/fs
tmpfs                         404612         36    404576   1% /run/user/1000
  • ② 仮想ディスク上の空き領域を0で埋める。
$ dd if=/dev/zero of=zero.file bs=1M ; /bin/rm zero.file
  • ③ VBoxManageで仮想ディスクを縮小する。
> C:\Program Files\Oracle\VirtualBox\VBoxManage.exe list hdds
UUID:           ca2a90cc-7db8-451e-8dab-f1c875bbaecc
Parent UUID:    base
State:          created
Type:           normal (base)
Location:       C:\Users\tora\VirtualBox VMs\ubuntu16.04\ubuntu1604.vdi
Storage format: VDI
Capacity:       512000 MBytes
Encryption:     disabled

調べたuuidでディスクを指定して、縮小する。

> C:\Program Files\Oracle\VirtualBox\VBoxManage.exe modifyhd ca2a90cc-7db8-451e-8dab-f1c875bbaecc --compact

 仮想ディスク(ubuntu1604.vdi)は、290,056,044,544 バイトから 109,592,969,216 バイトへと、約180GBの縮小。

参考:ソフトウェア/VirtualBox/ディスクサイズの拡大・縮小 - 武内@筑波大

3.7.5 共有フォルダの設定

 共有フォルダの設定を確認する。

 ユーザーフォルダ名を変更すると、パスが変更される。また、ubuntuやVirtualBoxのバージョンアップの影響で動作が変わることもしばしばある。

3.7.5.1「共有フォルダー」のパスを確認する

  • 「設定(S)」→「共有フォルダー」タブ→右端のアイコン(追加、編集、削除)のポップアップウィンドウで、設定する(「 永続化する(M)」にもチェック)。
    名前 パス 自動マウントアクセス権マウント先
    gdrive C:\Users\GoogleDriveはい 完全 /media/sf_gdrive
    win10 C:\Users\tora\home はい 完全 /media/sf_win10
    music C:\Users\tora\Music はい 読込専用/media/sf_music

3.7.5.2 自動マウントされない場合

 VirtualBox側で自動マウントを指定しても、自動マウントされない場合がある。

  • 対策1:/etc/fstab に以下を追加
gdrive  /media/sf_gdrive        vboxsf defaults 0 0
win10   /media/sf_win10         vboxsf defaults 0 0
music   /media/sf_music         vboxsf ro       0 0
  • groupをvboxsfにしたい場合には、gid=vboxsfを追加する。
  • VirtualBox側では自動マウントしない設定にしておく方が、無難
    (たぶん、fstabが優先)。

参考:Virtualboxの共有フォルダのマウント位置やパーミッションを調整する - わたしの開発日記

  • 対策2:マウントをスクリプト化し、必要なときにマウント。
    #!/bin/sh
    sudo mount.vboxsf gdrive /media/sf_gdrive
    sudo mount.vboxsf win10  /media/sf_win10
    sudo mount.vboxsf -r music  /media/sf_music
    

注:vboxsfには、umountコマンドがないが、guest additionのサービスを再起動すると、マウントが外れる。

sudo /opt/VBoxGuestAdditions-5.2.18/init/vboxadd restart

3.7.6 Ubuntuのバージョンアップ

 16.04から18.04へバージョンアップする。

  • ① 仮想マシンのクローン
    • 「仮想マシン(M)」→「クローン(O)」→「新しいマシン名」(「ubuntu18.04」を設定)→「すべてをクローン(F)」
  • ② 新しい仮想マシンを起動して、ubuntuを18.04へアップグレード(sudo do-release-upgrade)
  • ③ 不要なbootイメージを削除する(/bootの空きがなくなりエラー発生)。再実行。
$ dpkg --get-selections | grep linux- | grep deinstall  
linux-image-4.4.0-116-generic           deinstall
linux-image-4.4.0-119-generic           deinstall
linux-image-4.4.0-121-generic           deinstall
linux-image-4.4.0-124-generic           deinstall
 :
(これらのパッケージを削除する)
$ sudo apt-get purge linux-image-4.4.0-{116,119,121,124} 
$ sudo do-release-upgrade

参考:bootディレクトリの容量不足でUbuntuがアップデートできない時の対処 | Hornet|静岡拠点のWeb、ホームページ制作

  • ubuntuの18.04へのバージョンアップによって発生した現象
    • 共有フォルダが自動マウントされない
    • VBoxGuestAdditions.isoがエラーせずマウントできる
    • ウィンドウのスナップ設定がリセット
      • 18.04へのバージョンアップで、ウィンドウのスナップが有効化(MATE デスクトップは、1.12.1から1.20.1にバージョンアップ)。
      • 「システム」→「設定」→「ウィンドウ」→「場所」タブ→「ウィンドウのスナップ」→「Enable window tiling」をオフ

3.8 iTunes

  • 音楽データ
    • バックアップした「iTunes」フォルダを「ミュージック」フォルダにコピーする。
  • iTunesをインストールする。
  • iTunesの設定
    • 「アカウント(A)」→「サインイン(S)」
    • 「アカウント(A)」→「認証(A)」→「このコンピュータを認証(A)...」
      • 既に5台のパソコンが認証されていたので、1台のみに変更する。
        • 「アカウント(A)」→「マイアカウントを表示(V)...」→「すべて認証解除」
        • 改めて、「このコンピュータを認証(A)」を実行する
    • ダウンロードの設定
      • 「編集(E)」→「環境設定(F)」→「ダウンロード」タブ
      • 「ミュージック(M)」「映画(O)」にチェック
      • 「フルサイズHDビデオをダウンロード(V)」にチェック
  • iPhone のバックアップ
    • iPhone を接続
    • 「ファイル(F)」→「デバイス(V)」→「バックアップ」

3.9 Amazon Music

  • 音楽データ
    • バックアップした「Amazon Music」フォルダを「ミュージック」フォルダにコピー。
  • https://music.amazon.co.jp/→「ストア」→「Amazon Musicアプリ」からダウンロード

3.10 その他

4. 機器の設定

4.1 HPプリンタ Officejet 6500

  • Officejet 6500 E709c用のドライバ・ソフトウェア(HPソリューションセンター)のインストール(HP製)
  • Officejet 6500 E709のドライバのインストール(Windows標準ドライバ, 非推奨)
    • 「設定」→「デバイス」→「プリンターとスキャナー」→「プリンターとスキャナの追加」→「プリンタが一覧にない場合」→「少し古いプリンタを検索する(R)」
      • 検索して見つかった「Officejet 6500 E709n (HP)」を選択し、プリンタ名、プリンタ共有などを設定する。
      • 注:プリントしたときに、下の余白が大きすぎる。また、はがき印刷、写真印刷(フチなし)などの設定ができない。
  • 注:Officejet側の電源を入れて、スリープ状態から印刷可能な状態にしておくこと

4.2 Canonスキャナ imageFORMULA DR-C240

  • ドライバ
  • CapturePerfect
    • DVDからインストール
      • 「選んでインストール」
        • 「CapturePerfect」「ユーザーマニュアル(機能詳細編)」を選択
        • 「DR-C240ドライバー」「CaptureOnTouch」「eCopy PDF」は不要
    • CapturePerfect のバージョンアップデータ (オリジナルのDVDが必要)
  • 再起動

4.3 ZOXテレビチューナー DS-DT308

  • Chusei PVR
    • chusei_pvr8_32_01.exeを展開
    • setup.exeを実行
      • 「CHUSEI PVR」「Driver for 1 SEG Tuner」をインストール
      • シリアル番号を入力
      • 再起動
    • チャンネルスキャン
      • 都道府県を選択し、「地域スキャン」

5. 最後に

 内容は、随時、アップデートしていきます。

(2018/9/21)

関連記事

ubuntuのインストールメモ

【概要】
 ubuntuやlubuntuの筆者用のカスタマイズのための設定についてまとめました。

1. はじめに

 最近、ubuntuやlubuntuを何度となくインストールしました。筆者用のカスタマイズのための設定をメモしておきます。

  • 対象OS:ubuntu (MATE環境) , lubuntu の18.04

 ubuntu-mateは、15.10からアップグレードし続けているものを参考にしているので、クリアインストールした場合とは違う可能性があります。

 ubuntuとlubuntuで設定が違う場合もありますが、そこは、適宜対応。

2. インストールするパッケージ

 いつも使うパッケージをインストールする。

$ sudo apt install package1 package2 ...
  • emacs, ssh, tcsh, xrdp, gnome-screenshot, gpartedなど(スクリプトが最新版)
  • net-tools: ifconfigに必要
  • gnome-tweaks
    • CapsLockのCtrlへのアサイン、蓋を閉じたときの動作、ウィンドウフォーカスなどの設定
    • 蓋の動作、ウィンドウフォーカスなどには、gnome-tweak-toolやgnome-shellが必要かも。
#!/bin/sh

# basic tools
sudo apt install emacs tcsh gnome-screenshot gnome-tweaks

# networks
sudo apt install ssh net-tools xrdp

# file system and disk utility
sudo apt install ntfs-3g gnome-disk-utility gdisk gparted

# nfs client & server
sudo apt install nfs-common nfs-kernel-server

# might be required additionally 
# sudo apt install gnome-tweak-tool

# octave
# sudo apt install octave

# python3
# sudo apt install python3-numpy
  • パッケージを探す*1
$ apt search pattern
$ apt-cache search parttern
  • インストール済のパッケージに入っているツールをリストアップする
$ dpkg -L package1 package2 ...
  • ツールが入っているインストール済のパッケージを探す
$ dpkg -S pattern1 pattern2 ...

3. 設定

  • gnome-tweaks
     gnome-tweaksで、GUIベースで簡単にいろいろなカスタマイズを行える。

    • CapsLockをCntrlにアサインする
      • 「Ctrl position」と「CapsLock behavior」の二つがあるが、どちらかで設定すれば大丈夫。
    • ラップトップの蓋を閉じたときの動作
      • 「電源」→「ラップトップの蓋を閉じるとサスペンドする」をオフ  これは、ubuntu, lubuntuのデスクトップパネルからも設定できる(どちらが優先されるか不明)
        • lubuntu:「設定」→「電源管理」→「ラップトップの蓋」(「サスペンド」以外を選択)にも設定あり。BIOSの場合もあり。
        • ubuntu-mate:「システム」→「設定」→「ハードウェア」→「電源管理」(ここに、ノートパソコンならあると思う。未確認)
           
  • aliasesの設定
    .bash_aliases

    alias   ls='/bin/ls -CF'
    alias   mv='/bin/mv -i'
    alias   rm='/bin/rm -i'
    alias   cp='/bin/cp -i'
    

  • 固定IPアドレス
    /etc/network/interface を編集して、固定IP化するのが古典的方法。ifup や ifdownで制御できる。

source /etc/network/interfaces.d/*
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

#### dhcp setting ######
# auto enp0s3
# iface enp0s3 inet dhcp
 
#### static ip setting ######
auto enp0s3
iface enp0s3 inet static
address 192.168.1.180
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1
dns-nameserver 192.168.1.1

 DHCPサーバーになっているルーターでDHCP固定アドレス割当をすると、個別のマシンはDHCPの設定だけしておけばよい(でも、やったことはない)。

  • Network Manager
     無線LANの場合、古典的な方法ではwpa_supplicantを使うが、wpa_supplicantの設定が面倒。Network ManagerでGUIベースで設定するのが楽。
     但し、ログインしてからでないと、ネットワークに繋がらないので、rebootしたときに不便。変更するなら、有線接続にするか、真面目にwpa_supplicant.confを設定する。

    • ubuntu-mate:「システム」→「設定」→「インターネットとネットワーク」→「ネットワーク接続」→「歯車マーク」→いろいろ設定
    • lubuntu:「タスクバー」→「無線LANアイコン」→「接続を編集する」→SSIDを選択→「歯車マーク」→いろいろ設定

  • /etc/hosts の編集

  • xrdp

    $ sudo apt install xrdp
    $ sudo systemctl enable xrdp
    $ sudo systemctl restart xrdp
    

    • 昔は、日本語キーボード用の設定をする必要があったが、今は不要。
    • Windows側では、マシン名での指定もできたが、不安定。IPアドレスでの指定が安定する。
       これは、nmb (NetBIOS名)の扱いが不安定の模様。たぶん、Windows側の問題。windows側からのping などでも名前を指定では見つからない場合があるが、何回か繰り返すと、動くようになる。別のマシンでも、SMB(samba)によるネットワーク共有フォルダで、見つからない場合がしばしばある。
    • .xsessionの作成
      • lubuntuの場合:
        $ echo "lxsession -s Lubuntu -e LXDE" > ~/.xsession
      • ubuntu-mateの場合(要確認):
        $ echo "gnome-session --session=gnome"  > ~/.xsession
  • ディスク管理

    • gdisk, gparted, gnome-disk-utility
    • ラベルを付ける(e2fsprogs, ntfs-3g)
      • e2label /dev/sda1 labelname
      • ntfslabel /dev/sdb1 labelname

  • rootでログインできるようにする*2

    $ sudo su -
    $ passwd
    Enter new UNIX password:
    Retype new UNIX password:
    passwd: password updated successfully
    

  • デフォールトで使うエディタを設定する
    sudoeditを起動する前に設定しておく。

$ sudo update-alternatives --config editor
There are 4 choices for the alternative editor (providing /usr/bin/editor).

  Selection    Path               Priority   Status
------------------------------------------------------------
  0            /bin/nano           40        auto mode
  1            /bin/ed            -100       manual mode
  2            /bin/nano           40        manual mode
* 3            /usr/bin/emacs25    0         manual mode
  4            /usr/bin/vim.tiny   15        manual mode

Press  to keep the current choice[*], or type selection number: 3
  • 環境変数 LANG *3
LANG=C
LANG=ja_JP.utf8
  • NFS サーバ
$ sudo apt install nfs-kernel-server 

 /etc/exportsで共有するディレクトリを指定する。

/home/tora    *(rw)

 - 必要に応じて、* をクライアント側のマシンのhostnameにする。
 - sync, no_subtree_checkのオプションは、デフォールトなので不要。

 サーバ側でエクスポートする。

$ sudo exportfs -a

 クライアント側でマウントする。

$ sudo mount nfs-server-hostname:/home/tora /mnt/tora

3. 最後に

 気が付いたときに、随時、更新します。

(2018/9/21)

関連記事

サポート外となった17.04のLubuntuを18.04へアップデートした

【概要】
 サポート切れとなった17.04のlubuntuを18.04へアップグレードしました。基本的にはレポジトリの変更だけで、アップグレードできますので、難しい作業ではありませんでした。

1. いつの間にかサポート期限を過ぎていた

 HDD/SSDへの換装に当たって、久しぶりにLubuntuをインストールしたHP miniを起動しました。gdiskを入れていなかったので、apt-getでインストールしようとしたら、既にOSのバージョンが古くて、リポジトリがなくなっていました。

 入れていたOSのバージョンは、lubuntu17.04。LTSでない通常版のサポート期間は、9か月なので、2018年9月時点では、既にサポート対象期限を過ぎています。

 LTS (Long Term Support) は、サポート期間が5年間。たまにしか使わないマシンにはLTS版を入れるべきでした。

2. 17.04から18.04へのバージョンアップ

 以下の記事を参考に、バージョンアップを行いました。

Ubuntu 最新バージョンへのアップグレード - eTuts+ Server Tutorial

 この記事の「次へ進む前に軽く一読 : アップグレード可能と言ってるのに、アップグレードしようとするとアップグレード出来ないと怒られる」の説明と同様に、do-release-upgradeは拒否されました。

 A(17.04)→C(18.04)にバージョンアップしたいのだけど、A(17.04)のdo-release-upgradeは、A→B(17.10)のアップグレードしかサポートしない。だから、いきなり18.04へのアップグレードはできないということのようです。

 この記事の「歯抜け状態でのアップグレード その1. sources.list を書き換えて、A → B → C へ」の説明と同様に、/etc/apt/sources.list を編集することで、A(17.04) から B(17.10) に dist-upgrade したうえで、B(17.10) からC(18.04) に do-release-upgrade しました。

 具体的な作業は以下の通りです。

  • /etc/apt/sources.listを編集する。
    • "zesty"(17.04)をすべて "artful"(17.10) に置き換える。
  • sudo dist-upgrade を実行する。
    • この時点でリリースは、17.10となる
  • sudo do-release-upgrade を実行する
    • この結果、18.04になる
  • sudo reboot

 以上で、18.04へのアップグレードは完了です。レポジトリの変更だけで解決しました。

3. 最後に

 あまり使わないマシンにはLTS版を入れるべし。

(2018/9/20)

関連記事

付録:zestyをartfulに書換え後の/etc/apt/sources.list

 zestyをartfulに機械的に置き換えて構いません。

# deb cdrom:[Lubuntu 17.04 _Zesty Zapus_ - Release i386 (20170412)]/ zesty main multiverse restricted universe

# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb http://archive.ubuntu.com/ubuntu artful main restricted
# deb-src http://jp.archive.ubuntu.com/ubuntu/ artful main restricted

## Major bug fix updates produced after the final release of the
## distribution.
deb http://archive.ubuntu.com/ubuntu artful-updates main restricted
# deb-src http://jp.archive.ubuntu.com/ubuntu/ artful-updates main restricted

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team. Also, please note that software in universe WILL NOT receive any
## review or updates from the Ubuntu security team.
deb http://archive.ubuntu.com/ubuntu artful universe
# deb-src http://jp.archive.ubuntu.com/ubuntu/ artful universe
deb http://archive.ubuntu.com/ubuntu artful-updates universe
# deb-src http://jp.archive.ubuntu.com/ubuntu/ artful-updates universe

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu 
## team, and may not be under a free licence. Please satisfy yourself as to 
## your rights to use the software. Also, please note that software in 
## multiverse WILL NOT receive any review or updates from the Ubuntu
## security team.
deb http://archive.ubuntu.com/ubuntu artful multiverse
# deb-src http://jp.archive.ubuntu.com/ubuntu/ artful multiverse
deb http://archive.ubuntu.com/ubuntu artful-updates multiverse
# deb-src http://jp.archive.ubuntu.com/ubuntu/ artful-updates multiverse

## N.B. software from this repository may not have been tested as
## extensively as that contained in the main release, although it includes
## newer versions of some applications which may provide useful features.
## Also, please note that software in backports WILL NOT receive any review
## or updates from the Ubuntu security team.
deb http://archive.ubuntu.com/ubuntu artful-backports main restricted universe multiverse
# deb-src http://jp.archive.ubuntu.com/ubuntu/ artful-backports main restricted universe multiverse

## Uncomment the following two lines to add software from Canonical's
## 'partner' repository.
## This software is not part of Ubuntu, but is offered by Canonical and the
## respective vendors as a service to Ubuntu users.
# deb http://archive.canonical.com/ubuntu artful partner
# deb-src http://archive.canonical.com/ubuntu artful partner

deb http://archive.ubuntu.com/ubuntu artful-security main restricted
# deb-src http://security.ubuntu.com/ubuntu artful-security main restricted
deb http://archive.ubuntu.com/ubuntu artful-security universe
# deb-src http://security.ubuntu.com/ubuntu artful-security universe
deb http://archive.ubuntu.com/ubuntu artful-security multiverse
# deb-src http://security.ubuntu.com/ubuntu artful-security multiverse

http://archive.ubuntu.com でファイルが見つからない場合には、http://old-releases.ubuntu.com に変更すれば、更新できると思います。

 なお、ダウンロード元は「日本のサーバー」にしていましたが、「メインサーバー」に切り替えて作業しました。

f:id:toranosuke_blog:20180826212245p:plain:w500
日本のサーバーでもできるかは、よく分かりません。

Dynabook AZ77のSSDへの換装 (5) gpartedでパーティションをいろいろリサイズした

【概要】 ddで作ったクローンディスクのWindowsのシステムパーティションをgpartedを使ってリサイズし、Windowsの正常動作を確認しました。gpartedでは、ファイルシステムにエラーがある場合でも、それをチェックして、修正を促してくれます。GUIベースの便利なツールです。

1. はじめに

 dynabook の SSD への換装の記事を書いてきましたが、一応は、SSD へ換装できました。但し、クリアインストールです(涙)。

 リベンジと「ディスククローンの作り方」の記事の検証を兼ねて、クリアインストールしてセットアップした SSD イメージのクローンを作成しました。

 今度は、500GB の SSD がマスターイメージ、ターゲットディスクは 2TB の HDD です。パーティションを変更しない丸ごとコピーのクローン、縮小したクローン、拡大したクローンのそれぞれを作りました。

 まずは、パーティションを変更しない丸ごとコピーのクローンを作って動作確認した後に、次に同じハードディスクでパーティションを縮小したクローンを作り、最後にパーティションを拡大したクローンを作ります。

2. 丸ごとコピーのクローン

2.1 ddによるコピー

 まず、 SSD イメージを dd でハードディスクに dd で丸ごとコピーします。

$ dd if=/dev/sdS of=/dev/sdT bs=1M

 ここで、/dev/sdS は SSD、/dev/sdTは 2TB のハードディスクです(適宜、 /dev/sda、 /dev/sdb のように読み替えてください)。また、ddのオプションとしてブロックサイズを指定すると高速にコピーできます。環境によって違うと思いますが、1Mバイト(=2048セクタ×512バイト)を指定しました。

2.2 gdisk による GPT テーブルの修正

 gdiskによって、SSD、HDDのそれぞれについてパーティション構成を調べました(それぞれのログは付録A参照)。

 まず、gdiskは、SSD、HDDのいずれの場合にも、「The protective MBR's 0xEE partition is oversized! Auto-repairing. 」というエラーメッセージを出力します。GPTテーブルとディスクサイズの間に不整合があるようです*1

 当初、このエラーメッセージは、Windowsが認識しているディスサイズとLinuxで認識しているディスサイズの違いかと思いましたが、必ずしもそうではなさそうです。この問題については付録Bで考察します。

 また、gdiskの結果は、ディスクのハードウェア情報を除けば、一致しています。

(SSD)
Disk /dev/sdc: 976773168 sectors, 465.8 GiB
Model: Samsung SSD 860 

(HDD)
Disk /dev/sdb: 3907029168 sectors, 1.8 TiB
Model: ST2000LM003 HN-M

 ディスクとそのディスクが持つセクタ数の違いをgdiskは認識していますが、パーティション構成は同じで、空きスペースも同じと表示します。

Total free space is 3342 sectors (1.6 MiB)

 HDDに対してコマンドvで検証すると、第2GPTの位置がディスク末尾に来ていないという問題点を指摘されます。SSDの場合には、この問題は指摘されません。

Problem: The secondary header's self-pointer indicates that it doesn't 
reside at the end of the disk. If you've added a disk to a RAID array, 
use the 'e' option on the experts' menu to adjust the secondary 
header's and partition table's locations.

Identified 1 problems!

 コマンドeで第2GPTテーブルをディスク末尾にリロケーションすると、空きスペースの領域は、1.4TiBとなり、正しく認識されました。また、検証コマンドvでも問題点は指摘されないようになります。

(HDD:GPT修正前)
First usable sector is 34, last usable sector is 976773134
Total free space is 3342 sectors (1.6 MiB)

(HDD:GPT修正後)
First usable sector is 34, last usable sector is 3907029134
Total free space is 2930259342 sectors (1.4 TiB)

 また、gnome-disksでみるパーティション構成は、下図のようになりました。

f:id:toranosuke_blog:20180917090152p:plain
500GB の SSD のパーティション構成(gnome-disks)

f:id:toranosuke_blog:20180917090210p:plain
2TB の HDD のパーティション構成(gnome-disks)

 gnome-disksの出力は、GPTテーブルの修正前後で同一で、当初から1.5TBの空き領域を認識している。

 なお、今回、ディスクケースを変更したところ、ディスクケースによって、認識するディスクサイズが異なる場合があることに気づきました。詳しくは付録Cで説明します。

2.3 dynabookに内蔵して動作確認

 出来上がったクローンディスクをdynabookに入れ直して、動作確認しました。以前にSSDを内蔵したままHDDを外付けで繋いだら、内蔵していたオリジナルのディスクのWindowsシステムを破壊したことがあるので、SSDは外しました。SSDとdynabookには、物理的な接続はありません。

 さて、ディスクを入れ替えて、Windows10を起動したところ、無事、起動できました。

 起動に2分近くかかったので、途中あせりましたが、SSD での数秒の起動に慣れてしまうと、ハングアップしていると思ってしまいます。

 「コンピュータの管理」の「ディスクの管理」を確認すると、1.4TiB が未割り当てとして正常に認識されています。

f:id:toranosuke_blog:20180918105522p:plain
未割り当て領域は 1.4TiB。

 また、「イベントビューアー」にディスク構成の変更が記録されています。

f:id:toranosuke_blog:20180919100826p:plain
 デバイスが異なると、警告あり。

 イベントビューアーには、警告があります。今回のデバイス (シーゲートのHN-M201RAD) は、前回のデバイス (SamsungのSSD 860) と異なっているためです。この違いが、他の動作に影響を与えているか、不明ですが、今のところ、不具合はなさそうです。

 Windows 上の gdisk を使って、GPT テーブルの比較をしましたが、ディスクのモデル名を除き、Linux 上の結果と一致しました。

3. 縮小クローン

 次に同じディスクを用いて、縮小クローンを作成しました。Windows OSが入っている約450GBのNTFSのパーティションを390GBに縮小します。

3.1 gpartedによるパーティション構成の変更

 gpartedを起動し、GUIに従って、Windowsシステムが入っているパーティション3を約450GBから約390GBに縮小しました。それ以外のパーティションは変更せず、パーティション4、5については、ディスクの先頭方向(gparted画面の左方向)に移動させて、パーティション3を縮小してできた隙間を埋めます。

 処理時間は20分程度で、特に問題は発生しませんでした。gdiskでみたパーティション情報にも、特に問題はありませんでした。

$ gparted /dev/sdT

f:id:toranosuke_blog:20180919110056p:plain
変更前のパーティション構成。パーティション3は、452GB。

f:id:toranosuke_blog:20180919104524p:plain
パーティション3を452GBから390GBに縮小。

 但し、パーティション4、パーティション5は、パーティションタイプが「Microsoft Windows回復環境(システム)」(gnome-disks)であり、このパーティションを移動させようとすると、gparted は次の警告を与えます。ちょっと心配になります。

f:id:toranosuke_blog:20180919110851p:plain:w450
システム領域を変更しようとすると、起動の失敗の可能性ありと警告。

3.2 dynabookに内蔵して動作確認

 dynabook の内蔵ディスクを 2TB の縮小クローンディスクに入れ替え、Win10 を起動しましたが、特に問題ありませんでした。

f:id:toranosuke_blog:20180919111947p:plain
パーティション3は、390GBに縮小され、1460GBの未割り当て領域ができる。

 この段階の縮小クローンのディスクサイズは 2TB ですが、これを小さなディスクに、ddコピーし、gdiskでGPTテーブルを修復すれば、本当の縮小クローンの完成です。これについては、適当なディスクが手元になかったので、未検証ですが、たぶん、問題なく動作すると思います。

3.3 あっ、電源が!

 dynabook をちょっと動かしたときに電源が落ちてしまいました!AC電源を使っていたつもりだったのですが、電気が来ておらず、バッテリーだけで動作させている状態でした。バッテリーも、裏ブタをしっかりと閉めずにいたので、動かしたときに接点が外れたのでしょう。突然の電源オフです。

 この突然の不正シャットダウンでファイルシステムを壊したらしく、その後、アカウントにサインインできなくなりました。

f:id:toranosuke_blog:20180919112916p:plain
アカウントにサインインできない!

 マイクロソフトのホームページ*2に従って、ファイルシステムチェッカー*3を実行しましたが、復旧せず、諦めました。

f:id:toranosuke_blog:20180919113511p:plain
ファイルシステムチェッカーの実行。

 このことが、その後、パーティション拡大時のエラーの原因となります。

4. 拡大クローン

 次に同じディスクで、未割り当て領域の 1.4TiB をパーティション3 (Windows領域) に割り当てます。

4.1 gpartedによるパーティション構成の変更

 パーティション4、5を末尾に移動し、パーティション3を拡大します。

f:id:toranosuke_blog:20180919114257p:plain
変更予定のパーティション構成。

f:id:toranosuke_blog:20180919114359p:plain
パーティションp4, p5を右に移動し、p3 を拡大、その後、p4, p5を左に移動。

 パーティション4、5を右に移動し、パーティション3とパーティション4の間に1.4TiBの空きを作り、その空き領域を使って、パーティション3を拡大します。その後、パーティション3とパーティション4の間にできた1MiB程度の隙間を埋めるために、パーティション4,5を左に移動するようにしました。(アライメント調整の影響ですが、gpartedでは、なぜか、隙間ができたり、できなかったりします)

4.2 gpartedでエラーが発生!

 パーティション構成変更の実行中にエラーが発生しました。

f:id:toranosuke_blog:20180919115155p:plain:w400
エラー発生!

 詳細情報を見ると、ファイルシステムのエラーチェックで、エラーを検知して、処理を中断したようです。おそらく、前述した不意の電源断でファイルシステムを破壊したのだと思います。

f:id:toranosuke_blog:20180919115358p:plain
ファイルシステムのエラーチェックで処理を中断。

 エラーメッセージにあるように、Windowsで、chkdsk /f を行いました。2回行うことを指示していますが、再起動だけ2回なのか、chkdskも含めて2回なのか良く分からなかったので、「chkdsk→再起動→chkdsk→再起動→完全シャットダウン」と処理しました。

 その後、拡大処理から再開し、無事、パーティション3の拡大処理を完了しました。

f:id:toranosuke_blog:20180919120333p:plain
chkdsk後のパーティション構成。

f:id:toranosuke_blog:20180919120421p:plain
拡大処理後のパーティション構成。パーティション3が1.8TiBに拡大。

 今度のパーティション構成では、ディスク末尾の空き領域はなくなっていますが、gparted のアライメント調整の影響です。アライメント調整の制限を加えると、gparted では、隙間ができたり、できなかったりします。原因は不明です。

4.3 dynabookに内蔵して動作確認

 拡大クローンを dynabook に入れて、動作確認しましたが、サインインできない問題を除けば、問題なく動作しました。サインインの問題は、パーティションのリサイズが原因ではなく、不意の電源断が原因です。

f:id:toranosuke_blog:20180919121848p:plain
パーティション3は、1.8TiBと大きくなっている。

5. 最後に

 gpartedを使って、パーティションのリサイズ・移動を行いました。gpartedによってリサイズしても今のことろ動作には問題はないようです。

 WindowsのOSが入っているシステム領域のリサイズ・移動や、ハードウェアの変更によって、Windowsが起動できなくなると危惧していましたが、(突然の電源断がなければ)案外、簡単にクローンディスクを作れることが分かりました。

 但し、今回はパーティション1のEFIシステムや、パーティション2のMicrosoft予約領域については、一切、移動・リサイズを行っていません。パーティション4やパーティション5のWindowsの回復環境についても、移動はしましたが、リサイズは行っていません。

 おそらく、パーティション1やパーティション2のリサイズ・移動を行うと、Windowsの起動で問題が発生すると思っています。

(2018/9/19)

関連記事

付録A:SSDおよびddコピーしたHDDのgdiskの結果

  • 500GBのSSD
$gdisk /dev/sdS -l
GPT fdisk (gdisk) version 1.0.3

The protective MBR's 0xEE partition is oversized! Auto-repairing.  

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdc: 976773168 sectors, 465.8 GiB
Model: Samsung SSD 860 
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): D169EDB1-AEC2-11E8-A251-8FB413A6DF47
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 976773134
Partitions will be aligned on 2048-sector boundaries
Total free space is 3342 sectors (1.6 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          534527   260.0 MiB   EF00  EFI system partition
   2          534528          567295   16.0 MiB    0C01  Microsoft reserved ...
   3          567296       950381278   452.9 GiB   0700  Basic data partition
   4       950382592       952256511   915.0 MiB   2700  
   5       952256512       976773119   11.7 GiB    2700  Basic data partition
  • 2TBのHDD
$ sudo gdisk -l /dev/sdT
GPT fdisk (gdisk) version 1.0.3

The protective MBR's 0xEE partition is oversized! Auto-repairing.

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdb: 3907029168 sectors, 1.8 TiB
Model: ST2000LM003 HN-M
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): D169EDB1-AEC2-11E8-A251-8FB413A6DF47
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 976773134
Partitions will be aligned on 2048-sector boundaries
Total free space is 3342 sectors (1.6 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          534527   260.0 MiB   EF00  EFI system partition
   2          534528          567295   16.0 MiB    0C01  Microsoft reserved ...
   3          567296       950381278   452.9 GiB   0700  Basic data partition
   4       950382592       952256511   915.0 MiB   2700  
   5       952256512       976773119   11.7 GiB    2700  Basic data partition
  • GPTヘッダ修正後のHDD
$ gdisk -l /dev/sdT
GPT fdisk (gdisk) version 1.0.3

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdb: 3907029168 sectors, 1.8 TiB
Model: ST2000LM003 HN-M
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): D169EDB1-AEC2-11E8-A251-8FB413A6DF47
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 3907029134
Partitions will be aligned on 2048-sector boundaries
Total free space is 2930259342 sectors (1.4 TiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          534527   260.0 MiB   EF00  EFI system partition
   2          534528          567295   16.0 MiB    0C01  Microsoft reserved ...
   3          567296       950381278   452.9 GiB   0700  Basic data partition
   4       950382592       952256511   915.0 MiB   2700  
   5       952256512       976773119   11.7 GiB    2700  Basic data partition

付録B:Windowsが認識するディスクサイズ

 当初、gdiskが出力する「The protective MBR's 0xEE partition is oversized! Auto-repairing. 」の警告は、Windowsが認識しているディスクサイズが、Linuxでgdiskが認識しているディスクサイズよりも小さいために発生している現象と疑っていました。

 なぜなら、WIndowsの「システム情報」に書かれた「サイズ」が小さかったからです。しかし、「システム情報」の「サイズ」は、gdiskが定義するディスクサイズとは違い、Windowsが認識するディスクサイズが小さいとは必ずしも言えないようです。

 Windowsの「システム情報」の「サイズ」「セクター合計」は、実際に使えるディスクのサイズを表していません。今回のSSDでは、「セクター合計」は976,768,065 で、「サイズ」はセクタ数を512倍した 500,105,249,280 バイトです。一方、最後の「パーティション#3」の「開始オフセット」 (487,555,334,144バイト) と「パーティションサイズ 」(12,552,503,296バイト)から、パーティションの末尾は、500,107,837,440バイト目となります。これは、「サイズ」よりも、2,588,160バイト、セクタ数で言うと5,055セクタ大きい数字です。最後のパーティションは、ディスクを5,055セクタはみ出てしまうことになります。

 SSDは、クリアインストールしたディスクですので、Windowsの標準的なインストールによって、パーティションが作られています。パーティション作成で異常が発生していることもありえますが、可能性としては低いと思います。つまり、「システム情報」の「セクター合計」「サイズ」は、実際に使えるディスクサイズを反映していないと考えれます。

 では、「システム情報」の「セクタ合計」「サイズ」は何を表すかというと、「シリンダー合計」から算出したディスクサイズです。

  • 「サイズ」=「セクター合計」×512バイト
  • 「セクター合計」=「トラック合計」×63
  • 「トラック合計」=「シリンダー合計」×255

 よって、

  • 「サイズ」=「シリンダー合計」×255×63×512バイト

となります。この計算方法で、500GBのSSDや2TBのHDDの「システム情報」の「セクタ合計」「サイズ」を説明できます。

 Microsoftの公式ツールで、実際のディスクサイズの大きさを調べられるか、私は知りません。

 しかし、Windows版gdiskでは調べられます。Windows版のgdiskの結果は、Linux版gdiskと同一でした。このことからすれば、Windowsから見たディスクサイズとLinuxで見たディスクサイズは一致する可能性が高いと推測しています。

 しかし、同じgdiskなので、同じ数字を出力している可能性もあります。より詳しく調べるためには、直接、GPTテーブルを解析する必要もありそうです*4

追記:GPTテーブルを読み込むプログラムを作って解析しましたが、WindowsのGPTテーブルには問題はなさそうです。
GPTテーブルを読み取るプログラムを作って、Windowsディスクを調べた(in Python) - 虎之助の徒然記
f:id:toranosuke_blog:20180919220601p:plain
Windowsの「システム情報」。

付録C:ディスクケースによって、認識するディスクサイズが異なる

 今回、用いた外付けディスクケースは、ELUTENGですが、SMART情報が見れないため、別のディスクケースに繋いだところ、gdisk、gnome-disksで見たディスクサイズが異なっていることに気付きました。

C.1 ELUTENG・玄人志向・センチュリーのディスクケース

 そこで、次の3つのディスクケースについてgdisk、gnome-disksの動作を調べてみました。

  • ELUTENG
  • 玄人志向
  • センチュリー

C.2 ディスクサイズが小さくなる玄人志向・センチュリー

 500GBのSSD、2TBのHDDに対するgdiskやgnome-disksの出力の主な違いを表にまとめると、以下の通りです。

ELUTENG玄人志向センチュリー
gnome-disks
ディスクサイズ - 512バイト少ない512バイト少ない
SMART - 対応-
不良セクタ検知- 不良セクタを検知-
シリアルナンバー 認識せず正しく認識間違って認識
gdisk
セクタサイズ - 1セクタ少ない1セクタ少ない
モデル名 正しく認識間違って認識正しく認識

 gdisk、gnome-disksのどちらの場合でも、玄人志向・センチュリーのディスクケースではディスクサイズをELUTENGよりも小さく認識します。

C.3 gdiskにおける警告メッセージとディスク検証

 500GBのSSD(Windowsシステムの入ったディスク)に対して、それぞれgdiskを行うと次のメッセージが得られます。

  • ELUTENGの場合
    • 起動時のメッセージ
       パーティションが大きすぎると警告されるも、GPTテーブルは有効と判定。
      The protective MBR's 0xEE partition is oversized! Auto-repairing.
      Partition table scan: MBR: protective BSD: not present APM: not present GPT: present
      Found valid GPT with protective MBR; using GPT.
    • 検証コマンドvのメッセージ
       問題なし。
      No problems found. 3342 free sectors (1.6 MiB) available in 3
      segments, the largest of which is 2014 (1007.0 KiB) in size.
      
  • 玄人志向・センチュリーの場合
    • 起動時のメッセージ
       ディスクサイズが小さすぎ、第2GPTテーブルが壊れていると、警告される。
      Warning! Disk size is smaller than the main header indicates! Loading
      secondary header from the last sector of the disk! You should use 'v' to
      verify disk integrity, and perhaps options on the experts' menu to repair
      the disk.
      Caution: invalid backup GPT header, but valid main header; regenerating
      backup header from main header.
      Partition table scan: MBR: protective BSD: not present APM: not present GPT: damaged
      **************************************************************************** Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk verification and recovery are STRONGLY recommended. ****************************************************************************
    • 検証コマンドvのメッセージ
       GPTテーブルがディスク末尾にない、ディスクが小さすぎる、パーティションが大きすぎると、3つの問題点を指摘される。
      
      Problem: The secondary header's self-pointer indicates that it doesn't reside
      at the end of the disk. If you've added a disk to a RAID array, use the 'e'
      option on the experts' menu to adjust the secondary header's and partition
      table's locations.
      Problem: Disk is too small to hold all the data! (Disk size is 976773167 sectors, needs to be 976773168 sectors.) The 'e' option on the experts' menu may fix this problem.
      Partition(s) in the protective MBR are too big for the disk! Creating a fresh protective or hybrid MBR is recommended.
      Identified 3 problems!

C.4 結論:玄人志向・センチュリーはディスクサイズを1セクタ小さく誤認識する

 ”Disk size is 976773167 sectors, needs to be 976773168 sectors.”の指摘からすると、次のようなことかと思います。

  • SSDのGPTテーブルには、ディスクサイズは976,773,168セクタと書いてある。
  • 玄人志向・センチュリーの外付けケースを介して認識されたディスクサイズは、それよりも1つ少ない976,773,167セクタである。
  • このため、GPTテーブルのディスクサイズよりも、ディスクが小さすぎると指摘される。

 つまり、玄人志向・センチュリーの外付けディスクケースは正しくディスクサイズを認識せず、本来のディスクよりも1セクタ少ないディスクとして取り扱ってしまうということです*5

 以前の記事で、dynabookの1TBのHDDをddで読み込んだデータが1セクタ不足する現象が発生したと書きました。

 このときに用いたディスクケースは、センチュリーのケースです。センチュリーのディスクケースを使ったため、オリジナルのディスクを1セクタ少なく認識し、ddダンプしたデータが1セクタ不足するという現象が発生したと考えられます。

*1:"The oversized 0xEE partition ...(snip)... could indicate something strange to do with disk size detection." (Rod Smith, gdiskの開発者)
https://superuser.com/questions/1023343/after-upgrading-from-windows-7-to-windows-10-system-thinks-gpt-partition-is-mbr#comment1424307_1023702

*2:https://support.microsoft.com/ja-jp/help/4027881/windows-10-we-cant-sign-in-to-your-account

*3:https://support.microsoft.com/ja-jp/help/4026529

*4:GPTとMBRはどのように違うのか? - syuu1228's blog

*5:仕様上は、玄人志向・センチュリーの方が正しくて、ELUTENGやdynabookの内蔵ディスクのドライバが1つ多く間違って認識しているという可能性もあります。しかし、今回の場合は、dynabookの内蔵ディスクのドライバと同様にディスクサイズを認識する必要があるので、dynabookと一致する場合が「正しい」と考えることにします。また、この動作は、Linux上での動作であって、Windows上では異なった動作をするのかもしれません。