Dann und wann geschieht es: der lvm verliert seine Konfiguration oder durch andere traurige Umstände hat man lediglich noch einen Haufen „extents“ bekannter Länge vorliegen, die einmal eine Platte waren.
Dem Autor passierte dies beim versehentlichen Löschen einer falschen virtual Disk im IBM SVC („SAN Volume Controller“).
Bekannt war hier noch, dass ein Solaris UFS mit knapp 1TB Grösse verloren gegangen war.
Schaut man sich dann in Ruhe ein paar Grundlagen zu UFS-Datenstrukturen auf disk an, etwa in
http://www.informit.com/articles/article.aspx?p=605371&seqNum=2, und besorgt sich einige „magic“ Konstanten aus den guten alten .h-files in einem alten Solaris, kann man sich ein kleines C-Programm wie dieses
findb.c: #define _LARGEFILE64_SOURCE #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* #define FS_MAGIC 0x011954 #define CG_MAGIC 0x090255 */ #define FS_MAGIC 0x54190100 #define FSMOFF 343 #define CG_MAGIC 0x55020900 #define NEXT 2113 #define ESIZE 512*1024*1024 static char edata[ESIZE]; #define HEADL 32768*2048 #define SBDIST 6495 #define SBONE 2 long *edata32; main() { long rb; int fd; unsigned int extno,boff,isbi,x,cgno; off64_t offs, offo; extno=0; edata32=&edata; fd=open("/dev/sdb",O_RDONLY); if (fd<0) { printf("open errorn"); exit(99); } while(extno < NEXT) { printf("reading extent number %dn",extno); rb=read(fd,edata,ESIZE); if(rb != ESIZE) { printf("short readn"); exit(98); } /* rb=read(fd,edata,HEADL); if(rb != HEADL) { printf("short readn"); exit(98); } offs=ESIZE-HEADL; offo=lseek64(fd,offs,SEEK_CUR); if(offo == (off64_t) -1 ) {printf("seek errorn"); exit(97);} */ /* printf(" sb %08x cg %08x n",edata32[2050],edata32[4097]); */ boff=0; while((boff*4) <ESIZE) { /* while((boff*4) <HEADL) { */ /* if(edata32[343+boff]==FS_MAGIC) { printf("fs magic found, ext=%d off=%d sblkno= %08xn ",extno,boff/2048,edata32[2+boff]); */ /* isb=SBONE; while(isb < NEXT*65536) { if((isb & 0x0ffff) == boff/2048) break; isb=isb+SBDIST; } printf("isb=%d, ext=%dn",isb,isb/65536); } */ if(edata32[1+boff]==CG_MAGIC) { x=edata32[3+boff]; cgno= (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24); printf("CG_MAGIC match, ext=%d off=%d cgx=%d time=%08x cyls=%dn",extno,boff/2048,cgno,edata32[2+boff],edata32[4+boff]>>24); } boff=boff+2048; } extno=extno+1; } }
schreiben, was in der hier wiedergegebenen Form für alle Cylindergroup-Strukturen den Wert deren offset „cgx“ ausgibt, eben den laufenden index der jeweiligen cyclindergroup Struktur. Beachte die Zweckmässigkeit des Programms (nur ganz notwendige Fehlerabfragen) und die Tricks, um Solaris/Sparc Zahlen (BigEndian) in Linux/X86 Zahlen (LittleEndian) umzurechnen (Konstanten wurden per Hand konvertiert).
Durch Sortieren der extents (bekannter Grösse) kann man so mit etwas Glück die originale Reihenfolge der extents rekonstruieren. Dabei hat dieses script gute Dienste geleistet:
fsbchain.sh: #!/bin/sh -vx SBONE=0 SB=$SBONE sbf=xfb64ec.out while true ; do l1=`grep "cgx=$SB$" $sbf` if [ $? -ne 0 ]; then echo end of chain exit fi echo $l1 set - $l1 SBEXT=$1 echo `echo $SBEXT | cut -d = -f 2`>>extlist ln=`grep "$SBEXT " $sbf | tail -1` echo $ln SBL=`echo $ln | cut -d = -f 3` SB=`expr $SBL + 1` echo $SB #read done
Mit ein paar weiteren shell scripten, die sicherstellen, dass nur valide CG-Blocks gezählt werden (deswegen werden Grösse und Datum mit ausgegeben) und einem anderen kleinen Hilfsprogramm
copyb.c: #define _LARGEFILE64_SOURCE #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <limits.h> #define ESIZE 512*1024*1024 static char edata[ESIZE]; main(argc,argv) int argc; char *argv[]; { long rb; int infd, outfd; unsigned int extno,boff,isbi,x,cgno; off64_t ioffs, ooffs, offo; int ein,eout; ein=atoi(argv[1]); eout=atoi(argv[2]); printf("copying extent %d to extent %dn",ein,eout); infd=open("/dev/sdb",O_RDONLY); if (infd<0) { printf("input open errorn"); exit(99); } outfd=open("/dev/sdc",O_RDWR); if (outfd<0) { printf("output open errorn"); exit(99); } ioffs=(off64_t) ein * ESIZE; ooffs=(off64_t) eout * ESIZE; printf("input seek to %llu output seek to %llun",ioffs,ooffs); offo=lseek64(infd,ioffs,SEEK_SET); if(offo < 0 ) {printf("input seek errorn"); exit(97);} offo=lseek64(outfd,ooffs,SEEK_SET); if(offo < 0 ) {printf("output seek errorn"); exit(97);} rb=read(infd,edata,ESIZE); if(rb != ESIZE) { printf("short readn"); exit(98); } rb=write(outfd,edata,ESIZE); if(rb != ESIZE) { printf("short writen"); exit(98); } }
zum effektiven Zusammensetzen des disk /dev/sdc aus den Daten der /dev/sdb nach einer eingelesenen Steuerdatei der Form
[root@nisad-pc03 ~]# head -10 extlist 12 307 21 407 268 304 305 19 314 357 [root@nisad-pc03 ~]#
Ob ähnliches Vorgehen bei ext2/ext3 Filesystemen gelingen würde dort gibt es sogenannte block groups, die wohl den cylinder groups im ufs entsprechen, mit s_block_group_nr (block group # of this superblock) wird sich bei Gelegenheit zeigen (müssen).