Wersja skrócona: w jakich okolicznościach można dd
bezpiecznie kopiować dane, co oznacza, że nie ma ryzyka uszkodzenia z powodu częściowego odczytu lub zapisu?
Wersja długa - preambuła: dd
jest często używana do kopiowania danych, szczególnie z urządzenia lub na urządzenie ( przykład ). Czasami przypisuje się jej mistyczne właściwości dostępu do urządzeń na niższym poziomie niż inne narzędzia (kiedy tak naprawdę to plik urządzenia robi magię) - a jednak dd if=/dev/sda
to samo cat /dev/sda
. dd
czasami uważa się, że jest szybszy, ale cat
może go pokonać w praktyce . Niemniej jednak dd
ma unikalne właściwości, które sprawiają, że czasami jest naprawdę przydatny .
Problem: dd if=foo of=bar
w rzeczywistości nie jest taki sam jak cat <foo >bar
. W większości jednorożców¹ dd
wykonuje pojedyncze połączenie z read()
. (Uważam, że POSIX jest rozmyślny na temat tego, co stanowi „odczyt bloku wejściowego” dd
.) Jeśli read()
zwraca wynik częściowy (który, zgodnie z POSIX i innymi dokumentami referencyjnymi, jest dozwolony, chyba że dokumentacja implementacyjna mówi inaczej), blok częściowy jest kopiowany. Istnieje dokładnie ten sam problem write()
.
Uwagi : W praktyce odkryłem, że dd
radzi sobie z urządzeniami blokowymi i zwykłymi plikami, ale może to być po prostu to, że nie ćwiczyłem zbyt wiele. Jeśli chodzi o rury, nie jest trudno dd
winić; na przykład wypróbuj ten kod :
yes | dd of=out bs=1024k count=10
i sprawdź rozmiar out
pliku (prawdopodobnie będzie miał mniej niż 10 MB).
Pytanie : W jakich okolicznościach można dd
bezpiecznie używać do kopiowania danych? Innymi słowy, jakie warunki dotyczące rozmiarów bloków, implementacji, typów plików itp. Mogą zapewnić dd
skopiowanie wszystkich danych?
( GNU dd ma fullblock
flagę, która nakazuje mu wywołać read()
lub write()
w pętli, aby przekazać pełny blok. dd iflag=fullblock
Jest więc zawsze bezpieczny. Moje pytanie dotyczy przypadku, gdy te flagi (które nie istnieją w innych implementacjach) nie są używane .)
¹ Sprawdziłem OpenBSD, GNU coreutils i BusyBox.
count
Theiflag=fullblock
jest obowiązkowe (lub alternatywnieiflag=count_bytes
). Nie maoflag=fullblock
.Odpowiedzi:
Ze specyfikacji :
bs=
expr
argument jest określony i nie są wymagane żadne konwersje inne niżsync
,noerror
lub danenotrunc
są wymagane, dane zwrócone z każdego bloku wejściowego zapisuje się jako osobny blok wyjściowy; jeżeliread()
zwraca mniej niż pełny blok, async
konwersja nie jest określona, wynikowy blok wyjściowy ma taki sam rozmiar jak blok wejściowy.Prawdopodobnie to właśnie powoduje twoje zamieszanie. Tak, ponieważ
dd
jest przeznaczony do blokowania, domyślnie częścioweread()
s zostaną zamapowane 1: 1 na częściowewrite()
, lubsync
d na wypełnieniu ogona NUL lub znakami spacji dobs=
rozmiaru, gdyconv=sync
zostanie określony.Oznacza to, że
dd
można bezpiecznie używać do kopiowania danych (bez ryzyka uszkodzenia z powodu częściowego odczytu lub zapisu) w każdym przypadku, z wyjątkiem jednego, w którym jest on arbitralnie ograniczony przezcount=
argument, ponieważ w przeciwnym razie jego dane wyjściowe w blokach o identycznych rozmiarachdd
będą szczęśliwiewrite()
do tych, w których jego wkład był,read()
dopóki nieread()
przejdzie przez to całkowicie. I nawet to zastrzeżenie jest prawdziwe tylko wtedy, gdybs=
jest określone lub nieobs=
jest określone, ponieważ następne zdanie w specyfikacji stwierdza:bs=
expr
argument nie jest określony lub wymagana jest konwersja inna niżsync
,noerror
lubnotrunc
wymagane jest, dane wejściowe będą przetwarzane i gromadzone w pełnowymiarowych blokach wyjściowych, aż do osiągnięcia końca danych wejściowych.Bez argumentów
ibs=
i / lubobs=
nie ma to znaczenia - ponieważibs
iobs
oba mają domyślnie ten sam rozmiar. Możesz jednak uzyskać wyraźne informacje na temat buforowania danych wejściowych, określając dla nich różne rozmiary i nie określającbs=
(ponieważ ma to pierwszeństwo) .Na przykład, jeśli wykonasz:
... wtedy POSIX
dd
będziewrite()
składał się z 512 bajtów, zbierając każdy pojedynczyread()
bajt w pojedynczy blok wyjściowy.W przeciwnym razie, jeśli zrobisz ...
... POSIX
dd
będzie miał jednocześnieread()
maksymalnie 512 bajtów, alewrite()
każdy blok wyjściowy wielkości megabajta (jądro pozwala i może z wyjątkiem ostatniego - ponieważ to jest EOF) w całości, zbierając dane wejściowe do pełnowymiarowych bloków wyjściowych .Jednak również ze specyfikacji:
count=n
count=
mapuje nai?bs=
bloki, więc aby poradzić sobie z dowolnym ograniczeniemcount=
przenośnym, potrzebujesz dwóchdd
s. Najbardziej praktycznym sposobem na zrobienie tego za pomocą dwóchdd
s jest podłączenie wyjścia jednego z nich do wejścia drugiego, co z pewnością stawia nas w sferze czytania / pisania specjalnego pliku, niezależnie od pierwotnego typu wejścia.Rura IPC oznacza, że przy określaniu
[io]bs=
argumentów, że aby to zrobić bezpiecznie, musisz utrzymać takie wartości w ramach zdefiniowanegoPIPE_BUF
limitu systemu. POSIX stwierdza, że jądro systemu musi zagwarantować tylko atomoweread()
S iwrite()
S w granicachPIPE_BUF
, jak określono wlimits.h
. Gwarancje POSIX, którePIPE_BUF
będą co najmniej ...{_POSIX_PIPE_BUF}
... (który również jest domyślnym
dd
rozmiarem bloku we / wy) , ale rzeczywista wartość wynosi zwykle co najmniej 4k. W aktualnym systemie Linux jest to domyślnie 64k.Więc kiedy konfigurujesz swoje
dd
procesy, powinieneś to zrobić na podstawie współczynnika blokowego opartego na trzech wartościach:PIPE_BUF
lub mniej)Lubić:
Musisz zsynchronizować i / ow /,
dd
aby obsłużyć niewidoczne dane wejściowe. Innymi słowy, wyraź bufory potoku i przestań być problemem. Po todd
jest. Nieznana ilość tutaj jestyes
wielkości bufora - ale jeśli zablokujesz ją na znanej ilości z inną,dd
to małe, świadome zwielokrotnienie możedd
bezpiecznie wykorzystać do kopiowania danych (bez ryzyka uszkodzenia z powodu częściowego odczytu lub zapisu) nawet gdy arbitralnie ograniczasz wejście w /count=
w / dowolny typ wejścia w dowolnym systemie POSIX i bez utraty jednego bajtu.Oto fragment specyfikacji POSIX :
ibs=
expr
expr
obs=
expr
expr
bs=
expr
expr
bajty, zastępującibs=
iobs=
. Jeśli konwersja nie inna niżsync
,noerror
inotrunc
jest określona, każdy blok wejściowy zostanie skopiowany na wyjście jako pojedynczy blok bez agregacji krótkie bloki.Znajdziesz tu także niektóre to lepiej wyjaśnione tutaj .
źródło
W przypadku gniazd, potoków lub ttys read () i write () mogą przesyłać mniej niż żądany rozmiar, więc jeśli używasz na nich dd, potrzebujesz flagi fullblock. Jednak w przypadku zwykłych plików i urządzeń blokujących są tylko dwa razy, gdy mogą wykonać krótki odczyt / zapis: po osiągnięciu EOF lub w przypadku błędu. To dlatego starsze implementacje dd bez flagi fullblock były bezpieczne do powielania dysków.
źródło
mke2fs
po cichu, ponieważ wywołano gowrite()
z jakimś innym formatem niż power-of-2 (3kB IIRC) i jądro zostało zaokrąglone do potęgi 2.)cat </dev/sda >/dev/sdb
działa dobrze, aby sklonować dysk.cat
wybiera rozmiar bufora dla wydajności; nie otrzymuje żadnych informacji związanych z urządzeniem z jądra. Oprócz taśm, możeszread()
iwrite()
do urządzenia blokowego o dowolnym rozmiarze. Przynajmniej w systemie Linuxst_blksize
zależy tylko od systemu plików, w którym znajduje się i-węzeł urządzenia blokowego, a nie od urządzenia bazowego.