piątek, 6 czerwca 2008

Odzyskiwanie plików raw z Canon EOS 300D (crw) w systemie linux

Jak pisałem w poprzednim wpisie Canon EOS 40D zniszczył istotne struktury systemu plików na karcie pamięci która była sformatowana w trochę nietypowy ale zgodny ze standardem sposób, a której używałem z Canonem 300D. W tym wpisie przedstawię zdarzenie bardziej szczegółowo od strony technicznej oraz dla mniej zainteresowanych tematem podam gotowe rozwiązanie na wypadek gdyby ktoś znalazł się w podobnej sytuacji*.

*Prawdopodobnie istnieją programy dzięki którym można odzyskać pliki crw szybciej i bezpieczniej. Jeśli interesuje cię tylko odzyskanie plików lepiej poszukaj czegoś innego niż sposób tu opisany. Jeśli chcesz się dowiedzieć czegoś o tym, dlaczego i jak 40D zniszczył dane, budowie pliku crw lub użyciu bardzo uniwersalnego narzędzia jakim jest magicrescue - zapraszam do lektury.

Przyczyna:

System plików używany powszechnie w przypadku kart pamięci to FAT 16 lub FAT 32 będący już bardzo leciwym tworem. Popularność tego systemu plików wynika z jego prostoty oraz obsługi przez starsze wersje systemu Windows. Niestety system FAT był z biegiem lat rozszerzany i poddawany modyfikacjom - Microsoft w zasadzie dodawał do tego systemu co tylko potrzebował nie precyzując żadnego standardu (miłośnikom czarnego humoru informatycznego polecam "Microsoft FAT 32 White Paper", który, gdyby nie prawa autorskie, powinien się w większej części znaleźć tutaj).

W wyniku takiego postępowania wytworzył się "niby standard", który jest "niby obsługiwany" w różnych urządzeniach. W moim antycznym Canonie 300D jest system operacyjny oparty o DOS więc obsługa FAT jest w miarę prawidłowa, w 40D natomiast, podobnie jak w wielu innych urządzeniach, obsługa FAT jest uproszczona i polega na pewnych założeniach w stosunku do struktury systemu plików.

Nie wnikając w motywy producentów sprzętu, które są najprawdopodobniej natury finansowej, mogę zaakceptować takie ograniczenia obsługi systemu plików przez urządzenie, gdyż w większości przypadków nośnik będzie w obsługiwanym formacie a jeśli urządzenie nie będzie w stanie obsłużyć zastanego formatu wystarczy kartę sformatować w urządzeniu z którym ma działać. Jeśli urządzenie nie może poprawnie obsłużyć systemu plików jaki znajdzie na nośniku powinno co najwyżej wyświetlić komunikat o błędzie.

Niestety inżynierowie w firmie Canon nie przewidzieli takiej możliwości albo też księgowi i prawnicy canona stwierdzili że taniej będzie umieścić w instrukcji obsługi informację że należy sformatować kartę w aparacie (lub coś w tym rodzaju - nie chce mi się szukać instrukcji żeby sprawdzić dokładnie) niż zabezpieczyć się przed uszkodzeniem danych użytkownika.

Najmniej zrozumiałym jednak jest dla mnie fakt, że uszkodzenie danych nastąpiło przy próbie wyświetlenia zdjęć - widocznie aparat coś zapisuje na karcie także podczas przeglądania.

Skutki:


Canon 40D nadpisał "niewiadomoczym" (głównie zerami) tablicę partycji, początek partycji łącznie z obiema tablicami FAT oraz wpisy w katalogu głównym. Nie wiem dokładnie jak daleko od początku partycji sięgały zniszczenia - pierwszy plik crw na karcie nie został zniszczony.

Analiza uszkodzonych danych:

Próby odzyskania tablicy partycji nie powiodły się z powodu wyzerowania początku partycji, podobnie programy do odzyskiwania plików bazujące na dostępności tablic FAT i/lub wpisów w katalogu dysku nie miały żadnego punktu odniesienia, natomiast programy odczytujące pliki bezpośrednio z kolejnych bloków dysku nie posiadały obsługi plików typu crw (sprawdzałem tylko programy na linuksa).

Aby wstępnie ocenić prawdopodobieństwo odzyskania danych posłużyłem się programem "recoverjpeg" (używam systemu linux). Jako że 300D zapisuje jpegi wewnątrz plików raw w 2 rozmiarach - miniaturki oraz jpeg do podglądu na lcd, oraz że wasia (shakowane w Rosji firmware do 300D) pozwala wybrać rozmiar jpega zaszytego w pliku raw, możliwe jest odzyskanie zdjęć w formacie jpeg w pełnej rozdzielczości (tak miałem ustawione, oryginalne oprogramowanie zapisuje jpeg w mniejszej rozdzielczości).
Odzyskanie plików jpeg jest tylko półśrodkiem, ale pozwala ocenić stan nośnika - jeśli wszystkie odzyskane zdjęcia w formacie jpeg będą czytelne istnieje duże prawdopodobieństwo że pliki raw także nie zostały uszkodzone.

Odzyskiwanie plików crw:

Uwaga! Zakładam że czytający te słowa posiada odpowiednie umiejętności w zakresie obsługi systemu linux, jeśli coś jest niejasne lub nie czujesz się zbyt pewnie NIE RÓB TEGO gdyż możesz uszkodzić sobie dane nie tylko na karcie pamięci ale także w komputerze. Nie ponoszę odpowiedzialności za jakiekolwiek uszkodzenia wynikające z niewłaściwego zastosowania informacji przedstawionych w tym wpisie.

Opisany tu sposób będzie działał także w przypadku fizycznych uszkodzeń karty jeśli możliwe jest odczytanie nieuszkodzonych bloków do pliku obrazu.

Odzyskiwanie plików oczywiście zacząłem od wykonania obrazu karty pamięci - ten krok jest NIEZBĘDNY - nigdy nie ryzykujcie jakichkolwiek operacji na jedynej kopii uszkodzonych danych.

Jeśli, tak jak w moim przypadku, nastąpiło uszkodzenie informacji a nie nośnika kopię karty można wykonać za pomocą komendy dd, w przypadku uszkodzonych sektorów należało by użyć dd_rescue (najlepiej z opcją zapisywania zer w miejscu nieczytelnych sektorów).
W moim przypadku zgranie karty do obrazu wygląda tak (karta pod /dev/sda):

dd bs=512 if=/dev/sda of=cf.img
Komenda ta spowoduje zapisanie obrazu całej karty do pliku cf.img.
W tym momencie można kartę wyjąć z czytnika i schować w bezpieczne miejsce gdyż dalsze operacje przebiegać będą na pliku obrazu. Jeśli karta miała błędy fizyczne i użyłeś dd_rescue warto zrobić kopię pliku obrazu gdyż drugi raz odczyt może się już nie udać).

Kolejnym krokiem w moim przypadku było użycie recoverjpeg aby oszacować czy dane nie zostały "podziurawione". Ponieważ w plikach raw są dwa jpegi należy podać rozmiar bloku równy 1, przy domyślnym odzyskane zostaną tylko miniaturki.
Krok ten nie jest wymagany do odzyskania plików raw, może też zostać wykonany po odzyskaniu plików raw jeśli niektóre są uszkodzone - istnieje szansa że osadzone w nich jpegi mogły się zachować w całości.

recoverjpeg -d 1 cf.img

To polecenie przeskanuje zawartość obrazu karty i zapisze wszystkie znalezione jpegi do aktualnego katalogu. Wszystkie znaczy WSZYSTKIE, nawet te stare, których już nikt nie pamięta i które znajdują się w ostatnich sektorach - należy przeglądać ze zwiększoną ostrożnością, szczególnie jeśli komuś pożyczaliście aparat lub kartę ;)

Odzyskanie plików crw jest już trochę bardziej skomplikowane. Jeśli nie interesują Cię szczegóły techniczne budowy pliku i sposobu jego odzyskania możesz przeskoczyć tekst aż do "odzyskiwanie plików crw w praktyce".

Plik crw powstał w celu jak najsprawniejszego zapisu przez urządzenia o ograniczonych zasobach (pamięć i czas procesora) i został zoptymalizowany tylko i wyłącznie pod kątem wydajności. Powoduje to że odzyskanie takiego pliku jest trochę bardziej niewygodne niż plików zapisanych w sposób "bardziej normalny".
Plik CRW składa się z nagłówka, dzięki któremu można określić typ pliku, zaraz za nim znajduje się blok danych, za blokiem danych znajduje się katalog określający co znajduje się w bloku danych a ostatnie cztery bajty pliku to wskaźnik początku katalogu. Wszystko działa sprawnie jeśli znana jest wielkość pliku.

Odzyskując dane można oprzeć się na nagłówku pliku aby znaleźć jego początek. Ponieważ mamy do czynienia z aparatem można także założyć że zdjęcia zostały zapisane kolejno jedno za drugim. Założenie takie jest prawdziwe w momencie gdy zdjęcia robione były na pustej karcie bez usuwania. W moim przypadku karta była pusta (wcześniej były na niej zdjęcia i zostały usunięte), pierwsze zrobione zdjęcie zostało usunięte zaraz po zrobieniu (tego zdjęcia nie dało się odzyskać, jedynie zaszyty w nim plik jpeg), kolejne ok 30 zdjęć po kolei (odzyskane wszystkie), jedno usunięte zaraz po zrobieniu (odzyskane w całości) i dalsze po kolei (także odzyskane). Sytuacja może wyglądać nieco gorzej jeśli karta zostanie zapełniona a następnie częściowo zwolniona przez wykasowanie pojedynczych zdjęć - prawdopodobnie część zdjęć nie zostanie odzyskana gdyż mogą się nie mieścić w "dziurze" po usuniętym zdjęciu.

Także koniec pliku można z grubsza określić za pomocą nagłówka - jeśli zaczął się następny plik tzn że poprzedni już się skończył (tak, wiem, porażająco prosta logika ale metoda bardzo skuteczna i pozwala zachować plik nawet jeśli jest niepełny). Taki sposób jest najczęściej wystarczający w przypadku plików mających pełny nagłówek na początku gdyż program otwierający plik najczęściej po prostu odrzuci dodatkowe dane. W pliku crw niestety wszystko liczone jest od końca pliku więc należy się koniecznie pozbyć niepotrzebnych "śmieci".

Do wyznaczenia końca pliku można wykorzystać fakt, że wskaźnik na końcu pliku jest równy wielkości pliku pomniejszonej o wielkość nagłówka i katalogu. W przypadku 300D wydaje się że różnica zawsze wynosi 62 bajty.

Odzyskiwanie plików crw w praktyce:


Niestety nie miałem czasu na dokładną analizę plików crw i zachowania się aparatu więc przedstawione tu rozwiązanie może wymagać modyfikacji.

Do odzyskania plików postanowiłem użyć programu "magicrescue" (link do strony domowej w tej chwili nie działa, prawdopodobnie program jest dostępny w repozytoriach wielu dystrybucji linuksa)

Program ten skanuje dysk lub plik obrazu szukając wzorca pliku (np nagłówek) a po znalezieniu przesyła go do skryptu lub programu który musi zadbać o wydobycie szukanej treści z podanych danych.
Utworzenie wzorca pliku dla magicrescue polega na utworzeniu nowego pliku w katalogu "recipes" (katalog ten może się znajdować w jakimś dziwnym miejscu jeśli magicrescue pochodzi z pakietu - w debianie jest to /usr/share/magicrescue/recipes).

plik crw:

# Extracts crw images.
# Depends on perl: http://perl.com
6 string HEAPCCDR
extension crw
command crwextract.pl > "$1"
min_output_file 20


Jak widać plik określa wyszukanie ciągu znaków "HEAPCCDR" na 6 bajcie od początku bloku (odczyt jest blokowy gdyż tak są zapisywane pliki na dyskach)

należy także dodać plik crwextract.pl do katalogu tools (w debianie /usr/lib/magicrescue/tools i symlink /usr/share/magicrescue/tools)

# W tym miejscu pragnę przeprosić wszystkich miłośników perla, programistów i estetów za ten kod ;) zależało mi na szybkich rezultatach a teraz nie mam motywacji żeby go przepisać (jeśli brakuje wcięć, spowodował to edytor tego bloga).

Jeśli Twoje pliki crw bywają większe niż 12MB lub mniejsze niż 5MB zmień odpowiednio $max_file_len i $min_file_len. Wartość $magic_offset możesz łatwo znaleźć jeśli posiadasz pliki crw ze swojego aparatu - wystarczy otworzyć hex-edytorem i sprawdzić wartość w ostatnich czterech bajtach (little endian). Różnica wielkości pliku i tej wartości to właśnie $magic_offset. Zmienna ta jest kluczowa dla działania skryptu gdyż jej nieprawidłowa wartość spowoduje niewykrywanie końca pliku i tym samym niemożność otwarcia plików crw. Wszystkie pliki które sprawdzałem na dysku i te odzyskane mają $magic_offset = 62 (pliki Canon eos 300D).

crwextract.pl


#!/usr/bin/env perl
use strict;

use Fcntl qw(:seek);

my $max_file_len = 12*1024*1024;
my $min_file_len = 5*1024*1024;
my $magic_offset = 62; #dla EOS 300D

my ($buf, $len, $type, $rlen, $i);
my $byte1 = 0;
my $byte2 = 0;
my $byte3 = 0;
my $byte4 = 0;

$rlen = 512;

read(STDIN, $buf, 512) == 512 or die "header read error: $!\n";
substr($buf, 0, 14) eq "II\032\000\000\000HEAPCCDR" or die "bad magic\n";

print $buf;
my $written = $rlen;

while (read(STDIN, $buf, $rlen) == $rlen) {

if (substr($buf, 0, 14) eq "II\032\000\000\000HEAPCCDR") {
sysseek STDIN, -$rlen, SEEK_CUR
or die "seek error";
exit 0;
}
if ($written > $min_file_len) {
#checking for file end
for($i=0;$i<$rlen;$i++){
$byte1 = $byte2;
$byte2 = $byte3;
$byte3 = $byte4;
$byte4 = unpack('C',substr($buf,$i,1));
my $val = (($byte4<<24)+($byte3<<16)+($byte2<<8)+$byte1)+$magic_offset;
if($val == ($written+$i+1)){
print(substr($buf,0,$i+1));
exit 0;
}
}
}
print $buf;
$written += $rlen;
if ($written > $max_file_len) {
print STDERR "File too long, aborting\n";
exit 1;
}
}



Plik crwextract.pl powinien mieć uprawnienia do uruchamiania przez użytkownika który będzie uruchamiał magicrescue:

chmod a+x /usr/share/magicrescue/tools/crwextract.pl


Po przygotowaniu plików można uruchomić program:

magicrescue -r crw -d katalog_docelowy cf.img


Skrypt crwextract.pl i plik recipes/crw powstały na bazie plików załączonych do magicrescue i są na licencji GPLv2.

Powodzenia w odzyskiwaniu zdjęć. Jeśli masz jakieś pytania dot. odzyskiwania danych umieść je w komentarzu lub wyślij mail jeśli to pilna sprawa. Nie odpowiadam na pytania związane z obsługą linuxa a w szczególności konkretnych dystrybucji.

Jeśli szukasz czegoś o odzyskiwaniu plików cr2 zapisywanych przez nowsze wersje aparatów canona: dobra wiadomość jest taka, że odzyskiwanie powinno być łatwiejsze gdyż pliki podobno są w formacie zgodnym z tiff (tylko zawierają inną informację o obrazie), zła natomiast jest taka, że gotowe rozwiązanie opublikuję dopiero jak 40D mi popsuje kartę z własnymi zdjęciami ;)

Na koniec jedno z odzyskanych zdjęć (miałem promile więc pewnie krzywe ;) )


Brak komentarzy: