#!/dcs/packages/python-2.4/bin/python # Purpose: # Take a label from a source disk and call it A, take a label from a # destination disk and call it B, then the dest disk's partitioning # should be identical to the source disk's, and the destination disk's # label should be "A: was B". # Caution: # This -could- toast your disk, but it probably won't. Be sure there's # nothing on the dest disk you want to keep. The program is very # careful about checking, EG, that both disk labels have a "magic # number" known to this program, which should be a pretty strong # indication that the label is in a format we expect. # see /usr/include/sys/dklabel.h # this program is something of an experiment in the payoff of insanely # high levels of error checking. Many things are checked in more than # one place, to stop errors in their tracks as early as possible. import sys import string import os # autoinst-strombrg> cat t.c #include #include # #main() # { # struct dk_label dk_label; # printf("dkl_asciilabel offset: %d\n", # ((char *)&dk_label.dkl_asciilabel) - ((char *)&dk_label)); # printf("dkl_magic offset: %d\n", # ((char *)&dk_label.dkl_magic) - ((char *)&dk_label)); # printf("dkl_cksum offset: %d\n", # ((char *)&dk_label.dkl_cksum) - ((char *)&dk_label)); # } # # #autoinst-strombrg> ./t #dkl_asciilabel offset: 0 #dkl_magic offset: 508 #dkl_cksum offset: 510 # dkl_magic should be at 124 and 125 # dkl_cksum should be at 126 and 127 # ...assuming the first character in the last 128 byte block is numbered 0 def usage(): sys.stderr.write('Usage: %s source-device destination-device\n' % \ sys.argv[0]) sys.stderr.write('EG: %s /dev/dsk/c0t0d0s2 /dev/dsk/c1t3d0s2\n' % \ sys.argv[0]) sys.stderr.write('\n') sys.stderr.write('If you want an exact duplicate label, then just\n') sys.stderr.write('dd the first 512 bytes. That is not what this\n') sys.stderr.write('program is about. This program mostly just copies\n') sys.stderr.write('a disk label from one disk to another, but it\n') sys.stderr.write('-attempts- to leave a note to future admins in the\n') sys.stderr.write('new label, that this was done.\n') sys.stderr.write('\n') sys.stderr.write('This program does some /strange/ stuff, so if it\n') sys.stderr.write('ever "kills" your disk(s), just go into sun format\n') sys.stderr.write('use the "type" command, and then select\n') sys.stderr.write('"autoconfigure" in the menu. This should restore\n') sys.stderr.write('your label to what the manufacturer wanted it to be\n') sys.stderr.write('\n') sys.stderr.write('If your disk still does not work, you were warned!\n') sys.exit(0) un=os.uname()[0] if un != 'SunOS': sys.stderr.write('Sorry, %s requires a SunOS system.\n' % sys.argv[0]) if un[0:1] in 'aAeEiIoOuUyY': an='n' else: an='' sys.stderr.write('You are on a%s %s system.\n' % (an, un)) sys.stderr.write('\n') usage() sys.exit(0) if sys.argv[2:] and not sys.argv[3:]: dev1=sys.argv[1] dev2=sys.argv[2] else: usage() try: file1 = open(dev1,'r') except: sys.stderr.write('%s: Could not open %s for reading\n\n' % \ (sys.argv[0], dev1)) usage() try: file2 = open(dev2,'r+') except: sys.stderr.write('%s: Could not open %s for writing\n\n' % \ (sys.argv[0], dev2)) usage() dev1strings=[] for i in range(4): dev1strings.append(file1.read(128)) file1.close() dev1_512=string.join(dev1strings,'') dev2strings=[] for i in range(4): dev2strings.append(file2.read(128)) dev2_512=string.join(dev2strings,'') if len(dev1_512) != 512: sys.stderr.write('%s: internal error: length of dev1_512 is not 512\n' \ % sys.argv[0]) if len(dev2_512) != 512: sys.stderr.write('%s: internal error: length of dev2_512 is not 512\n' \ % sys.argv[0]) def padout(s): if len(s) > 128: retval = s[:128] else: retval = s while len(retval) < 128: retval=retval+'\0' return retval def strip_trailing_nulls(s): retval=s.strip('\0') return retval def get_uint16(s,offset): #print 'debug:',len(s),offset return ord(s[offset:offset+1])*256 + ord(s[offset+1:offset+2]) #dkl_magic offset: 508 def get_magic(s): return get_uint16(s,508) #dkl_cksum offset: 510 def get_checksum(s): return get_uint16(s,510) def add_new_checksum(s,c): (hi, lo) = divmod(c,256) cstring = chr(hi) + chr(lo) retval = s[:-2]+cstring if len(retval) != 512: sys.stderr.write('%s: internal error: retval len not 512') sys.exit(0) gc = get_checksum(retval) if gc != c: sys.stderr.write('%s: internal error: pulled different checksum than pushed') sys.exit(0) return retval def compute_checksum(s): if len(s) != 512: sys.stderr.write('%s: internal error: string in compute_checksum\n' % sys.argv[0]) sys.stderr.write('is not 512 bytes long\n') sys.exit(1) temp=get_uint16(s,0) # intentionally skipping the last two bytes, which -are- the checksum for offset in range(2,510,2): #print offset temp ^= get_uint16(s,offset) return temp # check that the magic numbers look reasonable magic1 = get_magic(dev1_512) magic2 = get_magic(dev2_512) sys.stderr.write('%s magic number: %x\n' % (dev1,magic1)) sys.stderr.write('%s magic number: %x\n' % (dev2,magic2)) # this should go a long way toward making sure we don't toast some label # that's in an unexpected format if magic1 == 0xdabe: if magic2 == 0xdabe: sys.stderr.write('%s: Good both magic numbers are correct\n' % \ sys.argv[0]) else: sys.stderr.write('%s: Not good: source magic good, dest magic bad\n' % \ sys.argv[0]) else: if magic2 == 0xdabe: sys.stderr.write('%s: Not good: source magic bad, dest magic good\n' % \ sys.argv[0]) else: sys.stderr.write('%s: Not good: both source magic and dest magic bad\n' \ % sys.argv[0]) # verify that our checksum algorithm is correct - for both devices real_checksum1=get_checksum(dev1_512) test_checksum1=compute_checksum(dev1_512) if real_checksum1 == test_checksum1: sys.stderr.write('Good, checkums for %s match\n' % dev1) else: sys.stderr.write('Not good, checkums for %s do not match\n' % dev1) sys.exit(1) real_checksum2=get_checksum(dev2_512) test_checksum2=compute_checksum(dev2_512) if real_checksum2 == test_checksum2: sys.stderr.write('Good, checkums for %s match\n' % dev2) else: sys.stderr.write('Not good, checkums for %s do not match\n' % dev2) sys.exit(1) # the ASCII versions of the original labels, should now be in # strings[0] - all 128 bytes of it, and nothing more. They should be # 128 bytes, and padded with nulls. The remainder of the disk labels # should be in dev?strings[1..3]. # Changes we'll be making: # 1) We'll be making new_dev_strings[0] an amalgam of # dev1strings[0] and dev2strings[0] # 2) new_dev_strings[1..3] will become duplicates of dev1strings[1..3] # 3) We'll need to compute and replace the checksum # 4) Finally, we'll write all four new_dev_strings to dev2 short_ascii_label1 = strip_trailing_nulls(dev1strings[0]) short_ascii_label2 = strip_trailing_nulls(dev2strings[0]) offset = short_ascii_label2.find(', was:') if offset == -1: new_ascii_label = \ padout(short_ascii_label1 + ', was: ' + short_ascii_label2) else: # this label was generated with this program before, most likely. So # rather than making the label longer and longer, just pull out the # original stuff, and use that, instead of the entire (non-original) # label sys.stderr.write('This disk appears to have seen %s before!\n' % \ sys.argv[0]) sys.stderr.write('Will attempt to use original label instead of current.\n') new_ascii_label = \ padout(short_ascii_label1 + short_ascii_label2[offset:]) # quicky sanity check if len(new_ascii_label) != 128: sys.stderr.write('%s: internal error: new_ascii_label is not 128 bytes\n' \ % sys.argv[0]) sys.exit(1) new_dev_strings=[] new_dev_strings.append(new_ascii_label) new_dev_strings.append(dev1strings[1]) new_dev_strings.append(dev1strings[2]) new_dev_strings.append(dev1strings[3]) # at this point, new_dev_strings should hold 4 128-byte strings. The # first is a custom label identifying this disk (if there's enough room) # as something that has had a label duplicated from another disk, and # what the original label was. # the remaining 3 128-byte strings are the partition data (and perhaps # something more than that too?) from the source disk # all four of these strings are about to be written to the destination # disk block=string.join(new_dev_strings,'') # another quicky sanity check if len(block) != 512: sys.stderr.write('%s: internal error: new_ascii_label is not 128 bytes\n' \ % sys.argv[0]) sys.exit(1) new_checksum = compute_checksum(block) newblock=add_new_checksum(block,new_checksum) # go ahead and write it... file2.seek(0) file2.write(newblock) file2.close() sys.stderr.write('%s: new label written successfully\n' % sys.argv[0]) sys.stderr.write('%s: new ascii portion of label written was:\n' % sys.argv[0]) sys.stderr.write('%s\n' % new_ascii_label.rstrip('\0'))