Home | History | Annotate | Download | only in device_remap
      1 #!/usr/bin/perl
      2 
      3 #
      4 # CDDL HEADER START
      5 #
      6 # The contents of this file are subject to the terms of the
      7 # Common Development and Distribution License (the "License").
      8 # You may not use this file except in compliance with the License.
      9 #
     10 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     11 # or http://www.opensolaris.org/os/licensing.
     12 # See the License for the specific language governing permissions
     13 # and limitations under the License.
     14 #
     15 # When distributing Covered Code, include this CDDL HEADER in each
     16 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     17 # If applicable, add the following below this CDDL HEADER, with the
     18 # fields enclosed by brackets "[]" replaced with your own identifying
     19 # information: Portions Copyright [yyyy] [name of copyright owner]
     20 #
     21 # CDDL HEADER END
     22 #
     23 
     24 #
     25 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     26 # Use is subject to license terms.
     27 #
     28 
     29 use Getopt::Std;
     30 use Cwd;
     31 
     32 use strict;
     33 
     34 package MDesc;
     35 
     36 use constant {
     37     MDEND  => 0x45,
     38     MDNODE => 0x4e,
     39     MDARC  => 0x61,
     40     MDDATA => 0x64,
     41     MDSTR  => 0x73,
     42     MDVAL  => 0x76,
     43 };
     44 
     45 
     46 sub new {
     47     my $class = shift;
     48     my $self = {};
     49     $self->{FILE} = undef;
     50     $self->{MAJOR} = undef;
     51     $self->{MINOR} = undef;
     52     $self->{NODE_SEC_SZ} = undef;
     53     $self->{NAME_SEC_SZ} = undef;
     54     $self->{DATA_SEC_SZ} = undef;
     55     $self->{NODES} = undef;
     56     $self->{NAMES} = undef;
     57     $self->{DATA} = undef;
     58     bless($self, $class);
     59     return $self;
     60 }
     61 
     62 sub open {
     63     my $self = shift;
     64     my ($mdhdr, $size);
     65 
     66     if (@_) {
     67         $self->{NAME} = shift;
     68     } else {
     69         $self->{NAME} = '/dev/mdesc';
     70     }
     71     return  unless open(MD, "$self->{NAME}");
     72 
     73     # Read and parse MD header
     74     unless (read(MD, $mdhdr, 16) == 16) {
     75         close (MD);
     76 	return;
     77     }
     78 
     79     ($self->{MAJOR}, $self->{MINOR},
     80      $self->{NODE_SEC_SZ},
     81      $self->{NAME_SEC_SZ},
     82      $self->{DATA_SEC_SZ}) = unpack("nnNNN", $mdhdr);
     83 
     84     $size = read(MD, $self->{NODES}, $self->{NODE_SEC_SZ});
     85     $size = read(MD, $self->{NAMES}, $self->{NAME_SEC_SZ});
     86     $size = read(MD, $self->{DATA}, $self->{DATA_SEC_SZ});
     87 
     88     1;
     89 }
     90 
     91 #
     92 # return hash of given node's information
     93 #
     94 sub getnode {
     95     my ($self, $nodeid) = @_;
     96     my ($tag, $name, $namelen, $nameoff, $datalen, $dataoff, %node);
     97 
     98     ($tag, $namelen, $nameoff, $datalen, $dataoff) =
     99       unpack("CCx2NNN", substr($self->{NODES}, $nodeid * 16, 16));
    100     $name = substr($self->{NAMES}, $nameoff, $namelen);
    101     %node = (tag => $tag, name => $name, nameid => $nameoff);
    102 
    103     if ($tag == MDSTR || $tag == MDDATA) {
    104         $node{'datalen'} = $datalen;
    105 	$node{'dataoff'} = $dataoff;
    106     } elsif ($tag == MDVAL) {
    107         $node{'val'} = ($datalen << 32) | $dataoff;
    108     } elsif ($tag == MDARC || $tag == MDNODE) {
    109         $node{'idx'} = ($datalen << 32) | $dataoff;
    110     }
    111 
    112     return %node;
    113 }
    114 
    115 
    116 #
    117 # return hash of given property's information
    118 #
    119 sub getprop {
    120     my ($self, $propid) = @_;
    121     my (%node, $tag, %prop);
    122 
    123     %node = $self->getnode($propid);
    124     $tag = $node{'tag'};
    125     %prop = (name => $node{'name'}, tag => $tag);
    126 
    127     if ($tag == MDSTR) {
    128         $prop{'string'} =
    129 	  substr($self->{DATA}, $node{'dataoff'}, $node{'datalen'} - 1);
    130     } elsif ($tag == MDARC) {
    131 	$prop{'arc'} = $node{'idx'};
    132     } elsif ($tag == MDVAL) {
    133 	$prop{'val'} = $node{'val'};
    134     } elsif ($tag == MDDATA) {
    135 	$prop{'length'} = $node{'datalen'};
    136 	$prop{'offset'} = $node{'dataoff'};
    137     } else {
    138 	return undef;
    139     }
    140 
    141     return %prop;
    142 }
    143     
    144 
    145 #
    146 # find name table index of given name
    147 #
    148 sub findname {
    149     my ($self, $name) = @_;
    150     my ($idx, $next, $p);
    151 
    152     for ($idx = 0; $idx < $self->{NAME_SEC_SZ}; $idx = $next + 1) {
    153         $next = index($self->{NAMES}, "\0", $idx);
    154 	$p = substr($self->{NAMES}, $idx, $next - $idx);
    155 	return $idx  if ($p eq $name);
    156     }
    157 
    158     return -1;
    159 }
    160 
    161 
    162 #
    163 # find given property in node
    164 #
    165 sub findprop {
    166     my ($self, $nodeid, $propname, $type) = @_;
    167     my (%node, $nameid);
    168 
    169     %node = $self->getnode($nodeid);
    170     return -1  if ($node{'tag'} != MDNODE);
    171 
    172     $nameid = $self->findname($propname);
    173     return -1  if ($nameid == -1);
    174 
    175     do {
    176         $nodeid++;
    177 	%node = $self->getnode($nodeid);
    178 	if ($node{'tag'} == $type && $node{'nameid'} == $nameid) {
    179 	    return $nodeid;
    180 	}
    181     } while ($node{'tag'} != MDEND);
    182 
    183     return -1;
    184 }
    185 
    186 
    187 #
    188 # lookup property in node and return its hash
    189 #
    190 sub lookup {
    191     my ($self, $nodeid, $propname, $type) = @_;
    192     my ($propid);
    193 
    194     $propid = $self->findprop($nodeid, $propname, $type);
    195     return undef  if ($propid == -1);
    196 
    197     return $self->getprop($propid);
    198 }
    199 
    200 
    201 sub scan_node {
    202     my ($self, $nodeid, $nameid, $arcid, $ret, $seen) = @_;
    203     my (%node);
    204 
    205     return  if ($seen->[$nodeid] == 1);
    206     $seen->[$nodeid] = 1;
    207 
    208     %node = $self->getnode($nodeid);
    209     return                if ($node{'tag'} != MDNODE);
    210     push(@$ret, $nodeid)  if ($node{'nameid'} == $nameid);
    211 
    212     do {
    213 	$nodeid++;
    214 	%node = $self->getnode($nodeid);
    215 	if ($node{'tag'} == MDARC && $node{'nameid'} == $arcid) {
    216 	    $self->scan_node($node{'idx'}, $nameid, $arcid, $ret, $seen);
    217 	}
    218     } while ($node{'tag'} != MDEND);
    219 }
    220 
    221 
    222 #
    223 # scan dag from 'start' via 'arcname'
    224 # return list of nodes named 'nodename'
    225 #
    226 sub scan {
    227     my ($self, $start, $nodename, $arcname) = @_;
    228     my ($nameid, $arcid, @ret, @seen);
    229 
    230     $nameid = $self->findname($nodename);
    231     $arcid = $self->findname($arcname);
    232     $self->scan_node($start, $nameid, $arcid, \@ret, \@seen);
    233     return @ret;
    234 }	
    235 
    236 
    237 
    238 package main;
    239 
    240 
    241 #
    242 # 'find' needs to use globals anyway, 
    243 # so we might as well use the same ones
    244 # everywhere
    245 #
    246 our ($old, $new);
    247 our %opts;
    248 
    249 
    250 #
    251 # fix path_to_inst
    252 #
    253 sub fixinst {
    254     use File::Copy;
    255     my ($oldpat, $newpat);
    256     my ($in, $out);
    257 
    258     $oldpat = '"' . $old . '/';
    259     $newpat = '"' . $new . '/';
    260 
    261     $in = "etc/path_to_inst";
    262     $out = "/tmp/path$$";
    263 
    264     open(IN, "<", $in)     or die "can't open $in\n";
    265     open(OUT, ">", $out)   or die "can't open $out\n";
    266 
    267     my ($found, $path);
    268     #
    269     # first pass
    270     # see if there are any old paths that need to be re-written
    271     #
    272     $found = 0;
    273     while (<IN>) {
    274         ($path, undef, undef) = split;
    275         if ($path =~ /^$oldpat/) {
    276             $found = 1;
    277 	    last;
    278 	}
    279     }
    280     # return if no old paths found
    281     if ($found == 0) {
    282         close(IN);
    283 	close(OUT);
    284         unlink $out;
    285         return 0;
    286     }
    287 
    288     print "replacing $old with $new in /etc/path_to_inst\n";
    289     #
    290     # 2nd pass
    291     # substitute new for old
    292     #
    293     seek(IN, 0, 0);
    294     while (<IN>) {
    295         ($path, undef, undef) = split;
    296         if ($path =~ /^$oldpat/) {
    297             s/$oldpat/$newpat/;
    298 	}
    299         print OUT;
    300     }
    301     close(IN);
    302     close(OUT);
    303 
    304     if ($opts{v}) {
    305         print "path_to_inst changes:\n";
    306         system("/usr/bin/diff", $in, $out);
    307         print "\n";
    308     }
    309 
    310     move $out, $in        or die "can't modify $in\n";
    311 
    312     return 1;
    313 }
    314 
    315 
    316 our $oldpat;
    317 
    318 sub wanted {
    319     my $targ;
    320 
    321     -l or return;
    322     $targ = readlink;
    323     if ($targ =~ /$oldpat/) {
    324         $targ =~ s/$old/$new/;
    325         unlink;
    326 	symlink $targ, $_;
    327         print "symlink $_ changed to $targ\n"  if ($opts{v});
    328     }
    329 }
    330 
    331 #
    332 # fix symlinks
    333 #
    334 sub fixdev {
    335     use File::Find;
    336     $oldpat = "/devices" . $old;
    337 
    338     print "updating /dev symlinks\n";
    339     find \&wanted, "dev";
    340 }
    341 
    342 
    343 #
    344 # fixup path_to_inst and /dev symlinks
    345 #
    346 sub fixup {
    347     # setup globals
    348     ($old, $new) = @_;
    349 
    350     # if fixinst finds no matches, no need to run fixdev
    351     return  if (fixinst == 0);
    352     fixdev;
    353     print "\n"  if ($opts{v});
    354 }
    355 
    356 #
    357 # remove caches
    358 #
    359 sub rmcache {
    360     unlink "etc/devices/devid_cache";
    361     unlink "etc/devices/devname_cache";
    362     unlink <etc/devices/mdi_*_cache>;
    363     unlink "etc/devices/retire_store";
    364     unlink "etc/devices/snapshot_cache";
    365     unlink "dev/.devlink_db";
    366 }
    367 
    368 
    369 # $< == 0              or die "$0: must be run as root\n";
    370 
    371 getopts("vR:", \%opts);
    372 
    373 if ($opts{R}) {
    374     chdir $opts{R}   or die "can't chdir to $opts{R}\n";
    375 }
    376 cwd() ne "/"         or die "can't run on root directory\n";
    377 
    378 if ($#ARGV == 1) {
    379     #
    380     # manual run (no MD needed)
    381     #
    382     fixup @ARGV;
    383     rmcache;
    384     exit;
    385 }
    386 
    387 
    388 my ($md, @nodes, $nodeid, @aliases, $alias);
    389 my (%newpath, %roots);
    390 
    391 #
    392 # scan MD for ioaliases
    393 #
    394 $md = MDesc->new;
    395 $md->open;
    396 
    397 @nodes = $md->scan(0, "ioaliases", "fwd");
    398 $#nodes == 0    or die "missing ioaliases node\n";
    399 
    400 #
    401 # foreach ioalias node, replace any 'alias' paths
    402 # with the 'current' one
    403 #
    404 # complicating this is that the alias paths can be
    405 # substrings of each other, which can cause false
    406 # hits in /etc/path_to_inst, so first gather all
    407 # aliases with the same root into a list, then sort
    408 # it by length so we always fix the longer alias
    409 # paths before the shorter ones
    410 #
    411 @nodes = $md->scan(@nodes[0], "ioalias", "fwd");
    412 foreach $nodeid (@nodes) {
    413     my (%prop, $current);
    414 
    415     %prop = $md->lookup($nodeid, "aliases", $md->MDSTR);
    416     @aliases = split(/ /, $prop{'string'});
    417 
    418     %prop = $md->lookup($nodeid, "current", $md->MDSTR);
    419     $current = $prop{'string'};
    420 
    421     foreach $alias (@aliases) {
    422         next  if ($alias eq $current);
    423 
    424         my ($slash, $root);
    425 	$newpath{$alias} = $current;
    426 	$slash = index($alias, '/', 1);
    427 	if ($slash == -1) {
    428 	    $root = $alias;
    429 	} else {
    430 	    $root = substr($alias, 0, $slash);
    431 	}
    432 	push(@{ $roots{$root} }, $alias);
    433     }
    434 }
    435 
    436 my $aref;
    437 foreach $aref (values %roots) {
    438     @aliases = sort { length($b) <=> length($a) } @$aref;
    439     foreach $alias (@aliases) {
    440         fixup $alias, $newpath{$alias};
    441     }
    442 }
    443 
    444 rmcache;
    445