Recovering photos from flash card
During Thanksgiving weekend, I had an interesting challenge.
A friend of mine lost his photos on a xD when transferring them to his PC running the you-know-what-OS. Somehow the disk became "unformatted". He came to me with little hope that those photos can be recovered.
The first thing I thought was that maybe it is just the partition table been destroyed, if I can restore the partition table, which should be relative simple, then we will be good.
Anyway, we needed to get the disk image first. So with my laptop running Nevada build 50, I did following:
$ dd if=/dev/dsk/c3t0d2p0 of=xd.img
Used xxd which is distributed with vim, I peeked into the first sector to see if there is 0x55AA mark. It looked like a directory structure for FAT and of course, 0x55AA was not there. Look further, the FAT table was ruined as well.
Now it would be hard to recover from the file system perspective. Since this is digital camera, most of it would be JPEG files and those are what we care about, and more likely it would be continuous data rather than scatter around the disk. With that assumption, I googled for the JPEG file format, and find this page by TsuruZoh Tachibanaya. With that, I was able to write a simply Ruby script to play with the disk image.
Viola, 567 images are recovered and only 4 of them having problem. Happy Thanksgiving!
For those who might be interested, here is the script. Not perfect but working, and given that I am still learning the language, comments are welcome.
def fn(sno, ext="jpg")
sprintf("F%06d.%s", sno, ext)
end
def to_hex(x)
sprintf("%X", x)
end
def find_SOI(f)
found = false
until f.eof? || found
x = f.getc
if x == 0xff then
x = f.getc
if x == 0xd8 then
found = true
print "SOI detected at #{to_hex(f.pos - 2)}\n"
else
f.ungetc(x)
end
end
end
found
end
def find_EOI(f, of)
found = false
until f.eof? || found
x = f.getc
of.putc(x)
if x == 0xff then
x = f.getc
if x == 0xd9 then
found = true
of.putc(x)
print "EOI detected at #{to_hex(f.pos - 2)}\n"
else
f.ungetc(x)
end
end
end
found
end
def find_SOS(f, of)
found = false
until found
x = f.getc
if x != 0xff then
print "Error: Marker expected at #{to_hex(f.pos - 1)}\n"
return false
end
of.putc(x)
x = f.getc
of.putc(x)
print "Marker 0xFF#{to_hex(x)} detected at #{to_hex(f.pos - 2)},"
if x == 0xda then
found = true
end
x = f.getc
of.putc(x)
sz = x << 8
x = f.getc
of.putc(x)
sz |= x
print " with size #{sz}\n"
of.write(f.read(sz - 2))
end
found
end
f = File.open(ARGV[0])
seq = 0
until f.eof?
rv = find_SOI(f)
if rv then
seq += 1
of=File.open("#{fn(seq)}", "w")
of.putc(0xff)
of.putc(0xd8)
rv = find_SOS(f, of) && find_EOI(f, of)
of.close
unless rv then
File.rename(fn(seq), fn(seq, "dat"))
end
end
end
f.close
# vim: set ts=2 sw=2 tw=80 et: