Tworzenie dużych plików

Jak mozna szybko utworzyc duzy plik? To zalezy, czy jestes programista czy administratorem. Administrator uzyje fsutil file createnew <nazwa> <rozmiar> a programista uzywajac SetFilePointerEx przesunie kursor w pliku w to miejsce, które okreslil jako koniec pliku a potem wywola funkcje SetEndOfFile.
Obie metody maja ten sam skutek i dokladnie ta sama wade. Skutek jest jasny. W mgnieniu oka, na dysku powstaje plik o zadanym rozmiarze.

A co jest w takim pliku w srodku...? I tu sprawa nie jest taka trywialna jak sie na pierwszy rzut oka wydaje.

Z jednej strony moze sie wydawac, ze w takim pliku powinno byc to, co wczesniej lezalo w sektorach na dysku. Bo przeciez nie po to blyskawicznie tworzymy plik, zeby pózniej pracowicie wypelniac jego sektory zerami. Poza tym, o ile taka operacja jest dosc szybka na plikach mierzonych w kilobajtach, o tyle dla kilku gigabajtów moze juz potrwac dosc dlugo.
Z drugiej strony, mozliwosc utworzenia takiego pliku to niezla luka w zabezpieczeniach! Sektory na dysku nie maja praw dostepu i nie ma zadnej gwarancji, ze w tym miejscu, w którym tworzymy nasz plik, wczesniej nie lezal skasowany juz dokument innego uzytkownika. A swój plik mozna latwo otworzyc i dane z jego wnetrza dadza sie odczytac...

Jak wiec, tak naprawde, zachowa sie system Windows?

Otóz kazdy plik w systemie ma oprócz rozmiaru, przypisana specjalna wartosc Valid Data Length czyli liczona od poczatku pliku ilosc danych, która zostala wyzerowana. Oznacza to, ze plik faktycznie tworzy sie blyskawicznie, jednak ilosc zainicjowanych danych jest równa 0. Teoretycznie, plik zawiera same zera, jednak faktycznie pojawia sie one na dysku dopiero, gdy ktos spróbuje po nie siegnac.  Poniewaz rozwiazanie polega na liczeniu ilosci przygotowanych danych, siegniecie w pliku w miejsce X, oznacza, ze wszystkie informacje od 0 do X zostaly zainicjowane. To, co lezy poza X, zainicjowane bedzie, gdy ktos tam siegnie pierwszy raz.

Efekt jest ciekawy: zalozenie pliku 100GB trwa kilka sekund, a zapisanie (albo nawet odczyt!) na jego koncu jednego bajta - godzine. Dlatego, ze siegniecie do tego bajta wymaga przesuniecia az do niego wskaznika konca zainicjowanych danych czyli faktycznego zapisania tych wszystkich zer po drodze. Po takim zainicjowaniu, oczywiscie kolejne zapisy odbywaja sie juz blyskawicznie.

Praktycznych przykladów nie jest wiele. Rzadko trafia sie na duze pliki, których poczatek nie jest wazny, za to koncówka sie liczy. Jednak sytuacje takie sie zdarzaja i wtedy moze byc ciekawie. Sa to pliki VHD i pliki baz danych. Zwlaszcza VHD jest ciekawy, poniewaz opis struktury pliku znajduje sie na jego koncu.

Czy mozna sobie z tym jakos poradzic szybciej niz zerujac sektor po sektorze? Zwlaszcza wiedzac (a jest tak w przypadku i baz danych i plików VHD) ze za chwile i tak nadpiszemy te dane. Mozna! Wskaznik konca zainicjowanych danych daje sie "na sile" przemiescic w dowolne miejsce pliku. Oczywiscie prowadzi to do wspomnianego wczesniej naruszenia poufnosci danych, wiec wymaga specjalnego prawa w systemie. Prawo to widoczne jest jako "Perform volume maintenance tasks" w lokalnych zasadach zabezpieczen.

Dla SQLowców oczywiscie ta sama rzecz nazywa sie inaczej i w takim przypadku mówia oni o "Instant File Initialization".

Tak czy inaczej, jezeli trzeba, to mozna szybko utworzyc duzy plik, a pózniej bez oczekiwania na jego wyzerowanie siegac w dowolne miejsce. Warto o tym wiedziec, bo osobiscie spedzilem kilka ostatnich nocy sledzac, dlaczego plik tworzy sie w kilka sekund a pojedynczy bajt w nim zapisuje sie kilkadziesiat minut.

Autor: Grzegorz Tworek [MVP]

 

PS. Stary jak komputery problem "bajtu" czy "bajta" jest bardzo jasno opisany w Slowniku PWN: tylko "bajta" jest poprawnie. Jest to jednak jeden z tych nielicznych przypadków, kiedy slownik nie jest scisly. Tak naprawde, obie formy sa dopuszczalne i poprawne, wiec wylacznie od upodoban piszacego zalezy, która zostanie uzyta. Odradzam tylko mieszanie obu form w jednym tekscie, bo dziwnie sie to wtedy czyta.