Dlaczego moja dokładnie 100 partycja MiB o wielkości bloku 1 KiB nie ma odpowiednich dostępnych bloków / przestrzeni?


33

Mam zwirtualizowane środowisko o bardzo dużej gęstości z kontenerami, dlatego staram się, aby każdy kontener był naprawdę mały. „Naprawdę mały” oznacza 87 MB na podstawowym Ubuntu 14.04 (Trusty Tahr) bez naruszenia kompatybilności menedżera pakietów.

Dlatego używam LVM jako miejsca do przechowywania moich pojemników, a ostatnio znalazłem bardzo dziwne liczby. Tutaj są.

Stwórzmy logiczny wolumin 100 MiB (tak, potęga 2).

sudo lvcreate -L100M -n test1 /dev/purgatory

Chciałbym sprawdzić rozmiar, więc wystawiam sudo lvs --units k

test1             purgatory  -wi-a----  102400.00k

Kochanie, to naprawdę 100 MiB.

Teraz stwórzmy system plików ext4 . I oczywiście pamiętamy -m 0parametr, który zapobiega marnowaniu przestrzeni.

sudo mkfs.ext4 -m 0 /dev/purgatory/test1

mke2fs 1.42.9 (4-Feb-2014)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
25688 inodes, 102400 blocks
0 blocks (0.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=67371008
13 block groups
8192 blocks per group, 8192 fragments per group
1976 inodes per group
Superblock backups stored on blocks:
        8193, 24577, 40961, 57345, 73729

Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

Słodko i czysto. Zwróć uwagę na rozmiar bloku - nasz wolumin logiczny jest mały, więc mkfs.ext4 postanowił stworzyć blok o wielkości 1 KiB, a nie zwykły 4 KiB.

Teraz go zamontujemy.

sudo mount /dev/purgatory/test1 /mnt/test1

I zadzwońmy dfbez parametrów (chcielibyśmy zobaczyć 1 blok KiB)

/dev/mapper/purgatory-test1     95054    1550     91456   2% /mnt/test1

Zaczekaj, o cholera ~

Łącznie mamy 95054 bloków. Ale samo urządzenie ma 102400 bloków 1 KiB. Mamy tylko 92,8% naszego magazynu. Gdzie są moje bloki, stary?

Spójrzmy na to na prawdziwym urządzeniu blokowym. A ma dysk wirtualny 16 GiB, 16777216 bloków 1K, ale tylko 15396784 bloków jest w formacie df. 91,7%, co to jest?

Teraz następuje śledztwo (spoiler: brak wyników)

  1. System plików nie może rozpocząć się na początku urządzenia. To dziwne, ale możliwe. Na szczęście ext4 ma bajty magiczne, sprawdźmy ich obecność.

    sudo hexdump -C / dev / purgatory / test1 | grep „53 ef”

To pokazuje superblok:

00000430  a9 10 e7 54 01 00 ff ff  53 ef 01 00 01 00 00 00  |...T....S.......|

Heks 430 = 1072 grudnia, więc gdzieś po pierwszym kilobajcie. Wygląda rozsądnie, ext4 pomija pierwsze 1024 bajty w przypadku takich osobliwości, jak VBR itp.

  1. To jest dziennik!

Nie, nie jest. Dziennik zabiera miejsce z Dostępne, jeśli wyjście df.

  1. Och, mamy dump2fs i możemy sprawdzić tam rozmiary!

... dużo greps ...

sudo dumpe2fs /dev/purgatory/test1 | grep "Free blocks"

Ojej.

Free blocks:              93504
  Free blocks: 3510-8192
  Free blocks: 8451-16384
  Free blocks: 16385-24576
  Free blocks: 24835-32768
  Free blocks: 32769-40960
  Free blocks: 41219-49152
  Free blocks: 53249-57344
  Free blocks: 57603-65536
  Free blocks: 65537-73728
  Free blocks: 73987-81920
  Free blocks: 81921-90112
  Free blocks: 90113-98304
  Free blocks: 98305-102399

I mamy inny numer. 93504 wolnych bloków.

Pytanie brzmi: co się dzieje?

  • Zablokuj urządzenie: 102400k (lvs mówi)
  • Rozmiar systemu plików: 95054k (mówi df)
  • Darmowe bloki: 93504k (mówi dumpe2fs)
  • Dostępny rozmiar: 91456 k (mówi df)

Dlatego nadal używam ext2do małych partycji.
frostschutz

@frostschutz ext2wygląda tutaj rozsądnie, pewnie
maniaque

Odpowiedzi:


32

Spróbuj tego: mkfs.ext4 -N 104 -m0 -O ^has_journal,^resize_inode /dev/purgatory/test1

Myślę, że to pozwala zrozumieć „co się dzieje”.

-N 104 (ustaw liczbę iNodes, które powinien mieć system plików)

  • każde „iNode” kosztuje ” powierzchnię użytkową (128 bajtów)

-m 0(bez zarezerwowanych bloków)
-O ^has_journal,^resize_inode(dezaktywuj funkcje has_journaliresize_inode

  • resize_inode„kosztuje” wolne miejsce (większość z 1550 bloków 1K / 2%, które widzisz w swoim df- 12K jest wykorzystywana do folderu „utracone + znalezione”)
  • has_journal„kosztuje” powierzchnię użytkową (4096 bloków 1K w twoim przypadku)

Wydostajemy 102348się 102400, kolejne 52 bloki są bezużyteczne (jeśli usunęliśmy folder „lost + found”). Dlatego nurkujemy w dumpe2fs:

Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
  Checksum 0x5ee2, unused inodes 65533
  Primary superblock at 1, Group descriptors at 2-2
  Block bitmap at 3 (+2), Inode bitmap at 19 (+18)
  Inode table at 35-35 (+34)
  8150 free blocks, 0 free inodes, 1 directories, 65533 unused inodes
  Free blocks: 17-18, 32-34, 48-8192
  Free inodes: 
Group 1: (Blocks 8193-16384) [BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x56cf, unused inodes 5
  Backup superblock at 8193, Group descriptors at 8194-8194
  Block bitmap at 4 (+4294959107), Inode bitmap at 20 (+4294959123)
  Inode table at 36-36 (+4294959139)
  8190 free blocks, 6 free inodes, 0 directories, 5 unused inodes
  Free blocks: 8193-16384
  Free inodes: 11-16
Group 2: (Blocks 16385-24576) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x51eb, unused inodes 8
  Block bitmap at 5 (+4294950916), Inode bitmap at 21 (+4294950932)
  Inode table at 37-37 (+4294950948)
  8192 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 16385-24576
  Free inodes: 17-24
Group 3: (Blocks 24577-32768) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x3de1, unused inodes 8
  Backup superblock at 24577, Group descriptors at 24578-24578
  Block bitmap at 6 (+4294942725), Inode bitmap at 22 (+4294942741)
  Inode table at 38-38 (+4294942757)
  8190 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 24577-32768
  Free inodes: 25-32
Group 4: (Blocks 32769-40960) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x79b9, unused inodes 8
  Block bitmap at 7 (+4294934534), Inode bitmap at 23 (+4294934550)
  Inode table at 39-39 (+4294934566)
  8192 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 32769-40960
  Free inodes: 33-40
Group 5: (Blocks 40961-49152) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x0059, unused inodes 8
  Backup superblock at 40961, Group descriptors at 40962-40962
  Block bitmap at 8 (+4294926343), Inode bitmap at 24 (+4294926359)
  Inode table at 40-40 (+4294926375)
  8190 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 40961-49152
  Free inodes: 41-48
Group 6: (Blocks 49153-57344) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x3000, unused inodes 8
  Block bitmap at 9 (+4294918152), Inode bitmap at 25 (+4294918168)
  Inode table at 41-41 (+4294918184)
  8192 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 49153-57344
  Free inodes: 49-56
Group 7: (Blocks 57345-65536) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x5c0a, unused inodes 8
  Backup superblock at 57345, Group descriptors at 57346-57346
  Block bitmap at 10 (+4294909961), Inode bitmap at 26 (+4294909977)
  Inode table at 42-42 (+4294909993)
  8190 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 57345-65536
  Free inodes: 57-64
Group 8: (Blocks 65537-73728) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0xf050, unused inodes 8
  Block bitmap at 11 (+4294901770), Inode bitmap at 27 (+4294901786)
  Inode table at 43-43 (+4294901802)
  8192 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 65537-73728
  Free inodes: 65-72
Group 9: (Blocks 73729-81920) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x50fd, unused inodes 8
  Backup superblock at 73729, Group descriptors at 73730-73730
  Block bitmap at 12 (+4294893579), Inode bitmap at 28 (+4294893595)
  Inode table at 44-44 (+4294893611)
  8190 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 73729-81920
  Free inodes: 73-80
Group 10: (Blocks 81921-90112) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x60a4, unused inodes 8
  Block bitmap at 13 (+4294885388), Inode bitmap at 29 (+4294885404)
  Inode table at 45-45 (+4294885420)
  8192 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 81921-90112
  Free inodes: 81-88
Group 11: (Blocks 90113-98304) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0x28de, unused inodes 8
  Block bitmap at 14 (+4294877197), Inode bitmap at 30 (+4294877213)
  Inode table at 46-46 (+4294877229)
  8192 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 90113-98304
  Free inodes: 89-96
Group 12: (Blocks 98305-102399) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x9223, unused inodes 8
  Block bitmap at 15 (+4294869006), Inode bitmap at 31 (+4294869022)
  Inode table at 47-47 (+4294869038)
  4095 free blocks, 8 free inodes, 0 directories, 8 unused inodes
  Free blocks: 98305-102399
  Free inodes: 97-104

i policz używane bloki (w przypadku superbloku kopii zapasowej, deskryptorów grup, mapy bitowej bloku, mapy bitowej Inode i tabeli Inode) lub my grepi policz:

LANG=C dumpe2fs /dev/mapper/vg_vms-test1 | grep ' at ' | grep -v ',' | wc -l

co daje nam liczbę linii, które mają pojedynczy blok (w naszym przykładzie) i

LANG=C dumpe2fs /dev/mapper/vg_vms-test1 | grep ' at ' | grep ',' | wc -l

co daje nam liczbę linii, które mają dwa bloki (w naszym przykładzie).

Mamy więc (w naszym przykładzie) 13linie z jednym blokiem każdy i 19linie z dwoma blokami każdy.

13+19*2

co daje nam 51bloki, które są używane przez sam ext4. Wreszcie został tylko jeden blok. Blok 0, czyli pomijane 1024bajty na początku dla rzeczy takich jak sektor rozruchowy.


A jeśli dziennik zajmuje tylko 4096 tys., Nie mam tego numeru (95054 - 4096)! = 91456?
maniaque

Wszystkie liczby są tutaj w k, więc 95054k łącznie - 4096k dziennika! = 91456k dostępne.
maniaque

1
dfna fs z dziennikiem: 95054k - dfna fs bez jorunal 99150k - i nie mieszaj „użytecznej” i „wolnej” przestrzeni.
xx4h

Niektóre systemy plików, np. Xfs, dynamicznie przydzielają przestrzeń dla i-węzłów w razie potrzeby. Możesz spróbować xfs i btrfs, jeśli jesteś ciekawy. mkfs.xfs -l size=512 -d agcount=1stworzy system plików z absolutnie minimalnym rozmiarem dziennika (inaczej dziennika), ale wydajność zapisu może się pogorszyć. Nie sądzę, aby obsługa kodu XFS działała bez dziennika. Prawdopodobnie tylko do odczytu, aby obsłużyć przypadki, w których uszkodzone jest zewnętrzne urządzenie rejestrujące. (również agcount=1jest prawdopodobnie kolejnym strasznym pomysłem na wydajność zapisu, zwłaszcza równoległym. A nagłówki grup alokacji są prawdopodobnie również małe.)
Peter Cordes

Zainteresowałem się i wypróbowałem XFS. Jeśli istnieje kombinacja opcji dla systemu Linux XFS, które pozwolą, aby minimalny rozmiar dziennika spadł do absolutnego minimum 512 bloków, IDK co to jest. mkfs.xfs -d agcount=1na partycji 100 MB udostępniono FS 95980kiB, przy użyciu 5196k, dostępnych 90784k. Domyślne konto to 4, a domyślny rozmiar dziennika to 1605 bloków (również minimalny). Tak więc XFS używa tak małego logu, jaki pozwala ci określić, dla małych FS.
Peter Cordes

19

Krótka odpowiedź:

Nie cała przestrzeń na urządzeniu blokowym staje się dostępną przestrzenią dla twoich danych: część surowego miejsca jest potrzebna na elementy wewnętrzne systemu plików, księgowość za kulisami.

Ta księgowość obejmuje superblok, deskryptory grup bloków, mapy bitowe bloków i i-węzłów oraz tabelę i-węzłów. Ponadto w wielu lokalizacjach tworzone są kopie superbloku do celów tworzenia kopii zapasowych / odzyskiwania. Długie informacje na temat wewnętrznych elementów systemu plików EXT4 można znaleźć na stronie ext4.wiki.kernel.org .

Ponieważ EXT4 jest kronikowanym systemem plików, który również zajmuje trochę miejsca.

Dodatkowo część miejsca jest zarezerwowana na przyszłe rozszerzenia systemu plików.

Długa odpowiedź:

Odtworzyłem Twój scenariusz na jednym z moich systemów testowych:

lvcreate -L 100M -n test MyVG
mkfs.ext4 -b 1024 /dev/MyVG/test 

Następnie, nawet przed zamontowaniem systemu plików, dumpe2fspokazuje:

Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              25688
Block count:              102400
Reserved block count:     5120
Free blocks:              93504
Free inodes:              25677
First block:              1
Block size:               1024
Fragment size:            1024
Reserved GDT blocks:      256
Blocks per group:         8192
Fragments per group:      8192
Inodes per group:         1976
Inode blocks per group:   247
Flex block group size:    16
Filesystem created:       Fri Feb 20 13:20:54 2015
Last mount time:          n/a
Last write time:          Fri Feb 20 13:20:55 2015
...
Journal size:             4096k  
...

a po montażu:

df /tmp/test/
Filesystem              1K-blocks  Used Available Use% Mounted on
/dev/mapper/MyVG-test       99150  5646     88384   7% /tmp/test

Co więc dfnam pokazuje? Z 102400 bloków surowego urządzenia pamięci masowej 99150 bloków 1K jest widocznych dla systemu plików, co oznacza, że ​​3250 1-kilobajtowych bloków surowego miejsca do przechowywania stało się bezużyteczne do faktycznego przechowywania danych.

Gdzie się podziały te bloki? Przewijanie w dół dumpe2fswyników pokazuje dokładnie, gdzie:

Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
  Checksum 0x0d67, unused inodes 1965
  Primary superblock at 1, Group descriptors at 2-2
  Reserved GDT blocks at 3-258
  Block bitmap at 259 (+258), Inode bitmap at 275 (+274)
  Inode table at 291-537 (+290)
  4683 free blocks, 1965 free inodes, 2 directories, 1965 unused inodes
  Free blocks: 3510-8192
  Free inodes: 12-1976

1 block (blok # 0) Pierwsze 1024 bajty są pomijane, aby umożliwić instalację sektorów rozruchowych x86 i innych osobliwości.
1 block jest zajęty przez główny super blok.
1 block zawiera deskryptory grupy.
256 blockszarezerwowane dla tabeli deskryptorów grupy, aby umożliwić przyszłą zmianę rozmiaru systemu plików. 16 blocks są przypisane do bitmapy blokowej.
16 blockssą przypisane do bitmapy i-węzłowej.
246 blockssą przypisane do tabeli i-węzłów.

To już stanowi 537 z 3250 brakujących bloków. System plików ext4 jest podzielony na szereg grup bloków, a przewijanie w dół pokazuje podobny przydział surowej pojemności pamięci do wewnętrznych elementów systemu plików w innych grupach bloków:

Group 1: (Blocks 8193-16384) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x0618, unused inodes 1976
  Backup superblock at 8193, Group descriptors at 8194-8194
  Reserved GDT blocks at 8195-8450
  Block bitmap at 260 (+4294959363), Inode bitmap at 276 (+4294959379)
  Inode table at 538-784 (+4294959641)
  7934 free blocks, 1976 free inodes, 0 directories, 1976 unused inodes
  Free blocks: 8451-16384
  Free inodes: 1977-3952
Group 2: (Blocks 16385-24576) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
  Checksum 0xcfd3, unused inodes 1976
  Block bitmap at 261 (+4294951172), Inode bitmap at 277 (+4294951188)
  Inode table at 785-1031 (+4294951696)
  8192 free blocks, 1976 free inodes, 0 directories, 1976 unused inodes
  Free blocks: 16385-24576
  Free inodes: 3953-5928 
Group ....

Teraz wróć do dfwyjścia:

df /tmp/test/
Filesystem              1K-blocks  Used Available Use% Mounted on
/dev/mapper/MyVG-test       99150  5646     88384   7% /tmp/test

Powodem, dla którego w tym nowym systemie plików jest już zaznaczone 7% pojemności jako w użyciu, jest:

99150 (rozmiar systemu plików) MINUS 5120 (liczba zarezerwowanych bloków) MINUS 5646 (zużyte bloki, z których 4096 pochodzi z dziennika (ponownie część danych wyjściowych dumpe2fs))
= 88384

Ilość wolnych bloków w dumpe2fs jest dostępnym rozmiarem systemu plików minus faktyczne użycie (i nie bierze pod uwagę zarezerwowanych bloków), więc 99150 - 5646 = 93504.


0

Brak odpowiedzi na pytanie, ale zainteresowałem się, więc wyobrażam sobie, że inni to zrobią. Ponieważ miałem już bootowany liveCD i miałem twardy dysk, z którym mogłem zadzierać, nie martwiąc się, że literówki mogą coś uszkodzić, poszedłem do przodu i przetestowałem.

Zrobiłem partycje ze wszystkimi FSami, dla których Ubuntu 14.10 dostarcza mkfs, na partycjach 100 MB. (z wyjątkiem minix, który obsługuje tylko 64MiB i bfs, co jest rzeczą SCO, o której nigdy nie słyszałem.)

Najpierw spojrzałem na df -kdostępną przestrzeń (z domyślnymi ustawieniami mkfs), a następnie ddprzeszedłem /dev/zerodo pliku na każdym FS, aby upewnić się, że można je całkowicie wypełnić. (tj. sprawdź, czy roszczenie available spacebyło naprawdę dostępne).
for i in /media/ubuntu/small-*;do sudo dd if=/dev/zero of="$i/fill" bs=16k;done

* FS: empty `df -k` : non-zero `df -k` when full (false bottom)
* jfs:  101020k
* fat32:100808k  : 4
* ntfs:  99896k
* btrfs: 98276k  : 4428
* ext2:  92480k
* xfs:   90652k  : 20
* ext4:  86336k
* ext3:  88367k
* reiserfs(v3): 69552k

Dlaczego btrfs ma tyle niepotrzebnego miejsca? Może dla metadanych? no nie:

$ for i in /media/ubuntu/small-*;do sudo touch "$i/touched";done
touch: cannot touch ‘/media/ubuntu/small-btrfs/touched’: No space left on device
touch: cannot touch ‘/media/ubuntu/small-reiser/touched’: No space left on device

Oba systemy plików oparte na drzewach nie mogą nigdzie spakować pustego pliku, ale wszystkie inne potrafią.

Lub po prostu zobacz, jak duży plik możesz utworzyć:

$ ls -SdlG --block-size=1k /media/ubuntu/small-*/*
-rw-r--r-- 1 root   101020 Feb 21 11:55 /media/ubuntu/small-jfs/fill
-rw-r--r-- 1 ubuntu 100804 Feb 21 11:55 /media/ubuntu/small-fat/fill
-rw------- 1 ubuntu  99848 Feb 21 11:55 /media/ubuntu/small-ntfs/fill
-rw-r--r-- 1 root    97216 Feb 21 11:55 /media/ubuntu/small-ext2/fill
-rw-r--r-- 1 root    93705 Feb 21 11:27 /media/ubuntu/small-btrfs/foo
-rw-r--r-- 1 root    93120 Feb 21 11:55 /media/ubuntu/small-ext3/fill
-rw-r--r-- 1 root    91440 Feb 21 11:55 /media/ubuntu/small-ext/fill
-rw-r--r-- 1 root    90632 Feb 21 11:55 /media/ubuntu/small-xfs/fill
-rw-r--r-- 1 root    69480 Feb 21 11:55 /media/ubuntu/small-reiser/fill
drwx------ 2 root       12 Feb 21 11:33 /media/ubuntu/small-ext2/lost+found
drwx------ 2 root       12 Feb 21 11:43 /media/ubuntu/small-ext3/lost+found
drwx------ 2 root       12 Feb 21 11:29 /media/ubuntu/small-ext/lost+found

(Nazywałem moją partycję ext4 „small-ext”, ponieważ nie planowałem wariować i tworzyć każdego systemu plików. Więc tutaj ext = ext4. NIE oryginalna wersja ext-ext2).

I df -kwyjdź po ich usunięciu:

/dev/sdd6          95980    5328     90652   6% /media/ubuntu/small-xfs
/dev/sdd7          95054    1550     86336   2% /media/ubuntu/small-ext
/dev/sdd5         102400   93880    101020  96% /media/ubuntu/small-btrfs
/dev/sdd8         101168  101168         0 100% /media/ubuntu/small-jfs
/dev/sdd9          99150    1550     92480   2% /media/ubuntu/small-ext2
/dev/sdd10        102392   32840     69552  33% /media/ubuntu/small-reiser
/dev/sdd11        100808       1    100808   1% /media/ubuntu/small-fat
/dev/sdd12        102396    2548     99848   3% /media/ubuntu/small-ntfs
/dev/sdd13         95054    1567     88367   2% /media/ubuntu/small-ext3

(jfs wrócił do 1% wykorzystanego po tym, jak usunąłem również „dotknięty”. Albo było opóźnienie, albo trzeba było innego zapisu, aby uzyskać dostępną wielkość do aktualizacji.)

W każdym razie myślę, że o to chodzi z mojej ciekawości.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.