Do spisu treści

Skomentowany kod zawartości pliku "fs/ext2/balloc.c"


Na tej stronie znajduje się skomentowany kod następujących funkcji:

ext2_new_block()
get_group_desc()
read_block_bitmap()
load__block_bitmap()
load_block_bitmap()
ext2_free_blocks()


/*
 *  linux/fs/ext2/balloc.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI - Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
 */

/*
 * balloc.c contains the blocks allocation and deallocation routines
 */

/*
 * The free blocks are managed by bitmaps.  A file system contains several
 * blocks groups.  Each group contains 1 bitmap block for blocks, 1 bitmap
 * block for inodes, N blocks for the inode table and data blocks.
 *
 * The file system contains group descriptors which are located after the
 * super block.  Each descriptor contains the number of the bitmap block and
 * the free blocks count in the block.  The descriptors are loaded in memory
 * when a file system is mounted (see ext2_read_super).
 */

#include 
#include 
#include 
#include 
#include 
#include 

#include 

#define in_range(b, first, len)		((b) >= (first) && (b) <= (first) + (len) - 1)


static struct ext2_group_desc * get_group_desc (struct super_block * sb, unsigned int block_group, struct buffer_head ** bh) {
Funkcja ta zwraca adres do nagłówka bufora, który mapuje blok dyskowy zawierający wskazany deskryptor grupy oraz wskaźnik bezpośrednio do tego deskryptora.

	unsigned long group_desc;
	unsigned long desc;
	struct ext2_group_desc * gdp;

Poniżej sprawdzana jest poprawność parametrów. W rzeczywistości funkcje nadrzędne zawsze weryfikują swoje parametry i poniższa sytuacja, gdy numer grupy jest niewłaściwy, nigdy nie będzie miała miejsca.

	if (block_group >= sb->u.ext2_sb.s_groups_count)
		ext2_panic (sb, "get_group_desc",
			    "block_group >= groups_count - "
			    "block_group = %d, groups_count = %lu",
			    block_group, sb->u.ext2_sb.s_groups_count);

Obliczamy numer bloku (w obrębie listy bloków zawierających deskryptory) zawierającego deskryptor wskazanej grupy dzieląc numer grupy przez ilość deskryptorów przypadającą na jeden blok. Reszta z tego dzielenia jest numerem deskryptora w obrębie tego bloku.
 

	group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
	desc = block_group % EXT2_DESC_PER_BLOCK(sb);

Oczekujemy, że bloki zawierające deskryptory są załadowane do buforów w pamięci. Jeżeli nie, zgłoszony jest błąd systemowy.

	if (!sb->u.ext2_sb.s_group_desc[group_desc])
		ext2_panic (sb, "get_group_desc",
			    "Group descriptor not loaded - "
			    "block_group = %d, group_desc = %lu, desc = %lu",
			     block_group, group_desc, desc);

Adres nagłówka bufora zawierającego ten deskryptor zwracamy poprzez zmienną "bh", zaś adres w pamięci (równy adresowi bufora plus obliczony wcześniej offset deskryptora od początku bufora) zwracamy jako wynik funkcji.

	gdp = (struct ext2_group_desc *) 
	      sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
	if (bh)
		*bh = sb->u.ext2_sb.s_group_desc[group_desc];
	return gdp + desc;
}


static int read_block_bitmap (struct super_block * sb, unsigned int block_group, unsigned long bitmap_nr) { struct ext2_group_desc * gdp; struct buffer_head * bh; int retval = 0;
Funkcja ta wczytuje z dysku bitmapę zajętości bloków dla zadanej grupy do bufora w pamięci, nagłówek bufora umieszcza zaś pod wskazanym indeksem w cache trzymającym załadowane bitmapy. Funkcja ta wywoływana jest na rzecz funkcji load__block_bitmap(). W przypadku błędu zwracany jest kod błędu, w przeciwnym przypadku zero.
 

Pobieramy deskryptor grupy i odczytujemy z niego, gdzie znajduje się bitmapa wskazanej grupy. Następnie używamy tej informacji jako parametru do standardowej funkcji "bread" zwracającej bufor (ładującej go, jeśli trzeba) mapujący blok danej bitmapy. Jeśli to się nie uda, wychodzimy z błędem. W cache pod odpowiednim indeksem pozostaje jednak zero, nie zostawiamy informacji o błędzie. W ten sposób następnym razem, gdy pojawi się odwołanie do tej bitmapy, próba odczytu będzie ponowiona.
	
	gdp = get_group_desc (sb, block_group, NULL);
	bh = bread (sb->s_dev, gdp->bg_block_bitmap, sb->s_blocksize);
	if (!bh) {
		ext2_error (sb, "read_block_bitmap",
			    "Cannot read block bitmap - "
			    "block_group = %d, block_bitmap = %lu",
			    block_group, (unsigned long) gdp->bg_block_bitmap);
		retval = -EIO;
	}

Jeżeli udało się, zapisujemy w cache informację pod danym indeksem, że znajduje się tam nagłówek bufora bitmapy o tym numerze i umieszczamy tam wskaźnik na ten nagłówek oraz zwracamy zero, czyli suksec.

	sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
	sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
	return retval;
}


static int load__block_bitmap (struct super_block * sb, unsigned int block_group) { int i, j, retval = 0; unsigned long block_bitmap_number; struct buffer_head * block_bitmap;
Funkcja ta ładuje do pamięci bitmapę zajętości dla wskazanej grupy. Normalnie wywoływana jest w tym celu funkcja load_block_bitmap(), która sprawdza kilka oczywistych przypadków, które można obsłużyć natychmiast i dopiero wtedy, kiedy to się nie uda, wywołuje poniższą funkcję, realizującą pełny algorytm. Poniższa funkcja trzyma załadowane bitmapy w cache zarządzanym zgodnie z algorytmem LRU. (Informacje o bitmapach są trzymane w postaci samoorganizującej się listy zaimplementowanej na tablicy o ustalonej długości). Dla każdego filesystemu jest osobny cache. Jeżeli filesystem jest na tyle mały, że wszystkie bitmapy mieszczą się w cache, wówczas cache dla tego filesystemu jest w istocie tablicą, w której grupa o zadanym numerze ma swoją bitmapę pod takim samym indeksem i żaden algorytm nie jest potrzebny. Taka sytuacja wykorzystywana jest w funkcji load_block_bitmap() do przyspieszania ładowania (wtedy tak naprawdę po prostu nie ma żadnego ładowania). Jeżeli ładowanie się powiedzie, funkcja zwraca indeks bufora w cache zawierającego bitmapę, a jeśli nie, kod błędu.

Rutynowe sprawdzenie poprawności argumentów. W zasadzie całkowicie bezsensowne, bo ta funkcja wywoływana jest tylko przez load_block_bitmap(), która takiego samego sprawdzenia dokonuje na samym początku i zawsze podaje poprawną wartość.

	if (block_group >= sb->u.ext2_sb.s_groups_count)
		ext2_panic (sb, "load_block_bitmap",
			    "block_group >= groups_count - "
			    "block_group = %d, groups_count = %lu",
			    block_group, sb->u.ext2_sb.s_groups_count);

Jeżeli mamy do czynienia z małym filesystemem (por. opis wyżej), to jeśli do odpowiedniego bufora załadowana jest bitmapa, ale nie ta, której szukamy, to musiał wystąpić jakiś błąd, bo ta funkcja nie mogłaby doprowadzić do takiej sytuacji.

	if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
		if (sb->u.ext2_sb.s_block_bitmap[block_group]) {
			if (sb->u.ext2_sb.s_block_bitmap_number[block_group] !=
			    block_group)
				ext2_panic (sb, "load_block_bitmap",
					    "block_group != block_bitmap_number");
			else

Jeżeli natomiast numer bitmapy się zgadza, to nic więcej nie trzeba robić, jej numer jest natychmiast zwracany.

				return block_group;
		} else {

Jeżeli znaleźliśmy się tutaj, to znaczy, że bitmapa nie została jeszcze wczytana, więc wywołujemy funkcję, która ją dla nas wczyta. Jeżeli ta funkcja wykona się bezbłędnie, to zwracamy numer bufora, do którego kazaliśmy jej czytac, a jeżeli nie, to wychodzimy z błędem.

			retval = read_block_bitmap (sb, block_group, block_group);

			if (retval < 0)
				return retval;
			return block_group;
		}
	}

Jeżeli filesystem jest odpowiednio duży, to nie można załadować wszystkich bitmap naraz. Poniższy kod realizuje algorytm LRU zarządzania cache bitmap. Zaczynamy go od przejrzenia całego cache liniowo w celu znalezienia bufora z szukaną bitmapą.

	for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
		    sb->u.ext2_sb.s_block_bitmap_number[i] != block_group; i++)
		;

Jeżeli szukana bitmapa została zlokalizowana, to przesuwamy wskaźnik na nią na początek listy trzymanej przez cache, tak, że następnym razem dostęp do niej będzie natychmiastowy. Poniższy kod realizuje operację przesunięcia wybranego elementu tablicy na początek.
 

	if (i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
  	    sb->u.ext2_sb.s_block_bitmap_number[i] == block_group) {
		block_bitmap_number = sb->u.ext2_sb.s_block_bitmap_number[i];
		block_bitmap = sb->u.ext2_sb.s_block_bitmap[i];
		for (j = i; j > 0; j--) {
			sb->u.ext2_sb.s_block_bitmap_number[j] =
				sb->u.ext2_sb.s_block_bitmap_number[j - 1];
			sb->u.ext2_sb.s_block_bitmap[j] =
				sb->u.ext2_sb.s_block_bitmap[j - 1];
		}
		sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
		sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;

Może się zdarzyć tutaj pewien przypadek szczególny - jeżeli indeks bitmapy równy jest zero, to znaczy, że zawiodła ostatnia próba wczytywania i to, co zostało zapamiętane w cache, to po prostu wiadomość o błędzie. W takiej sytuacji musimy spróbować ponownie wczytać bitmapę.

		if (!block_bitmap)
			retval = read_block_bitmap (sb, block_group, 0);
	} else {

Jeżeli doszliśmy tutaj, to znaczy, że szukana bitmapa nie została znaleziona w cache. Jeżeli w cache jest jeszcze miejsce, to możemy bez problemu przejść do fazy ładowania bitmapy, w przeciwnym razie trzeba będzie najpierw usunąć jakąś inną bitmapę z cache.

		if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
			sb->u.ext2_sb.s_loaded_block_bitmaps++;
		else

Tą, która będzie usunięta, jest ta, do której najdłużej nie było odwołań, czyli ostatnia na liście. Zwalniamy jej bufor.

			brelse (sb->u.ext2_sb.s_block_bitmap[EXT2_MAX_GROUP_LOADED - 1]);

Skoro już jest miejsce, to przesuwamy cały cache, tak, aby zrobić miejsce na początku.

		for (j = sb->u.ext2_sb.s_loaded_block_bitmaps - 1; j > 0;  j--) {
			sb->u.ext2_sb.s_block_bitmap_number[j] =
				sb->u.ext2_sb.s_block_bitmap_number[j - 1];
			sb->u.ext2_sb.s_block_bitmap[j] =
				sb->u.ext2_sb.s_block_bitmap[j - 1];
		}

Teraz na początek cache ładowana jest bitmapa. Jeżeli się uda, to zwracany jest jej numer, a jeśli nie, kod błędu.

		retval = read_block_bitmap (sb, block_group, 0);
	}
	return retval;
}


static inline int load_block_bitmap (struct super_block * sb, unsigned int block_group) { int slot;
Funkcja ta ładuje bitmapę zajętości bloków dla zadanej grupy bloków. Najpierw sprawdza kilka oczywistych przypadków, które można obsłużyć od razu, dopiero w ostateczności wywołuje właściwą funkcję ładującą bitmapy. Zwraca indeks miejsca w cache'u, gdzie znajduje się bitmapa, lub kod błędu. W poniższym algorytmie jest pewne niedociągnięcie: jeżeli ilość wszystkich grup w systemie plików jest mniejsza niż maksymalna ilość bitmap grup, jaka może być załadowana do pamięci (EXT2_MAX_GROUP_LOADED), to nie ma sposobu, aby odróżnić przypadek, kiedy dla danej grupy nigdy nie wczytano bitmapy od przypadku, kiedy dla danej grupy nie powiodła się ostatnia próba wczytania bitmapy z dysku.

Najpierw sprawdzamy, czy bitmapa, o którą prosimy, nie jest tą samą, o którą prosilismy ostatnim razem (wówczas musi znajdować się na początku listy bitmap w cache'u). Jeśli tak, zwracamy indeks dla niej (musi to być pierwszy indeks w cache'u, czyli zero).
	
	if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
	    sb->u.ext2_sb.s_block_bitmap_number[0] == block_group &&
	    sb->u.ext2_sb.s_block_bitmap[block_group]) {
		slot = 0;
	}

Możliwe też, że filesystem jest na tyle mały, że wszystkie bitmapy mieszczą się naraz w cache'u, w takim razie najprawdopodobniej są załadowane do cache po kolei, czyli każda ma indeks w cache'u taki jak numer jej grupy w filesystemie. Więc sprawdzamy, czy pod tym indeksem w cache'u nie siedzi bitmapa, której szukamy.

	else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && 
		 sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
		 sb->u.ext2_sb.s_block_bitmap[block_group]) {
		slot = block_group;
	} 

Jeżeli żadna z powyższych sytuacji nie zachodzi, to wywołujemy właściwą funkcję ładującą.

	else {
		slot = load__block_bitmap (sb, block_group);
	}

Jeżeli właściwa funkcja ładująca zwróciła kod błędu, to nic się więcej nie da zrobić, należy zwrócić kod błędu.

	if (slot < 0)
		return slot;

Nawet, jeżeli udało się ustalić właściwy indeks w cache, możliwe, że operacja wejścia-wyjścia dla odpowiedniego bufora ostatnio nie powiodła się i wskaźnik nagłówka bufora trzymającego bitmapę pod tym indeksem w cache jest pusty. W tej sytuacji zwracany jest błąd wejścia-wyjścia.
	

	if (!sb->u.ext2_sb.s_block_bitmap[slot])
		return -EIO;

Jeżeli nie było błędów, zwracamy indeks bufora w cache.
	
	return slot;
}


void ext2_free_blocks (const struct inode * inode, unsigned long block, unsigned long count) inode - wskażnik do i_węzła pliku do, którego należą zwalniane bloki block - numer bloku, od którego zaczynamy zwalniać count - liczba bloków do zwolnienia. Procedura zwalnia spójny ciąg bloków dyskowych począwszy od bloku o numerze block. Zwalniany ciąg liczy sobie count bloków. { struct buffer_head * bh; /* wskaźnik do nagłówka bufora zawierającego bitmapę */ struct buffer_head * bh2; /* wskaźnik bufora zawierającego deskryptor grupy */ unsigned long block_group; /* numer grupy bloków */ unsigned long bit; /* numer bloku w grupie */ unsigned long i; int bitmap_nr; /* numer bitmapy grupy w której są bloki do zwolnienia */ struct super_block * sb; /* wskaźnik do bloku identyfikacyjnego w pamięci */ struct ext2_group_desc * gdp;/* wskaźnik do deskryptora grupy */ struct ext2_super_block * es;/* wskaźnik do bloku identyfikacyjnego */ sb = inode->i_sb; if (!sb) { printk ("ext2_free_blocks: nonexistent device"); return; } /**** załóż blokadę na blok identyfikacyjny *****/ lock_super (sb); es = sb->u.ext2_sb.s_es; /**** sprawdź poprawność argumentów ****/ /**** 1. Czy bloki do zwolnienia leżą w strefie bloków z danymi ****/ if (block < es->s_first_data_block || (block + count) > es->s_blocks_count) { ext2_error (sb, "ext2_free_blocks", "Freeing blocks not in datazone - " "block = %lu, count = %lu", block, count); unlock_super (sb); return; } ext2_debug ("freeing block %lu\n", block); /**** 2. Czy zwalniane bloki leżą w jednej grupie ****/ block_group = (block - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb); bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb); if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) ext2_panic (sb, "ext2_free_blocks", "Freeing blocks across group boundary - " "Block = %lu, count = %lu", block, count); /***** Wczytaj bitmapę grupy bloków i deskryptor tej grupy ****/ /***** Funkcje te jeśli nie ma w pamięci wczytują bitmapę i deskryptor z dysku ****/ bitmap_nr = load_block_bitmap (sb, block_group); bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; gdp = get_group_desc (sb, block_group, &bh2); if (test_opt (sb, CHECK_STRICT) && (in_range (gdp->bg_block_bitmap, block, count) || in_range (gdp->bg_inode_bitmap, block, count) || in_range (block, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group) || in_range (block + count - 1, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group))) ext2_panic (sb, "ext2_free_blocks", "Freeing blocks in system zones - " "Block = %lu, count = %lu", block, count); /**** argumenty sprawdzone *****/ /**** zwalniamy kolejne bloki *****/ for (i = 0; i < count; i++) { /* czyszcząc bit w bitmapie */ if (!clear_bit (bit + i, bh->b_data)) ext2_warning (sb, "ext2_free_blocks", "bit already cleared for block %lu", block); else { /* jeśli wyczyściliśmy bit w bitmapie */ if (sb->dq_op) /* to uaktualnij dane w i-węźle */ sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_bloc->s_blocksize)); /* zwiększ licznik wolnych bloków */ gdp->bg_free_blocks_count++; /* w deskryptorze grupy */ es->s_free_blocks_count++; /* w bloku identyfikacyjnym */ } } /* zaznacz, że aktualizowano struktury w pamięci */ mark_buffer_dirty(bh2, 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); /* bufor zawierający */ /*blok identyfikacyjny */ mark_buffer_dirty(bh, 1); /* bufor zawierający bitmapę */ if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } sb->s_dirt = 1; /* zaznaczamy sam blok identyfikacyjny */ unlock_super (sb); /* zdejmij blokadę z bloku identyfikacyjnego */ return; }
int ext2_new_block (const struct inode * inode, unsigned long goal, u32 * prealloc_count, u32 * prealloc_block, int * err) { struct buffer_head * bh; struct buffer_head * bh2; char * p, * r; int i, j, k, tmp; int bitmap_nr; struct super_block * sb; struct ext2_group_desc * gdp; struct ext2_super_block * es; *err = -ENOSPC;
Próba alokacji super-bloku bieżącej grupy. Funkcje "lock_super" i "unlock_super" zdefiniowane są jako inline w pliku "include/linux/locks.h".

	sb = inode->i_sb;
	if (!sb) {
		printk ("ext2_new_block: nonexistent device");
		return 0;
	}
	lock_super (sb);
	es = sb->u.ext2_sb.s_es;

Jeżeli zostały już co najwyżej zarezerwowane bloki, a użytkownik nie ma odpowiednich uprawnien, to zwolnij super-blok i wyjdź. Funkcja "fsuser" zdefiniowana jest w "include/linux/kernel.h", sprawdza ona, czy identyfikator bieżącego użytkownika używany przy operacjach na systemie plików jest identyfikatorem roota.

	if (es->s_free_blocks_count <= es->s_r_blocks_count &&
	    (!fsuser() && (sb->u.ext2_sb.s_resuid != current->fsuid) &&
	     (sb->u.ext2_sb.s_resgid == 0 ||
	      !in_group_p (sb->u.ext2_sb.s_resgid)))) {
		unlock_super (sb);
		return 0;
	}

	ext2_debug ("goal=%lu.\n", goal);

repeat:

Jeżeli wskazany blok nie leży w obszarze wolnych bloków, ustaw numer alokowanego bloku na pierwszy wolny.

	if (goal < es->s_first_data_block || goal >= es->s_blocks_count)
		goal = es->s_first_data_block;

Ustal, do której grupy należy ten blok i pobierz deskryptor tej grupy, a następnie sprawdź, czy w tej grupie w ogóle są jakieś wolne bloki. Jeżeli są, to wykonuj się dalej, a jeżeli nie, to przeskocz ten fragment i szukaj w innych grupach

	i = (goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb);
	gdp = get_group_desc (sb, i, &bh2);
	if (gdp->bg_free_blocks_count > 0) {

Ustal, jaka jest pozycja tego bloku względem początku grupy

		j = ((goal - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb));

Załaduj do pamięci bitmapę dla tej grupy. Jeżeli w polu tej bitmapy odpowiadającym szukanemu blokowi jest zero, to blok jest wolny, więc idź do dalszej częsci, omijając fragment poniżej.

		bitmap_nr = load_block_bitmap (sb, i);
		bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
		if (!test_bit(j, bh->b_data)) {
			goto got_block;
		}

Ponieważ szukany blok był zajęty, szukaj dalej, posuwając się naprzód, ale nie dalej niż do końca bieżącej grupy i nie dalej niż do najbliższego bloku o numerze będącym wielokrotnością 64. Jeżeli znajdziemy blok, omijamy poniższy fragment, tak, jak poprzednio.

		if (j) {
			int end_goal = (j + 63) & ~63;
			j = find_next_zero_bit(bh->b_data, end_goal, j);
			if (j < end_goal)
				goto got_block;
		}
	
W najbliższym sąsiedztwie nie znaleziono bloku. Poszukiwania kontynuowane są teraz do końca bieżącej grupy, przy czym najpierw szuka się spójnego ciągu ośmiu wolnych bloków, takich, że pierwszy z nich ma numer będący wielokrotnością ośmiu.

		p = ((char *) bh->b_data) + (j >> 3);
		r = memscan(p, 0, (EXT2_BLOCKS_PER_GROUP(sb) - j + 7) >> 3);
		k = (r - ((char *) bh->b_data)) << 3;

Jeżeli udało się znaleźć taki spójny ciąg, to już wiemy, że będzie w nim wolny blok, więc można opuścić dalsze poszukiwania i skoczyć dalej.

		if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
			j = k;
			goto search_back;
		}

Jeżeli nie było takiego spójnego ciągu bloków, szukamy od bieżącej pozycji do końca grupy jakiegokolwiek wolnego bloku.

		k = find_next_zero_bit ((unsigned long *) bh->b_data, 
					EXT2_BLOCKS_PER_GROUP(sb),
					j);

Jeżeli znaleziono w bieżącej grupie, skaczemy dalej, do miejsca, w którym sprawdza się quotę.

		if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
			j = k;
			goto got_block;
		}
	}

Jeżeli w bieżącej grupie nie ma wolnego bloku, poszukujemy go w pozostałych grupach. Korzystamy przy tym z informacji o ilości wolnych bloków w grupach, które zawarte są w deskryptorach grup. Deskryptory grup dynamicznie doładowujemy z dysku. Jeżeli przeszukamy wszystkie grupy i nie znajdziemy wolnego bloku, wychodzimy z funkcji z błędem.

	for (k = 0; k < sb->u.ext2_sb.s_groups_count; k++) {
		i++;
		if (i >= sb->u.ext2_sb.s_groups_count)
			i = 0;
		gdp = get_group_desc (sb, i, &bh2);
		if (gdp->bg_free_blocks_count > 0)
			break;
	}
	if (k >= sb->u.ext2_sb.s_groups_count) {
		unlock_super (sb);
		return 0;
	}

Ładujemy bitmapę zajętości bloków grupy, w której, zgodnie z informacjami zawartymi w deskryptorze, są wolne bloki i, podobnie jak poprzednio, skanujemy najpierw w poszukiwaniu wolnego ciągu ośmiu bloków, a jeśli się nie uda, w poszukiwaniu dowolnego wolnego bloku.

	bitmap_nr = load_block_bitmap (sb, i);
	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
	r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3);
	j = (r - bh->b_data) << 3;
	if (j < EXT2_BLOCKS_PER_GROUP(sb))
		goto search_back;
	else
		j = find_first_zero_bit ((unsigned long *) bh->b_data,
					 EXT2_BLOCKS_PER_GROUP(sb));

Jeżeli okaże się, że nie znaleziono wolnego bitu w bitmapie zajętości, to oznacza, że struktury danych opisujące filesystem na dysku utraciły integralność. Zgłaszamy błąd systemowy i wychodzimy.

	if (j >= EXT2_BLOCKS_PER_GROUP(sb)) {
		ext2_error (sb, "ext2_new_block",
			    "Free blocks count corrupted for block group %d", i);
		unlock_super (sb);
		return 0;
	}

Do tego miejsca skaczemy wtedy, gdy znajdziemy spójny ciąg ośmiu wolnych bloków. Ten ciąg może być częscią większego wolnego obszaru i szukamy początku tego obszaru. Łatwo zauważyć, że wystarczy się tu ograniczyć do przeszukania siedmiu bloków w tył.

search_back:
	for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh->b_data); k++, j--);
	
Kiedy już znajdziemy wolny blok, sprawdzamy, czy alokacja tego bloku nie naruszy warunku quoty nałożonej na filesystem. W tym celu próbujemy zwiększyć wartość opisującą ilość bajtów zaalokowanych w filesystemie o rozmiar bloku. Jesli to się nie uda, wychodzimy, a jeśli się uda, pozostawiamy tak zmienioną wartość i idziemy dalej. Operacje alokacji i dealokacji dotyczące quoty dokonywane są za pomocą funkcji wskazywanych przez pola "alloc_block" i "free_block" należące do struktury "dquot_operations" w strukturze opisującej super-blok.

got_block:
	if (sb->dq_op)
		if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, sb->s_blocksize))) {
			unlock_super (sb);
			*err = -EDQUOT;
			return 0;
		}

Obliczmy fizyczny numer bloku na dysku i, jeśli opcja ścisłej kontroli numerów alokowanych bloków została włączona, sprawdzamy, czy alokowany blok nie leży w obszarze systemowym. Może tak się zdarzyć, jeśli w bitmapie zajętości bloków grupy bloki systemowe (tzn. bloki zawierające bitmapy zajętości bloków i i-węzłów oraz tablice i-węzłów) mają skasowane bity zajetości, co świadczy o utracie integralności struktur trzymających informacje o filesystemie. Jeżeli ta nieprawdopodobna sytuacja się wydarzy, wychodzimy z błędem.

	tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;

	if (test_opt (sb, CHECK_STRICT) &&
	    (tmp == gdp->bg_block_bitmap ||
	     tmp == gdp->bg_inode_bitmap ||
	     in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group)))
		ext2_panic (sb, "ext2_new_block",
			    "Allocating block in system zone - "
			    "block = %u", tmp);

Następnie próbujemy ustawić bit zajętości dla znalezionego bloku w bitmapie zajętości jego grupy. Jeżeli ten bit jest juz ustawiony, to inny proces wyprzedził nas w dostępie do tego bloku i cały proces poszukiwania bloku musimy zacząć od nowa, kontynuujemy go jednak od miejsca, w którym aktualnie zakonczyliśmy. Z uwagi na fakt, że proces alokacji bloku zaczyna się i kończy zajęciem i zwolnieniem semafora chroniącego super-blok, taka sytuacja wydaje się nieprawdopodobna, jednak może się zdarzyć, że blok został zaalokowany w inny sposób, bez użycia funkcji "ext2_new_block". Przed powrotem do początku musimy pamiętać o przywróceniu prawidłowej wartości quoty.

	if (set_bit (j, bh->b_data)) {
		ext2_warning (sb, "ext2_new_block",
			      "bit already set for block %d", j);
		if (sb->dq_op)
			sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
		goto repeat;
	}

Teraz dokonujemy prealokacji bloków. Dopuszczalność prealokacji jest rozstrzygana w momencie kompilacji jądra, natomiast chęć prealokacji zgłaszana jest za pośrednictwem parametru "prealloc_block". Próbujemy prealokować 7 kolejnych bloków, jednak nie więcej niż pozostało ich do końca bieżącej grupy i kończymy zaraz po pierwszej nieudanej próbie. Prealokacja każdego kolejnego bloku polega na próbie ustawienia bitu w bitmapie zajętości (o ile jest wolny, oczywiście) poprzedzonej testem quoty. Po zakończeniu prealokacji uaktualnia się informację w deskryptorze o ilości wolnych bloków w grupie o ilość bloków prealokowanych oraz informację w super-bloku o ilości wolnych bloków dostępnych w filesystemie.


#ifdef EXT2_PREALLOCATE
	if (prealloc_block) {
		*prealloc_count = 0;
		*prealloc_block = tmp + 1;
		for (k = 1;
		     k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
			if (sb->dq_op)
				if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)))
					break;
			if (set_bit (j + k, bh->b_data)) {
				if (sb->dq_op)
					sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
				break;
			}
			(*prealloc_count)++;
		}	
		gdp->bg_free_blocks_count -= *prealloc_count;
		es->s_free_blocks_count -= *prealloc_count;
		ext2_debug ("Preallocated a further %lu bits.\n",
			    *prealloc_count);
	}
#endif

	j = tmp;

Bufor w pamięci przechowujący zawartość super-bloku oznacza się jako brudny (funkcja "mark_buffer_dirty" ustawiająca odpowiedni bit w nagłówku bufora) i rozpoczyna transmisję (funkcja "ll_rw_block"), w miarę możliwości asynchroniczną. Jeżeli transmisja jest synchroniczna, czekamy na jej zakończenie (funkcja "wait_on_buffer").
 

	mark_buffer_dirty(bh, 1);
	if (sb->s_flags & MS_SYNCHRONOUS) {
		ll_rw_block (WRITE, 1, &bh);
		wait_on_buffer (bh);
	}

Teraz sprawdza się, czy numer alokowanego bloku nie jest większy niz maksymalny dostępny w filesystemie. Jeśli jest, wychodzimy z błędem.

	if (j >= es->s_blocks_count) {
		ext2_error (sb, "ext2_new_block",
			    "block >= blocks count - "
			    "block_group = %d, block=%d", i, j);
		unlock_super (sb);
		return 0;
	}

Wczytujemy do pamięci zaalokowany blok dyskowy. Ta operacja ma na celu powiązanie bufora w pamięci z blokiem na dysku, dzięki czemu zostanie on automatycznie zapisany na dysk, gdy proces skończy w nim pisać. Zajmie się tym podsystem obsługi buforów.

	if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) {
		ext2_error (sb, "ext2_new_block", "cannot get block %d", j);
		unlock_super (sb);
		return 0;
	}

Inicjalnie zerujemy zawartość bufora i oznaczamy go jako świeży, posiadający aktualne dane, oraz jako brundy, do natychmiastowego zapisu na dysk, po czym zwalniamy bufor.

	memset(bh->b_data, 0, sb->s_blocksize);
	mark_buffer_uptodate(bh, 1);
	mark_buffer_dirty(bh, 1);
	brelse (bh);

Ostatecznie zmniejszamy liczbę wolnych bloków w grupie zapisaną w deskryptorze grupy i liczbę wolnych bloków w filesystemie zapisaną w super-bloku, po czym oznaczamy bufor zawierający deskryptor oraz bufor zawierający super-blok jako brudne i zwalniamy super-blok.

	gdp->bg_free_blocks_count--;
	mark_buffer_dirty(bh2, 1);
	es->s_free_blocks_count--;
	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
	sb->s_dirt = 1;
	unlock_super (sb);

Ostatecznie ustawiamy kod błędu na zero i zwracamy numer zaalokowanego bloku (fizyczny na dysku, w odróżnieniu od logicznego numeru, który jest mniejszy od fizycznego o numer pierwszego bloku obszaru, w którym można alokować bloki).

	*err = 0;
	return j;
}


unsigned long ext2_count_free_blocks (struct super_block * sb) { #ifdef EXT2FS_DEBUG struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; int bitmap_nr; struct ext2_group_desc * gdp; int i; lock_super (sb); es = sb->u.ext2_sb.s_es; desc_count = 0; bitmap_count = 0; gdp = NULL; for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { gdp = get_group_desc (sb, i, NULL); desc_count += gdp->bg_free_blocks_count; bitmap_nr = load_block_bitmap (sb, i); if (bitmap_nr < 0) continue; x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr], sb->s_blocksize); printk ("group %d: stored = %d, counted = %lu\n", i, gdp->bg_free_blocks_count, x); bitmap_count += x; } printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n", es->s_free_blocks_count, desc_count, bitmap_count); unlock_super (sb); return bitmap_count; #else return sb->u.ext2_sb.s_es->s_free_blocks_count; #endif }
static inline int block_in_use (unsigned long block, struct super_block * sb, unsigned char * map) { return test_bit ((block - sb->u.ext2_sb.s_es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb), map); }
void ext2_check_blocks_bitmap (struct super_block * sb) { struct buffer_head * bh; struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; unsigned long desc_blocks; int bitmap_nr; struct ext2_group_desc * gdp; int i, j; lock_super (sb); es = sb->u.ext2_sb.s_es; desc_count = 0; bitmap_count = 0; gdp = NULL; desc_blocks = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { gdp = get_group_desc (sb, i, NULL); desc_count += gdp->bg_free_blocks_count; bitmap_nr = load_block_bitmap (sb, i); if (bitmap_nr < 0) continue; bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; if (!test_bit (0, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Superblock in group %d is marked free", i); for (j = 0; j < desc_blocks; j++) if (!test_bit (j + 1, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Descriptor block #%d in group " "%d is marked free", j, i); if (!block_in_use (gdp->bg_block_bitmap, sb, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Block bitmap for group %d is marked free", i); if (!block_in_use (gdp->bg_inode_bitmap, sb, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Inode bitmap for group %d is marked free", i); for (j = 0; j < sb->u.ext2_sb.s_itb_per_group; j++) if (!block_in_use (gdp->bg_inode_table + j, sb, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Block #%d of the inode table in " "group %d is marked free", j, i); x = ext2_count_free (bh, sb->s_blocksize); if (gdp->bg_free_blocks_count != x) ext2_error (sb, "ext2_check_blocks_bitmap", "Wrong free blocks count for group %d, " "stored = %d, counted = %lu", i, gdp->bg_free_blocks_count, x); bitmap_count += x; } if (es->s_free_blocks_count != bitmap_count) ext2_error (sb, "ext2_check_blocks_bitmap", "Wrong free blocks count in super block, " "stored = %lu, counted = %lu", (unsigned long) es->s_free_blocks_count, bitmap_count); unlock_super (sb); }

Autorzy: Krzysztof Ostrowski i Maciej Zarzycki