I・O DATA LANDISK HDL-GX300 HDLraid.pmとHDLdrive.pmの内容

参考のために。
/usr/local/bin/HDLraid.pm(変更後)

#
# HDLraid.pm: RAID 制御
#
# $Id: HDLraid.pm 2856 2006-09-15 06:45:18Z imai $
#
package HDLraid;
use integer;
use HDLsystem;
use HDLdisk;
use HDLusbmap;
use HDLdefines;

sub debug {
    my $form = shift;
    my (undef, $filename, $line) = caller;

    printf STDERR "$filename: $line: $form", @_;
}

%raid_part = (
              'HDL-GW' => {
                  'md1' => { 'hda1' => 1,
                             'hdb1' => 1, },
                  'md2' => { 'hda2' => 1,
                             'hdb2' => 1, },
                  'md5' => { 'hda5' => 1,
                             'hdb5' => 1, },
                  'md6' => { 'hda6' => 1,
                             'hdb6' => 1, },
              },
              'HDL-GZ' => {
                  'md1' => { 'hda1' => 1,
                             'hdb1' => 1,
                             'hdc1' => 1,
                             'hdd1' => 1, },
                  'md2' => { 'hda2' => 1,
                             'hdb2' => 1,
                             'hdc2' => 1,
                             'hdd2' => 1, },
                  'md5' => { 'hda5' => 1,
                             'hdb5' => 1,
                             'hdc5' => 1,
                             'hdd5' => 1, },
                  'md6' => { 'hda6' => 1,
                             'hdb6' => 1,
                             'hdc6' => 1,
                             'hdd6' => 1, },
              },
              'HDL-GL' => {
                  'md1' => { 'sda1' => 1,
                             'sdb1' => 1, },
                  'md2' => { 'sda2' => 1,
                             'sdb2' => 1, },
                  'md5' => { 'sda5' => 1,
                             'sdb5' => 1, },
                  'md6' => { 'sda6' => 1,
                             'sdb6' => 1, },
              },
              'HDL-GXR' => {
                  'md1' => { 'sda1' => 1,
                             'sdb1' => 1, },
                  'md2' => { 'sda2' => 1,
                             'sdb2' => 1, },
                  'md5' => { 'sda5' => 1,
                             'sdb5' => 1, },
                  'md6' => { 'sda6' => 1,
                             'sdb6' => 1, },
              },
              );

#
# コンストラクタ
#
# $raid = HDLraid->new ( [write => 1], [force_write => 1] );
#
sub new {
    my $class = shift;
    my %param = @_;
    my $self = {};
    
    $self->{model} = &$HDLdefines::model_name();
    ( !exists $raid_part{$self->{model}} ) and
        # RAID 非サポートモデル
        return undef;

    $self->{write} = ( $param{write}||$param{force_write} )  ? 1 : 0;
    $self->{change} = ( $param{force_write} ) ? 1 : 0;
    $self->{conf} = HDLconffile->new ( file => $HDLdefines::raid_config,
                                       write => $self->{write},
                                       force_write => $param{force_write}, );
    return undef if !defined $self->{conf};
    $self->{raid2part} = $raid_part{$sys{model}};
    # 逆引きテーブルの作成
    while ( my ( $r, $h ) = each %{$self->{raid2part}} ) {
        foreach ( keys %$h ) {
            $self->{part2raid}->{$_} = $r;
        }
    }

    # 物理パーティション名 -> 仮想パーティション名の変換
    my $map = HDLusbmap->new( model => $sys{model} );
    ( !defined $map ) and
        return undef;
    my %map2 = $map->get();
    ( !defined %map2 ) and
        return undef;
    while ( my ( $v, $p ) = each %map2 ) {
        next if $v =~ /hd[bcd]/;
        next if $v eq 'esata0';
        next if !defined $p;

        $self->{map}->{$p} = $v;
    }

    return bless $self;
}


sub set {
    my $self = shift;
    my $param = shift;

    $self->{conf}->set ( 'raid', $param );
    $self->{change} = 1;
    1;
}

sub get {
    my $self = shift;

    $self->{conf}->get ( 'raid' ),
}

sub commit {
    my $self = shift;

    $self->{conf}->commit() if $self->{write} && $self->{change};
    $self->{write} = $self->{change} = 0;
    1;
}

sub DESTROY {
    my $self = shift;
    commit ( $self ) if $self->{write} && $self->{change};
    1;
}



#
# パーティション名から,正常時に属する RAID 名を引く
#
# $name = $raid->part2raid ( part => 'hda1' );
#
sub part2raid {
    my $self = shift;
    my %param = @_;
    my $part = $param{part};

    !defined $part and return undef;
    !exists $self->{part2raid}->{$part} and return undef;
    
    $self->{part2raid}->{$part};
}

#
# RAID 名に対し,正常時に属するパーティションの一覧を返す
#
# $hash = $raid->raid2part ( [raid => 'md1'] );
#
sub raid2part {
    my $self = shift;
    my %param = @_;
    my $raid = $param{raid};

    ( !defined $raid || !exists $self->{raid2part}->{$raid} ) and
        return $self->{raid2part}; # ハッシュ全体
    $self->{raid2part}->{$raid};
}


#
# spot repair 進捗
# 返り値は (スキャン開始日時, 終了日時, 修復可否フラグ)
#
# 修復可否フラグは
#       0: 修復不要
#       1: 修復必要
#       2: 修復不可
#
sub __get_scan_progress {
    my $self = shift;
    my $raid = shift;
    my $defect_file = "/etc/landisk/defectlist.$raid";
    my ($begin, $end, $fixable ) = ( undef, undef, undef );
    my %defect_list = ();


    ( ! -f $defect_file ) and
        goto end0;
    open my $fh, $defect_file or
        goto end0;

    my $line = <$fh>;
    ( $line =~ /^BEGIN:([0-9]+)/ ) or
        goto end1;
    $begin = 0 + $1;

    $line = <$fh>;
    ( $line =~ /^ABORT:/ ) and
        $fixable = 'aborted',
        goto end1;
    ( $line =~ /^END:([0-9]+)/ ) or
        goto end1;
    $end = 0 + $1;

    $line = <$fh>;
    ( $line =~ /^STATUS:([0-9]+)/ ) or
        goto end1;


    my $dev;
    my $num;
    while ( <$fh> ) {
        chomp;
        /^DEV:(.*)/ and
            $dev = $1,
            $defect_list{$dev} = {},
            next;
        /^([0-9+])/ and
            $num = 0 + $1,
            $defect_list{$dev}->{$num} = 1,
            next;
    }

    $fixable = 'not need';
    foreach my $d ( keys %defect_list ) {
        foreach my $b ( keys %{$defect_list{$d}} ) {
            $fixable = 'need fix';
            my $f = 0;
            foreach my $d2 ( keys %defect_list ) {
                ( !exists $defect_list{$d2}->{$b} ) and
                    $f = 1,
                    last;
            }
            ( $f == 0 ) and
                $fixable = 'cannot fix',
                goto end1;
        }
    }

  end1:
    close ( $fh );
  end0:
    return ( $begin, $end, $fixable );
}

#
# RAID 状態の取得
#
# %status = $raid->status ( raid => 'md1' );
#
sub status {
    my $self = shift;
    my %param = @_;
    my %ret = ();

    my $raid = $param{raid};

    my $stt = `sudo /sbin/mdadm --detail /dev/$raid 2>/dev/null`;
    $? and return undef;

    ( $stt =~ /Raid Level : (.*)\n/ ) and
        $ret{level} = $1;
    ( $stt =~ /Array Size : ([0-9]*)/ ) and
        $ret{size} = sprintf('%lu',2*$1); # 512 byte 単位
    if ( $stt =~ /State : (.*)\n/ ) {
        $ret{state} = $1;
        if ( $ret{state} =~ /recovering/ || $ret{state} =~ /resyncing/ ) {
            $ret{jstate} = '再構築中';
        } elsif ( $ret{state} =~ /degraded/ ) {
            $ret{jstate} = '単体動作';
        } elsif ( $ret{state} =~ /clean/ || $ret{state} =~ /active/ ) {
            $ret{jstate} = '通常動作';
        } else {
            $ret{jstate} = '不明';
        };
    }

    ( $stt =~ /Rebuild Status : (.*) complete/ ) and
        $ret{rebuild_status} = $1;

    $ret{raid} = {};
    my @lines = split ( /\n/, $stt );
    foreach ( @lines ) {
        if ( m|(\S+ \S+) +/dev/(.*)| || m|(\S+) +/dev/(.*)| ) {
            my $state = $1;
            my $dev = $2;
            $dev =~ s/[0-9]+$//;
            $dev = $self->{map}->{$dev};
            $ret{raid}->{$dev} = $state;

            $state =~ /active sync/ and
                $ret{jraid}->{$dev} = '正常',
                next;
            $state =~ /faulty/ and
                $ret{jraid}->{$dev} = '故障',
                next;
            $state =~ /spare rebuilding/ and
                $ret{jraid}->{$dev} = '再構築中',
                next;

            $ret{jraid}->{$dev} = '不明';
        }
    }
    ( $ret{scan_begin}, $ret{scan_end}, $ret{scan_fixable} ) = __get_scan_progress ( $self, $param{raid} );
    %ret;
}


#
# スキャン結果のクリア
#
# $raid->clear_scan ( raid => 'md6' );
#
sub clear_scan {
    my $self = shift;
    my %param = @_;

    my $defect_file = "/etc/landisk/defectlist.$param{raid}";
    `sudo rm -f $defect_file 2>/dev/null`;
    1;
}

__END__

=head1 名前

HDLraid.pm -- RAID 管理

=head2 使いかた

 use HDLraid;

 $raid = HDLraid->new ();

 %list = $raid->status ( raid => 'md6' );

=head1 説明

=head2 $raid = HDLraid->new ();

コンストラクタです.

=head2 %list = $raid->status ( raid => 'md6' );

RAID デバイスのステータスを返します.

 キー         値の例                               説明
 ------------------------------------------------------------------------------
 'raid'                                         RAID 構成デバイスの状態
                'esata1' => 'active sync'
                'hda' => 'active sync'
 'jraid'                                                raid キーの日本語訳
                'esata1' => '正常'
                'hda' => '正常'
 'state'        'clean'                         RAID 状態
 'jstate'       '通常動作'
 'size'         31262208                        RAID 容量.単位はセクタ数.
 'level'        'raid1'
 'scan_begin'   1135838361                      spot repair スキャン開始時刻.
                                                UNIX 標準時
                                                1970/01/01 00:00:00 UTCからの
                                                経過秒数
                                                スキャンしていない時は undef
 'scan_end'     1135838673                      spot repair スキャン終了時刻.
                                                UNIX 標準時.
 'scan_fixable' 'cannot fix'                    spot repair の可否
                                                undef: スキャン未終了
                                                not need: 異常無し
                                                need fix: 異常あり・修復可
                                                cannot fix: 異常あり・修復不可
                                                aborted: スキャン中断


=head2 $raid->clear_scan ( raid => 'md6' );

spot repair スキャン結果をクリアします.

=cut

/usr/local/bin/HDLdrive.pm(変更後)

#
# HDLdrive.pm:: ドライブ操作
#
# $Id: HDLdrive.pm 2842 2006-09-15 01:20:13Z imai $
#
package HDLdrive;
use integer;

sub debug {
    my $form = shift;
    my (undef, $filename, $line) = caller;
    printf STDERR "$filename: $line: $form", @_;
}


#
# ファイルの読み込み
#
sub __cat {
    my @file = @_;
    my $f;
    my @ret = ();
    
    foreach $f ( @file ) {
        ( !open ( FILE, $f ) ) and
            #print STDERR "fail to open $f\n";
            return @ret;

        my @lines = <FILE>;
        debug ( "lines = %s", join ( '+', @lines ) );
        push @ret, @lines;
        close ( FILE );
    }
    @ret;
}


#
# パーティションリスト
#
sub __partition_list {
    my $device = shift;

    # 引数がパーティションの場合は親デバイスのパーティションを返す
    $device =~ s/[0-9]+$//;

    my $part = `echo /sys/block/$device/${device}*`;
    chomp $part;
    $part =~ s|/sys/block/$device/||g;

    split ( /\s+/, $part );
}


#
# ベンダ・モデル名
#
sub __modelname {
    my ( $device ) = @_;
    $device =~ s/[0-9]+$// if $device !~ /^sr/;

    my $model;
    ( $device =~ /^hd/ ) and
        # IDE ドライブ
        #debug("IDE\n");
        ( $model ) =  __cat( "/proc/ide/$device/model" ),
        chomp $model,
        return $model;

    # USB(SCSI) ドライブ
    debug("USB\n");
    my ( @model ) = __cat ( "/sys/block/$device/device/vendor",
                            "/sys/block/$device/device/model", );
    chomp @model;
    return join ( ' ', @model );
}


#
# ドライブ or パーティションの容量
#
sub __capacity {
    my $device = shift;
    my ($size, $path, $drive );

    if ( $device !~ /^sr[0-9]+/
         && $device !~ /^md[0-9]+/
         && $device =~ /(.*)[0-9]+$/ ) {
        # パーティション名
        $drive = $1;
        $path = "/sys/block/$drive/device/rescan";
        system ( "sudo echo 1 >$path >& /dev/null" ) if -f $path;
        ( $size ) = __cat ( "/sys/block/$drive/$device/size" );
        chomp $size;
        return $size;
    }


    # ドライブ名
    $path = "/sys/block/$device/device/rescan";
    system ( "sudo echo 1 > $path >& /dev/null" ) if -f $path;
    ( $size ) = __cat ( "/sys/block/$device/size" );
    return $size;
}


@guesstable = ( [ 'ext2' => 'ext2',],
                [ 'ext3'=> 'ext3', ],
                [ 'swap file' => 'swap',],
                [ 'XFS'  => 'xfs', ],
                ['ISO 9660 CD-ROM' => 'iso9660' ],
                ['FAT \(12 bit\)'  => 'FAT12',],
                ['FAT \(16 bit\)' => 'FAT16',],
                ['FAT \(32 bit\)' => 'FAT32',],
                ['OEM-ID.*NTFS'   => 'NTFS',],
                ['Minix filesystem' => 'minix' ],
                ['partition table' => 'partitioned',], # 怪しい.FAT かも
                ['x86 boot sector' => 'partitioned',], # 怪しい.FAT かも
                ['Claris clip art?' => 'partitioned',], # 怪しい.FAT かも
                );

#
# ファイルシステムの推定
#
sub __guess_fs {
    my $self = shift;
    my $device = shift;
    my $tmpfile = "/tmp/guessfs.$$";
    my $fs = 'none';
    
    system ( "sudo dd if=/dev/$device of=$tmpfile bs=64k count=1 >& /dev/null" ) and
        $fs = 'none',
        goto finish;

    my $guess = `file $tmpfile`;
    debug ( "file = $guess" );

    my $p;
    foreach $p ( @guesstable ) {
        ( $guess =~ /$$p[0]/ ) and
            $fs = $$p[1],
            last;
    }

    goto finish if $fs ne 'partitioned';

    # partitioned の場合

    if ( 0 ) {
        if ( $self->{model} eq 'HDL-GL' || $self->{model} eq 'HDL-GXR' ) {
            # RAID ペアかどうかチェック
            my $root = `sudo sfdisk -d /dev/sda 2>/dev/null`;
            my $target = `sudo sfdisk -d /dev/$device 2> /dev/null`;
            $root =~   s|/dev/[a-z0-9]*||g;
            $target =~ s|/dev/[a-z0-9]*||g;
            
            if ( $root eq $target ) {
                # RAID の片割れ
                $fs = 'RAID';
                
                # active かどうかチェック
                my $mdadm = `
sudo mdadm -D /dev/md1 2>/dev/null; \
sudo mdadm -D /dev/md2 2>/dev/null; \
sudo mdadm -D /dev/md5 2>/dev/null; \
sudo mdadm -D /dev/md6 2>/dev/null;
`;
                $fs = ( $mdadm =~ /$device/ ) ? 'RAID_LIVE': 'RAID_DEAD';
            }
        }
    }


    # FAT パーティションが x86 boot sector と認識される場合がある
    my $magic1 = `dd if=$tmpfile bs=1 skip=55 count=5 2>/dev/null`;
    chomp $magic1;
    my $magic11 = `dd if=$tmpfile bs=1 skip=54 count=5 2>/dev/null`;
    chomp $magic11;
    if ( ($magic1 eq 'FAT12')||($magic11 eq 'FAT12') ){
        $fs = 'FAT12';
    } elsif ( ($magic1 eq 'FAT16')||($magic11 eq 'FAT16') ) {
        $fs = 'FAT16';
    } else {
        my $magic2 = `dd if=$tmpfile bs=1 skip=84 count=5 2>/dev/null`;
        chomp $magic2;
        $fs = 'FAT32' if ( $magic2 eq 'FAT32' );
    }

  finish:
    system ( "sudo rm -f $tmpfile >& /dev/null" );
    return $fs;
}

#
# ファイルシステム容量・使用量・空き容量
#

sub __fs_usage {
    my $device = shift;
    my $tmpname = undef;
    my %cap = undef;

    # 既に mount されているかどうか
    my $line;
    $cap{mounted} = 0;
    foreach $line ( __cat ( "/proc/mounts" ) ) {
        ( $line =~ m|/dev/$device| ) and
            $cap{mounted} = 1,
            goto mounted;
    }

    # mount されていない
    # 一時的に mount.まずは mount point の作成
    $tmpname = "/tmp/$device.$$";
    ( !mkdir $tmpname, 000 ) and
        #print STDERR "fail to make mount point.\n";
        goto err0;

    system ( "sudo mount -o ro,noatime /dev/$device $tmpname" ) and
        # mount 失敗
        #printf STDERR "fail to mount $device.\n";
        goto err1;
    
    # df を実行
  mounted:
    open ( DF, "LANG=C df --block-size=512|" ) or
        #print STDERR "fail to df.\n";
        goto err2;

    while ( <DF> ) {
        my ( $all, $used, $free );
        ( m|^/dev/$device| ) and
            # filesystem all used free use% mount-point\n"
            ( undef, $all, $used, $free ) = split ( /\s+/, $_ ),
            $cap{fs_all} = $all,
            $cap{fs_used} = $used,
            $cap{fs_free} = $free,
            debug ( "all = $all, used = $used, free = $free\n" ),
            last;
    }
    close DF;

    # ファイルシステムの推定
    # reiserfs と UDF は mount してみないとわからんようだ
    open ( MOUNTS, "cat /proc/mounts|" ) or
        #print STDERR "fail to df.\n";
        goto err2;

    while ( <MOUNTS> ) {
        my ( $d, $mp, $fs, $rw ) = split ( / / );
        ( $d eq "/dev/$device" ) and
            $cap{filesystem} = $fs,
            last;
    }
    close MOUNTS;

  err2:
    system ( "sudo umount $tmpname" ) if ( -d $tmpname );

  err1:
    rmdir $tmpname if ( -d $tmpname );

  err0:
    return %cap;
}


#
# ext2/ext3 情報
#
sub __get_ext23info {
    my $part = shift;
    my %info = ();

    my $lines = `sudo tune2fs -l /dev/$part 2>/dev/null`;
    return () if $?;

    my $l;
    foreach $l ( split /[\r\n]+/, $lines ) {
        ( $l =~ s/^Filesystem UUID:\s+// ) and
            $info{filesystem_id} = $l,
            next;
        ( $l =~ s/^Last write time:\s+// ) and
            $info{last_mount} = $l,
            $info{last_write} = $l,
            next;
        ( $l = s/^Filesystem volume name:\s+// ) and
            $info{volume_name} = ($l ne '<none>') ? $l : '',
            next
    }
    %info;
}


#
# FAT 情報
#
sub __get_fatinfo {
    my $part = shift;
    my %info = ();
    my $tmpfile = "/tmp/tmpfile.$$";

    my $lines = `sudo dd if=/dev/$part of=$tmpfile bs=64k count=1 2>/dev/null && file $tmpfile 2>/dev/null && sudo rm -f $tmpfile 2>/dev/null`;
    return () if $?;

    ( $l =~ /serial number (\S+),/ ) and
        $info{filesystem_id} = $1;

    ( $l =~ /label: \"(.+)\"/ ) and
        $info{volume_name} = $1;

    %info;
}


#
# ディスクラベリング
#
%disklabel_offset = ( disklabel_magic => 0x0200,
                      disklabel_version => 0x0204,
                      disklabel_usage => 0x0208,
                      disklabel_model => 0x0210,
                      disklabel_name => 0x0300,
                      );

%disklabel_length = ( disklabel_magic => 4,
                      disklabel_version => 4,
                      disklabel_usage => 4,
                      disklabel_model => 16,
                      disklabel_name => 256,
                      );

sub __read_disklabel {
    my $self = shift;
    my $disk = shift;

    my ( $length, $result );
    my %ret = ();

    while ( my ( $key, $offset ) = each %disklabel_offset ) {
        $length = 0 + $disklabel_length{$key};
        $result = `sudo dd if=/dev/$disk bs=1 skip=$offset count=$length 2>/dev/null`;
        $result =~ s/\0+$//;
        $ret{$key} = $result;
    }
    return () if $ret{disklabel_magic} ne 'HDLG';
    %ret;
}

sub __write_disklabel {
    my $self = shift;
    my $disk = shift;
    my %param = @_;

    my ( $length, $val, $fh );
    $fh = undef;

    $param{disklabel_magic} = 'HDLG';
    $param{disklabel_version} = '0001';
    while ( my ( $key, $offset ) = each %disklabel_offset ) {
        next if !exists $param{$key};

        $length = 0 + $disklabel_length{$key};
        #$val = "\0" x $length;
        #substr ( $val, 0 ) = $param{$key};
        $val = $param{$key} . ( "\0" x ( $length - length($param{$key}) ) );

        open ( $fh, "|sudo dd of=/dev/$disk bs=1 seek=$offset count=$length 2>/dev/null" ) or return undef;
        print $fh $val;
        close $fh;
    }
    1;
}


                 
#
# コンストラクタ
#
# $drive = HDLdrive->new( [write => 1], [model => 'HDL-GL'] );
#
sub new {
    my $class = shift;
    my %param = @_;
    my $self = {};

    $self->{write} = $param{write};
    $self->{model} = $param{model};
    $self->{change} = 0;

    return bless $self, $class;
}

#
# キャッシュ情報のクリア
#
# $drive->uncache ( 'hda' );
#
sub uncache {
    my $self = shift;
    my $drive = shift;

    while ( my ($k, $v) = each %{$self->{guess_fs}} ) {
        ( $k =~ /^$drive/ ) and
            delete $self->{guess_fs}->{$k}
    }
    1;
}



#
# %hda = $drive->get ( 'hda' );
#
sub get {
    my $self = shift;
    my $name = shift;
    my %drive = ();

    return undef if !defined $name;

    if ( $name !~ /^sr[0-9]+/
         && $name !~ /^md[0-9]+/
         && $name =~ /^(.+)[0-9]+$/ ) {
        # パーティション
        my $d = $1;
        return () if ! -d "/sys/block/$d/$name";
    } else {
        # ドライブ
        return () if ! -d "/sys/block/$name";
        %drive = __read_disklabel ( $self, $name );
    }

    $drive{device} = $name;
    $drive{mounted} = 0;
    $drive{device_list} = join ( ',', __partition_list ( $name ) ) . ',';
    debug ( "=== %s\n", $drive{device} );
    $drive{model} = __modelname ( $drive{device} );
    ( !defined $drive{model} ) and
        #printf STDERR "fail to get model name.\n";
        return ();

    debug ( "=== %s\n", $drive{model} );

    $drive{capacity} = __capacity ( $name );
    ( !defined $drive{capacity} ) and
        #printf STDERR "fail to get capacity.\n";
        return ();
    debug ( "=== %u\n", $drive{capacity} );


    $drive{filesystem} = ( exists $self->{guess_fs}->{$name} ) ?
        $self->{guess_fs}->{$name} :
        ( $self->{guess_fs}->{$name} = __guess_fs ( $self, $name ) );
    #( !defined $drive{filesystem} ) and
        #printf STDERR "warning: cannot guess filesystem.\n";


    debug ( "=== %s\n", $drive{filesystem} );

    #if ( ( $drive{filesystem} ne 'none' )
    #&& ( $drive{filesystem} ne 'partitioned' ) ) {

    if ( $drive{filesystem} ne 'partitioned' ) {
        # ファイルシステム使用量を得る
        my %fs;
        if ( exists $self->{fs_usage}->{$name} ) {
            %fs = %{$self->{fs_usage}->{$name}};
        } else {
            %fs = __fs_usage ( $name );
            $self->{fs_usage} = { %fs };
        }
        if ( !exists $fs{fs_all} ) {
            printf STDERR "warning: cannot get filesystem usage.\n";
        } else {
            $drive{fs_all} = $fs{fs_all};
            $drive{fs_used} = $fs{fs_used};
            $drive{fs_free} = $fs{fs_free};
            $drive{mounted} = $fs{mounted};
            ( $drive{filesystem} eq 'none' && $fs{filesystem} ) and
                $drive{filesystem} = $fs{filesystem};
        }

        if ( $drive{filesystem} =~ /^ext/ ) {
            my %info = __get_ext23info ( $name );
            while ( my ( $k, $v ) = each %info ) {
                $drive{$k} = $v;
            }
        } elsif ( $drive{filesystem} =~ /^FAT/ ) {
            my %info = __get_fatinfo ( $name );
            while ( my ( $k, $v ) = each %info ) {
                $drive{$k} = $v;
            }
        }
    }
    %drive;
}


#
# $drive->set ( disk => 'hda', magic => 'HDLG', version => '0001', ... );
#
sub set {
    my $self = shift;
    my %param = @_;
    my $disk = $param{disk};
    my %p = ();
    delete $param{disk};
    return undef if !defined $disk;

    ( !exists $self->{disklabel}->{$disk} ) and
        %p = __read_disklabel ( $self, $disk ),
        $self->{disklabel}->{$disk} = \%p;
    while ( my ( $k, $v ) = each %param ) {
        $self->{disklabel}->{$disk}->{$k} = $v;
    }

    $self->{change} = 1;
    1;
}

sub commit {
    my $self = shift;

    if ( $self->{write} && $self->{change} ) {
        foreach ( keys %{$self->{disklabel}} ) {
            __write_disklabel ( $self, $_, %{$self->{disklabel}->{$_}} );
        }
        $self->{write} = $self->{change} = 0;
    }

    1;
}


sub DESTROY {
    my $self = shift;

    commit ( $self );
    1;
}


1;

__END__

$drive = HDLdrive->new ();

foreach $j ( 'hda', 'hda1', 'hda2', 'hda3', 'hda4', 'hda5' ) {
    %hash = $drive->get($j);
    foreach $i ( keys %hash ) {
        printf "$j:$i = %s\n", $hash{$i};
    }
}

__END__

=head1 名前

HDLdrive.pm -- ドライブ情報を得る

=head1 使いかた

 use HDLdrive;

 $drive = HDLdrive->new();

 %info = $drive->get( 'hda' );

=head1 説明

ドライブもしくはパーティションの情報を取得します.

=head2 $drive = HDLdrive->new( [model => 'HDL-GL']);

コンストラクタです.

=head2 %info = $drive->get( 'hda' );

ドライブ情報を返します.
ドライブ(例:hda)とパーティション(例:hda1)を指定した場合で,返される値が異なります.

 項目        ドライブ全体  パーティション  説明
 ----------------------------------------
 device      o             o              デバイス名(指定したもの)
 filesystem  o             o              デバイスのファイルシステム
 device_list o             *              パーティションリスト
 model       o             *              ドライブのモデル名
 capacity    o             o              ドライブorパーティションの全容量
 fs_all      -             o              ファイルシステム全容量
 fs_used     -             o              ファイルシステム使用量
 fs_free     -             o              ファイルシステム空き容量

* については,親ドライブ(例えば hda1 に対する hda)の情報です.

=head3 filesystem

指定されたドライブorパーティションのファイルシステム.
値は ext2 | ext3 | xfs | minix | udf | iso9660 | FAT12 | FAT | FAT32 | NTFS | partitioned | none.

=head3 capacity, fs_all, fs_used, fs_free

単位は 512 バイトです.

=head1 関連ファイル

特に無し.