/* ------------------------------------------------------------------ Filename : inf_vgda_mgr.c Author : Phil Gibbs - Trinem Consulting (pgibbs@trinem.co.uk) Date Created : 26/02/98 Description : Allows low-level manipulation of the VGDA/LVM reserved areas on a specified disk. Allows VGDA areas to be backed-up/restored, copied from other disks and for search and replace operations to be performed. COMPILATION : cc inf_vgda_mgr.c -g -o inf_vgda.mgr -------------------------------------------------------------------- Calls : Standard I/O library Called By : inf_disk_menu Returns : 0 success 1 failure Environment : AIX/6000 version 4.1.2 Pre-requisites : -------------------------------------------------------------------- Version History Version Date Author Brief Description of Change 10.1 26/02/98 PAG Initial Version -------------------------------------------------------------------- SCCS Information : @(#) inf_vgda_mgr.c 10.1@(#) SCCS fullpath : /data/repos/stores/opt/inf/bin/SCCS/s.inf_vgda_mgr.c SCCS Release : 10 SCCS Level : 1 SCCS Branch : 0 SCCS Sequence : 0 SCCS Last Modified : 98/02/26, 09:36:38 ----------------------------------------------------------------- */ extern char *sccs_id="@(#) inf_vgda_mgr.c 10.1@(#)"; #include #include #include #include #include #include #include #include #define SECTOR_SIZE 512 #define BLOCK_SIZE 512 typedef struct repl_t{ char old_pattern[8]; char new_pattern[8]; struct repl_t *next; } REPL; typedef struct pvid_t { unsigned long word1; unsigned long word2; } PVID; typedef struct vgda_bandr_t{ PVID pvid; struct lvm_rec lvm_record; char vgda_area[1]; } VGDA_BANDR; char *UsageString= "Usage : inf_vgda_mgr -d pvname [-w] [-b filename | -r filename] [-h] [-z] [-D]\n\ [-c VGDA source disk] [-v new vgid] [-p new pvid] \n\ [-o oldpattern -n newpattern]..\n\ inf_vgda_mgr -d pvname -B\n"; char *HelpString= "Where :\n\ \n\ -d diskname (hdisk0, hdisk1 etc) to scan or update.\n\ -w Write Back. Updates to the specified disk will NOT occur unless this\n\ flag is set. (unless restoring from a backup with -r)\n\ -b Backup. Write the VGDA area from the specified disk to the specified\n\ file.\n\ -r Restore. Restores the VGDA area to the specified disk from the\n\ specified file.\n\ -h This help text.\n\ -z Zap. Clear the VGDA of the specified disk.\n\ -D Dump out VGDA (in hex).\n\ -c VGDA source disk. If this option is given the VGDA area(s) are copied\n\ from the specified disk.\n\ -v VGID. New volume group id to write to the specified disk.\n\ -p PVID. New Physical Volume id to write to the specified disk.\n\ -o oldpattern and\n\ -n newpattern (must be given as pairs). Patterns to search and replace\n\ within the VGDA of the specified disk. Patterns must be specified\n\ as 16 hexadecimal characters (8 bytes). When the -o pattern is found\n\ it is replaced by the associated -n pattern. Patterns are only found\n\ on 8-byte boundaries and not as a \"rolling window\". Any number of\n\ -o/-n combinations may be given.\n\ -B Show Bad Block Relocation Table.\n\ "; extern int optind; extern int Optopt; extern int Opterr; extern char *optarg; short WriteBack; short NewVolumeGroup; unsigned long NvgWord1; unsigned long NvgWord2; void ExitWithError(char *errtext) { /* * Prints specified error text to stderr and exits code 1. */ fprintf(stderr,"%s\n",errtext); exit(1); } void FileRead( void *Pointer, size_t size, size_t NumberOfItems, FILE *Stream) { size_t ItemsTransferred; ItemsTransferred=fread(Pointer,size,NumberOfItems,Stream); if (ItemsTransferred!=NumberOfItems) { ExitWithError("General failure during disk read operation"); } } long GotoVGDA(FILE *rawdisk,short copyno,long *StartOfVGDA) { struct lvm_rec lvm_record; size_t res; /* * Goto the "lvm" information (held at sector PSN_LVM_REC) */ fseek(rawdisk,PSN_LVM_REC*SECTOR_SIZE,SEEK_SET); /* * Read the "lvm" information into the lvm_record structure. */ FileRead((void *)&lvm_record,sizeof(lvm_record),1,rawdisk); if (WriteBack && NewVolumeGroup) { /* * This disk needs its volume group id updating. */ lvm_record.vg_id.word1=NvgWord1; lvm_record.vg_id.word2=NvgWord2; fseek(rawdisk,PSN_LVM_REC*SECTOR_SIZE,SEEK_SET); res=fwrite((void *)&lvm_record,sizeof(lvm_record),1,rawdisk); if (res!=1) { ExitWithError("Cannot write back LVM area"); } } /* * Now seek to the start of the VGDA on this disk. There are * two copies the start of which are held in the array * "vgda_psn". */ *StartOfVGDA=(lvm_record.vgda_psn[copyno]*SECTOR_SIZE); fseek(rawdisk,*StartOfVGDA,SEEK_SET); return (lvm_record.vgda_len)*BLOCK_SIZE; } FILE *OpenRawDiskFile(char *Filename) { FILE *rawdisk; char infile[128]; strcpy(infile,"/dev/r"); strcat(infile,Filename); rawdisk=fopen(infile,"r+"); if (rawdisk) { return rawdisk; } else { ExitWithError("Cannot open raw disk"); } } FILE *OpenCookedDiskFile(char *Filename) { FILE *rawdisk; char infile[128]; strcpy(infile,"/dev/"); strcat(infile,Filename); rawdisk=fopen(infile,"r+"); if (rawdisk) { return rawdisk; } else { ExitWithError("Cannot open cooked disk"); } } void UpdatePVID(char *diskname, unsigned long PVIDword1, unsigned long PVIDword2) { /* * Changes the PVID on the named disk. */ size_t res; char buffer[256]; FILE *realdisk; PVID *pvidptr; realdisk=OpenCookedDiskFile(diskname); fseek(realdisk,0,SEEK_SET); FileRead((void *)buffer,1,sizeof(buffer),realdisk); pvidptr=(PVID *)&(buffer[128]); pvidptr->word1=PVIDword1; pvidptr->word2=PVIDword2; if (WriteBack) { fseek(realdisk,0,SEEK_SET); res=fwrite((void *)buffer,1,sizeof(buffer),realdisk); if (res!=sizeof(buffer)) { ExitWithError("Failed to write new PVID to disk"); } fflush(realdisk); } fclose(realdisk); } short HexChar(char c) { if ((c>='0' && c<='9') || (c>='a' && c<='f')) { return (c>='0' && c<='9')?((short)c-'0'):(((short)c-'a')+10); } else { ExitWithError("Invalid hex digit in pattern"); } } void TranslatePattern(char *IpPattern,char *OpPattern) { char *c,*p; short Val; c=IpPattern; p=OpPattern; if (strlen(c)==16) { while (*c) { Val=HexChar(*c++); Val=(Val*16)+HexChar(*c++); *p=(char)Val; p++; } } else { ExitWithError("Pattern does not have 16 characters (8 bytes)"); } } void GetNewEntry(REPL **sp,REPL **cp) { if (!(*sp)) { *sp=(REPL *)malloc(sizeof(REPL)); *cp=*sp; } else { (*cp)->next=(REPL *)malloc(sizeof(REPL)); *cp=(*cp)->next; } (*cp)->next=(REPL *)NULL; } long ReadFourBytes(char *pos) { short k; long total; for (k=0;k<=3;k++) { total=(total*256)+(short)pos[k]; } return total; } void ReadTwoWords( char *optarg, unsigned long *Word1, unsigned long *Word2) { char NewVGid[8]; TranslatePattern(optarg,NewVGid); *Word1=ReadFourBytes(NewVGid); *Word2=ReadFourBytes(&(NewVGid[4])); } void GetOption(char option,REPL **sp,REPL **cp,char *optarg) { static char last_option='\0'; static short GotOld=1; static short GotNew=1; if (GotOld && GotNew) { GetNewEntry(sp,cp); GotOld=0; GotNew=0; if (!last_option) last_option=(option=='n')?'o':'n'; } if (option=='o' && last_option=='n') { TranslatePattern(optarg,(*cp)->old_pattern); GotOld=1; } else if (option=='n' && last_option=='o') { TranslatePattern(optarg,(*cp)->new_pattern); GotNew=1; } else { ExitWithError("-n and -o pattern pairs must be together"); } last_option=option; } short CompareEightBytes(char *seq1,char *seq2) { short k; short match; match=1; for (k=0;k<=7 && match;k++) { if (seq1[k]!=seq2[k]) match=0; } return match; } char nibble(short nib) { return (nib<=9)?'0'+nib:'a'+(nib-10); } void EightByteString(char *seq,char *res) { short k,p; p=0; for (k=0;k<=7;k++) { res[p]=nibble(seq[k]>>4); res[p+1]=nibble(seq[k]&0xf); p=p+2; if (k&1) res[p++]=' '; } res[p]='\0'; } void CopyMirroredVGDA( FILE *destdisk, FILE *sourcedisk, long DestVGDAlen, short copyno) { char *buffer; size_t res; long Dummy; long SourceVGDAlen; /* * Get VGDA area from source disk. */ SourceVGDAlen=GotoVGDA(sourcedisk,copyno,&Dummy); if (SourceVGDAlen!=DestVGDAlen) { ExitWithError("VGDAs are different lengths - cannot copy"); } buffer=(char *)malloc(SourceVGDAlen); FileRead(buffer,SourceVGDAlen,1,sourcedisk); /* * Now write to the destination disk - we've already been lined up * to the correct point before the call is made to this function. */ if (WriteBack) { res=fwrite(buffer,DestVGDAlen,1,destdisk); if (res != 1) { ExitWithError("Cannot update VGDA from mirror"); } } free(buffer); } void RestoreVGDA(char *diskname,char *backup_file_name) { VGDA_BANDR *vgda_backup; unsigned long vgda_backup_size; long VGDAstart; long VGDAlen; PVID *pvidptr; FILE *rawdisk; FILE *ipfile; struct stat FileInfo; short res; size_t BytesWritten; short copyno; res=stat(backup_file_name,&FileInfo); if (res==-1) { ExitWithError("Cannot read VGDA backup file"); } vgda_backup_size=(unsigned long)FileInfo.st_size; ipfile=fopen(backup_file_name,"r"); if (!ipfile) { ExitWithError("Cannot open VGDA backup file"); } vgda_backup=(VGDA_BANDR *)malloc(vgda_backup_size); FileRead((void *)vgda_backup,vgda_backup_size,1,ipfile); fclose(ipfile); rawdisk=OpenRawDiskFile(diskname); /* * First Restore the LVM area */ fseek(rawdisk,PSN_LVM_REC*SECTOR_SIZE,SEEK_SET); BytesWritten=fwrite( (void *)&(vgda_backup->lvm_record), 1, sizeof(vgda_backup->lvm_record), rawdisk ); if (BytesWritten!=sizeof(vgda_backup->lvm_record)) { ExitWithError("Failed to write LVM area correctly"); } /* * Now update the PVID */ UpdatePVID(diskname,vgda_backup->pvid.word1,vgda_backup->pvid.word2); /* * Now restore the VGDA area itself (both copies) */ for (copyno=0;copyno<=1;copyno++) { VGDAlen=GotoVGDA(rawdisk,copyno,&VGDAstart); BytesWritten=fwrite( (void *)&(vgda_backup->vgda_area), 1, VGDAlen, rawdisk ); if (BytesWritten!=VGDAlen) { ExitWithError("Error restoring VGDA copy"); } } } void BackupVGDA(char *diskname,char *backup_file_name) { VGDA_BANDR *vgda_backup; unsigned long vgda_backup_size; PVID *pvidptr; size_t res; FILE *rawdisk; FILE *realdisk; FILE *opfile; char buffer[256]; long StartOfVGDA; struct lvm_rec lvm_record; rawdisk=OpenRawDiskFile(diskname); realdisk=OpenCookedDiskFile(diskname); /* * Get the LVM structure. This points to the VGDA and we will * be backing this up as well. */ fseek(rawdisk,PSN_LVM_REC*SECTOR_SIZE,SEEK_SET); FileRead( (void *)&lvm_record, sizeof(lvm_record), 1, rawdisk ); /* * Calculate size of backup area. This is the size of our * control structure + size of VGDA as read from the LVM * control block. */ vgda_backup_size=sizeof(VGDA_BANDR)+(lvm_record.vgda_len*BLOCK_SIZE); /* * Grab enough space... */ vgda_backup = (VGDA_BANDR *)malloc(vgda_backup_size); /* * Now copy the LVM structure into our control structure. */ vgda_backup->lvm_record = lvm_record; /* * Now goto the VGDA and read it into the "vgda_area" array of * our control structure. */ StartOfVGDA=(lvm_record.vgda_psn[0]*SECTOR_SIZE); fseek(rawdisk,StartOfVGDA,SEEK_SET); FileRead( &(vgda_backup->vgda_area), lvm_record.vgda_len*BLOCK_SIZE, 1, rawdisk); /* * Now get the PVID for this disk. */ fseek(realdisk,0,SEEK_SET); FileRead((void *)buffer,1,sizeof(buffer),realdisk); fclose(realdisk); pvidptr=(PVID *)&(buffer[128]); /* * Copy the PVID for this disk into our control structure. */ vgda_backup->pvid.word1 = pvidptr->word1; vgda_backup->pvid.word2 = pvidptr->word2; /* * Finally, write the control structure out to the backup file. */ opfile=fopen(backup_file_name,"w"); if (opfile) { res=fwrite((void *)vgda_backup,1,vgda_backup_size,opfile); if (res!=vgda_backup_size) { ExitWithError("Failed to write to backup file"); } fclose(opfile); } else { ExitWithError("Could not open backup file for writing"); } } void SearchAndReplaceVGDA( FILE *rawdisk, REPL *repl, long VGDAlen, long VGDAstart, short copyno, short zapVGDA) { REPL *cp; char *seq1; short match; char *buffer; char str1[21],str2[21]; size_t res; /* * Read entire area to be scanned into buffer. */ buffer=(char *)malloc(VGDAlen); if (zapVGDA) { /* * If zapVGDA set then clear entire buffer. */ bzero(buffer,VGDAlen); } else { /* * Search and replace VGDA area */ FileRead(buffer,VGDAlen,1,rawdisk); seq1=buffer; while (seq1old_pattern); if (match) { EightByteString(seq1,str1); EightByteString(cp->new_pattern,str2); printf("%07o %s <- %s\n", VGDAstart+((long)seq1-(long)buffer), str1,str2 ); bcopy(cp->new_pattern,seq1,8); } cp=cp->next; } seq1=seq1+8; } } if (WriteBack) { VGDAlen=GotoVGDA(rawdisk,copyno,&VGDAstart); res=fwrite(buffer,1,VGDAlen,rawdisk); if (res!=VGDAlen) { ExitWithError("bad reply from fwrite call"); } } free(buffer); } void DumpVGDAinHex(FILE *rawdisk,long VGDAlen) { char *buffer; char *ptr; long BytesDumped=0; char OutputLine[80]; buffer=(char *)malloc(VGDAlen); FileRead(buffer,VGDAlen,1,rawdisk); ptr=buffer; while (BytesDumped < VGDAlen) { sprintf(OutputLine,"%07o ",BytesDumped); EightByteString(&(buffer[BytesDumped]),&(OutputLine[9])); EightByteString(&(buffer[BytesDumped+8]),&(OutputLine[29])); BytesDumped=BytesDumped+16; printf("%s\n",OutputLine); } free(buffer); } void ShowBadBlockTable(char *diskname,FILE *rawdisk) { struct bb_hdr bb_header; struct bb_entry bb_entry; struct bb_entry *bbEntryPtr; unsigned long BytesToRead; short k; char *buffer; char *ReasonText; fseek(rawdisk,PSN_BB_DIR*SECTOR_SIZE,SEEK_SET); FileRead((void *)&bb_header,1,sizeof(bb_header),rawdisk); if (!strcmp(bb_header.id,"DEFECT")) { /* DEFECT list - okay to continue */ printf("%s: %hd Bad Block Relocates\n", diskname, bb_header.num_entries); if (bb_header.num_entries > 0) { /* dump out the relocation list */ BytesToRead=sizeof(bb_entry)*bb_header.num_entries; buffer=(char *)malloc(BytesToRead); FileRead((void *)buffer,1,BytesToRead,rawdisk); bbEntryPtr=(struct bb_entry *)buffer; for (k=0;kreason) { case BB_PVMNFCTR: ReasonText="BB_PVMNFCTR"; break; case BB_DIAGTST: ReasonText="BB_DIAGTST"; break; case BB_SYSTEM: ReasonText="BB_SYSTEM"; break; case BB_MNFCTRTST: ReasonText="BB_MNFCTRTST"; break; default: ReasonText="Unknown"; break; } printf("%ld -> %ld [%s]\n", bbEntryPtr->bb_lsn, bbEntryPtr->rel_lsn, ReasonText); bbEntryPtr++; } } } else if (!strcmp(bb_header.id,"UPDATE")) { printf("Bad Block relocation table is being updated\n"); } else { printf("Failed to access Bad Block relocation table\n"); exit(1); } } main(int argc,char **argv) { FILE *rawdisk; FILE *otherdisk; int option; char diskname[128]; char MirroredDiskName[128]; char BackupFilename[128]; REPL *sp,*cp; long VGDAlen; long StartOfVGDA; short copyno; short zapVGDA=0; short UpdatePVIDflag=0; short HelpGiven=0; short BackupFlag=0; short RestoreFlag=0; short ShowBadBlockTableFlag=0; short DumpVGDAFlag=0; unsigned long NewPvid1; unsigned long NewPvid2; optind=1; sp=(REPL *)0; WriteBack=0; NewVolumeGroup=0; MirroredDiskName[0]='\0'; diskname[0]='\0'; while ((option=getopt(argc,argv,"BDhzwd:o:n:v:c:p:b:r:")) != EOF) { switch(option) { case 'b': BackupFlag=1; strcpy(BackupFilename,optarg); break; case 'r': RestoreFlag=1; WriteBack=1; strcpy(BackupFilename,optarg); break; case 'D': DumpVGDAFlag=1; break; case 'h': printf(UsageString); printf(HelpString); HelpGiven=1; break; case 'z': zapVGDA=1; break; case 'p': ReadTwoWords(optarg,&NewPvid1,&NewPvid2); UpdatePVIDflag=1; break; case 'v': ReadTwoWords(optarg,&NvgWord1,&NvgWord2); NewVolumeGroup=1; break; case 'c': strcpy(MirroredDiskName,optarg); break; case 'd': strcpy(diskname,optarg); break; case 'o': case 'n': GetOption((char)option,&sp,&cp,optarg); break; case 'w': WriteBack=1; break; case 'B': ShowBadBlockTableFlag=1; break; case '?': ExitWithError(UsageString); break; } } if (HelpGiven) { exit(0); } if (BackupFlag && RestoreFlag) { ExitWithError("-b and -r options are mutually exclusive"); } if (BackupFlag) { /* Backup specified - backup before any changes made */ BackupVGDA(diskname,BackupFilename); } else if (RestoreFlag) { /* Restore specified - restore and then perform changes */ RestoreVGDA(diskname,BackupFilename); } if (diskname[0]) { rawdisk=OpenRawDiskFile(diskname); } else { /* no -d option given */ ExitWithError(UsageString); } if (ShowBadBlockTableFlag) { ShowBadBlockTable(diskname,rawdisk); exit(0); } if (MirroredDiskName[0]) otherdisk=OpenRawDiskFile(MirroredDiskName); if (UpdatePVIDflag) UpdatePVID(diskname,NewPvid1,NewPvid2); for (copyno=0;copyno<=1;copyno++) { /* * for each copy of the VGDA area... */ VGDAlen=GotoVGDA(rawdisk,copyno,&StartOfVGDA); if (DumpVGDAFlag && copyno==0) { DumpVGDAinHex(rawdisk,VGDAlen); } if (MirroredDiskName[0]) { CopyMirroredVGDA( rawdisk, otherdisk, VGDAlen, copyno); } else { if (sp || zapVGDA) { /* * Elements to search/replace */ SearchAndReplaceVGDA( rawdisk, sp, VGDAlen, StartOfVGDA, copyno, zapVGDA); } } } if (otherdisk) fclose(otherdisk); if (rawdisk) fclose(rawdisk); exit(0); }