Krátká verze: Za jakých okolností je dd
bezpečné použití pro kopírování dat, bezpečné znamená, že neexistuje žádné riziko poškození v důsledku částečného čtení nebo zápisu?
Dlouhá verze – preambule: dd
se často používá ke kopírování dat, zejména ze zařízení nebo do zařízení (příklad). Někdy se mu připisují mystické vlastnosti možnosti přístupu k zařízením na nižší úrovni než jiným nástrojům (i když ve skutečnosti je to soubor zařízení, kdo dělá kouzlo) – přesto dd if=/dev/sda
je to samé jako cat /dev/sda
. dd
je někdy považováno za rychlejší, ale cat
může to překonat v praxi. Nicméně dd
má jedinečné vlastnosti, díky kterým je někdy skutečně užitečný.
Problém: dd if=foo of=bar
není ve skutečnosti totéž jako cat <foo >bar
. Na většině unices¹, dd
provede jediné volání read()
. (Považuji POSIX za nejasný v tom, co představuje „čtení vstupního bloku“ v dd
.) Pokud read()
vrátí částečný výsledek (což je podle POSIX a dalších referenčních dokumentů povoleno, pokud implementační dokumentace neříká jinak), částečný blok se zkopíruje. Přesně stejný problém existuje pro write()
.
Postřehy :V praxi jsem zjistil, že dd
umí si poradit s blokovými zařízeními a běžnými soubory, ale to může být jen tím, že jsem to moc necvičil. Pokud jde o potrubí, není těžké zadat dd
na vině; zkuste například tento kód:
yes | dd of=out bs=1024k count=10
a zkontrolujte velikost out
(pravděpodobně bude mít mnohem méně než 10 MB).
Otázka :Za jakých okolností je dd
bezpečné použití pro kopírování dat? Jinými slovy, jaké podmínky ohledně velikostí bloků, implementace, typů souborů atd. mohou zajistit, že dd
zkopíruje všechna data?
(GNU dd má fullblock
flag, aby mu řekl, aby zavolal read()
nebo write()
ve smyčce tak, aby se přenesl celý blok. Takže dd iflag=fullblock
je vždy v bezpečí. Moje otázka se týká případu, kdy tyto příznaky (které v jiných implementacích neexistují) nejsou použity.)
¹
Zkontroloval jsem OpenBSD, GNU coreutils a BusyBox.
Přijatá odpověď:
Ze specifikace:
- Pokud je
bs=
expr
je zadán operand a žádné jiné konverze nežsync
,noerror
nebonotrunc
jsou požadovány, data vrácená z každého vstupního bloku se zapíší jako samostatný výstupní blok; pokudread()
vrátí méně než celý blok async
převod není specifikován, výsledný výstupní blok bude mít stejnou velikost jako vstupní blok.
Takže to je pravděpodobně to, co způsobuje váš zmatek. Ano, protože dd
je navrženo pro blokování ve výchozím nastavení částečné read()
s bude mapováno 1:1 na částečné write()
s nebo jinak sync
d ven na ocasní vycpávce NUL nebo mezerník na bs=
velikost při conv=sync
je zadáno.
To znamená, že dd
je bezpečné použití pro kopírování dat (bez rizika poškození v důsledku částečného čtení nebo zápisu) v každém případě kromě toho, ve kterém je libovolně omezena count=
argument, protože jinak dd
bude šťastně write()
jeho výstup v blocích stejné velikosti jako bloky, ve kterých byl jeho vstup read()
dokud read()
je úplně přes to. A i toto upozornění je pouze pravdivé když bs=
je zadáno nebo obs=
není specifikováno, protože hned další věta ve specifikaci uvádí:
- Pokud je
bs=
expr
není zadán operand nebo se jedná o jinou konverzi nežsync
,noerror
nebonotrunc
je požadováno, vstup bude zpracován a shromážděn do výstupních bloků plné velikosti dokud není dosaženo konce vstupu.
Bez ibs=
a/nebo obs=
argumenty na tom nezáleží – protože ibs
a obs
oba mají ve výchozím nastavení stejnou velikost. Nicméně můžete být explicitní o ukládání do vyrovnávací paměti zadáním různých velikostí pro buď a ne zadáním bs=
(protože má přednost) .
Pokud například uděláte:
IN| dd ibs=1| OUT
…potom POSIX dd
bude write()
v blocích po 512 bytech shromažďováním každý jednotlivě read()
byte do jednoho výstupního bloku.
Jinak, pokud tak učiníte…
IN| dd obs=1kx1k| OUT
…a POSIX dd
bude read()
maximálně 512 bajtů najednou, ale write()
každý megabajtový výstupní blok (jádro umožňuje a možná s výjimkou posledního – protože to je EOF) v plném rozsahu shromažďováním vstupu do výstupních bloků plné velikosti .
Také však ze specifikace:
count=n
- Kopírovat pouze n vstupní bloky.
count=
mapuje na i?bs=
bloky, a tak zvládnout libovolný limit na count=
přenosně budete potřebovat dva dd
s. Nejpraktičtější způsob, jak to udělat se dvěma dd
s je propojením výstupu jednoho do vstupu druhého, což nás jistě staví do oblasti čtení/zápisu speciálního souboru bez ohledu na původní typ vstupu.
IPC roura znamená, že při zadání [io]bs=
tvrdí, že k tomu, abyste tak učinili bezpečně, musíte tyto hodnoty udržovat v rámci systému definovaného PIPE_BUF
omezit. POSIX uvádí, že jádro systému musí zaručit pouze atomické read()
s a write()
s v mezích PIPE_BUF
jak je definováno v limits.h
. POSIX zaručuje, že PIPE_BUF
být nejméně …
{_POSIX_PIPE_BUF}
- Maximální počet bajtů, u kterých je zaručeno, že jsou atomické při zápisu do kanálu.
- Hodnota:512
…(což je také výchozí dd
velikost bloku i/o) , ale skutečná hodnota je obvykle minimálně 4k. Na aktuálním linuxovém systému je to standardně 64k.
Když tedy nastavíte dd
procesy, měli byste to udělat na blokovém faktoru na základě tří hodnot:
- bs =( obs =
PIPE_BUF
nebo menší ) - n =celkový požadovaný počet přečtených bajtů
- počet =n / bs
Jako:
yes | dd obs=1k | dd bs=1k count=10k of=/dev/null
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.1143 s, 91.7 MB/s
Musíte synchronizovat i/o s dd
pro zpracování nehledatelných vstupů. Jinými slovy, udělejte pipe-buffery explicitní a přestanou být problémem. To je to, co dd
je pro. Neznámá veličina je zde yes
„velikost vyrovnávací paměti – ale pokud ji zablokujete pro známé množství s jiným dd
pak trochu informované násobení může způsobit dd
bezpečné použití pro kopírování dat (bez rizika poškození v důsledku částečného čtení nebo zápisu) i při libovolném omezení vstupu pomocí count=
w/ libovolný libovolný typ vstupu na jakémkoli systému POSIX a bez vynechání jediného bajtu.
Zde je úryvek ze specifikace POSIX:
ibs=
expr
- Uveďte velikost vstupního bloku v bajtech pomocí
expr
(výchozí hodnota je 512) .
- Uveďte velikost vstupního bloku v bajtech pomocí
obs=
expr
- Uveďte velikost výstupního bloku v bajtech pomocí
expr
(výchozí hodnota je 512) .
- Uveďte velikost výstupního bloku v bajtech pomocí
bs=
expr
- Nastavte velikosti vstupních i výstupních bloků na
expr
bajtů, nahrazujícíchibs=
aobs=
. Pokud neproběhne jiná konverze nežsync
,noerror
anotrunc
je zadáno, každý vstupní blok se zkopíruje na výstup jako jeden blok bez agregace krátkých bloků.
- Nastavte velikosti vstupních i výstupních bloků na
Některé z toho také najdete lépe vysvětlené zde.