2010-04-03

ReadyNAS 600 RAID Recovery with Ubuntu

Assumptions: (you have the following)
Infrant (now Netgear) ReadyNAS 600, Sparc based, kernel: 2.6.17.14ReadyNAS
A Linux system running Ubuntu (would work with live image: Lucid Lynx used for this.) with 4x SATA ports.

Trying to recover data off the disks from a ReadyNAS 600 on Ubuntu is not as straightforward as the existing guides might lead one to believe, especially getting ext2fuse to compile.

First, the guides:
Written specifically for Ubuntu, and succinct. - Useful for an overview.
Recovering a failed ReadyNAS 1100 array - Has a bit more information.
Mounting Sparc-based ReadyNAS Drives in x86-based Linux - Step by step instructions; this is the most detailed, and the one I'm working off of; please refer to this one to make sense of the rest of this post.

If working off a LiveCD, or a new system, be sure to start off making your editor readable, after opening your terminal.

(This post assumes limited to no Linux experience, beyond being capable of burning an Ubuntu .iso to disc. Anyone attempting recovery of a failed RAID is assumed to have basic problem solving skills and the capacity to learn.)
sudo su
cat >> ~/.vimrc
highlight Normal ctermbg=black ctermfg=white
set background=dark
syntax on
CTRL-D
to exit

Now,
agt-get install libfuse-dev
prior to making ext2fuse.  Doing so should avoid this:

In file included from readdir.c:1:
readdir.h:6:27: error: fuse_lowlevel.h: No such file or directory
In file included from readdir.c:1:
readdir.h:8: error: expected ‘)’ before ‘req’
readdir.c:7: error: expected ‘)’ before ‘req’
readdir.c:46: error: expected ‘)’ before ‘req’
make[2]: *** [ext2fuse-readdir.o] Error 1
make[2]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1/src'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1'
make: *** [all] Error 2


You will want to do the following when presented with the library errors, but can safely skip steps 6 thru 8.
ln -s /lib/libcom_err.so.2.1 /lib/libcom_err.so
ln -s /lib/libext2fs.so.2.4 /lib/libext2fs.so

What follows are the errors from running make, and the solutions, with reference as necessary. If you don't get the errors on your system, due to newer headers and the like, you can disregard the (non)relevant solution.  Also, I've 'shown my work', so to speak, so as to lend to evaluation whether my solution is correct or not; after all, one shouldn't blindly follow advice from strangers on the intertubes. For the inpatient, simply scroll to the necessary fix.

First compile error:
imager.c:41: error: conflicting types for ‘ssize_t’
/usr/include/unistd.h:221: note: previous declaration of ‘ssize_t’ was here
make[2]: *** [libext2fs_a-imager.o] Error 1
make[2]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1/lib/ext2fs'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1'
make: *** [all] Error 2
in /usr/include/unistd.h around line 221 we see:
#ifndef __ssize_t_defined
typedef __ssize_t ssize_t;
# define __ssize_t_defined
#endif
and __ssize_t gets defined in: /usr/include/bits/types.h:180
__STD_TYPE __SSIZE_T_TYPE __ssize_t; /* Type of a byte count, or error.  */
__SSIZE_T_TYPE is defined in: /usr/include/bits/typesizes.h:60
#define __SSIZE_T_TYPE          __SWORD_TYPE
And __SWORD_TYPE is defined in /usr/include/bits/types.h:60
# define __SWORD_TYPE           int
So, the solution is to simply comment out the redefinition in imager.c
lib/ext2fs/imager.c
#ifndef HAVE_TYPE_SSIZE_T
#ifndef __FreeBSD__
typedef int ssize_t;
#endif
#endif
Solution:
vi lib/ext2fs/imager.c
//typedef int ssize_t;


Next error:
In function ‘open’,
    inlined from ‘check_mntent_file’ at ismounted.c:153:
/usr/include/bits/fcntl2.h:51: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT in second argument needs 3 arguments
make[2]: *** [libext2fs_a-ismounted.o] Error 1
make[2]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1/lib/ext2fs'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1'
make: *** [all] Error 2
From here: https://wiki.ubuntu.com/CompilerFlags:
When using open() with O_CREAT, best-practice is to define a valid mode argument. For the least modes, try using (S_IRUSR|S_IWUSR) first. If that doesn't work as expected in the program, then start adding back perms. For example, user and group: (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); user, group, and other: (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH).
And additionally: http://opengroup.org/onlinepubs/007908799/xsh/sysstat.h.html
So, the simple solution is:
vi lib/ext2f/ismounted.c, around line 153
from:
fd = open(TEST_FILE, O_RDWR|O_CREAT);
to:
fd = open(TEST_FILE, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);

Next (snipped some duplicate lines for brevity):
llseek.c:68: error: expected declaration specifiers or ‘...’ before ‘_llseek’
llseek.c:68: error: expected declaration specifiers or ‘...’ before ‘fd’
llseek.c:68: error: expected declaration specifiers or ‘...’ before ‘offset_high’
llseek.c:69: error: expected declaration specifiers or ‘...’ before ‘offset_low’
llseek.c:69: error: expected declaration specifiers or ‘...’ before ‘result’
llseek.c:70: error: expected declaration specifiers or ‘...’ before ‘origin’
llseek.c: In function ‘_syscall5’:
llseek.c:74: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token
llseek.c:68: error: parameter name omitted
llseek.c:117: error: expected ‘{’ at end of input
make[2]: *** [libext2fs_a-llseek.o] Error 1
make[2]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1/lib/ext2fs'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/usr/local/bin/ext2fuse-src-0.8.1'
make: *** [all] Error 2
I found a solution here: http://www.mail-archive.com/sisuite-users@lists.sourceforge.net/msg03353.html:
in lib/ext2fs/llseek.c: around line 66
#ifndef __i386__
static int _llseek (unsigned int, unsigned long,
                   unsigned long, ext2_loff_t *, unsigned int);
static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high,
                 unsigned long, offset_low,ext2_loff_t *,result,
                 unsigned int, origin)
comment out, and do:
static int _llseek (unsigned int fd, unsigned long oh,
                   unsigned long ol, long long *result,
                   unsigned int origin) {
       return syscall (__NR__llseek, fd, oh, ol, result, origin);
}

With any luck, it'll compile, and you can `make install` now. Then, this command is needed, from a developer post on the Netgear forums:
mdadm --create /dev/md2 -f -c 16 -l 5 -p la -n 4 /dev/sdb3 /dev/sdc3 /dev/sdd3 missing

Modify the above to suit your actual system (fdisk -lu to see your drives). Then, if you're unfortunate enough not to have a clean volume, you've a bit more work to do (this is with 250gb drives):
pvscan
  Incorrect metadata area header checksum
  PV /dev/md2                      lvm2 [691.11 GiB]
  Total: 1 [691.11 GiB] / in use: 0 [0   ] / in no VG: 1 [691.11 GiB]

pvdisplay /dev/md2
  Incorrect metadata area header checksum
  "/dev/md2" is a new physical volume of "691.11 GiB"
--- NEW Physical volume ---
  PV Name               /dev/md2
  VG Name            
  PV Size               691.11 GiB
  Allocatable           NO
  PE Size               0
  Total PE              0
  Free PE               0
  Allocated PE          0
  PV UUID               Epe2Ox-iNYp-T6nY-fD3F-9zag-HWmq-tMs5rq

So, this is the part you shouldn't do unless it's a last resort. (Reference: http://www.tldp.org/HOWTO/LVM-HOWTO/recovermetadata.html)
(volc.txt was created by doing:
dd if=/dev/md2 bs=512 count=255 skip=1 of=nasconfig
and then manually extracting the LVM config with ghex and putting it in a text file (see references at end))


pvcreate --uuid "Epe2Ox-iNYp-T6nY-fD3F-9zag-HWmq-tMs5rq" --restorefile /etc/lvm/archive/c_00000.vg /dev/md2
  Incorrect metadata area header checksum
  Incorrect metadata area header checksum
  Physical volume "/dev/md2" successfully created



vgcfgrestore -f volc.txt c
Restored volume group c

vgscan
  Reading all physical volumes.  This may take a while...
  Found volume group "c" using metadata type lvm2

vgdisplay
  --- Volume group ---
  VG Name               c
  System ID             nas-00-xx-xx  1221946398
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  2
  VG Access             read/write
  VG Status             resizable
  MAX LV                256
  Cur LV                1
  Open LV               0
  Max PV                256
  Cur PV                1
  Act PV                1
  VG Size               691.06 GiB
  PE Size               32.00 MiB
  Total PE              22114
  Alloc PE / Size       22114 / 691.06 GiB
  Free  PE / Size       0 / 0   
  VG UUID               ghNwGu-XNBV-Rnyc-ojT7-D5IE-hRCp-07uygA

vgchange -ay c
  1 logical volume(s) in volume group "c" now active

lvdisplay
  --- Logical volume ---
  LV Name                /dev/c/c
  VG Name                c
  LV UUID                000000-0000-0000-0000-0000-0000-000000
  LV Write Access        read/write
  LV Status              available
  # open                 0
  LV Size                691.06 GiB
  Current LE             22114
  Segments               1
  Allocation             normal
  Read ahead sectors     1024
  Block device           252:0


If you're lucky, or sacrificed the requisite virginal nerd while chanting source from the Editor of the Beast, you can now mount and retrieve data, wiser and forever having learned that a NAS device doesn't count as 'backing up'.
Further Reference:
http://www.readynas.com/forum/viewtopic.php?t=14828 - Forum thread on the Netgear forums.
http://www.linuxjournal.com/article/8874?page=0,2 - Format of the LVM metadata; good for creating the .txt file to feed back in.
http://www.akadia.com/services/logical_volume_manager.html - An excellent guide to LVM, with pretty pictures!