This is a Pliant (http://pliant.cams.ehess.fr/) script that should enable you to make changes in a RAID configuration with (or with the hope of) no data loss. Lets take an example: The old /etc/raidtab configuration file is: raiddev /dev/md0 raid-level 5 nr-raid-disks 3 nr-spare-disks 0 persistent-superblock 1 chunk-size 64 device /dev/hda5 raid-disk 0 device /dev/hda6 raid-disk 1 device /dev/hda7 raid-disk 2 The new one you want is: raiddev /dev/md0 raid-level 5 nr-raid-disks 4 nr-spare-disks 0 persistent-superblock 1 chunk-size 4 device /dev/hda5 raid-disk 0 device /dev/hda6 raid-disk 1 device /dev/hda7 raid-disk 2 device /dev/hda8 raid-disk 3 1) Run the following Pliant command: pliant module /sample/raidconvert.pli command 'raid_convert "/dev/md0" "/dev/hda5 /dev/hda6 /dev/hda7" "/dev/hda5 /dev/hda6 /dev/hda7 /dev/hda8" 5 4*1024' - pameter 2 of 'raid_convert' is the list of the raid devices in the old RAID configuration (sparce disks should not be listed) - pameter 3 is the list of the raid devices in the new RAID configuration (sparce disks should not be listed) - pameter 4 is the raid level in the new RAID configuration - pameter 5 is the new chunk size 2) Modify your /etc/raidtab 3) use 'mkraid' command in order to recreate the new RAID array (your data should be preserved) This script should also enables you to remove some disks, to change the raid level or the chunk size, BUT TEST IT ON SAMPLES BEFORE APPLYING TO ANY SERIOUS DATAS. You must use 'ext2resize' command BEFORE 1 if your new RAID array will be smaller than the old one, or after 3 if it's bigger. You should also be awared that if anything wrong appends during the conversion (any io error), the program will abort ungracefully and all datas will be lost. Good luck brave peoples Hubert Tonneau # Copyright (C) 1999 Hubert Tonneau [EMAIL PROTECTED] # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # version 2 along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # release 1 for Pliant release 25 module "/pliant/v1.pli" module "/pliant/meta.pli" constant sector_size 512 function os_llseek handle high low result whence -> err arg Int handle ; arg uInt high low ; arg Address result ; arg Int whence ; arg Int err kernel_function 140 function pgcd a b -> g arg Intn a b g var Intn x := a var Intn y := b g := y while x>0 g := x x := y%x y := g function ppcm a b -> m arg Intn a b m m := a*b\(pgcd a b) function min a b -> c arg Intn a b c c := shunt a<=b a b type Device field Int handle <- -1 field Str name method d open device_name arg_w Device d ; arg Str device_name d name := device_name var Str namez := device_name+"[0]" d handle := os_open namez:characters 2 0 if d:handle<0 error "Failed to open device "+d:name method d close arg_w Device d if (os_close d:handle)<>0 error "Failed to open device "+d:name d handle := -1 method d seek position arg Device d ; arg Intn position if position%sector_size<>0 error "Missaligned seek applyed to device "+d:name+" ("+(cast position Str)+")" var uInt high := cast position\(cast 2 Intn)^32 uInt var uInt low := cast position%(cast 2 Intn)^32 uInt check high*(cast 2 Intn)^32+low=position if (os_llseek d:handle high low addressof:(var uInt64 result) 0)<>0 error "Failed to set position for device "+d:name method d read buffer size arg Device d ; arg Address buffer ; arg Int size var Int red := os_read d:handle buffer size if red<>size error "Failed to read from device "+d:name method d write buffer size -> status arg Device d ; arg Address buffer ; arg Int size ; arg Status status var Int written := os_write d:handle buffer size if written<>size error "Failed to write to device "+d:name type Raid field Str device_name field Array:Device devices field Int level field Int chunk_size field Intn size method raid define device_name devices_names arg_rw Raid raid ; arg Str device_name devices_names raid:device_name := device_name raid:devices size := 0 var Str names := devices_names while names<>"" if (names match (var Str name1) _ (var Str name2)) (var Device d) open name1 raid:devices += d names := name2 else (var Device d) open names raid:devices += d names := "" method raid data_disks_count -> count arg Raid raid ; arg Int count if raid:level=0 count := raid:devices:size eif raid:level=1 count := 1 eif raid:level=5 count := raid:devices:size-1 else error "raid level "+(cast raid:level Str)+" is not supported yet." method raid query arg_rw Raid raid (var Stream s) open "/proc/mdstat" in while not s:atend if (s:readline match (var Str name) ":" word:"active" "raid" (var Int level) (var Str stat)) if "/dev/"+name=raid:device_name var Int disks_count := 0 while (stat match (var Str drop) "[[]" (var Int i) "[]]" _ (var Str stat2)) and (drop search " " -1)=(-1) disks_count += 1 stat := stat2 if disks_count<>raid:devices:size error "The number of disks in "+raid:device_name+" does not match your declarations." var CBool ok := false if level=0 and (stat match (var Int blocks) word:"blocks" (var Int chunk) "k" word:"chunks") raid chunk_size := chunk*1024 ok := true eif level=1 and (stat match (var Int blocks) word:"blocks" "[[]" (var Str drop) "[]]" "[[]" (var Str ups) "[]]") raid chunk_size := 512 ok := true eif level=5 and (stat match (var Int blocks) word:"blocks" word:"level" (var Int level) "," (var Int chunk) "k" word:"chunk" "," word:"algorithm" (var Int algorithm) "[[]" (var Str drop) "[]]" "[[]" (var Str ups) "[]]") raid chunk_size := chunk*1024 ok := algorithm=0 if ok raid level := level raid size := (cast 1024 Intn)*blocks if raid:size%(raid:chunk_size*raid:data_disks_count)<>0 error "The total "+raid:device_name+" size does not match the chunk size and number of devices" return error "Unexpected "+raid:device_name+" status !" method raid map raid_offset map_parity device device_offset -> continous_size arg Raid raid ; arg Intn raid_offset ; arg CBool map_parity ; arg_w Device device ; arg_w Intn device_offset ; arg Int continous_size if raid_offset<0 or raid_offset>=raid:size error "Attemted to access raid device outside boundaries" if raid:level=0 var Intn chunk_number := raid_offset\raid:chunk_size continous_size := (chunk_number+1)*raid:chunk_size-raid_offset var Int chunk_per_row := raid:devices:size var Intn row := chunk_number\chunk_per_row var Int col := chunk_number%chunk_per_row device := raid:devices col device_offset := row*raid:chunk_size eif raid:level=1 device := raid:devices 0 device_offset := raid_offset continous_size := min raid:size-raid_offset 64*2^10 eif raid:level=5 var Intn chunk_number := raid_offset\raid:chunk_size continous_size := (chunk_number+1)*raid:chunk_size-raid_offset var Int chunk_per_row := raid:devices:size-1 var Intn row := chunk_number\chunk_per_row var Int col := chunk_number%chunk_per_row var Int parity := raid:devices:size-1-row%raid:devices:size if col>=parity col += 1 if map_parity col := parity device := raid:devices col device_offset := row*raid:chunk_size else error "raid level "+(cast raid:level Str)+" is not supported yet." method raid read offset parity buffer size arg Raid raid ; arg Intn offset ; arg CBool parity ; arg Address buffer ; arg Int size var Int done := 0 while done<size var Int step := min (raid map offset+done parity (var Device d) (var Intn o)) size-done d seek o ? d read (buffer translate Byte done) step ? done += step method raid write offset parity buffer size arg Raid raid ; arg Intn offset ; arg CBool parity ; arg Address buffer ; arg Int size var Int done := 0 while done<size var Int step := min (raid map offset+done parity (var Device d) (var Intn o)) size-done d seek o ? d write (buffer translate Byte done) step ? if raid:level=1 for (var Int i) 1 raid:devices:size-1 raid:devices:i write (buffer translate Byte done) step ? done += step function memory_xor buffer1 buffer2 size arg Address buffer1 buffer2 ; arg Int size for (var Int i) 0 size step Int:size (buffer1 translate Byte i) map Int := ((buffer1 translate Byte i) map Int) .xor. ((buffer2 translate Byte i) map Int) function raid_convert raid_device old_devices new_devices new_raid_level new_chunk_size arg Str raid_device old_devices new_devices ; arg Int new_raid_level new_chunk_size (var Raid old) define raid_device old_devices (var Raid new) define raid_device new_devices shell "umount "+raid_device+" 2>/dev/null" shell "raidstop "+raid_device+" 2>/dev/null" if (shell "raidstart "+raid_device)<>0 error "Failed to start RAID array "+raid_device ? old:query ? console "[lf]The old RAID" old:level " array is a " old:size\2^20 " MB RAID" old:level " array on " old:devices:size " disks and " old:chunk_size\1024 " K chunks.[lf]" var Int old_unit := old:chunk_size*old:data_disks_count new level := new_raid_level new chunk_size := new_chunk_size new size := old:size*new:data_disks_count\old:data_disks_count console "The new RAID" new:level " array will be a " new:size\2^20 " MB RAID" new:level " array on " new:devices:size " disks and " new:chunk_size\1024 " K chunks.[lf]" var Int new_unit := new:chunk_size*new:data_disks_count os_sysinfo (var os_SysInfo sys) var Int memory_physical := sys totalram var Int common_unit := ppcm old_unit new_unit while common_unit<64*2^10 common_unit*=2 # console "Conversion step will be " common_unit\2^10 " KB (physical memory is " memory_physical\2^20 " MB)[lf]" if common_unit>4*2^20 and common_unit>memory_physical\2 error "The conversion would consume too much memory" ? console "[lf]THIS IS VERY ALPHA CODE, IT MAY DESTROY ALL YOUR DATAS ![lf]" console "You have 10 seconds to press Ctrl+C if you changed your mind.[lf]" sleep 10 console "So, let's go.[lf][lf]" if true console "step 1: checking consistency in the existing array[lf]" console "It is still safe to stop while step 1 is running.[lf]" (var Device raid) open raid_device var Address md := memory_allocate old_unit null var Address chunks := memory_allocate old_unit null var Address checksum1 := memory_allocate old:chunk_size null var Address checksum2 := memory_allocate old:chunk_size null var Intn position := 0 while position<old:size if position\2^20<>(position-old_unit)\2^20 console "checked " position\2^20 " MB (" 100*position\old:size "%)[cr]" raid seek position ? raid read md old_unit old read position false chunks old_unit if (memory_compare md old_unit chunks old_unit)<>compare_equal error "This program is buggy !" ? if old:level=5 memory_clear checksum1 old:chunk_size for (var Int i) 0 old_unit-1 step old:chunk_size memory_xor checksum1 (chunks translate Byte i) old:chunk_size old read position true checksum2 old:chunk_size if (memory_compare checksum1 old:chunk_size checksum2 old:chunk_size)<>compare_equal error "The raid array is corrupted !" ? position += old_unit memory_free md memory_free chunks memory_free checksum1 memory_free checksum2 raid close if (shell "raidstop "+raid_device)<>0 error "Failed to stop RAID array "+raid_device ? if true console "step 2: moving the datas around[lf]" ? console "IF THIS PROCESS IS STOPPED IN THE MIDDLE, YOUR DATAS WILL BE LOST.[lf]" sleep 2 var Address buffer := memory_allocate common_unit null var Intn size := min old:size new:size var CBool forward := new:data_disks_count>=old:data_disks_count var Intn position := shunt forward 0 (size-1)\common_unit*common_unit while (shunt forward position<size position>=0) console "moved " (shunt forward position size-position)\2^20 " MB (" 100*(shunt forward position size-position)\size "%)[cr]" var Int step := min size-position common_unit old read position false buffer step new write position false buffer step position += shunt forward common_unit -common_unit memory_free buffer console "The conversion is done.[lf]" console "Please update your /dev/raidtab file accordingly[lf]" console "then use 'mkraid --force "+raid_device+"' command[lf]" console "in order to update the raid superblocks"+(shunt new:level>0 " and resync" "")+".[lf]" export raid_convert