pax_global_header 0000666 0000000 0000000 00000000064 14166610522 0014515 g ustar 00root root 0000000 0000000 52 comment=eb19c790398a283302a94b12400afe86b91b2bda
snapraid-12.1/ 0000775 0000000 0000000 00000000000 14166610522 0013241 5 ustar 00root root 0000000 0000000 snapraid-12.1/.circleci/ 0000775 0000000 0000000 00000000000 14166610522 0015074 5 ustar 00root root 0000000 0000000 snapraid-12.1/.circleci/config.yml 0000664 0000000 0000000 00000002666 14166610522 0017076 0 ustar 00root root 0000000 0000000 # Build configuration for https://circleci.com/
version: 2.1
orbs:
win: circleci/windows@2.2.0
workflows:
main:
jobs:
- build_ubuntu
- build_docker
jobs:
build_docker:
docker:
- image: circleci/buildpack-deps:stable
steps:
- checkout
- run: autoreconf -i && ./configure --enable-warning-as-error && make all dist
build_ubuntu:
machine:
image: ubuntu-2004:202101-01
steps:
- checkout
- run: autoreconf -i && ./configure --enable-warning-as-error && make all dist
build_win:
executor:
name: win/default
steps:
- run:
name: Installing MSYS2
shell: powershell.exe
command: 'choco install msys2'
- run:
name: Installing tools
shell: powershell.exe
command: 'C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S autoreconf automake mingw-w64-x86_64-toolchain"'
- checkout
- run:
name: Autoreconf
shell: C:\\tools\\msys64\\usr\\bin\\bash.exe -l
command: 'cd /c/Users/circleci/project && autoreconf -i'
- run:
name: Configure
shell: C:\\tools\\msys64\\usr\\bin\\bash.exe -l
command: 'cd /c/Users/circleci/project && ./configure --enable-warning-as-error'
- run:
name: Make
shell: C:\\tools\\msys64\\usr\\bin\\bash.exe -l
command: 'cd /c/Users/circleci/project && make'
snapraid-12.1/.drone.yml 0000664 0000000 0000000 00000000401 14166610522 0015144 0 ustar 00root root 0000000 0000000 # Build configuration for https://www.tea-ci.org
build:
image: teaci/msys$$arch
shell: mingw$$arch
pull: true
commands:
- autoreconf -i
- ./configure --enable-warning-as-error
- make
- make check
matrix:
arch:
- 64
- 32
snapraid-12.1/.github/ 0000775 0000000 0000000 00000000000 14166610522 0014601 5 ustar 00root root 0000000 0000000 snapraid-12.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14166610522 0016636 5 ustar 00root root 0000000 0000000 snapraid-12.1/.github/workflows/build.yml 0000664 0000000 0000000 00000001733 14166610522 0020464 0 ustar 00root root 0000000 0000000 name: Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build-mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: brew install automake
- run: autoreconf -i
- run: ./configure --enable-warning-as-error
- run: make all
- run: make distcheck
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: autoreconf -i
- run: ./configure --enable-warning-as-error
- run: make all
- run: make distcheck
build-win:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: mingw-w64-x86_64-gcc autoconf automake make
- run: autoreconf -i
- run: ./configure --enable-warning-as-error
- run: make all
- run: make distcheck
snapraid-12.1/.github/workflows/codeql-analysis.yml 0000664 0000000 0000000 00000004614 14166610522 0022456 0 ustar 00root root 0000000 0000000 # For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '20 6 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# âšī¸ Command-line programs to run using the OS shell.
# đ https://git.io/JvXDl
# âī¸ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
snapraid-12.1/.gitignore 0000664 0000000 0000000 00000000775 14166610522 0015242 0 ustar 00root root 0000000 0000000 # archives
*.zip
*.tar.gz
*.tgz
# backups
*~
# logs
*.log
stream*.bin
# upload
makepush.sh
# autotools
Makefile
Makefile.in
compile
aclocal.m4
autom4te.cache/
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
install-sh
missing
stamp-h1
.dirstamp
# objects
snapraid
mktest
mkstream
*.exe
*.o
*.gcda
*.gcno
*.gcov
# projects
*.dst
*.epr
*.geany
# AFL
makeafl.sh
afl/
afl_corpus/
# specific
cov/
contrib/
paper/
kernel/
obj/
bench/
support/
archive/
www/
wiki/
beta/
ftp/
snapraid-12.1/.travis.yml 0000664 0000000 0000000 00000000304 14166610522 0015347 0 ustar 00root root 0000000 0000000 # Travis CI configuration file
sudo: false
language: c
script: autoreconf -i && ./configure --enable-warning-as-error && make all distcheck
compiler:
- clang
- gcc
os:
- linux
- osx
snapraid-12.1/AUTHORS 0000664 0000000 0000000 00000001143 14166610522 0014310 0 ustar 00root root 0000000 0000000 SnapRAID AUTHORS
================
The author of SnapRAID is Andrea Mazzoleni.
You can contact me sending an email at:
amadvance@gmail.com
Please don't send support requests at this address, but use the
SnapRAID Forum.
ACKNOWLEDGMENTS
===============
Special thanks to Leifi, the king of the Forum!
Thanks for the testing, suggestions and bug reports to klbl,
jwill42, tholiin, uhclem, reardonia, Jens, rubylaser and the
whole SnapRAID Forum.
Thanks to Maxim Tikhonov for making the Ubuntu packages.
Also thanks for the support to Ben_in_COSprings, Darin, micksh, RamanRB
and the whole AVS Forum.
snapraid-12.1/CHECK 0000664 0000000 0000000 00000001375 14166610522 0014007 0 ustar 00root root 0000000 0000000 SnapRAID CHECK
==============
The regression test of SnapRAID is run using the command:
make check
You can also run the regression test in valgrind with:
./configure --enable-valgrind
make check
To run a coverage test you should use:
./configure --enable-coverage
make lcov_reset
make check
make lcov_capture
make lcov_html
and open the file ./cov/index.html in your browser to see the results.
Please note that in the coverage analysis we exclude the handling of all
the error conditions that result in an immediate termination of the program.
You can recognize this excluded code because it's enclosed between
the LCOV_EXCL_START and LCOV_EXCL_STOP keywords.
To test with the clang static analyzer use:
scan-build ./configure
scan-build make
snapraid-12.1/CHECKSUMS 0000664 0000000 0000000 00000031332 14166610522 0014513 0 ustar 00root root 0000000 0000000 sha256
761eaf08937139a89d79d1ffbad42508f270d3aa603eac50475f7449ae278dcf snapraid-1.2.tar.gz
10e64f2629eb469a32eabc84e1f9a59593cbbf61282ead964b622af1ec0c4681 snapraid-1.2-windows-x64.zip
27da3c54fe408ab53c6e2b4b398ae46328a2dbc701b785a55bf76ffed6046eb2 snapraid-1.2-windows-x86.zip
2e6aaa660a5a74665044ead40959006632dcd1ee1e21091e6260f5b845304f96 snapraid-1.3.tar.gz
0d0cefeabf8230b66414a8731ceb52a0adaaedb5be0fa11d07a6be44e9f2fb6d snapraid-1.3-windows-x64.zip
4358802fb022ba5e2f8d1323ce963155adccea99069abd5f901eadf1411b6366 snapraid-1.3-windows-x86.zip
157b0ca7cfa9cba43cf995b4c32b0b206e299f5ffff58062069bb6ec41c77c0d snapraid-1.4.tar.gz
5b210dd4667369799fd02757e8efc4d733c704cfe72319d297d4ab969b80c962 snapraid-1.4-windows-x64.zip
77047e1fa1bfcb12db7d5c3167e7dbbbb1718909f1003fd185cdf219cd952427 snapraid-1.4-windows-x86.zip
f460bf9aee4b02b6c08f19b0caa2928b075c73ca4cfc41c7f426f015f67cffbc snapraid-1.5.tar.gz
d19262bdf9e572fa80285887886a8fbe9a7b9bd4159496f112d383bf9b4db797 snapraid-1.5-windows-x64.zip
28816c0cde4cf8ce8e91a585eb9aab5552344a331237b6a2afd50396685814fd snapraid-1.5-windows-x86.zip
e0684d036cfd6b9fcdf23792c057fec4f4531d25aab7c677ff35e7a8be07b098 snapraid-1.6.tar.gz
9c13f99d67aafe43f5c540d97d0f6b40289856f547b8f33ee8aee27b270a7fae snapraid-1.6-windows-x64.zip
ae94db99a06068e17134ce69009492c4bfd8f6e54ea1848df105bb89f9f7ba28 snapraid-1.6-windows-x86.zip
1cf7a8f58533d6bc717ff6d396114ec4ed61eb66ff2756370e4cc730ee4edc4d snapraid-1.7.tar.gz
0cb49c676f3f9fd1f0fc938513b6dcaaf15390b64098e17cff5066667e4d01d5 snapraid-1.7-windows-x64.zip
44bc6b947ba845a89ecf812b82e751dca2dd4a6143ff06391c3b8b285a7ed3a2 snapraid-1.7-windows-x86.zip
da1c09f0139e683c03942c2b9489c2d9b989c59d147f7c28258c8e58c3938037 snapraid-1.8.tar.gz
fca7352fece5d7243af45ba8c5a07e08cc0e24d226468a619b9d86a3767e7099 snapraid-1.8-windows-x64.zip
35520cab33b3b3f9efc4ba4d7a968a7d7d49637b5e256921e0091424f5dd60ee snapraid-1.8-windows-x86.zip
504c3fe288b66c84bc65173979e7cede06370ca2a5f02ef3ca200c2fc8b09ea9 snapraid-1.9.tar.gz
ddba596451a05c61b20ce1e82c8039ba3c13be67b7469f2c75c6b0b7b4b18356 snapraid-1.9-windows-x64.zip
5a0dd3cdc0edf69cd03d926b594e41f5ca7e07564ef1cb3d6ac417c76f16b77b snapraid-1.9-windows-x86.zip
5e4eee49748faf966ebcc1cb3b9127ad7047542baf8952c6e0a9cbb32dac4d88 snapraid-1.10.tar.gz
7812a37f8767beb9542def762b2e131caafffc1b1b21f1d9422030e93e367f12 snapraid-1.10-windows-x64.zip
b899784498050c5d41752464bd31cec7de9e77ec865f55e2192fea75f52787d5 snapraid-1.10-windows-x86.zip
c349c4c25e3cb794d908dc75422ca9b04b44162c295b41d4ea55081af1a3451f snapraid-1.11.tar.gz
47e8f09f3d012d0a34d71e6ece7b9171c82af27766cca709fc9bbd104328cc7d snapraid-1.11-windows-x64.zip
8a8b0562192732022ae1732f5ae48e5f8745e217f73683123b2822e316918204 snapraid-1.11-windows-x86.zip
bd89ef0ddc3909c50fecc30da1d5d70342b92952b5d3ae0a2e31a66c8755dc8a snapraid-1.12.tar.gz
290c5ac8667ca7ada12a0d550e06c9fbc5f4e29abb5af0ef105eba38fb274c5d snapraid-1.12-windows-x64.zip
8cbc4e447f2152c3e76025cdbad03c54e7c85e004bb535f9dc7dde9b86605fe8 snapraid-1.12-windows-x86.zip
7c824abf6cacd8b847467916a1b4c28f48161e4b9f9b6c2f607c4de473a663c9 snapraid-1.13.tar.gz
22f6e9bf1dff5df4abff29b7cf364dc029959991dc158257f2a283721581abf4 snapraid-1.13-windows-x64.zip
9801b1a0331c461ce70c7824e2624dd2866358d61d923ff854253f340985947e snapraid-1.13-windows-x86.zip
68a48c961fcc15ffcfb86e4c5ea2371d562252e61f58fe87a529f479e81fb2bb snapraid-2.0.tar.gz
a2e1fb0610c8bfc57d73da1dbd276c238a032bf9b4c73a41977fcc57e773fff4 snapraid-2.0-windows-x64.zip
0fc3bee4d107bcd5e55a1c5bb81015558d9791d66a44f9ae2e3723906c3199df snapraid-2.0-windows-x86.zip
788377f579486c2b16548563a3d25f385594ae4276f1328c8f82cd0d5e0af9b6 snapraid-2.1.tar.gz
6e32fc0603136c4d3ad60fc1f04e6c919b7e7159267750ebf0dd8ad20ee6b442 snapraid-2.1-windows-x64.zip
d2813aec37f96506bcf98f2b5196204f065786731a5874f495f18f58fe87064b snapraid-2.1-windows-x86.zip
faf9545f0a66865e754ad587731483becf895a954c22e023501a2f28af44682a snapraid-2.2.tar.gz
2ea4b610c8d99f3766f6d2c89be5eafd1e16a17454d9df02d982fa184b517744 snapraid-2.2-windows-x64.zip
e2dac0663546f06ac3e679a93ba5de9e7de91563e09c1396fd367c5b16e011be snapraid-2.2-windows-x86.zip
68e610d67087c02715289934472c5fa3d7aa77c8085298ba54f02f26f7d396ad snapraid-3.0.tar.gz
ca7943f270a940554ddbd8c09698228120da2bac5b6d78486a34f5b301e24f47 snapraid-3.0-windows-x64.zip
c6a47d8bf544e69d9b301fe525d193c8faa8ef056c78f8a0649637d2e99b93c6 snapraid-3.0-windows-x86.zip
b5b22350c221e145c3347426f186f188f5d64616d03f196b35d75db564e78f7f snapraid-3.1.tar.gz
6d275cf3d579a5050e936879db85acbcb34b6522b36d5bc81b2f3764ca011c87 snapraid-3.1-windows-x64.zip
66f5967e559368193aef7b13a1c641694ca62f64e4b5749a7e2709aac6dc5759 snapraid-3.1-windows-x86.zip
99617ef379c4e46cc4366f2b3baced653f786ef08a40d74dca23a11a9240b096 snapraid-3.2.tar.gz
94d121d8cc0b9ab7ef1648e42edbfe1cfc602231c86a086d21984177da566f77 snapraid-3.2-windows-x64.zip
1d1ed69d3eab8247a7444e603ba09a56ec77979d3865489b2d8a7fc5db65848f snapraid-3.2-windows-x86.zip
c5a6e77613fab5cecb1aa55750f2045934d9374ba70471b6d35296c34509e5a9 snapraid-4.0.tar.gz
a59c911fef0d4d3e95d5521c6a180b40c455870be3ee9dbb5991b05b2cdd2f05 snapraid-4.0-windows-x64.zip
97ec4ae7b6ec6cd05e0c2ae835def526d12c35fa3546d27bd93978d978cd62aa snapraid-4.0-windows-x86.zip
7f5a37be7b8b78056d6602cef8ef702fe4f0d0b6ac3fa4bcefeb70743425b6fe snapraid-4.2.tar.gz
bf7861274639f2e9e5819ec3a89eed911cd79a212824fe3a6b64e45c0d1e2cc4 snapraid-4.2-windows-x64.zip
c7d9330be1936d7d7b07c51668fc8d3f88ce270e9e74945288d75eda2405a377 snapraid-4.2-windows-x86.zip
09f1ebfb5c45680c50651e94287d59e58260c46aee922d8864fb06fd591740e8 snapraid-4.3.tar.gz
ba1008452ff679c1e144206c73a81e4cb2689006c9ce37b60ceab45ef4c99dda snapraid-4.3-windows-x64.zip
23b21e162d5d18a4f68c8635e1f111fddb91d380514d8ddab9040b18a49d9994 snapraid-4.3-windows-x86.zip
aa22ea2e01a8db45ba43a3139d763cb2d12548de96f957a1ea1b5f80f04a4d83 snapraid-4.4.tar.gz
ca68dd88741290a6ee6494a9186b58766cab2b838f4a55ab23921b524b15ee82 snapraid-4.4-windows-x64.zip
3d9cccc2080d1435e7e747c7eeeffc85e0b68ad45b390c236de321b8aca44d46 snapraid-4.4-windows-x86.zip
ba43b0534197c6064b7a171321762be2ac2c848d4849c578007cc65aa963bed5 snapraid-5.1.tar.gz
54a31c5c979b9c3ad481cd918a48e220f11ce04de0a8ec6f41a957af7ba259fc snapraid-5.1-windows-x64.zip
6758966b3a120bf5b97f21748f649b2f147800b10377a0ee69d922be318ee399 snapraid-5.1-windows-x86.zip
eab07c21201eceb4204f8039f021ff0032515719aa5e640c330da45dd8b8e7a3 snapraid-5.2.tar.gz
554cc7be40fff0a0f249522bda957dfba229066e81102624b979213666b1aaae snapraid-5.2-windows-x64.zip
44f909157ba89d769af59e971c71b15bd38dfd62359b2e2c7eeb8cef0f9dc081 snapraid-5.2-windows-x86.zip
81e00fced6bb040d49aa6e3dc6fda97f22bf677d00f6c0c490e312c23222b421 snapraid-5.3.tar.gz
2627b2491b4d432a311cca1c924f4b40cef7a64c930b7e5eca55b3b9a8a62e61 snapraid-5.3-windows-x64.zip
d1019dc29922d4787c3fd9d4222dfc3b84a6a2e0c6b2db298340747238d8fb46 snapraid-5.3-windows-x86.zip
d836200058b98f189e576ad5619d4ffcd867e5dc8d2a4b775bff07d4f201c938 snapraid-6.0.tar.gz
06f2e56bc30c89e4a1561ec32cf72b240765456505f17da539af4b4338a6b12c snapraid-6.0-windows-x64.zip
6ae0a3fa973a28c64489bc28d7a7f4b354d3fc6703bcb83ef661cf76e994b34d snapraid-6.0-windows-x86.zip
1709c09b0e0699ce38452be7340c279c561b91433a6810600386bb9d8c0491e3 snapraid-6.1.tar.gz
40d995a88a23d799a32eba15f64c983a012771c527208cec2ad98787a45ef4c3 snapraid-6.1-windows-x64.zip
f8e09b3300717d1f9ee9b69bb132038642839c19a15a1f47810f3de10ad559eb snapraid-6.1-windows-x86.zip
b182328227ed9b87b01037f7d005d38a6e3cfb0f675e919978c696dcc6246787 snapraid-6.2.tar.gz
72ccfbf6548b83bd6f229d4862763a4e4cf6001c94d9ee84bf7b2a379a9a7743 snapraid-6.2-windows-x64.zip
55f1bc131f9148ed2b52e0e252c7155e50f5b4383fe82cd74ec2edace2ddc9c6 snapraid-6.2-windows-x86.zip
1f1961385b865bff5282bd16aff76f372f3ed19b685ef7a3b27d907a499385d2 snapraid-6.3.tar.gz
bb55d74d6b3cb08b21f70fded8b706ec1e790222cb265524cb2dc5499ec73391 snapraid-6.3-windows-x64.zip
3e4fa161ea26b8472dbbbfb4e61b34aca904ff9621f5afafbc83280d334a89b2 snapraid-6.3-windows-x86.zip
7cad74422d45c8a05435a03d3228a2d621a962fb866caedde0497f180bd9f60d snapraid-6.4.tar.gz
59471b49733c36acfbdeb16b28fbee063c4582abab2c9168abc7abc75610f9fc snapraid-6.4-windows-x64.zip
abe80b7c019980da2af5e07e0ea8b1ac1e1cdb2eb663483c07dab6ad6bd15985 snapraid-6.4-windows-x86.zip
554684520204d45f8b7cee9abb0269dd2bdba272cb0130c3b26062bea551a791 snapraid-7.0.tar.gz
a617b435f97cd746b4076d00cde68f947cca21adaca6ce603fec4676827b7ae9 snapraid-7.0-windows-x64.zip
301af727522912184f6d4a8290757950fed4577da625d9951809ff69f50b6e4f snapraid-7.0-windows-x86.zip
dd9005b6d7ea701e4aa0f854a0e34dabe68d7765b75f12fc6b3e1fda4d5c2cef snapraid-7.1.tar.gz
0d3a9625a6156ffe9b9eb4703aa2fc63141c51b83c69e98fbecce5430b1980e4 snapraid-7.1-windows-x64.zip
e0afdb753ae6a3bb3484a1bfe1cf01b9178010a8c11604e4bbc44b2f83884842 snapraid-7.1-windows-x86.zip
a912628dd003b3d70f2736629d48eeb738e7d27cba74014001dcc46fce3bc3be snapraid-8.0.tar.gz
9c2158fb9e2c08570f6a03f5cea7ab68d2a424adb1f42bb6b3d970ba9dc8a15d snapraid-8.0-windows-x64.zip
3c2c4357450564010d80bbd2cb1d5583281885b32cf659a0cf3dadce4766e167 snapraid-8.0-windows-x86.zip
6bf89a1319ac3403958cd2c98a9c6102728c0070cfa1aedd90c4561d93c54e5d snapraid-8.1.tar.gz
83ad064d5c3938fdacb989983c6b0fafa0932c236dc4baa93512f3c97fa019bc snapraid-8.1-windows-x64.zip
25e5cffade3667348a242f44fb23719328d578010d79bb28a5cab662fc75916b snapraid-8.1-windows-x86.zip
dff5f4e9a41865313ddd60b6379d5c0b308880d33f197934f98313e8241c6274 snapraid-9.0.tar.gz
624c3aa6cb0bdb7c90566545ca831721ea5e543c843418a00ff4de753af07953 snapraid-9.0-windows-x64.zip
6ace6dafacd902bdf058e42c7d39591e327aef1bb19a94b05496f0f14aca8e1e snapraid-9.0-windows-x86.zip
397fdd7709e941e372b37025457794f8b0ce63a5a8455703ad17770cbccd3823 snapraid-9.1.tar.gz
4c56b2c5d556a3685239e444cb58ce84c398e86a173be34ff7cfa3a2b31a5b34 snapraid-9.1-windows-x64.zip
515ca2078cdcb58b59afc005770a06648d8f17adb27afa5751ca6f68de2c05c6 snapraid-9.1-windows-x86.zip
77de90645a5debd177995d7897d58277b7c778632f0d9852740f5670f3321176 snapraid-9.2.tar.gz
483d96dc8937f6df48b7f454f8ec8f7ee51b5e973fa8f004e59048d60525d2fb snapraid-9.2-windows-x64.zip
99c950ff31c73cc55a1b83e4dfdbb6e1717420af54721f8bbe813fbbba484e04 snapraid-9.2-windows-x86.zip
61c4f3ce8bd5ebb1178fe76ab5933e90c4cead6dba31669f11e262d258c2b2ba snapraid-9.3.tar.gz
178e743b6246265f936e63ab47fe2a942bf414992cd752478854b81bdf264913 snapraid-9.3-windows-x64.zip
2cb888833cdddc82431822f818de0cfd046ceda5a7aa17bf9c410b6147ed7e20 snapraid-9.3-windows-x86.zip
f7dcf19480256fc2c1db9ab976aa12f786e76da6044cc397f0451524e8031ad6 snapraid-10.0.tar.gz
a3b4b2563f44f17072bb20963d61aae53d9f0e6b9446509fee76bbfa9b51fd23 snapraid-10.0-windows-x64.zip
95a1859163beb8c8d02208b27ecf5298068972498c7e2b99b488693ad5f8f133 snapraid-10.0-windows-x86.zip
30a72b8853ea750128c96784b73bb55f7faa4b16367b2e03f40c1f78515c5771 snapraid-11.0.tar.gz
af6f24adb81008e7ccdb7a8f2c46d1187542bd290294563de90acf5d817d772d snapraid-11.0-windows-x64.zip
fedc7591c8de0ce2775482e6b18033cdaca62078a0a16d45421d5fb5dd5615d6 snapraid-11.0-windows-x86.zip
b9acafeb6cece61fd426f08362b596ba89eea0564231955b82156fd09c0e6884 snapraid-11.1.tar.gz
6468d1a55a9a8043eae3c7c292c908c0fbd1974af3f175df23d39dd4748d9bf9 snapraid-11.1-windows-x64.zip
e70929b44ed88701b542abbccadc57dd89469f58cf7d6ca0bf80ce706d854553 snapraid-11.1-windows-x86.zip
735cdeb7656ac48cbb0b4a89a203dd566505071e465d5effbcc56bcb8fd3a0d7 snapraid-11.2.tar.gz
025faabb6390b6af600034523e5988e14160015b835541d8427475ca7fa52057 snapraid-11.2-windows-x64.zip
de16e2083054f34e1206b552bd05c64218c0dffcf787d716c6639f8728457d20 snapraid-11.2-windows-x86.zip
d35ad92157936db3411e2cf7325665fc59e2d159204f9424895cfa9f33122af1 snapraid-11.3.tar.gz
b764a3341783d4694242412e0fca7c112e44e84a4a89a7c27b0f74a836a84baf snapraid-11.3-windows-x64.zip
f8e51bee80ae0223f402afe68f5192f1036434bab816c42333bf2e5f9fbc82a2 snapraid-11.3-windows-x86.zip
1de47b8277c7dd9c339396efdd2b12a865ff82fdf6ab47cb945f2e21717451c9 snapraid-11.4.tar.gz
0734ca7a521ba7c05eea6898f481ca812d2a2d78da4545f7990cd27e6381dc75 snapraid-11.4-windows-x64.zip
061075df60b584ade5c76551ca54d76726441421576a544cdfd621acb23d55e1 snapraid-11.4-windows-x86.zip
1f5267261bdbcf4d48b9359ce67184df11905590739140f740327fb73bcecafa snapraid-11.5.tar.gz
e18f6b825a25b71947c2c41a315440a3d394de348afca80e2ab13a525bbb8a2b snapraid-11.5-windows-x64.zip
a66753201e55c115699df78f89dd562c1b71e876aba5be35fcaf507bfe1e9e52 snapraid-11.5-windows-x86.zip
f030a3830449a78d10af41320da0be21c60f88dc8d328ebd056e0eb7596161cf snapraid-11.6.tar.gz
8a3b8fd240285b5abf62c3ce222a5c674b475a83b3c48a40e457d34bfbcd1744 snapraid-11.6-windows-x64.zip
e8e9140423552cbccbe9a162100a830cd7d2e29bae0893cca9f5bd1943393508 snapraid-11.6-windows-x86.zip
f07652261e9821a5adfbfa8dad3350aae3e7c285f42a6bd7d96a854e5bc56dda snapraid-12.0.tar.gz
a9cd1e30042b09ecc42b508955a1c1144b446a0399c83ecfb78b5455d528938c snapraid-12.0-windows-x64.zip
a037d16bb9d226da2c88df00e410f9e637f1acb6153ac078c9a3805e721282a6 snapraid-12.0-windows-x86.zip
snapraid-12.1/COPYING 0000664 0000000 0000000 00000104513 14166610522 0014300 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
snapraid-12.1/HISTORY 0000664 0000000 0000000 00000111604 14166610522 0014330 0 ustar 00root root 0000000 0000000 SnapRAID HISTORY
================
12.1 2022/01
============
* Reduce stack usage to work in enviromnents with limited stack size, like MUSL.
* Increase the default disk cache from 8 MiB to 16 MiB.
12.0 2021/12
============
* Parallel disk scanning. It's always enabled but it doesn't cover the -m option
that still process disks sequentially.
11.6 2021/10
============
* The 'fix' and 'check' command with the -e option now process the whole
files that have bad blocks, and not only the block marked bad.
This allows to restore the timestamp and to print the paths of
processed files and the final state of the files like 'recovered' or
'unrecovered'. The previous behaviour is available with the -b,
--filter-block-error option.
* Improved the speed of the filtering in 'fix' and 'check'. This phase
happens after the "Selecting..." message. [UhClem]
11.5 2020/05
============
* Removed the default -march=native to allow to deploy in any machine.
* Fixed typos [Andrea Gelmini]
11.4 2020/05
============
* Fix build errors due new gcc 10 default for -fno-common.
* In fixing, if a parity is filtered out, don't attempt to recover its size,
and proceed without it if missing.
* Avoid unnecessary parity read when fixing the parity itself.
This improves the 'fix' speed when a parity file is completely missing.
* Removed a build warning about major/minor defined now in sys/sysmacros.h.
11.3 2018/11
============
* Fixed handing of Linux devices that have multiple slaves. This affects
the smart/list/devices/down commands [Valentin Hilbig].
* The 'list' command in verbose mode prints the full nanosecond
timestamp precision.
* After writing content files also sync their directory.
* Fix a invalid time computation that could result in future scrub dates.
Such dates are fixed automatically at the next scrub or sync.
11.2 2017/12
============
* Fixed recognition of NTFS hardlinks. They behave differently than
standard Unix hardlinks and this could result in SnapRAID reporting
internal inconsistency errors for detecting links to the same file
with different metadata attributes.
* More efficient 'pool' command that updates only the links
that need to be updated. This ensures that no change is
done, avoiding to trigger a directory rescan of other programs.
* In Linux use by default the advise "discard" mode instead of "flush".
This avoids to swap-out the other process memory, leaving the system
more responsive.
* Changed the fallocate() use to work better with Btrfs with parity disks.
* Changed the --test-io-stats screen to print the file name in process
for each disk.
11.1 2017/05
============
* Fixed the check command to correctly ignore errors on unused parity.
This was broken in version 9.0.
* Allow increasing the number of parity splits of existing parity.
* Fixed quoting when printing in Linux. This fixes the UTF-8 screen
output. Windows version was not affected.
* Fixed recognition of 'hashsize' in the configuration file.
The previous incorrect 'hash_size' is still supported for backward
compatibility.
* Fixed building in platforms that don't provide major/minor definitions
in sys/types.h.
* When creating 'pool' symbolic links, set their time as the linked files.
* Added support for the Windows 10 symbolic link unprivileged creation,
using SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE.
* Windows binaries built with gcc 4.9.4 using the MXE cross compiler at
commit ae56efa2b23a793b0146508bfef33027cdb09fd2 with targets
i686-w64-mingw32 and x86_64-w64-mingw32 and optimization -O2.
11.0 2016/11
============
* Added support for splitting the parity in multiple partitions. You
can now specify multiple files for a single parity. As soon a file
cannot grow anymore, the next one starts growing.
In the configuration file, just put more files in the same 'parity'
line, separated by , (comma).
Note that if this feature is used, the saved content file won't be
read by older SnapRAID versions.
In Windows, 256 MB are left free in each disk to avoid the warning
about full disks.
* Added a new 'hashsize' configuration option. It could be useful in
systems with low memory, to reduce the memory usage.
Note that if this feature is used, the saved content file won't be
read by older SnapRAID versions.
* In Linux added the missing support for Btrfs file-systems. Note that
to have full support you need also the 'libblkid' library, otherwise
you won't get the UUIDs.
* In screen messages don't print the disk directory in file path. You
can control the format with the test option:
--test-fmt file|disk|path.
* In Windows allows to use the escape char '^' to handle file patterns
containing real characters matching the globbing '*?[]' ones. In Unix
it was already possible to do the same escaping with '\'.
* Added a new -R, --force-realloc option to reallocate all the
parity information keeping the precomputed hash.
This is the previous -F, --force-full that instead now maintains the
same parity organization and just recomputes it.
* Added test options for selecting the file advise mode to use:
--test-io-advise-none for standard mode
--test-io-advise-sequential advise sequential access (Linux/Windows)
--test-io-advise-flush flush cache after every operation (Linux)
--test-io-advise-flush-window flush cache every 8 MB (Linux)
--test-io-advise-discard discard cache after every operation (Linux)
--test-io-advise-discard-window discard cache every 8 MB (Linux)
--test-io-advise-direct use direct/unbuffered mode (Linux/Windows)
The new default mode is 'flush' in Linux (before it was 'sequential'),
and 'sequential' in Windows (like before).
* For Seagate SMR (Shingled Magnetic Recording) ignore the SMART
attribute Command_Timeout 188 as not reliable.
* Fixed running in Windows platforms that miss the RtlGenRandom()
function.
* Added the --test-io-cache=1 option to disable the multi-thread IO
mode.
10.0 2016/02
============
* Boosts the speed of the 'sync' and 'scrub' commands with a new
multi-thread implementation. It uses one thread for each disk,
dedicated exclusively to read-ahead data and parity and to
write-behind parity. This maximizes the data throughput keeping
disks always busy.
You can control the number of blocks to cache with the option
--test-io-cache=NUMBER, where the number is between 3 and 128.
The default is 8 MiB of blocks.
You can show run-time stats during the process with the
--test-io-stats option. You will see a graph with the number of
cached blocks, and a graph with the wait time percentage for all the
disks and computations.
* The -h, --pre-hash command, saves the content file only after having
verified all the hashes. This allows recovering of moved files in
case a silent error is found during the hash verification check.
* Allows to use the -d, --filter-disk option in the 'up' and 'down'
commands.
* Allows to run the 'smart' command without a configuration file.
In such case it operates on all the disks of the machine.
* In the configuration file 'data' is now a synonymous of 'disk'.
* Adds the 'touch' command intended to arbitrarily set all the zero
sub-second timestamps. This improves the SnapRAID capabilities to
identify files. The 'status' command recommends to run 'touch' if
required.
* Restores the functionality of the -D, --force-device option when used
to workaround the use of the same disk for two logical data drives
when running the 'fix' command.
* Uses a correct shell quoting in the example commands that involve
files.
* The minimum Windows version supported is now Windows Vista. This is
required to use the native Windows thread support for the new
multi-thread implementation. If you need to run on Windows XP, you
have to stick on SnapRAID 9.x.
9.3 2016/01
===========
* Fixes an invalid assumption in the copy detection mechanism that
could result in an internal inconsistency, and with the impossibility
to run the 'sync' and 'diff' commands.
This was triggered by a very specific pattern of identical files.
At least three of them, with one already in the parity, and at a
higher disk number than the others that should be instead new ones.
This had no bad effect, if not preventing the 'sync' command to run.
A workaround was to just run 'sync' one time with the -N,
--force-nocopy option to disable the copy detection.
* Restored the -O2 optimization option for Windows binaries, as -Og has
a too big performance penalty.
9.2 2016/01
===========
* Fixes support for symlinks pointing to an empty target. Before they
were only partially supported, and their presence could result in a
content file not readable.
This also disables multi-thread content write, as this was the issue
we tried to detect with this feature, and it doesn't provide a
performance advantage. Content verification is instead still multi
thread.
* Autorename disks using the matching UUID. To rename a disk you can
now change directly the name in the configuration file, and run a
'sync' command.
* Improves the physical offset ordering for the Btrfs file-system,
correctly detecting files that have not a physical offset, for
whatever reason.
* Adds UUID support to Btrfs file-systems. It's present only if the
'libblkid' development library is available on the system.
Usually this requires to install the libblkid-dev or libblkid-devel
package.
* Added a new --no-warnings option to disable some repetitive warnings
that could be annoying to power users.
* Improves the error reporting, printing a complete stack trace, that
can be used to track down bugs more easily.
For this reason the Windows binaries are now built with optimization
option -Og, instead than -O2.
9.1 2015/11
===========
* Fixes a bug when reading a content file with a deleted entry bigger
than 4 GB. This was a regression introduced in version 9.0 that could
result in the impossibility to read a valid content file, after a
deletion of a file bigger than 4 GB in the array.
If this happened to you, just upgrading to 9.1 fixes the issue, and
it allows you to continue to work.
Note that this bug only prevented to run 9.0, but your data was still
protected and could have been recovered using the versions 8.1 or
9.1.
* In Windows disables the file zero check requiring the --force-zero
option. This check is intended for possible case using ext3/4 in
Linux, and there is no evidence that in Windows it's possible at all.
* Windows binaries built with gcc 4.9.3 using the MXE cross compiler at
commit 62bcdbee56e87c81f1faa105b8777a5879d4e2e with targets
i686-w64-mingw32 and x86_64-w64-mingw32 and optimization -O2.
9.0 2015/11
===========
* Fixes an invalid assumption that could happen when using the -e,
--filter-error option with "fix" or "check".
This was triggered by a very specific pattern of fragmented files
and bad blocks combination, not so easy to reproduce.
This had no bad effect, if not preventing the command to run.
* Drastically reduces the memory usage. For each block, it now uses 17
bytes of memory, instead of the previous 28 bytes (for 32 bit) or 36
bytes (for 64 bit).
This could result is a memory saving of up the 50%.
* The -p, --plan option (old --percentage) can be used to define a
scrub plan: "new", "bad" and "full".
The "new" plan scrubs all the new synced blocks not yet scrubbed.
This allows to verify as early as possible that the written parity
during sync is really correct. You can use the "status" command to
show the amount blocks not yet scrubbed.
The "bad" plan scrubs only bad blocks.
The "full" plan scrubs all blocks.
* The graph in the "status" command now show scrubbed blocks with '*',
and synced, but not yet scrubbed, blocks with 'o'.
Note that when upgrading from a previous version, all blocks are
assumed scrubbed the first time.
* Content files are now written asynchronously from different threads
to avoid the unfortunate condition that a memory error affects all of
them in the same way.
After writing, they are read again to verify their CRC.
This is done to ensure that they are really OK, even in the case of
the worst possible silent errors.
* Extends the -D, --force-device option to ignore more erroneous
conditions in the 'fix' command, like inaccessible disks, or disks
sharing the same physical device.
* Extends the -d, --filter-disk option to allow to filter also by
parity disk.
* Extends the -h, --pre-hash option to also verify moved and copied
files into the array before running a 'sync'.
* Updates 'best' RAID functions for recent Atom CPUs.
* Validates filters specifications rejecting relative paths.
8.1 2015/05
===========
* Fixes build issues in generic Unix platforms, including Mac OS X.
* The "diff" command returns with error code 2 if a "sync" is required,
to differentiate with the generic error code 1.
* Reduces the effect of SMART attribute 193 on the failure probability
to avoid some false positive reports.
8.0 2015/04
===========
* Allows "sync" and "scrub" to continue after the first bunch of disk
errors. Blocks with errors are marked as bad, and you can fix them
with the "fix -e" command.
The fix is expected to force the disk firmware to reallocate the
bad sector, likely fixing the problem.
You can control the number of allowed errors with the new
-L, --error-limit option. The default is 100.
* The -e, --filter-error option doesn't write anymore fixes to
unsynced files. This helps in case you are running it on a not
synced array, removing the risk to revert some files to an old state.
* The -e, --filter-error option is now optimal and reads only the
minimal amount of data necessary to fix the errors.
* The "diff" command returns with an error code if a "sync" is
required.
* Adds new "smart" command to print a SMART report of the array.
* Adds new "up" and "down" commands to spin up and down the disks of
the array.
* Adds new "devices" command to print the devices associations in the
array.
* Changes the log handling. If no log file is specified, all the
warnings and not fatal errors messages goes to stderr. If a log file
is specified, only fatal error messages are printed on the screen.
You can control the amount of informative messages on stdout with
the -q, --quiet and -v, --verbose options, that can be specified
multiple times to be more quiet or verbose.
* In the "status" command the "Wasted" column now shows a negative
number for the amount of space that you can still waste without
filling up the parity.
* In the "status" and others commands we now use GB instead of GiB,
when referring to disk space.
* Renames the -s and -t options to -S and -B as they are intended to
be manual only operations.
* Windows binary built with gcc 4.8.1 using the MXE cross compiler
2.23, with targets i686-w64-mingw32 and x86_64-w64-mingw32. Before
the x86 target was i686-pc-mingw32.
7.1 2015/01
===========
* In 'scrub' and 'sync' detects and reports Input/Output errors
separately from generic file system errors.
* In 'diff' doesn't print the "add" entry if a "copy" one is already
printed.
* Fixes build with old compilers in the x64 platforms [Leigh Phillips].
* Fixes out-of-dir builds [Christoph Junghans].
7.0 2014/11
===========
* In 'check' and 'fix' the array is scanned to find any moved files
that could be used to recover missing data. Files are identified by
time-stamp, and then they are recognized also if moved to a different
disk. Note that even if there are false positive they are identified
checking the hash, so they have not effect, besides making the
process a little slower. To disable this new behavior you can use
the -N, --force-nocopy option.
* The -i, --import command now identifies files by time-stamp making it
very fast in importing directories.
* More detailed 'status' report with single disk stats and free space
available.
* A lot faster directory listing for Windows.
* Adds AVX2 support to improve parity generation speed.
* Prints the time spent waiting for each disk also in 'scrub'.
* The CPU usage, speed and ETA estimations are now based on the last
100 seconds rather than from the start.
* Keeps track of the UUID of the parity disks to check them before
operating.
* Windows binary built with gcc 4.8.1 using the MXE cross compiler
2.23.
6.4 2014/11
===========
* Adds support for the new binary format of SnapRAID 7.0.
This allows to downgrade from version 7.0 to 6.x or previous.
6.3 2014/7
==========
* The -N, --force-nocopy option now also works if you used previously
"sync" commands without it.
* In 'sync' keeps stats about the amount of time spent waiting for each
disk and what is spent in CPU computation.
* Auto exclude the lock file.
* A more precise counting of how may block to scrub. Now it's exact
regardless the order of the blocks timing.
* Don't prints the 'UUID set' message anymore because it's the normal
condition for empty disks.
* In Windows, if the disk doesn't support reading physical offsets,
allows SnapRAID to continue anyway.
* Added a new -F, --force-full option that forces a full sync reusing
the hash data present in the content file.
6.2 2014/5
==========
* Fixed the regression test when run as root.
* Added a new heuristic to detect file copies. Now a file is assumed
to be a copy if name, size and nanosecond time-stamp are matching,
but if the nanosecond part of the time-stamp is 0, it requires
the full path matching and not only the name.
* Added the -N, --force-nocopy option to disable completely the copy
detection. SnapRAID also suggests to use this option in the error
message of a data mismatch if likely caused by the copy detection.
6.1 2014/4
==========
* Fixed build and regression test in Mac OS X.
6.0 2014/3
==========
* In "sync", even if a silent error is found, continue to update the
parity if it's possible to correct the error.
Note that the block will be marked bad, and the data will be fixed
only at the next "fix -e" call.
But any new data added will be protected if you are using enough
parity to fix both the silent error and at least another potential
error.
* Detect copied files from one disk to another and reuse the already
computed hash information to validate them in "sync".
Files are assumed copied if they matches the name, size and
time-stamp.
* For "sync", added a new -h, --pre-hash option to run a preliminary
hashing step for all the new files to ensure to detect silent errors
caused by the heavy machine usage of the parity computation.
* In "fix", if a previous fixing attempt was made resulting in a
.unrecoverable file, uses this file as starting point for the
new attempt.
* In the log file name allows the use of the '>>', %D, %T modifiers
to select append mode, and to insert the date and time in the name.
* The options -p, --percentage and -o, --older-than now keep their
default value even if the other one is specified.
* Moved the .lock file in the same dir of the first specified content
file. This avoid to spin-up the parity disks in all commands.
* The "diff", "list", "dup", "status" and "pool" commands don't access
anymore the parity disks that can now stay powered down.
* The default configuration file in Windows is now searched in the same
directory where the snapraid.exe file resides.
* New source code organization. The RAID engine is now an external
component usable also in other projects.
5.3 2014/3
==========
* Don't warn about UUID changed if it's for an empty disk.
* Fixed the number of blocks that scrub has to process when
selecting a high percentage of the array.
* Removed duplicate recovery attempts in synced state.
5.2 2013/12
===========
* If a disk changes UUID, automatically disable the inode
recognition, because this is likely a new file-system with
all the inodes reassigned, and we don't want to risk a false
positive when searching for inode/time-stamp/size.
* Allow to run a fix command with disks that doesn't need to be
fixed mounted as read-only.
* After a failed sync, always reallocates new files with a not
yet computed parity to ensure to minimize the parity usage,
if some other file is deleted in the meantime.
* Doesn't count empty dirs as files in the diff counters.
* Added a new "share" configuration option to allow to share
in the network the pool directory also in Windows.
* Fixed build problems in OpenBSD due the old assembler.
* Fixed build problems in platforms different than x86.
5.1 2013/12
===========
* Fixed a potential crash if a file is deleted during a "sync/scrub".
This is a problem introduced in version 5.0 due new logging.
If happened to you to have a crash in sync, you don't need to take
any special action, just run "sync" again.
* Restored the functionality of -C, --gen-conf command.
* Prints the files with duplicate physical offset if the -v, --verbose
option is specified.
5.0 2013/11
===========
* Added support for up to six levels of parity.
* Added a specific and faster triple parity format for CPUs that
don't support SSSE3 instructions like ARM and AMD Phenom, Athlon
and Opteron.
* Faster RAID5 and RAID6 implementation for ARM 64 bit CPUs.
* If a silent error is found during a "sync" command, directly marks
the block as bad like in "scrub", without stopping the "sync"
process.
* Sort files by inode when listing the directory. This improves
the scanning performance.
* For files with changes only in some blocks, updates the parity
only for blocks that really are changed.
This improves the performance in sync for modified files.
* Added a new "list" command to see the stored list of files.
* Removed the detailed list of errors from the screen output.
To get it you must explicitly use the -l, --log option.
It's now too detailed for the screen, because it contains a lot
of info.
* Changed the output format of some commands to make it similar
at the new "list" one.
* Reduced memory usage removing some unnecessary allocations.
* Added a memory test on the memory buffers used in sync, scrub, check,
fix before using them.
4.4 2013/10
===========
* Relaxed the check about small parity files, to allow to recover after
a failed sync before resizing the parity files.
4.3 2013/10
===========
* Fixed the scrub command with the -p0 option. Now it really scrubs
only the blocks marked as bad and not the full array.
4.2 2013/10
===========
* Fixed the wrong warning about physical offsets not supported caused
by files not having a real offset because too small.
For example, in NTFS it's possible to store such files in the MFT.
It's just a cosmetic change, and not a functional one.
* Remove unexpected 'Restore' entries in the diff output when dealing
with file-system without persistent inodes like NTFS in Linux.
* Added support for filenames containing newlines. This happens in Mac
OS X.
4.1 2013/9
==========
* If the underline file-system doesn't support the FIEMAP command,
automatically fall back to use FIBMAP for sorting files.
* Fixed the import of content files from previous version of SnapRAID
that are the result of an incomplete sync.
* Added a new -C, --gen-conf option to generate a dummy configuration
file from the info in the content file.
Just in case that you lose everything, except the content file.
* At the end of sync/scrub/check/fix prints "Everything OK" if no error
was found. This should make clear that everything is really OK.
4.0 2013/9
==========
* New 'scrub' command to periodically check the oldest blocks for
silent errors without the need to scan the whole array.
* New 'status' command to check the fragmentation, the last check time
distribution, and the silent error status of the array.
* Added the new Spooky hash. It's faster in 64 bit architectures.
To convert you can use the new 'rehash' command.
* Changed to a binary content file to improve speed and reduce size.
* Removed the --find-by-name, -N option. Now it always searches
by name if a file is not found searching by inode, automatically
reassigning inodes in restored files without needing to sync
again the file.
This happens only if the file has the same path, size and timestamp
at nanosecond precision.
* Added a hash seed to make harder intentional collision attacks.
* When inserting files for the first time, sort them by their physical
address to improve read performance.
* Optimized the cache use for the all the RAID computations.
This improves a lot the RAID performance.
* Better selection of the RAID6 implementation for different CPUs.
* Added RAID5/RAID6 mmx and sse2 implementations with unrolling by 4.
They are a little faster than the previous unroll by 2.
* Added a lock file to avoid multiple running instances on the same
array. The file is named as parity file adding the .lock extension.
There is also the undocumented --test-skip-lock to avoid to check it.
* Automatically ignores, with warning, mount points inside the array
directory tree.
* Changes the 'dup' output format to include the size of each duplicate
file.
3.2 2013/7
==========
* Fixed a directory creation problem in Windows when the "disk" option
points to the root directory of a drive. Now SnapRAID won't complain
about the inability to create such directory.
If you encounter this problem when trying to recover your data, just
upgrade to this version, and you'll be able to complete the
recovering process.
No need to upgrade for platforms different than Windows.
3.1 2013/5
==========
* Direct use of Windows API for disk access to improve error reporting.
* If the 'fix' process is aborted, it removes all the new files
partially recovered, to allow to reuse again the -m, --filter-missing
flag.
* In Windows don't exclude anymore system files. Only system
directories are excluded.
* In Windows applies filters in case insensitive way.
* The Windows binaries are now built with gcc 4.7.2.
* Reduced memory occupation for hardlinks and directories.
* In 'dup' don't list files with 0 size.
3.0 2013/3
==========
* Added pooling support with the new 'pool' command. It creates a
virtual view of the array using symbolic links pointing to the
original files.
* Added a new -m, --filter-missing option that allow to undelete files,
without checking/fixing the others.
* Added a new -i, --import option to automatically import deleted files
when fixing.
* Added a new -l, --log option to save to disk the detailed log.
* Added support for hardlinks and empty directories.
* Added support to save symlinks to files in Windows. Note that only
the symlink is saved and not the linked file.
Note that Windows Symlinks to dirs and junctions are still not
supported.
* Files without read permission generate an error instead of a warning.
You now must explicitly exclude them in the configuration file with
exclusion rules.
* In 'check' and 'fix', if verbose is enabled, prints the result for
each processed file.
* Added an UUID check to detect when a disk is replaced, and to prevent
unwanted disk swaps.
2.1 2013/1
==========
* Checks for wrong empty fields in the configuration file.
* Filter rules for files are not anymore applied to directories.
2.0 2012/12
===========
* Added a new -a option to make the 'check' command to only check file
hashes without checking the parity data.
* Added a new -d option to filter by disk name.
* The file modification time is now saved using nanosecond precision.
This allows to restore the exact modification time in 'fix'.
The new 'content' files written with this version are not backward
compatible, but it's still possible to read the old format.
* Fixed hard-links automatic exclusion. All the hardlinks after the
first one are now correctly ignored.
* If it isn't possible to grow a parity file, prints the list of files
outside the maximum size allocated.
* Autosave isn't triggered if we are near the end of the 'sync'
process.
* Before starting a 'sync', we wait for two seconds, to workaround the
FAT limitation of having two seconds modification time precision.
This a safe measure to be 100% sure to always detect file changes.
* Always fill the memory after allocating it to avoid the OOM (Out Of
Memory) killer in Linux.
* Fixed compilation in Solaris/OpenIndiana for lacking both futimes()
and futimens().
* Now 'sync' ensures that the parity files are not too small to contain
the just loaded data.
* Removed the '-H,--filter-nohidden' option. It doesn't make sense to
have it as command line option.
You must use the 'nohidden' option in the configuration file.
* When opening files in read-only mode, also specify the noatime flag,
to avoid to update the file access time.
* Exclude rules for files are now also applied to directories.
This allows to excludes some file/directory without the need to call
the stat() function on them.
* The -N, --find-by-name option also ignores the nanosecond part of
timestamps to work with copy programs not supporting nanoseconds.
* Fixed deduplicated files handling in Windows Server 2012.
* Removed MD5 support.
1.13 2012/11
============
* Fixed a Segmentation Fault when checking/fixing if there are three
or more errors in a specific block.
1.12 2012/9
===========
* Fixed file renaming in Windows during a 'fix' command.
This is only a Windows only issue, no reason to upgrade for other
platforms.
1.11 2012/7
===========
* Fixed again directories inclusion. Exclusion rules for directories
were ignored.
1.10 2012/6
===========
* Fixed directory inclusion, in case the last rule is an "include" one.
* Fixed very long paths in Windows. We now always use the special '\\?'
prefix to remove the 260 chars limitation.
* If a file is excluded, it prints explicitly which attribute caused
the exclusion.
* Automatically excludes also the temporary copy of content file,
the one with the ".tmp" extension.
* Avoid Windows to go in automatic sleep mode when running.
1.9 2012/3
==========
* Implemented a more sophisticated recovering in case a harddisk
failure happens during a 'sync' command.
When using RAID6 it improves the chances of recovering data with
partially computed parity, after an aborted 'sync'.
* Fixed the count of new files.
* Added a new 'autosave' configuration option to save the intermediate
'sync' state.
* Supported file-systems with read requests returning less data than
requested.
* In Windows ensures that the disk serial number is not zero.
1.8 2012/1
==========
* Added a new "dup" command to find all the duplicate files.
* Added a new option "--filter-nohidden" to exclude hidden files.
* Faster and parallel writing of content files.
* The example configuration files now put the content files in the data
disks instead than in the parity disks.
* Added a checksum at the content file to ensure its integrity.
* Using fallocate() instead posix_fallocate() to avoid the very slow
posix_fallocate() fall back of writing the whole file.
1.7 2011/11
===========
* If a file is modified or removed during a sync, the sync process
doesn't stop anymore, but it will simply skip the file, resulting in
an incomplete sync. Note that the sync will terminate with an error.
* If the content file is placed in a data disk, it's automatically
excluded from the sync process.
* Increased by one the minimum number of content files. Before it was
only a suggestion, but now it's a requirement because you are allowed
to put content files in data disks.
* Added checks to ensure that data and parity disks are different, and
to correctly count the number of copies of "content" files in
different disks.
* Removed the dependency of the "disk" order specification in the
configuration file. The used order is now saved in the content file
to avoid to damage the dual parity in case the order is changed by
the user. It easily allows to remove or add disks from the array when
using a dual parity.
* Improved the "fix" performance when a lot of files or the parity have
to be recreated from scratch.
* When getting unrecoverable errors, the printed log line now starts
with "unrecoverable:" instead of "error:" to allow an easier
identification.
* Added a new option "--find-by-name" to allow to sync using only the
file path and not the inode. This is useful to avoid long sync when
you replace one disk with another copying manually the files.
* If "fix" cannot recover a file, it's renamed adding the
".unrecoverable" extension.
* Checking and fixing also empty files with size 0.
1.6 2011/9
==========
* The content file is now saved also at the start of the "sync"
command. This avoids parity errors if the sync process is aborted
without saving the content file and you made changes at the disk
array before another "sync" command was done.
More specifically, deletions or truncations of not yet synced files
after the aborted sync, and before the next sync command, may have
damaged the parity data. New file additions were instead safe.
If these conditions may have happened to you a "check" command (also
with older version of the program) is recommended to ensure the
correctness of your parity data.
* The "diff" command now recognizes the reuse of inodes.
* Windows hidden files are now saved like any other files.
* Symbolic links are now saved in *nix. Not supported in Windows.
* The "fix" command restores also the original modification time.
* The message asking to use the --force-empty option now lists all the
empty disks.
1.5 2011/7
==========
* Ignores extra spaces in the configuration file.
* Changed the output of check/fix to allow a more easy post-processing
with other tools like awk and sort.
* Added the hidden option -G/--gui to enable the output of progress
information for a potential GUI for SnapRAID.
* Added a new "diff" command to print the list of changes detected at
file level.
* Faster loading of content file. Approx three times faster.
1.4 2011/6
==========
* Ignoring in sync System and Hidden files in Windows.
* Files without read permission are ignored in sync.
* If a file is ignored a warning message is printed. You have to
exclude it to remove the warning.
* In fixing, if a file cannot be written for missing permission, an
error is reported only if a write is effectively required.
* Ignores any symbolic links. They are not saved.
1.3 2011/5
==========
* Fixed the restore of directory with unicode chars in Windows.
* Fixed support of file names starting or ending with a space.
* Removes files before inserting new ones to minimize the parity size.
1.2 2011/5
==========
* Fixed use of file names out of the codepage in Windows. All the names
are now stored in UTF8 in the content file.
1.1 2011/5
==========
* Fixed a bug in the check command when detecting garbage data over the
expected end of the file.
The parity data was anyway computed correctly, and no special action
is required to update.
* Changed the default checksum to Murmur3 hash. It's a lot faster than
MD5. You can check its speed with the "snapraid -T" command.
MD5 is still supported for backward compatibility.
To convert to the new Murmur3 hash, simply remove the 'content' file,
and start a new complete 'sync'.
* Added RAID6 support. It's used the very good RAID6 library made by H.
Peter Anvin also used in the Linux Kernel. It contains optimized
implementations for SSE2 and MMX.
* Added support for multiple 'content' files. You can save extra copies
to be able to verify the checksums also if you lose all the 'content'
files in the parity disks.
* Added a filtering include logic, where anything not explicitly
included is excluded. For example, it allow to include only the files
in a predefined set of directories.
* The check command returns with an error code if any kind of error is
present. Previously it was returning an error only if unrecoverable
errors were present.
* Opening the files in sequential mode in Windows. This should give a
speedup in Windows.
* In Windows you can use the backslash \ in the filter definitions
instead of the forward slash /.
1.0 2011/4
==========
* No relevant change.
0.4 2011/4
==========
* Added hidden 'dry' command mainly for speed measurement.
* As default, uses the OpenSSL crypto MD5 implementation.
0.3 2011/4
==========
* Added --filter option to select a subset of file in check and fix.
* Better ETA estimation in all the commands.
* Added support for OpenSSL crypto library to use its optimized MD5
implementation.
* Added test vectors and a speed test for MD5.
0.2 2011/3
==========
* Second public test release of SnapRAID.
* Functionally complete in check and fix.
* Files are identified by inode and not anymore by name.
* Exclusion list of files and directories.
* Precise error management.
* More regression tests.
0.1 2011/3
==========
* First public test release of SnapRAID.
snapraid-12.1/INSTALL 0000664 0000000 0000000 00000001274 14166610522 0014276 0 ustar 00root root 0000000 0000000 SnapRAID INSTALL
================
To build and install SnapRAID you need to download the source
code from http://www.snapraid.it and unpack it with:
tar xf snapraid-*.tar.gz
cd snapraid-*
To configure and build run:
./configure
make
To check for correctness of the application run:
make check
If it terminates with "Success", you can install the application and
the documentation running as root:
sudo make install
To start using SnapRAID you have to change the example configuration
file snapraid.conf.example to fit your needs and copy it in /etc/snapraid.conf
To get more help, see the "Getting Started" section in the snapraid manpage
typing:
man snapraid
snapraid-12.1/INSTALL.windows 0000664 0000000 0000000 00000000511 14166610522 0015760 0 ustar 00root root 0000000 0000000 SnapRAID INSTALL for Windows
============================
To start using SnapRAID you have to change the example configuration
file snapraid.conf.example to fit your needs and copy it with the
name snapraid.conf in the directory where you run snapraid.exe.
To get more help, see the "Getting Started" section in snapraid.txt.
snapraid-12.1/Makefile.am 0000664 0000000 0000000 00000130332 14166610522 0015277 0 ustar 00root root 0000000 0000000 bin_PROGRAMS = snapraid
check_PROGRAMS = mktest mkstream
AM_CPPFLAGS = -DSYSCONFDIR="\"${sysconfdir}\""
snapraid_SOURCES = \
raid/raid.c \
raid/check.c \
raid/module.c \
raid/tables.c \
raid/int.c \
raid/x86.c \
raid/intz.c \
raid/x86z.c \
raid/helper.c \
raid/memory.c \
raid/test.c \
raid/tag.c \
tommyds/tommy.c \
cmdline/snapraid.c \
cmdline/io.c \
cmdline/util.c \
cmdline/stream.c \
cmdline/support.c \
cmdline/elem.c \
cmdline/state.c \
cmdline/scan.c \
cmdline/sync.c \
cmdline/check.c \
cmdline/dry.c \
cmdline/rehash.c \
cmdline/scrub.c \
cmdline/status.c \
cmdline/dup.c \
cmdline/list.c \
cmdline/pool.c \
cmdline/parity.c \
cmdline/handle.c \
cmdline/touch.c \
cmdline/device.c \
cmdline/fnmatch.c \
cmdline/selftest.c \
cmdline/speed.c \
cmdline/import.c \
cmdline/search.c \
cmdline/mingw.c \
cmdline/unix.c
noinst_HEADERS = \
raid/raid.h \
raid/helper.h \
raid/internal.h \
raid/cpu.h \
raid/gf.h \
raid/combo.h \
raid/memory.h \
raid/test.h \
tommyds/tommyarray.h \
tommyds/tommyarrayblkof.h \
tommyds/tommychain.h \
tommyds/tommyhash.h \
tommyds/tommyhashdyn.h \
tommyds/tommylist.h \
tommyds/tommytree.h \
tommyds/tommytypes.h \
tommyds/tommyarray.c \
tommyds/tommyarrayblkof.c \
tommyds/tommyhash.c \
tommyds/tommyhashdyn.c \
tommyds/tommylist.c \
tommyds/tommytree.c \
cmdline/portable.h \
cmdline/snapraid.h \
cmdline/io.h \
cmdline/util.h \
cmdline/stream.h \
cmdline/support.h \
cmdline/elem.h \
cmdline/state.h \
cmdline/parity.h \
cmdline/handle.h \
cmdline/murmur3.c \
cmdline/murmur3test.c \
cmdline/spooky2.c \
cmdline/spooky2test.c \
cmdline/metro.c \
cmdline/fnmatch.h \
cmdline/import.h \
cmdline/search.h \
cmdline/mingw.h \
cmdline/unix.h
mktest_SOURCES = \
cmdline/mktest.c \
cmdline/mingw.c \
cmdline/unix.c \
cmdline/support.c
mkstream_SOURCES = \
cmdline/mkstream.c \
cmdline/mingw.c \
cmdline/unix.c \
cmdline/support.c \
cmdline/util.c \
cmdline/stream.c \
raid/memory.c
# Add the .version file in the distribution
dist-hook:
$(srcdir)/autover.sh > $(distdir)/.version
EXTRA_DIST = \
autogen.sh autover.sh \
README AUTHORS HISTORY INSTALL COPYING TODO CHECK INSTALL.windows \
snapraid.d snapraid.1 snapraid.txt \
test/test-par1.conf \
test/test-par2.conf \
test/test-par3.conf \
test/test-par4.conf \
test/test-par5.conf \
test/test-par6.conf \
test/test-par6-hole.conf \
test/test-par6-noaccess.conf \
test/test-par6-rename.conf \
snapraid.conf.example \
configure.windows-x86 configure.windows-x64 snapraid.conf.example.windows \
acinclude.m4 \
tommyds/LICENSE \
raid/COPYING \
raid/test/Makefile \
raid/test/fulltest.c \
raid/test/selftest.c \
raid/test/speedtest.c \
raid/test/invtest.c
man_MANS = snapraid.1
clean-local:
rm -f valgrind.log callgrind.log cachegrind.log
rm -rf bench
rm -f test*.log output.log stream*.bin
rm -f cmdline/*.gcda cmdline/*.gcno cmdline/*.gcov
rm -f raid/*.gcda raid/*.gcno raid/*.gcov
rm -f tommyds/*.gcda tommyds/*.gcno tommyds/*.gcov
rm -f lcov.info
rm -f gmon.out
maintainer-clean-local:
rm -f snapraid.1 snapraid.txt
# Automatic testing
# Common options
# --test-skip-device
# Is required to allow testing in the same disk
#
# --test-skip-self
# It just makes the test a little faster
CHECKFLAGS_COMMON = --test-skip-device --test-skip-self --test-force-progress --no-warnings --test-parity-limit=3333333
# --test-force-order-alpha
# Ensures to process files always in the same order despite
# the inode, physical location, and dir order assigned by the OS.
CHECKFLAGS_ALPHA = $(CHECKFLAGS_COMMON) --test-force-order-alpha
# Physical offset options
CHECKFLAGS_PHYSICAL = $(CHECKFLAGS_COMMON) --test-force-order-physical -q -q -q
# Verbose options
CHECKFLAGS_VERBOSE = $(CHECKFLAGS_ALPHA) -v -G
# Quiet options
CHECKFLAGS = $(CHECKFLAGS_ALPHA) -q -q -q
# Number of files to create
if !HAVE_MEMORY_CHECKER
if HAVE_POSIX
CHECKCOUNT = 1000
else
CHECKCOUNT = 500
endif
else
CHECKCOUNT = 500
endif
# Size of files to create
if !HAVE_MEMORY_CHECKER
CHECKSIZE = 4000
else
CHECKSIZE = 100
endif
CONF = $(srcdir)/test/test-par6.conf
HOLE = $(srcdir)/test/test-par6-hole.conf
NOACCESS = $(srcdir)/test/test-par6-noaccess.conf
RENAME = $(srcdir)/test/test-par6-rename.conf
PAR1 = $(srcdir)/test/test-par1.conf
PAR2 = $(srcdir)/test/test-par2.conf
PAR3 = $(srcdir)/test/test-par3.conf
PAR4 = $(srcdir)/test/test-par4.conf
PAR5 = $(srcdir)/test/test-par5.conf
PAR6 = $(srcdir)/test/test-par6.conf
MSG = @echo =====
check-local:
rm -rf bench
rm -f test.log
mkdir bench
mkdir bench/disk1
mkdir bench/disk2
mkdir bench/disk3
mkdir bench/disk4
mkdir bench/disk5
mkdir bench/disk6
mkdir bench/pool
$(MSG) Stream
$(TESTENV) ./mkstream$(EXEEXT)
if HAVE_THREAD_CHECKER
#### THREAD TEST ####
$(MSG) Create some files
$(TESTENV) ./mktest$(EXEEXT) generate 1 6 $(CHECKCOUNT) $(CHECKSIZE)
$(MSG) Some commands that use threads
# First sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
# Remove, move and add some more
rm bench/disk4/a/9*
rm bench/disk5/a/9*
rm bench/disk6/a/9*
mv bench/disk1/a/9* bench/disk4/a
mv bench/disk2/a/9* bench/disk5/a
mv bench/disk3/a/9* bench/disk6/a
$(TESTENV) ./mktest$(EXEEXT) change 2 500 bench/disk2/b/* bench/disk3/b/*
# Sync again
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
# Other commands that uses threads
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) test-rewrite
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) scrub -p full
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) scrub -p full --test-io-cache 3
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) scrub -p full --test-io-cache 128
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync -F --test-io-cache 1
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) scrub -p full --test-io-cache 1
else
#### COMMAND LINE ####
$(MSG) Pre test
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -H
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -V
$(TESTENV) ./snapraid$(EXEEXT) test
$(TESTENV) ./mktest$(EXEEXT)
if !HAVE_MEMORY_CHECKER
if HAVE_EMULATOR
$(MSG) Speed
# Run the self test in AVX2
$(TESTENV_AVX2) ./snapraid$(EXEEXT) --test-skip-device -c $(CONF) status
# Run the speed test in different architectures
$(TESTENV_SSE2) ./snapraid$(EXEEXT) --test-skip-device -T
$(TESTENV_SSSE3) ./snapraid$(EXEEXT) --test-skip-device -T
$(TESTENV_SSE42) ./snapraid$(EXEEXT) --test-skip-device -T
$(TESTENV_AVX2) ./snapraid$(EXEEXT) --test-skip-device -T
else
# Run the self test natively
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -c $(CONF) status
# Run the speed test natively
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -T
endif
endif
#### EMPTY ####
$(MSG) Some commands with empty array
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) diff
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) dup
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) list > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l ">&1"
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l ">&2"
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l ">>test.log"
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l test-%D-%T.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) up
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) down -d parity -d disk1
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) devices
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) smart --no-warnings
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) smart --test-fake-device --test-expect-failure
if HAVE_POSIX
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) pool
endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -a check
#### CONTROLLED ####
$(MSG) Filesystem allocation test
head -c 8192 /dev/zero > bench/disk1/TEST
# Copy the file to trigger the copy optimization check
cp -a bench/disk1/TEST bench/disk2/TEST
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
# Now delete the file, and do a partial sync to force a deallocation in the middle of a file
rm bench/disk1/TEST
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E -S 1 -B 1 sync
# Now force a deallocation at the end
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E -S 7 -B 1 sync
rm bench/disk2/TEST
# Have the sync to fail for empty disk
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync --test-expect-failure
# Now sync will complete with -E
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E sync
$(MSG) Filesystem fragmentation test
head -c 8192 /dev/zero > bench/disk1/TEST1
head -c 8192 /dev/zero > bench/disk1/TEST2
head -c 8192 /dev/zero > bench/disk1/TEST3
head -c 8192 /dev/zero > bench/disk1/TEST4
head -c 8192 /dev/zero > bench/disk1/TEST5
head -c 8192 /dev/zero > bench/disk1/TEST6
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
# Now delete some files, and create a bigger one to fill the holes
rm bench/disk1/TEST1
rm bench/disk1/TEST3
rm bench/disk1/TEST5
head -c 65536 /dev/zero > bench/disk1/TESTX
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync -l ">&1"
rm -r bench/disk1/TEST*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E sync
# Now enforce copy detection of a file without parity
# Create a file in a high number disk
head -c 8192 /dev/urandom > bench/disk6/STEP1
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
# Create two new copies. The one in disk1 will copy
# the hash from the one in disk6, and the one in disk2
# will copy the hash from the one in disk1, that
# doesn't have parity
cp -a bench/disk6/STEP1 bench/disk1/STEP1
cp -a bench/disk6/STEP1 bench/disk2/STEP1
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
# Now create, duplicate, move and partial sync
head -c 8192 /dev/zero > bench/disk1/INVA1
head -c 8192 /dev/zero > bench/disk1/INVA2
head -c 8192 /dev/zero > bench/disk1/INVA3
head -c 8192 /dev/zero > bench/disk1/MOVE1
cp -a bench/disk1/MOVE1 bench/disk1/MOVE2
cp -a bench/disk1/MOVE1 bench/disk1/MOVE3
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
mv bench/disk1/MOVE2 bench/disk2/MOVE2
mv bench/disk1/MOVE3 bench/disk3/MOVE3
head -c 8192 /dev/zero > bench/disk4/MOVE4
cp -a bench/disk1/INVA1 bench/disk1/EXTRA1
cp -a bench/disk1/INVA2 bench/disk1/EXTRA2
cp -a bench/disk1/INVA3 bench/disk1/EXTRA3
rm bench/disk1/INVA1
touch bench/disk1/INVA2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-kill-after-sync sync
# Diff should report the need of a sync for invalid parity
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(PAR1) --test-expect-need-sync diff
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
mv bench/disk2/MOVE2 bench/disk4/MOVE4
mv bench/disk3/MOVE3 bench/disk5/MOVE5
mv bench/disk4/MOVE4 bench/disk6/MOVE6
rm bench/disk1/EXTRA1
touch bench/disk1/EXTRA2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E sync
# Change UUID
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-fake-uuid sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### NOT EMPTY ####
$(MSG) Create some files
$(TESTENV) ./mktest$(EXEEXT) generate 1 6 $(CHECKCOUNT) $(CHECKSIZE)
echo DUP > bench/disk1/DUP1
echo DUP > bench/disk1/DUP2
echo -n > bench/disk1/ZERO
$(MSG) Some commands with a not empty array
# Run a sync using physical offset
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_PHYSICAL) -c $(CONF) --test-expect-need-sync diff > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_PHYSICAL) -c $(CONF) sync
# Now reset the array, as we normally test with alpha order and murmur3
rm bench/content bench/?-content
# Now rebuild the array with alpha order and murmur3 and do some commands
# Later we will convert it to spooky2 to test both hashes
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) --test-expect-need-sync diff > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-murmur3 --test-force-autosave-at 100 sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) dup -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) list -l test.log > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) test-rewrite
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) test-read
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l test.log
if HAVE_POSIX
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) pool
endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -a check
$(MSG) Dry
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) test-dry
$(MSG) Copy detection
# Create a file and sync with it
echo 123 > bench/disk1/COPY
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
# Corrupt and move it to another disk
$(TESTENV) ./mktest$(EXEEXT) damage 1 1 1 bench/disk1/COPY
mv bench/disk1/COPY bench/disk2/COPY
# Now sync with failure as the data won't match. We have two points of failure, pre-hash and sync
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-expect-failure -h sync
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-expect-failure sync
# Now sync with force-nocopy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --force-nocopy sync
$(MSG) Nano
touch -t 200102011234.56 bench/disk1/a/a*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) touch
$(MSG) Check the --gen-conf command
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --gen-conf bench/content
$(MSG) Filter
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) -d disk1 check > output.log
if HAVE_POSIX
# Only in real unix, as with MingW the * trigger path replacement
# Like /b/a* -> B:/a* and SnapRAID then complain about relative paths
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) -f \\*a -f a/ -f /b/a\\* check > output.log
endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) --test-expect-failure -f . check > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) --test-expect-failure -f ./ check > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) --test-expect-failure -f a/a check > output.log
$(MSG) Delete some files and sync
rm bench/disk4/a/9*
rm bench/disk5/a/9*
rm bench/disk6/a/9*
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) --test-expect-need-sync diff > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) sync -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Move some files, sync and check
mv bench/disk1/a/9* bench/disk4/a
mv bench/disk2/a/9* bench/disk5/a
mv bench/disk3/a/9* bench/disk6/a
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) --test-expect-need-sync diff > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
#### MORE FILES ####
$(MSG) Create some more files, hardlinks and empty directories, delete others, sync PAR1 and check
rm bench/disk4/a/8*
rm bench/disk5/a/8*
rm bench/disk6/a/8*
$(TESTENV) ./mktest$(EXEEXT) generate 2 6 $(CHECKCOUNT) $(CHECKSIZE)
mkdir bench/disk1/empty1
mkdir bench/disk2/empty2
mkdir bench/disk3/empty3a
mkdir bench/disk3/empty3a/emptyinner
mkdir bench/disk3/empty3b
echo TARGET1 > bench/disk1/target1
ln bench/disk1/target1 bench/disk1/file_hardlink1
ln bench/disk1/target1 bench/disk1/file_hardlink2
ln bench/disk1/target1 bench/disk1/file_hardlink3
if HAVE_POSIX
ln -s bench/disk1/target1 bench/disk1/file_symlink1
ln -s bench/disk1/target1 bench/disk1/file_symlink2
ln -s bench/disk1/target1 bench/disk1/file_symlink3
endif
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(PAR1) --test-expect-need-sync diff > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-fallocate -c $(PAR1) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-io-advise-none -c $(PAR1) sync -F --test-io-cache 1
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-io-advise-sequential -c $(PAR1) sync -F --test-io-stats
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-io-advise-flush-window -c $(PAR1) sync -F
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-io-advise-discard-window -c $(PAR1) sync -F
#### CHANGE LINKS ####
# Use a different size ("22" instead of "1") to ensure to recognize the file different
# even if it gets the same timestamp in case subsecond timestamp is no available
echo TARGET22 > bench/disk1/target2
rm bench/disk1/file_hardlink1
ln bench/disk1/target2 bench/disk1/file_hardlink1
if HAVE_POSIX
rm bench/disk1/file_symlink1
ln -s bench/disk1/target2 bench/disk1/file_symlink1
endif
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) --test-expect-need-sync diff > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) sync -l ">&1"
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) check -l ">&1"
#### MISC COMMANDS ####
$(MSG) Some commands with a not empty array
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(PAR1) dup
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(PAR1) list --test-fmt file > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(PAR1) list --test-fmt disk > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(PAR1) list --test-fmt path > output.log
if HAVE_POSIX
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) pool
endif
$(MSG) Extend PAR1 to max parity with fix and check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(CONF) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -d 2-parity -d 3-parity -d 4-parity -d 5-parity -d 6-parity -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check -l ">&1"
$(MSG) Fix with unaccessible parity and disk
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(NOACCESS) fix --test-expect-failure
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(NOACCESS) fix --force-device --test-expect-recoverable -l test.log
$(MSG) Rename a disk
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RENAME) --test-match-first-uuid sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-match-first-uuid sync
#### SCRUB ####
$(MSG) Scrub some times
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-scrub-at 100 scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-scrub-at 1000 scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-scrub-at 100000 scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
#### SYNC WITH RUNTIME CHANGE ####
$(MSG) Modify files during a sync
echo RUN > bench/disk1/RUN-RM
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "rm bench/disk1/RUN-RM" --test-expect-failure sync
echo RUN > bench/disk1/RUN-CHMOD
if HAVE_POSIX
# Doesn't run this test as root because the root user override permissions
if [[ $$EUID -ne 0 ]]; then \
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "chmod a-r bench/disk1/RUN-CHMOD" --test-expect-failure sync; \
fi
endif
echo RUN > bench/disk1/RUN-SIZE
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "echo RUNRUN > bench/disk1/RUN-SIZE" --test-expect-failure sync
echo RUN > bench/disk1/RUN-TOUCH
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "touch -t 200001011200 bench/disk1/RUN-TOUCH" --test-expect-failure sync
echo RUN > bench/disk1/RUN-INODE
cp -p bench/disk1/RUN-INODE bench/disk1/RUN-INODE-COPY
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "mv bench/disk1/RUN-INODE-COPY bench/disk1/RUN-INODE" --test-expect-failure sync
rm bench/disk1/RUN-CHMOD
rm bench/disk1/RUN-SIZE
rm bench/disk1/RUN-TOUCH
rm bench/disk1/RUN-INODE
echo HASH > bench/disk1/HASH-RM
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "rm bench/disk1/HASH-RM" --test-expect-failure -h sync
echo HASH > bench/disk1/HASH-CHMOD
if HAVE_POSIX
# Doesn't run this test as root because the root user override permissions
if [[ $$EUID -ne 0 ]]; then \
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "chmod a-r bench/disk1/HASH-CHMOD" --test-expect-failure -h sync; \
fi
endif
echo HASH > bench/disk1/HASH-SIZE
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "echo HASHHASH > bench/disk1/HASH-SIZE" --test-expect-failure -h sync
echo HASH > bench/disk1/HASH-TOUCH
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "touch -t 200001011200 bench/disk1/HASH-TOUCH" --test-expect-failure -h sync
echo HASH > bench/disk1/HASH-INODE
cp -p bench/disk1/HASH-INODE bench/disk1/HASH-INODE-COPY
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-run "mv bench/disk1/HASH-INODE-COPY bench/disk1/HASH-INODE" --test-expect-failure -h sync
rm bench/disk1/HASH-CHMOD
rm bench/disk1/HASH-SIZE
rm bench/disk1/HASH-TOUCH
rm bench/disk1/HASH-INODE
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### SILENT ERRORS ####
$(MSG) Silently corrupt some files, scrub and fix filtering for error. Test scrub patterns.
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./mktest$(EXEEXT) damage 1 1 1 bench/disk1/a/*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-expect-recoverable --test-force-scrub-at 100000 scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -e
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --percentage bad scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --plan 1 scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -o 0 scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -p full scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -p new scrub
$(MSG) Silently corrupt some files, and sync with error presents
$(TESTENV) ./mktest$(EXEEXT) write 2 1 1 bench/disk1/a/*
$(TESTENV) ./mktest$(EXEEXT) damage 3 1 1 bench/disk2/a/*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-expect-recoverable sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
# Note that this fix -e command doesn't fix all the errors because
# only the blocks touched by the sync are marked bad
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -e
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check --test-expect-recoverable
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -p full scrub
$(MSG) Full sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -F sync
$(MSG) Realloc sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -R sync
#### SYNC PARTIAL ####
$(MSG) Abort sync with additions. Delete some of them, and add others and sync again.
$(MSG) This triggers files reallocation inside parity
mkdir bench/disk1/c
mkdir bench/disk1/d
mkdir bench/disk1/e
cp -pR bench/disk2/a/c* bench/disk1/c
cp -pR bench/disk2/a/d* bench/disk1/d
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-kill-after-sync sync
rm -r bench/disk1/c
cp -pR bench/disk2/a/e* bench/disk1/e
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Abort sync with both deletions and additions. And delete and add some more and sync again.
# Note that here we are depending of the previous state leaving dirs "d" and "e"
mkdir bench/disk1/f
cp -pR bench/disk3/a/f* bench/disk1/f
rm -r bench/disk1/d
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-kill-after-sync sync
mkdir bench/disk1/g
cp -pR bench/disk4/a/g* bench/disk1/g
rm -r bench/disk1/e
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
rm -r bench/disk1/f
rm -r bench/disk1/g
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Make a hole in the disk array and sync with --force-empty
mv bench/disk2 bench/disk2.old
mkdir bench/disk2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --force-empty sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Use with the hole
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) check
rm -r bench/disk1/a
rm -r bench/disk3/a
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) test-dry
$(MSG) Fill the hole with a new disk
rm -r bench/disk2
mv bench/disk2.old bench/disk2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Corrupt the content file
$(TESTENV) ./mktest$(EXEEXT) write 1 100 100 bench/content
$(FAILENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-failure -c $(CONF) sync
rm bench/content
#### AGAIN MORE FILES ####
$(MSG) Delete some files, create some new, sync and check
rm bench/disk1/a/1*
rm bench/disk2/a/2*
rm bench/disk3/a/3*
rm bench/disk4/a/4*
$(TESTENV) ./mktest$(EXEEXT) generate 3 6 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Move some files, sync and check
mv bench/disk1/a/7* bench/disk1/b/
mv bench/disk2/a/7* bench/disk2/b/
mv bench/disk3/a/7* bench/disk3/b/
mv bench/disk4/a/7* bench/disk4/b/
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
if !HAVE_MEMORY_CHECKER
#### RECOVER 1 ####
$(MSG) Delete one disk, fix and check with PAR1
rm -r bench/disk3
mkdir bench/disk3
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### RECOVER 2 ####
$(MSG) Delete two disks, fix and check with PAR2
rm -r bench/disk1
mkdir bench/disk1
rm -r bench/disk2
mkdir bench/disk2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) fix -l test-fail-strategy1.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### RECOVER 3 ####
$(MSG) Delete three disks, fix and check with PAR3
rm -r bench/disk1
mkdir bench/disk1
rm -r bench/disk2
mkdir bench/disk2
rm -r bench/disk3
mkdir bench/disk3
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR2) fix -l test-fail-strategy2.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR3) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR3) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### RECOVER 4 ####
$(MSG) Delete four disks, fix and check with PAR4
rm -r bench/disk3
mkdir bench/disk3
rm -r bench/disk4
mkdir bench/disk4
rm -r bench/disk5
mkdir bench/disk5
rm -r bench/disk6
mkdir bench/disk6
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR3) fix -l test-fail-strategy3.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR4) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR4) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### RECOVER 5 ####
$(MSG) Delete five disks, fix and check with PAR5
rm -r bench/disk1
mkdir bench/disk1
rm -r bench/disk2
mkdir bench/disk2
rm -r bench/disk3
mkdir bench/disk3
rm -r bench/disk5
mkdir bench/disk5
rm -r bench/disk6
mkdir bench/disk6
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR4) fix -l test-fail-strategy4.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR5) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR5) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### RECOVER 6 ####
$(MSG) Delete six disks, fix and check with PAR6
rm -r bench/disk1
mkdir bench/disk1
rm -r bench/disk2
mkdir bench/disk2
rm -r bench/disk3
mkdir bench/disk3
rm -r bench/disk4
mkdir bench/disk4
rm -r bench/disk5
mkdir bench/disk5
rm -r bench/disk6
mkdir bench/disk6
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR5) fix -l test-fail-strategy5.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR6) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR6) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
endif
#### MULTI STEP ####
$(MSG) Delete some files and create some new, sync and check in multiple steps
rm bench/disk1/a/4*
rm bench/disk2/a/5*
rm bench/disk3/a/6*
rm bench/disk4/a/6*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -B 10 sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check -l test.log
$(TESTENV) ./mktest$(EXEEXT) generate 4 6 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -B 100 sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -B 1000 sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Rehash to spooky2 and rehash even blocks
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-spooky2 rehash
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-scrub-even scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Full sync to complete rehash
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -F sync
$(MSG) Delete files from three disks and check/fix with import by data in PAR2
rm -r bench/disk1/a
rm -r bench/disk2/a
mv bench/disk3/a bench/a
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-import-content bench/a -c $(PAR2) fix -l test.log
rm -r bench/a
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Delete files from three disks and check/fix with import by timestamp in PAR2
rm -r bench/disk1/a
rm -r bench/disk2/a
mv bench/disk3/a bench/a
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -i bench/a -c $(PAR2) fix -l test.log
rm -r bench/a
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Delete files from three disks and check/fix with automatic import in PAR2
rm -r bench/disk1/a
rm -r bench/disk2/a
mv bench/disk3/a bench/disk1/a_from_disk3
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
rm -r bench/disk1/a_from_disk3
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
#### SYNC ABORT ####
$(MSG) Abort sync late with additions, delete them and recover with PAR2
$(MSG) This triggers the recovering with q using p to check the validity
cp -pR bench/disk1/a bench/disk1/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-kill-after-sync -c $(CONF) sync
rm -r bench/disk1/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
rm -r bench/disk1/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Abort sync early with additions, delete them and recover with PAR2
$(MSG) This triggers the recovering with q using p to check the validity, but failing for new blocks being filled with zero
cp -pR bench/disk1/a bench/disk1/a_copy
# Ensure that there is at least one zero filled file
head -c 8192 /dev/zero > bench/disk1/a_copy/ZERO_FILLED
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -B 1 --test-kill-after-sync -c $(CONF) sync
rm -r bench/disk1/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR2) fix -l test-fail-earlysyncadd.log
rm -r bench/disk1/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Abort sync late with additions with prehash and then delete the new additions and sync again
cp -pR bench/disk1/a bench/disk1/a_copy
cp -pR bench/disk2/a bench/disk2/a_copy
cp -pR bench/disk3/a bench/disk3/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-kill-after-sync -c $(CONF) -h sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
rm -r bench/disk1/a_copy
rm -r bench/disk2/a_copy
rm -r bench/disk3/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Abort sync late with additions and then delete the new additions and sync again
cp -pR bench/disk1/a bench/disk1/a_copy
cp -pR bench/disk2/a bench/disk2/a_copy
cp -pR bench/disk3/a bench/disk3/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-kill-after-sync -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
rm -r bench/disk1/a_copy
rm -r bench/disk2/a_copy
rm -r bench/disk3/a_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Abort sync late with additions and then delete some unchanged stuff and fix with PAR1
cp -pR bench/disk1/b bench/disk1/b_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) --test-kill-after-sync sync
rm -r bench/disk2
mkdir bench/disk2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
$(MSG) Fixes again to restore all the parity
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -l test.log
rm -r bench/disk1/b_copy
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(MSG) Abort sync late with more deletions than additions and then delete some unchanged stuff and fix with PAR1
cp -pR bench/disk1/a bench/disk1/a_copy
mv bench/disk1/a bench/a
mv bench/disk1/b bench/b
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-kill-after-sync sync
rm -r bench/disk2
mkdir bench/disk2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
$(MSG) Fixes again to restore all the parity
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -l test.log
-rm -r bench/disk1/a
-rm -r bench/disk1/b
rm -r bench/disk1/a_copy
mv bench/a bench/disk1/a
mv bench/b bench/disk1/b
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Abort sync early with more deletions than additions and then delete some unchanged stuff and fix with PAR2
cp -pR bench/disk1/a bench/disk1/a_copy
mv bench/disk1/a bench/a
mv bench/disk1/b bench/b
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -B 1 --test-kill-after-sync sync
rm -r bench/disk2
mkdir bench/disk2
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) fix -l test-fail-earlysyncdel.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
-rm -r bench/disk1/a
-rm -r bench/disk1/b
rm -r bench/disk1/a_copy
mv bench/a bench/disk1/a
mv bench/b bench/disk1/b
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
#### RECOVER MISSING ####
$(MSG) Delete some files, fix with -m and check with PAR1
rm bench/disk1/a/8*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) -m fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Delete some dirs in six disk, fix with -m and check
rm -r bench/disk1/b
rm -r bench/disk2/b
rm -r bench/disk3/b
rm -r bench/disk4/b
rm -r bench/disk5/b
rm -r bench/disk6/b
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR5) fix -l test-full5.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(CONF) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -m fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
#### RECOVER BY DISK ####
$(MSG) Delete some dirs in two disks, fix and check with PAR2 using the -d option for each disk
rm -r bench/disk2/b
rm -r bench/disk5/b
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) check -l test-full1.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) -d disk2 fix -l test-part1.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) -d disk5 fix -l test-part2.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
#### RECOVER PARITY ####
$(MSG) Delete the parity, fix and check with PAR1
rm bench/parity*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Delete the parity and the 2-parity, fix and check with PAR2
rm bench/parity*
rm bench/2-parity*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Delete the parity and a disk, fix and check with PAR2
rm bench/parity*
rm -r bench/disk2/b
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Delete the 2-parity and a disk, fix and check with PAR2
rm bench/2-parity*
rm -r bench/disk3/a
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Corrupt the parity, fix and check with PAR1
$(TESTENV) ./mktest$(EXEEXT) write 1 100 10000 bench/parity*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) -a check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Corrupt the parity and the 2-parity, fix and check with PAR2
$(TESTENV) ./mktest$(EXEEXT) write 2 100 10000 bench/parity*
$(TESTENV) ./mktest$(EXEEXT) write 2 100 10000 bench/2-parity*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -a check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
#### RECOVER FILES AND PARITY ####
$(MSG) Corrupt some files, fix and check with PAR1
$(TESTENV) ./mktest$(EXEEXT) change 3 500 bench/disk2/b/*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) -a check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Corrupt some files, fix and check with PAR2 in verbose mode
$(TESTENV) ./mktest$(EXEEXT) change 4 500 bench/disk2/b/*
$(TESTENV) ./mktest$(EXEEXT) change 4 500 bench/disk3/b/*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) --test-expect-unrecoverable -c $(PAR1) check -l test.log > /dev/null
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) --test-expect-unrecoverable -c $(PAR1) fix -l test-fail-corrupt-verbose.log > /dev/null
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) -a check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Corrupt some files, fix and check with PAR2
$(TESTENV) ./mktest$(EXEEXT) change 5 500 bench/disk2/b/*
$(TESTENV) ./mktest$(EXEEXT) change 5 500 bench/disk3/b/*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) check -l test-fail-corrupt-data.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Corrupt the parity and some files, fix and check with PAR2
$(TESTENV) ./mktest$(EXEEXT) write 6 100 10000 bench/parity*
$(TESTENV) ./mktest$(EXEEXT) change 6 500 bench/disk1/a/*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) check -l test-fail-corrupt-parity.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Corrupt the 2-parity and some files, fix and check with PAR2
$(TESTENV) ./mktest$(EXEEXT) write 7 100 10000 bench/2-parity*
$(TESTENV) ./mktest$(EXEEXT) change 7 500 bench/disk1/a/*
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(MSG) Sync after all the fixes
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
endif
$(MSG) Regression test completed with SUCCESS!
$(MSG) Please ignore any error message printed above, they are expected!
$(MSG) Everything OK
# Manual testing
MANUAL = ./snapraid --test-skip-device -c test/test-par1.conf sync
valgrind:
valgrind \
--tool=memcheck \
--track-origins=yes \
--read-var-info=yes \
--leak-check=full \
-v $(MANUAL) \
2> valgrind.log
tail valgrind.log
callgrind:
valgrind \
--tool=callgrind \
--dump-instr=yes \
--trace-jump=yes \
--collect-systime=yes \
-v $(MANUAL) \
2> callgrind.log
tail callgrind.log
cachegrind:
valgrind \
--tool=cachegrind \
-v $(MANUAL) \
2> cachegrind.log
tail cachegrind.log
lcov_reset:
lcov --directory . -z
rm -f ./lcov.info
lcov_capture:
lcov --directory . --capture -o lcov.info
lcov_html:
rm -rf ./cov
mkdir cov
genhtml -o ./cov lcov.info
# Rules for documentation
if HAVE_ADVD2
snapraid.1 : snapraid.d
advd2 man < $(srcdir)/$< > $@
snapraid.txt : snapraid.d
advd2 txt < $(srcdir)/$< | todos > $@
snapraid.html : snapraid.d
advd2 html < $(srcdir)/$< > $@
snapraid.hh : snapraid.d
advd2 frame < $(srcdir)/$< > $@
endif
# Windows distribution
DISTWIN = \
snapraid.txt \
snapraid.exe
distwindows-x86: $(DISTWIN)
rm -f $(PACKAGE)-$(VERSION)-windows-x86.zip
mkdir tmp
cp $(DISTWIN) tmp
cp support/smartctl.exe tmp/smartctl.exe
todos < snapraid.conf.example.windows > tmp/snapraid.conf.example
todos < INSTALL.windows > tmp/install.txt
todos < README > tmp/readme.txt
todos < AUTHORS > tmp/authors.txt
todos < HISTORY > tmp/history.txt
todos < COPYING > tmp/copying.txt
todos < TODO > tmp/todo.txt
cd tmp && zip -r ../$(PACKAGE)-$(VERSION)-windows-x86.zip *
rm -r tmp
distwindows-x64: $(DISTWIN)
rm -f $(PACKAGE)-$(VERSION)-windows-x64.zip
mkdir tmp
cp $(DISTWIN) tmp
cp support/smartctl.exe tmp/smartctl.exe
todos < snapraid.conf.example.windows > tmp/snapraid.conf.example
todos < INSTALL.windows > tmp/install.txt
todos < README > tmp/readme.txt
todos < AUTHORS > tmp/authors.txt
todos < HISTORY > tmp/history.txt
todos < COPYING > tmp/copying.txt
todos < TODO > tmp/todo.txt
cd tmp && zip -r ../$(PACKAGE)-$(VERSION)-windows-x64.zip *
rm -r tmp
snapraid-12.1/README 0000664 0000000 0000000 00000002134 14166610522 0014121 0 ustar 00root root 0000000 0000000 SnapRAID
========
SnapRAID is a backup program for disk arrays. It stores parity
information of your data and it recovers from up to six disk
failures.
SnapRAID is mainly targeted for a home media center, where you
have a lot of big files that rarely change.
Beside the ability to recover from disk failures, the other
features of SnapRAID are:
* You can use disk already filled with files, without the need to
reformat them. You will access them like now.
* All your data is hashed to ensure data integrity and to avoid
silent corruption.
* If the failed disks are too many to allow a recovery,
you lose the data only on the failed disks.
All the data in the other disks is safe.
* If you accidentally delete some files in a disk, you can
recover them.
* The disks can have different sizes.
* You can add disks at any time.
* It doesn't lock-in your data. You can stop using SnapRAID at any
time without the need to reformat or move data.
* To access a file, only a single disk needs to spin, saving power and
producing less noise.
The official site of SnapRAID is:
http://www.snapraid.it/
snapraid-12.1/TODO 0000664 0000000 0000000 00000043231 14166610522 0013734 0 ustar 00root root 0000000 0000000 SnapRAID TODO
=============
This is the list of TODO items for SnapRAID.
- Next
- Minor
* Add a new -u, --filter-updated command that filters files
with a different timestamp, to be able to restore only them to the previous state.
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/e26e787d/
* Allow to filter in diff/list by disk name.
Not checked disks should be allowed to be missing.
* Add an option to ignore subsecond timestamp.
Like when you copy data to a filesystem with less timestamp precision.
* Use threads to scan all the disks at the same time.
- After 7.0 Windows changes it seems fast enough even
with a mono thread implementation with 100.0000 files.
+ But if you have millions of files, it could take minutes.
* Support more parity levels
It can be done with a generic computation function, using
intrinsic for SSSE3 and AVX instructions.
It would be interesting to compare performance with the hand-written
assembler functions. Eventually we can convert them to use intrinsic also.
https://sourceforge.net/p/snapraid/discussion/1677233/thread/9dbd7581/
* Extend haspdeep to support the SnapRAID hash :
https://github.com/jessek/hashdeep/
https://sourceforge.net/p/snapraid/discussion/1677233/thread/90b0e9b2/?limit=25
* Add a "noaccessdenied" config option to exclude not readable files/dirs.
Like the "nohidden".
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/1409c26a/
* Don't insert new files if they are new opened by other applications.
Not yet sure how to check if a file is open in a fast way.
In case we can exclude files created too recently, like with a --min-age option.
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/a1683dd9/?limit=25#1e16
* Add markdown support for output
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/661cce8b/
* Add ZFS support for SMART and UUID.
See latest messages at: https://sourceforge.net/p/snapraid/discussion/1677233/thread/42defa3b/
* Change all the path printed in the terminal as relative and not absolute.
https://sourceforge.net/p/snapraid/discussion/1677233/thread/648955ec/ (Leifi in diff)
https://sourceforge.net/p/snapraid/discussion/1677233/thread/5b6ef1b9/ (Miles in check + filter)
Partially done. Now you can control it with --test-fmt
* Add ReFS support with 128 bits inode
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/2be14f63/
https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/miniFilter/avscan/filter/utility.h
https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/miniFilter/avscan/filter/utility.c
https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/miniFilter/delete/delete.c
Partially done. Until the inodes are less than 64 bit, everything works.
If a 128 bit inode is found, it aborts with "Invalid inode number! Is this ReFS?"
* When a disk is replaced, and SnapRAID detect this by a UUID change, we could clear the
scrub information for that disk.
https://sourceforge.net/p/snapraid/discussion/1677233/thread/ee87901b/
* In fix, automatic --import excluded directory in the config file.
https://sourceforge.net/p/snapraid/discussion/1677233/thread/c80c42e5/?limit=25
* Allow to have "partial" parity disks, smaller than required ?
https://sourceforge.net/p/snapraid/discussion/1677233/thread/e924c663/
* How to handle corrupt copied/moved files ? At now pre-hash checks them,
but if a corruption is found, it stops the sync process.
This is not optimal, because the 'sync' command is stopped instead to continue.
A possible solution could be:
If a copied block is corrupt, we can recover the original one from parity (if moved),
or read it directly from disk (if copied), and we can use this one to build the parity,
working around the corruption, that can be later fixed with "fix".
This requires to allocate blocks of moved files in parity positions *after* the
currently used one.
* Some programs are misusing the FILE_ATTRIBUTE_TEMPORARY.
In fact, it makes sense as it's a good way to control the cache on the file,
so, despite the name, that usage could be common for not temporary files.
https://sourceforge.net/p/snapraid/discussion/1677233/thread/57d40108/
* Restore ownership and permissions, at least in Unix.
* Restore directory timestamps.
* Add an option for dup to list only duplicates with different name.
This supposing that if a file has the same name, it's intentionally duplicated.
* In fix an existing symlink with the same name of a file to be recovered may stop
the process making the create() operation to fail.
The same for directories, when recreating the directory tree.
* If a directory exists with the same name of a parity/content file be more explicative
on the error message. See: https://sourceforge.net/projects/snapraid/forums/forum/1677233/topic/4861034
* We don't try to do partial block recovering. A block is correct or not.
But if only some bytes, or a sector, is wrong, it should be possible to recover all the
rest of the block.
The problem is that we don't have any hash to ensure that the block is partially recovered,
or completely garbage. But it makes sense to anyway write the "most likely" correct one.
- Naming
* A new 'init' command to differentiate the first 'sync' operation.
This 'init' will work also without a content file, and parity files.
Instead 'sync' will require all of them.
This will also help when running with the parity filesystem unmounted.
* Rename sync->backup and fix->restore. It seems to me a naming expressing
better the meaning of the commands. But not yet sure.
- Pooling
* Add a new "commit" command to move changes from the pool to the array.
It should:
- Move files copied into the pool (that are no links) to the array.
The files should be moved to the disk that contains most of the files
in the directory. If no space, try with the disk with less files
in the directory, and eventually the disk in the array with more free space.
- Detect renames, and apply them in the array.
The file will be renamed and moved to the new directory, if changed,
but kept in the same disk of the array.
- Detect deletes, and move file in the array to a "/trash/" directory
of the same disk. For safety no real deletion is done.
File with the same name will get an extra extension like ".1", ".2".
- Major
* Uses a separated file for storing hashes, to allow to use a memory
mapped file to decrease memory utilization.
The file can contains hashes stored in the same order they are accessed
in sync/check/scrub.
+ A lot less memory utilization
- It will be slow in --pre-hash as the files are accessed in another order,
but no much slow.
- How to handle multiple .content file copy ? When working we can have
only a single file. When storing the .content we'll have to copy it
in all the places.
* Can we mix the existing and new approach ? We can create this
hash file at startup in a memory mapped "temporary" file.
It may takes some time to create it, but then it will be fast.
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/cdea773f/
* Allocate parity minimizing concurrent use of it
Each parity allocation should check for a parity
range with less utilization by other disks.
We need to take care do disable this mechanism when the parity space
is near to fillup the parity partition.
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/1797bf7d/
+ This increase the possibility of recovering with multiple failures with not
enough parity, as the benefit of existing parity is maximized.
- Possible increase of fragmentation of parity allocation
- No real benefit when the array is filled
Rejected TODO
=============
This is a list of rejected TODO items.
* Allow multiple parity files and to coexist with data
in the same disk.
This is possible if the data in the same disk uses parity addresses not
contained in the parity file present in the same disk.
+ It's a nice trick. The disk containing parity, will have less space available,
and then it will need less parity, resolving the problem of the parity disk being too small.
+ We can also think at an automated parity files naming and creation, removing
the need of the user to specify that. We can also completely remove the
concept of parity drive, automatically allocating parity in the most free data drives.
- It won't be not nice to have the program to automatically
choose where to create the parity, because the choice cannot be optimal.
With a dedicated disk manually chosen, it's instead optimal.
+ We can limit this coexist possibility only to latest parity file,
allowing the user to choose where to put it.
- It won't work with disks of different size.
Suppose to have all disks of size N, with only one of size M>N.
To fully use the M space, you can allocate a full N parity in such disk,
but the remaining space will also need additional parity in the other disks,
in fact requiring a total of M parity for the array.
In the end, we cannot avoid that the first biggest disk added is fully
dedicated to parity, even if it means to leave some space unused.
* Directly access disks for parity skipping the filesystem layer
- No real performance or space gain compared to a custom filesystem configuration like:
mkfs.ext3 -i 16777216 -m0 -O ^dir_index,large_file,sparse_super /dev/sdX1
See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/9c0ef324/?limit=25
* Allow to specify more than one disk directories to cover the case of multi partitions.
Different partitions have duplicate inode. The only way to support this is to
add also a kind of device_id, increasing the memory required.
But it should be only few bits for each file. So, it should be manageable.
A lot of discussions about this feature :)
https://sourceforge.net/p/snapraid/discussion/1677233/thread/b2cd9385/
- The only benefit is to distribute better the data. This could help the recovery process,
in case of multiple failures. But no real usability or functionality benefit in the normal
case.
* https://sourceforge.net/p/snapraid/discussion/1677233/thread/2cb97e8a/
Have two hashes for each file. One for the first segment, that may use only a part of the first parity block.
And a second hash that takes care of the final segment, also using only part of a parity block.
Intermediate blocks can be handled like now.
The first and last segment can use only a part of the parity block, sharing it with other files,
and then allowing big parity blocks.
+ Big parity block, like 1MB, and small file allocation, like 4KB.
- It won't be possible to copy hash info from one disk to another, as copied files
may have a different block splitting. Copied file should be allocated with the same exact
splitting.
- Dup detection can be handled with a dedicated hash covering the full file. But increasing
the number of hash for each file to three.
- No way to handle block import.
* Create a filesystem snapshot at every sync, and use it in all the other commands
automatically.
At the next sync, drop the old snapshot and create a new one.
This should help recovering, because we'll have the exact copy used by sync.
This feature can be enabled with a specific option, and available
in Windows using Shadow Copy, and in Linux using Btrfs, and in a generic
Unix using ZFS.
See Jens's Windows script at: http://sourceforge.net/p/snapraid/discussion/1677233/thread/a1707211/
Note that a different between Windows and Unix is that in Windows old snapshots
are automatically deleted.
* Checks if splitting hash/parity computation in 4K pages
can improve speed in sync. That should increase cache locality,
because we read the data two times for hash and parity,
and if we manage to keep it in the cache, we should save time.
- We now hash first the faster disks, and this could
reduce performance as we'll have to wait for all disks.
* Enable storing of creation time NTFS, crtime/birth time EXT4.
But see: http://unix.stackexchange.com/questions/50177/birth-is-empty-on-ext4
coreutils stat has an example, but it doesn't work in Linux (see lib/stat-time.h)
- Not supported in Linux.
* In the content file save the timestamp of the parity files.
If they do not match, stop the processing.
This can be done to avoid to use not synchronized parity and content files,
resulting in wrong data.
But if the sync process is killed we need a way to resyncronize them.
Or maybe we should allow parity newer than content, but not viceversa.
- The corner cases are too many. A fixed parity may be never.
A someway modified content may be never. So, the time is not really significant.
* Use Async IO for Linux (libaio).
See thread: https://sourceforge.net/p/snapraid/discussion/1677233/thread/a300a10b/
Docs at: http://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt
- Implemented for scrub in the "libaio" branch, but it's slower of
about 20% in my machine.
* Allow to put parity directly into the underline block device without the need
of any filesystem.
That would allow to increase a lot the free space for parity.
We can implement some kind of filesystem detection to avoid to overwrite an already
existing filesystem.
- Still risky if stuffs go wrong.
- In Linux with largefile4 there is only a very small amount of space wasted.
In the order of 0.01%. Not really worth to do it.
- With NTFS the saving is also limited, because the "reserved" MFT of 12.5% is
not really exclusively reserved, but can be used also for normal files,
when all the remaining space is filled.
* The data could be compressed before processing, resulting in parity block of
fixed size, but matching different data block sizes.
The complexity is that a file blocks will have to be allocated at runtime,
and you may run out of them in the middle of the processing.
We need also a way to compress a stream until the compressed data reach the
block size, but no more, and then start a new block.
For each block, we'll have also to store. "size_uncompressed", "size_compressed",
"hash".
- It will be too slow.
- Not addressing the problem of a lot of small files, as still one block will be
allocated for each file.
* Uses different block size for parity and file allocation.
We can use a big size, like 1MB for parity allocation and hash computation,
and at the same time using a 4K blocks for files allocation.
This means that a parity blocks may contain more than one file.
- We'll have to drop the dup and import feature,
because it won't be possible anymore to compare the hash of files,
as it would depend also on the start address inside the parity block.
- When a hash fail, it won't be possible to tell which file is really
broken, because more file may share the same parity block.
* Have special parity blocks containing the last blocks of more files
until it's filled up.
For such parity blocks, we'll have more than one hash. One for each
file contained.
- Parity will be too fragmented, because we'll have parity blocks
containing the last blocks of many files.
* In "pool" for Windows, and for unique directories a junction to the
directory could be used, avoiding to use symlinks to files.
This allows to overcome the problem of sharing symlinks.
- It would work, in fact it would work to well. The problem is that
Windows will treat the junction as the real directory, like *really*
deleting its content from Explorer pressing "del" and also from the
command line with "rd". Too dangerous.
* When fixing, before overwriting the present file, make a copy of it just in
case that the original file cannot be completely recovered.
We can always open files in read-only mode, if a write is required, we close it,
rename it to with a .bak extension, and rewrite it up to the required size.
The same for symlink if a file with the same name exist or viceversa.
- The idea behind this is to avoid to leave untouched a file if we cannot
restore it completely. But it's debatable what's better in this case.
Anyway, considering the typical use, it's not really relevant.
* In the import list, uses also all the blocks in the array.
But we must cover the case of bad blocks. Likely we can just check the
hash after reading, and in case, skip it, and retry with another copy.
- It will work only for duplicate files. Not really worth do to it.
* Save the content file in compressed .gz format to save space.
- Compression is too slow. Even using the very fast lzo.
$ time lzop -1 < content > content.lzo
real 1m23.014s
user 0m40.822s
sys 0m3.389s
$ time ./gzip -1 < content > content.gz
real 1m47.463s
user 1m23.732s
sys 0m3.290s
$ time ./gzip --huffonly < content > contentH.gz
real 1m51.607s
user 1m30.032s
sys 0m3.245s
Similar command done with snapraid without compression, and involving also decoding
and encoding takes less time.
$ time ./snapraid --test-skip-device --test-skip-self -v -c ./test.conf test-rewrite
real 0m59.087s
user 0m14.164s
sys 0m4.398s
* Recognizes that a file is moved from one disk to another, and if the parity
data doesn't overlap, do not recompute it.
- It's going to work only in RAID5 mode and only in special cases.
* Implements a multithread sync command to share HASH and RAID computations
to different CPUs.
- At now it's questionable if it will result in a performance improvement.
The murmur3 hash, and the RAID5/6 computations are so fast that even a single
thread should be able to do them.
Use the "snapraid -T" comment to see the speed.
* In the repair() function the heuristic to detect if we recovered after the sync,
can be extended to all the previous blocks, because we always proceed in block
order during a sync.
So, if for a block we can detect that we recovered using updated parity data,
also for all the previous blocks this is true.
Anyway, the case where this information could be useful should be present
only if changes are committed after an aborted sync.
- No real advantage on that, beside some speed gain in fix.
The risk would be instead to miss some recovery opportunity.
So, makes sense to have it a little slower but trying any
possible recovery strategy.
snapraid-12.1/acinclude.m4 0000664 0000000 0000000 00000000621 14166610522 0015431 0 ustar 00root root 0000000 0000000 dnl @synopsis AC_CHECK_CC_OPT(flag, ifyes, ifno)
dnl
dnl Shows a message as like "checking whether gcc accepts flag ... no"
dnl and executes ifyes or ifno.
AC_DEFUN([AC_CHECK_CC_OPT],
[
AC_MSG_CHECKING([whether ${CC-cc} accepts $1])
echo 'void f(){}' > conftest.c
if test -z "`${CC-cc} -c $1 conftest.c 2>&1`"; then
AC_MSG_RESULT([yes])
$2
else
AC_MSG_RESULT([no])
$3
fi
rm -f conftest*
])
snapraid-12.1/autogen.sh 0000775 0000000 0000000 00000000277 14166610522 0015250 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
echo "Generating build information using autoreconf"
# All is done by autoreconf
autoreconf -f -i
# Run configure for this platform
echo "Now you are ready to run ./configure"
snapraid-12.1/autover.sh 0000775 0000000 0000000 00000000513 14166610522 0015264 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
if [ -d .git ]; then
# Get version from git tags, removing the 'v' prefix
VERSION=`git describe --match 'v*' 2>/dev/null | sed 's/^v//'`
fi
if [ -f .version ]; then
# Get version from the .version file
VERSION=`cat .version`
fi
if [ -z $VERSION ]; then
VERSION="none"
fi
printf '%s' "$VERSION"
snapraid-12.1/cmdline/ 0000775 0000000 0000000 00000000000 14166610522 0014654 5 ustar 00root root 0000000 0000000 snapraid-12.1/cmdline/check.c 0000664 0000000 0000000 00000211202 14166610522 0016073 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "util.h"
#include "elem.h"
#include "import.h"
#include "search.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
#include "raid/raid.h"
#include "raid/combo.h"
/****************************************************************************/
/* check */
/**
* A block that failed the hash check, or that was deleted.
*/
struct failed_struct {
/**
* If we know for sure that the block is garbage or missing
* and it needs to be recovered and rewritten to the disk.
*/
int is_bad;
/**
* If that we have recovered may be not updated data,
* an old version, or just garbage.
*
* Essentially, it means that we are not sure what we have recovered
* is really correct. It's just our best guess.
*
* These "recovered" block are also written to the disk if the block is marked as ::is_bad.
* But these files are marked also as FILE_IS_DAMAGED, and then renamed to .unrecoverable.
*
* Note that this could happen only for CHG blocks.
*/
int is_outofdate;
unsigned index; /**< Index of the failed block. */
struct snapraid_block* block; /**< The failed block */
struct snapraid_disk* disk; /**< The failed disk. */
struct snapraid_file* file; /**< The failed file. 0 for DELETED block. */
block_off_t file_pos; /**< Offset inside the file */
struct snapraid_handle* handle; /**< The handle containing the failed block, or 0 for a DELETED block */
};
/**
* Check if a block hash matches the specified buffer.
* Return ==0 if equal
*/
static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_block* block, unsigned pos_size, unsigned char* buffer, unsigned char* buffer_zero)
{
unsigned char hash[HASH_MAX];
/* now compute the hash of the valid part */
if (rehash) {
memhash(state->prevhash, state->prevhashseed, hash, buffer, pos_size);
} else {
memhash(state->hash, state->hashseed, hash, buffer, pos_size);
}
/* compare the hash */
if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
return -1;
}
/* compare to the end of the block */
if (pos_size < state->block_size) {
if (memcmp(buffer + pos_size, buffer_zero + pos_size, state->block_size - pos_size) != 0) {
return -1;
}
}
return 0;
}
/**
* Check if the hash of all the failed block we are expecting to recover are now matching.
*/
static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, void** buffer, void* buffer_zero)
{
unsigned j;
int hash_checked;
hash_checked = 0; /* keep track if we check at least one block */
/* check if the recovered blocks are OK */
for (j = 0; j < failed_count; ++j) {
/* if we are expected to recover this block */
if (!failed[failed_map[j]].is_outofdate
/* if the block has a hash to check */
&& block_has_updated_hash(failed[failed_map[j]].block)
) {
/* if a hash doesn't match, fail the check */
unsigned pos_size = file_block_size(failed[failed_map[j]].file, failed[failed_map[j]].file_pos, state->block_size);
if (blockcmp(state, rehash, failed[failed_map[j]].block, pos_size, buffer[failed[failed_map[j]].index], buffer_zero) != 0) {
log_tag("hash_error: Hash mismatch on entry %u\n", failed_map[j]);
return 0;
}
hash_checked = 1;
}
}
/* if nothing checked, we reject it */
/* note that we are excluding this case at upper level */
/* but checking again doesn't hurt */
if (!hash_checked) {
/* LCOV_EXCL_START */
return 0;
/* LCOV_EXCL_STOP */
}
/* if we checked something, and no block failed the check */
/* recompute all the redundancy information */
raid_gen(diskmax, state->level, state->block_size, buffer);
return 1;
}
/**
* Check if specified parity is now matching with a recomputed one.
*/
static int is_parity_matching(struct snapraid_state* state, unsigned diskmax, unsigned i, void** buffer, void** buffer_recov)
{
/* recompute parity, note that we don't need parity over i */
raid_gen(diskmax, i + 1, state->block_size, buffer);
/* if the recovered parity block matches */
if (memcmp(buffer[diskmax + i], buffer_recov[i], state->block_size) == 0) {
/* recompute all the redundancy information */
raid_gen(diskmax, state->level, state->block_size, buffer);
return 1;
}
return 0;
}
/**
* Repair errors.
* Return <0 if failure for missing strategy, >0 if data is wrong and we cannot rebuild correctly, 0 on success.
* If success, the parity are computed in the buffer variable.
*/
static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, void** buffer, void** buffer_recov, void* buffer_zero)
{
unsigned i, n;
int error;
int has_hash;
int id[LEV_MAX];
int ip[LEV_MAX];
/* no fix required, already checked at higher level, but just to be sure */
if (failed_count == 0) {
/* LCOV_EXCL_START */
/* recompute only the parity */
raid_gen(diskmax, state->level, state->block_size, buffer);
return 0;
/* LCOV_EXCL_STOP */
}
n = state->level;
error = 0;
/* setup vector of failed disk indexes */
for (i = 0; i < failed_count; ++i)
id[i] = failed[failed_map[i]].index;
/* check if there is at least a failed block that can be checked for correctness using the hash */
/* if there isn't, we have to sacrifice a parity block to check that the result is correct */
has_hash = 0;
for (i = 0; i < failed_count; ++i) {
/* if we are expected to recover this block */
if (!failed[failed_map[i]].is_outofdate
/* if the block has a hash to check */
&& block_has_updated_hash(failed[failed_map[i]].block)
)
has_hash = 1;
}
/* if we don't have a hash, but we have an extra parity */
/* (strictly-less failures than number of parities) */
if (!has_hash && failed_count < n) {
/* number of parity to use, one more to check the recovering */
unsigned r = failed_count + 1;
/* all combinations (r of n) parities */
combination_first(r, n, ip);
do {
/* if a parity is missing, do nothing */
for (i = 0; i < r; ++i) {
if (buffer_recov[ip[i]] == 0)
break;
}
if (i != r)
continue;
/* copy the parities to use, one less because the last is used for checking */
for (i = 0; i < r - 1; ++i)
memcpy(buffer[diskmax + ip[i]], buffer_recov[ip[i]], state->block_size);
/* recover using one less parity, the ip[r-1] one */
raid_data(r - 1, id, ip, diskmax, state->block_size, buffer);
/* use the remaining ip[r-1] parity to check the result */
if (is_parity_matching(state, diskmax, ip[r - 1], buffer, buffer_recov))
return 0;
/* log */
log_tag("parity_error:%u:", pos);
for (i = 0; i < r; ++i) {
if (i != 0)
log_tag("/");
log_tag("%s", lev_config_name(ip[i]));
}
log_tag(":parity: Parity mismatch\n");
++error;
} while (combination_next(r, n, ip));
}
/* if we have a hash, and enough parities */
/* (less-or-equal failures than number of parities) */
if (has_hash && failed_count <= n) {
/* number of parities to use equal at the number of failures */
unsigned r = failed_count;
/* all combinations (r of n) parities */
combination_first(r, n, ip);
do {
/* if a parity is missing, do nothing */
for (i = 0; i < r; ++i) {
if (buffer_recov[ip[i]] == 0)
break;
}
if (i != r)
continue;
/* copy the parities to use */
for (i = 0; i < r; ++i)
memcpy(buffer[diskmax + ip[i]], buffer_recov[ip[i]], state->block_size);
/* recover */
raid_data(r, id, ip, diskmax, state->block_size, buffer);
/* use the hash to check the result */
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
/* log */
log_tag("parity_error:%u:", pos);
for (i = 0; i < r; ++i) {
if (i != 0)
log_tag("/");
log_tag("%s", lev_config_name(ip[i]));
}
log_tag(":hash: Hash mismatch\n");
++error;
} while (combination_next(r, n, ip));
}
/* return the number of failed attempts, or -1 if no strategy */
if (error)
return error;
log_tag("strategy_error:%u: No strategy to recover from %u failures with %u parity %s hash\n",
pos, failed_count, n, has_hash ? "with" : "without");
return -1;
}
static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, void** buffer, void** buffer_recov, void* buffer_zero)
{
int ret;
int error;
unsigned j;
int n;
int something_to_recover;
int something_unsynced;
char esc_buffer[ESC_MAX];
error = 0;
/* if nothing failed, just recompute the parity */
if (failed_count == 0) {
raid_gen(diskmax, state->level, state->block_size, buffer);
return 0;
}
/* logs the status */
for (j = 0; j < failed_count; ++j) {
const char* desc;
const char* hash;
const char* data;
struct snapraid_block* block = failed[j].block;
unsigned block_state = block_state_get(block);
switch (block_state) {
case BLOCK_STATE_DELETED : desc = "delete"; break;
case BLOCK_STATE_CHG : desc = "change"; break;
case BLOCK_STATE_REP : desc = "replace"; break;
case BLOCK_STATE_BLK : desc = "block"; break;
/* LCOV_EXCL_START */
default : desc = "unknown"; break;
/* LCOV_EXCL_STOP */
}
if (hash_is_invalid(block->hash)) {
hash = "lost";
} else if (hash_is_zero(block->hash)) {
hash = "zero";
} else {
hash = "known";
}
if (failed[j].is_bad)
data = "bad";
else
data = "good";
if (failed[j].file) {
struct snapraid_disk* disk = failed[j].disk;
struct snapraid_file* file = failed[j].file;
block_off_t file_pos = failed[j].file_pos;
log_tag("entry:%u:%s:%s:%s:%s:%s:%u:\n", j, desc, hash, data, disk->name, esc_tag(file->sub, esc_buffer), file_pos);
} else {
log_tag("entry:%u:%s:%s:%s:\n", j, desc, hash, data);
}
}
/* Here we have to try two different strategies to recover, because in case the 'sync' */
/* process is aborted, we don't know if the parity data is really updated just like after 'sync', */
/* or if it still represents the state before the 'sync'. */
/* Note that if the 'sync' ends normally, we don't have any DELETED, REP and CHG blocks */
/* and the two strategies are identical */
/* As first, we assume that the parity IS updated for the current state */
/* and that we are going to recover the state after the last 'sync'. */
/* In this case, parity contains info from BLK, REP and CHG blocks, */
/* but not for DELETED. */
/* We need to put in the recovering process only the bad blocks, because all the */
/* others already contains the correct data read from disk, and the parity is correctly computed for them. */
/* We are interested to recover BLK, REP and CHG blocks if they are marked as bad, */
/* but we are not interested in DELETED ones. */
n = 0;
something_to_recover = 0; /* keep track if there is at least one block to fix */
for (j = 0; j < failed_count; ++j) {
if (failed[j].is_bad) {
unsigned block_state = block_state_get(failed[j].block);
assert(block_state != BLOCK_STATE_DELETED); /* we cannot have bad DELETED blocks */
/* if we have the hash for it */
if ((block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP)
/* try to fetch the block using the known hash */
&& (state_import_fetch(state, rehash, failed[j].block, buffer[failed[j].index]) == 0
|| state_search_fetch(state, rehash, failed[j].file, failed[j].file_pos, failed[j].block, buffer[failed[j].index]) == 0)
) {
/* we already have corrected it! */
log_tag("hash_import: Fixed entry %u\n", j);
} else {
/* otherwise try to recover it */
failed_map[n] = j;
++n;
/* we have something to try to recover */
something_to_recover = 1;
}
}
}
/* if nothing to fix */
if (!something_to_recover) {
log_tag("recover_sync:%u:%u: Skipped for already recovered\n", pos, n);
/* recompute only the parity */
raid_gen(diskmax, state->level, state->block_size, buffer);
return 0;
}
ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
if (ret == 0) {
/* reprocess the CHG blocks, for which we don't have a hash to check */
/* if they were BAD we have to use some heuristics to ensure that we have recovered */
/* the state after the sync. If unsure, we assume the worst case */
for (j = 0; j < failed_count; ++j) {
/* we take care only of BAD blocks we have to write back */
if (failed[j].is_bad) {
unsigned block_state = block_state_get(failed[j].block);
/* BLK and REP blocks are always OK, because at this point */
/* we have already checked their hash */
if (block_state != BLOCK_STATE_CHG) {
assert(block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP);
continue;
}
/* for CHG blocks we have to 'guess' if they are correct or not */
/* if the hash is invalid we cannot check the result */
/* this could happen if we have lost this information */
/* after an aborted sync */
if (hash_is_invalid(failed[j].block->hash)) {
/* it may contain garbage */
failed[j].is_outofdate = 1;
log_tag("hash_unknown: Unknown hash on entry %u\n", j);
} else if (hash_is_zero(failed[j].block->hash)) {
/* if the block is not filled with 0, we are sure to have */
/* restored it to the state after the 'sync' */
/* instead, if the block is filled with 0, it could be either that the */
/* block after the sync is really filled by 0, or that */
/* we restored the block before the 'sync'. */
if (memcmp(buffer[failed[j].index], buffer_zero, state->block_size) == 0) {
/* it may contain garbage */
failed[j].is_outofdate = 1;
log_tag("hash_unknown: Maybe old zero on entry %u\n", j);
}
} else {
/* if the hash is different than the previous one, we are sure to have */
/* restored it to the state after the 'sync' */
/* instead, if the hash matches, it could be either that the */
/* block after the sync has this hash, or that */
/* we restored the block before the 'sync'. */
unsigned pos_size = file_block_size(failed[j].file, failed[j].file_pos, state->block_size);
if (blockcmp(state, rehash, failed[j].block, pos_size, buffer[failed[j].index], buffer_zero) == 0) {
/* it may contain garbage */
failed[j].is_outofdate = 1;
log_tag("hash_unknown: Maybe old data on entry %u\n", j);
}
}
}
}
return 0;
}
if (ret > 0)
error += ret;
if (ret < 0)
log_tag("recover_sync:%u:%u: Failed with no attempts\n", pos, n);
else
log_tag("recover_sync:%u:%u: Failed with %d attempts\n", pos, n, ret);
/* Now assume that the parity IS NOT updated at the current state, */
/* but still represent the state before the last 'sync' process. */
/* In this case, parity contains info from BLK, REP (old version), CHG (old version) and DELETED blocks, */
/* but not for REP (new version) and CHG (new version). */
/* We are interested to recover BLK ones marked as bad, */
/* but we are not interested to recover CHG (new version) and REP (new version) blocks, */
/* even if marked as bad, because we don't have parity for them and it's just impossible, */
/* and we are not interested to recover DELETED ones. */
n = 0;
something_to_recover = 0; /* keep track if there is at least one block to fix */
something_unsynced = 0; /* keep track if we have some unsynced info to process */
for (j = 0; j < failed_count; ++j) {
unsigned block_state = block_state_get(failed[j].block);
if (block_state == BLOCK_STATE_DELETED
|| block_state == BLOCK_STATE_CHG
|| block_state == BLOCK_STATE_REP
) {
/* If the block is CHG, REP or DELETED, we don't have the original content of block, */
/* and we must try to recover it. */
/* This apply to CHG and REP blocks even if they are not marked bad, */
/* because the parity is computed with old content, and not with the new one. */
/* Note that this recovering is done just to make possible to recover any other BLK one, */
/* we are not really interested in DELETED, CHG (old version) and REP (old version). */
something_unsynced = 1;
if (block_state == BLOCK_STATE_CHG
&& hash_is_zero(failed[j].block->hash)
) {
/* If the block was a ZERO block, restore it to the original 0 as before the 'sync' */
/* We do this to just allow recovering of other BLK ones */
memset(buffer[failed[j].index], 0, state->block_size);
/* note that from now the buffer is definitively lost */
/* we can do this only because it's the last retry of recovering */
/* try to fetch the old block using the old hash for CHG and DELETED blocks */
} else if ((block_state == BLOCK_STATE_CHG || block_state == BLOCK_STATE_DELETED)
&& hash_is_unique(failed[j].block->hash)
&& state_import_fetch(state, rehash, failed[j].block, buffer[failed[j].index]) == 0) {
/* note that from now the buffer is definitively lost */
/* we can do this only because it's the last retry of recovering */
} else {
/* otherwise try to recover it */
failed_map[n] = j;
++n;
/* note that we don't set something_to_recover, because we are */
/* not really interested to recover *only* old blocks. */
}
/* avoid to use the hash of this block to verify the recovering */
/* this applies to REP blocks because we are going to recover the old state */
/* and the REP hash represent the new one */
/* it also applies to CHG and DELETE blocks because we want to have */
/* a successful recovering only if a BLK one is matching */
failed[j].is_outofdate = 1;
} else if (failed[j].is_bad) {
/* If the block is bad we don't know its content, and we try to recover it */
/* At this point, we can have only BLK ones */
assert(block_state == BLOCK_STATE_BLK);
/* we have something we are interested to recover */
something_to_recover = 1;
/* we try to recover it */
failed_map[n] = j;
++n;
}
}
/* if nothing to fix, we just don't try */
/* if nothing unsynced we also don't retry, because it's the same try as before */
if (something_to_recover && something_unsynced) {
ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
if (ret == 0) {
/* reprocess the REP and CHG blocks, for which we have recovered and old state */
/* that we don't want to save into disk */
/* we have already marked them, but we redo it for logging */
for (j = 0; j < failed_count; ++j) {
/* we take care only of BAD blocks we have to write back */
if (failed[j].is_bad) {
unsigned block_state = block_state_get(failed[j].block);
if (block_state == BLOCK_STATE_CHG
|| block_state == BLOCK_STATE_REP
) {
/* mark that we have restored an old state */
/* and we don't want to write it to the disk */
failed[j].is_outofdate = 1;
log_tag("hash_unknown: Surely old data on entry %u\n", j);
}
}
}
return 0;
}
if (ret > 0)
error += ret;
if (ret < 0)
log_tag("recover_unsync:%u:%u: Failed with no attempts\n", pos, n);
else
log_tag("recover_unsync:%u:%u: Failed with %d attempts\n", pos, n, ret);
} else {
log_tag("recover_unsync:%u:%u: Skipped for%s%s\n", pos, n,
!something_to_recover ? " nothing to recover" : "",
!something_unsynced ? " nothing unsynced" : ""
);
}
/* return the number of failed attempts, or -1 if no strategy */
if (error)
return error;
else
return -1;
}
/**
* Post process all the files at the specified block index ::i.
* For each file, if we are at the last block, closes it,
* adjust the timestamp, and print the result.
*
* This works only if the whole file is processed, including its last block.
* This doesn't always happen, like with an explicit end block.
*
* In such case, the check/fix command won't report any information of the
* files partially checked.
*/
static int file_post(struct snapraid_state* state, int fix, unsigned i, struct snapraid_handle* handle, unsigned diskmax)
{
unsigned j;
int ret;
char esc_buffer[ESC_MAX];
char esc_buffer_alt[ESC_MAX];
/* for all the files print the final status, and does the final time fix */
/* we also ensure to close files after processing the last block */
for (j = 0; j < diskmax; ++j) {
struct snapraid_block* block;
struct snapraid_disk* disk;
struct snapraid_file* collide_file;
struct snapraid_file* file;
block_off_t file_pos;
uint64_t inode;
disk = handle[j].disk;
if (!disk) {
/* if no disk, nothing to do */
continue;
}
block = fs_par2block_find(disk, i);
if (!block_has_file(block)) {
/* if no file, nothing to do */
continue;
}
file = fs_par2file_get(disk, i, &file_pos);
/* if it isn't the last block in the file */
if (!file_block_is_last(file, file_pos)) {
/* nothing to do */
continue;
}
/* if the file is excluded, we have nothing to adjust as the file is never written */
if (file_flag_has(file, FILE_IS_EXCLUDED)
|| (state->opt.syncedonly && file_flag_has(file, FILE_IS_UNSYNCED))) {
/* nothing to do, but close the file */
goto close_and_continue;
}
/* finish the fix process if it's the last block of the files */
if (fix) {
/* mark that we finished with this file */
/* to identify later any NOT finished ones */
file_flag_set(file, FILE_IS_FINISHED);
/* if the file is damaged, meaning that a fix failed */
if (file_flag_has(file, FILE_IS_DAMAGED)) {
/* rename it to .unrecoverable */
char path[PATH_MAX];
char path_to[PATH_MAX];
pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
pathprint(path_to, sizeof(path_to), "%s%s.unrecoverable", disk->dir, file->sub);
/* ensure to close the file before renaming */
if (handle[j].file == file) {
ret = handle_close(&handle[j]);
if (ret != 0) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
return -1;
/* LCOV_EXCL_STOP */
}
}
ret = rename(path, path_to);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error renaming '%s' to '%s'. %s.\n", path, path_to, strerror(errno));
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
return -1;
/* LCOV_EXCL_STOP */
}
log_tag("status:unrecoverable:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
msg_info("unrecoverable %s\n", fmt_term(disk, file->sub, esc_buffer));
/* and do not set the time if damaged */
goto close_and_continue;
}
/* if the file is not fixed, meaning that it is untouched */
if (!file_flag_has(file, FILE_IS_FIXED)) {
/* nothing to do, but close the file */
goto close_and_continue;
}
/* if the file is closed or different than the one expected, reopen it */
/* a different open file could happen when filtering for bad blocks */
if (handle[j].file != file) {
/* close a potential different file */
ret = handle_close(&handle[j]);
if (ret != 0) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc_tag(handle[j].file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
return -1;
/* LCOV_EXCL_STOP */
}
/* reopen it as readonly, as to set the mtime readonly access it's enough */
/* we know that the file exists because it has the FILE_IS_FIXED tag */
ret = handle_open(&handle[j], file, state->file_mode, log_error, 0);
if (ret != 0) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Open error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
return -1;
/* LCOV_EXCL_STOP */
}
}
log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
msg_info("recovered %s\n", fmt_term(disk, file->sub, esc_buffer));
inode = handle[j].st.st_ino;
/* search for the corresponding inode */
collide_file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode));
/* if the inode is already in the database and it refers at a different file name, */
/* we can fix the file time ONLY if the time and size allow to differentiate */
/* between the two files */
/* for example, suppose we delete a bunch of files with all the same size and time, */
/* when recreating them the inodes may be reused in a different order, */
/* and at the next sync some files may have matching inode/size/time even if different name */
/* not allowing sync to detect that the file is changed and not renamed */
if (!collide_file /* if not in the database, there is no collision */
|| strcmp(collide_file->sub, file->sub) == 0 /* if the name is the same, it's the right collision */
|| collide_file->size != file->size /* if the size is different, the collision is identified */
|| collide_file->mtime_sec != file->mtime_sec /* if the mtime is different, the collision is identified */
|| collide_file->mtime_nsec != file->mtime_nsec /* same for mtime_nsec */
) {
/* set the original modification time */
ret = handle_utime(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
/* mark the file as damaged */
file_flag_set(file, FILE_IS_DAMAGED);
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
return -1;
/* LCOV_EXCL_STOP */
}
} else {
log_tag("collision:%s:%s:%s: Not setting modification time to avoid inode collision\n", disk->name, esc_tag(file->sub, esc_buffer), esc_tag(collide_file->sub, esc_buffer_alt));
}
} else {
/* we are not fixing, but only checking */
/* print just the final status */
if (file_flag_has(file, FILE_IS_DAMAGED)) {
if (state->opt.auditonly) {
log_tag("status:damaged:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
msg_info("damaged %s\n", fmt_term(disk, file->sub, esc_buffer));
} else {
log_tag("status:unrecoverable:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
msg_info("unrecoverable %s\n", fmt_term(disk, file->sub, esc_buffer));
}
} else if (file_flag_has(file, FILE_IS_FIXED)) {
log_tag("status:recoverable:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
msg_info("recoverable %s\n", fmt_term(disk, file->sub, esc_buffer));
} else {
/* we don't use msg_verbose() because it also goes into the log */
if (msg_level >= MSG_VERBOSE) {
log_tag("status:correct:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
msg_info("correct %s\n", fmt_term(disk, file->sub, esc_buffer));
}
}
}
close_and_continue:
/* if the opened file is the correct one, close it */
/* in case of excluded and fragmented files it's possible */
/* that the opened file is not the current one */
if (handle[j].file == file) {
/* ensure to close the file just after finishing with it */
/* to avoid to keep it open without any possible use */
ret = handle_close(&handle[j]);
if (ret != 0) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
return -1;
/* LCOV_EXCL_STOP */
}
}
}
return 0;
}
/**
* Check if we have to process the specified block index ::i.
*/
static int block_is_enabled(struct snapraid_state* state, block_off_t i, struct snapraid_handle* handle, unsigned diskmax)
{
unsigned j;
unsigned l;
/* filter for bad blocks */
if (state->opt.badblockonly) {
snapraid_info info;
/* get block specific info */
info = info_get(&state->infoarr, i);
/*
* Filter specifically only for bad blocks
*/
return info_get_bad(info);
}
/* filter for the parity */
if (state->opt.badfileonly) {
snapraid_info info;
/* get block specific info */
info = info_get(&state->infoarr, i);
/*
* If the block is bad, it has to be processed
*
* This is not necessary in normal cases because if a block is bad,
* it necessary needs to have a file related to it, and files with
* bad blocks are fully included.
*
* But some files may be excluded by additional filter options,
* so it's not always true, and this ensures to always check all
* the bad blocks.
*/
if (info_get_bad(info))
return 1;
} else {
/* if a parity is not excluded, include all blocks, even unused ones */
for (l = 0; l < state->level; ++l) {
if (!state->parity[l].is_excluded_by_filter) {
return 1;
}
}
}
/* filter for the files */
for (j = 0; j < diskmax; ++j) {
struct snapraid_block* block;
/* if no disk, nothing to check */
if (!handle[j].disk)
continue;
block = fs_par2block_find(handle[j].disk, i);
/* try to recover all files, even the ones without hash */
/* because in some cases we can recover also them */
if (block_has_file(block)) {
struct snapraid_file* file = fs_par2file_get(handle[j].disk, i, 0);
if (!file_flag_has(file, FILE_IS_EXCLUDED)) { /* only if the file is not filtered out */
return 1;
}
}
}
return 0;
}
static int state_check_process(struct snapraid_state* state, int fix, struct snapraid_parity_handle** parity, block_off_t blockstart, block_off_t blockmax)
{
struct snapraid_handle* handle;
unsigned diskmax;
block_off_t i;
unsigned j;
void* buffer_alloc;
void** buffer;
unsigned buffermax;
int ret;
data_off_t countsize;
block_off_t countpos;
block_off_t countmax;
unsigned error;
unsigned unrecoverable_error;
unsigned recovered_error;
struct failed_struct* failed;
unsigned* failed_map;
unsigned l;
char esc_buffer[ESC_MAX];
char esc_buffer_alt[ESC_MAX];
bit_vect_t* block_enabled;
handle = handle_mapping(state, &diskmax);
/* we need 1 * data + 2 * parity + 1 * zero */
buffermax = diskmax + 2 * state->level + 1;
buffer = malloc_nofail_vector_align(diskmax, buffermax, state->block_size, &buffer_alloc);
if (!state->opt.skip_self)
mtest_vector(buffermax, state->block_size, buffer);
/* fill up the zero buffer */
memset(buffer[buffermax - 1], 0, state->block_size);
raid_zero(buffer[buffermax - 1]);
failed = malloc_nofail(diskmax * sizeof(struct failed_struct));
failed_map = malloc_nofail(diskmax * sizeof(unsigned));
error = 0;
unrecoverable_error = 0;
recovered_error = 0;
msg_progress("Selecting...\n");
/* first count the number of blocks to process */
countmax = 0;
block_enabled = calloc_nofail(1, bit_vect_size(blockmax)); /* preinitialize to 0 */
for (i = blockstart; i < blockmax; ++i) {
if (!block_is_enabled(state, i, handle, diskmax))
continue;
bit_vect_set(block_enabled, i);
++countmax;
}
if (fix)
msg_progress("Fixing...\n");
else if (!state->opt.auditonly)
msg_progress("Checking...\n");
else
msg_progress("Hashing...\n");
/* check all the blocks in files */
countsize = 0;
countpos = 0;
state_progress_begin(state, blockstart, blockmax, countmax);
for (i = blockstart; i < blockmax; ++i) {
unsigned failed_count;
int valid_parity;
int used_parity;
snapraid_info info;
int rehash;
if (!bit_vect_test(block_enabled, i)) {
/* continue with the next block */
continue;
}
/* If we have valid parity, and it makes sense to check its content. */
/* If we already know that the parity is invalid, we just read the file */
/* but we don't report parity errors */
/* Note that with auditonly, we anyway skip the full parity check, */
/* because we also don't read it at all */
valid_parity = 1;
/* If the parity is used by at least one file */
used_parity = 0;
/* keep track of the number of failed blocks */
failed_count = 0;
/* get block specific info */
info = info_get(&state->infoarr, i);
/* if we have to use the old hash */
rehash = info_get_rehash(info);
/* for each disk, process the block */
for (j = 0; j < diskmax; ++j) {
int read_size;
unsigned char hash[HASH_MAX];
struct snapraid_disk* disk;
struct snapraid_block* block;
struct snapraid_file* file;
block_off_t file_pos;
unsigned block_state;
/* if the disk position is not used */
disk = handle[j].disk;
if (!disk) {
/* use an empty block */
memset(buffer[j], 0, state->block_size);
continue;
}
/* if the disk block is not used */
block = fs_par2block_find(disk, i);
if (block == BLOCK_NULL) {
/* use an empty block */
memset(buffer[j], 0, state->block_size);
continue;
}
/* get the state of the block */
block_state = block_state_get(block);
/* if the parity is not valid */
if (block_has_invalid_parity(block)) {
/* mark the parity as invalid, and don't try to check/fix it */
/* because it will be recomputed at the next sync */
valid_parity = 0;
/* follow */
}
/* if the block is DELETED */
if (block_state == BLOCK_STATE_DELETED) {
/* use an empty block */
memset(buffer[j], 0, state->block_size);
/* store it in the failed set, because potentially */
/* the parity may be still computed with the previous content */
failed[failed_count].is_bad = 0; /* note that is_bad==0 <=> file==0 */
failed[failed_count].is_outofdate = 0;
failed[failed_count].index = j;
failed[failed_count].block = block;
failed[failed_count].disk = disk;
failed[failed_count].file = 0;
failed[failed_count].file_pos = 0;
failed[failed_count].handle = 0;
++failed_count;
continue;
}
/* here we are sure that the parity is used by a file */
used_parity = 1;
/* get the file of this block */
file = fs_par2file_get(disk, i, &file_pos);
/* if we are only hashing, we can skip excluded files and don't even read them */
if (state->opt.auditonly && file_flag_has(file, FILE_IS_EXCLUDED)) {
/* use an empty block */
/* in true, this is unnecessary, because we are not checking any parity */
/* but we keep it for completeness */
memset(buffer[j], 0, state->block_size);
continue;
}
/* if the file is closed or different than the current one */
if (handle[j].file == 0 || handle[j].file != file) {
/* close the old one, if any */
ret = handle_close(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc_tag(handle[j].file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
log_fatal("Stopping at block %u\n", i);
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* if fixing, and the file is not excluded, we must open for writing */
if (fix && !file_flag_has(file, FILE_IS_EXCLUDED)) {
/* if fixing, create the file, open for writing and resize if required */
ret = handle_create(&handle[j], file, state->file_mode);
if (ret == -1) {
/* LCOV_EXCL_START */
if (errno == EACCES) {
log_fatal("WARNING! Please give write permission to the file.\n");
} else {
log_fatal("DANGER! Without a working data disk, it isn't possible to fix errors on it.\n");
}
log_fatal("Stopping at block %u\n", i);
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* check if the file was just created */
if (handle[j].created != 0) {
/* if fragmented, it may be reopened, so remember that the file */
/* was originally missing */
file_flag_set(file, FILE_IS_CREATED);
}
} else {
/* open the file only for reading */
if (!file_flag_has(file, FILE_IS_MISSING))
ret = handle_open(&handle[j], file, state->file_mode,
log_error, state->opt.expected_missing ? log_expected : 0);
else
ret = -1; /* if the file is missing, we cannot open it */
if (ret == -1) {
/* save the failed block for the check/fix */
failed[failed_count].is_bad = 1;
failed[failed_count].is_outofdate = 0;
failed[failed_count].index = j;
failed[failed_count].block = block;
failed[failed_count].disk = disk;
failed[failed_count].file = file;
failed[failed_count].file_pos = file_pos;
failed[failed_count].handle = &handle[j];
++failed_count;
log_tag("error:%u:%s:%s: Open error at position %u\n", i, disk->name, esc_tag(file->sub, esc_buffer), file_pos);
++error;
/* mark the file as missing, to avoid to retry to open it again */
/* note that this can be done only if we are not fixing it */
/* otherwise, it could be recreated */
file_flag_set(file, FILE_IS_MISSING);
continue;
}
}
/* if it's the first open, and not excluded */
if (!file_flag_has(file, FILE_IS_OPENED)
&& !file_flag_has(file, FILE_IS_EXCLUDED)) {
/* check if the file is changed */
if (handle[j].st.st_size != file->size
|| handle[j].st.st_mtime != file->mtime_sec
|| STAT_NSEC(&handle[j].st) != file->mtime_nsec
/* don't check the inode to support file-system without persistent inodes */
) {
/* report that the file is not synced */
file_flag_set(file, FILE_IS_UNSYNCED);
}
}
/* if it's the first open, and not excluded and larger */
if (!file_flag_has(file, FILE_IS_OPENED)
&& !file_flag_has(file, FILE_IS_EXCLUDED)
&& !(state->opt.syncedonly && file_flag_has(file, FILE_IS_UNSYNCED))
&& handle[j].st.st_size > file->size
) {
log_error("File '%s' is larger than expected.\n", handle[j].path);
log_tag("error:%u:%s:%s: Size error\n", i, disk->name, esc_tag(file->sub, esc_buffer));
++error;
if (fix) {
ret = handle_truncate(&handle[j], file);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Unexpected truncate error in a data disk, it isn't possible to fix.\n");
log_fatal("Stopping at block %u\n", i);
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
log_tag("fixed:%u:%s:%s: Fixed size\n", i, disk->name, esc_tag(file->sub, esc_buffer));
++recovered_error;
}
}
/* mark the file as opened at least one time */
/* this is used to avoid to check the unsynced and size */
/* more than one time, in case the file is reopened later */
file_flag_set(file, FILE_IS_OPENED);
}
/* read from the file */
read_size = handle_read(&handle[j], file_pos, buffer[j], state->block_size,
log_error, state->opt.expected_missing ? log_expected : 0);
if (read_size == -1) {
/* save the failed block for the check/fix */
failed[failed_count].is_bad = 1; /* it's bad because we cannot read it */
failed[failed_count].is_outofdate = 0;
failed[failed_count].index = j;
failed[failed_count].block = block;
failed[failed_count].disk = disk;
failed[failed_count].file = file;
failed[failed_count].file_pos = file_pos;
failed[failed_count].handle = &handle[j];
++failed_count;
log_tag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc_tag(file->sub, esc_buffer), file_pos);
++error;
continue;
}
countsize += read_size;
/* always insert CHG blocks, the repair functions needs all of them */
/* because the parity may be still referring at the old state */
/* and the repair must be aware of it */
if (block_state == BLOCK_STATE_CHG) {
/* we DO NOT mark them as bad to avoid to overwrite them with wrong data. */
/* if we don't have a hash, we always assume the first read of the block correct. */
failed[failed_count].is_bad = 0; /* we assume the CHG block correct */
failed[failed_count].is_outofdate = 0;
failed[failed_count].index = j;
failed[failed_count].block = block;
failed[failed_count].disk = disk;
failed[failed_count].file = file;
failed[failed_count].file_pos = file_pos;
failed[failed_count].handle = &handle[j];
++failed_count;
continue;
}
assert(block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP);
/* compute the hash of the block just read */
if (rehash) {
memhash(state->prevhash, state->prevhashseed, hash, buffer[j], read_size);
} else {
memhash(state->hash, state->hashseed, hash, buffer[j], read_size);
}
/* compare the hash */
if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
unsigned diff = memdiff(hash, block->hash, BLOCK_HASH_SIZE);
/* save the failed block for the check/fix */
failed[failed_count].is_bad = 1; /* it's bad because the hash doesn't match */
failed[failed_count].is_outofdate = 0;
failed[failed_count].index = j;
failed[failed_count].block = block;
failed[failed_count].disk = disk;
failed[failed_count].file = file;
failed[failed_count].file_pos = file_pos;
failed[failed_count].handle = &handle[j];
++failed_count;
log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u/%u\n", i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, diff, BLOCK_HASH_SIZE * 8);
++error;
continue;
}
/* always insert REP blocks, the repair functions needs all of them */
/* because the parity may be still referring at the old state */
/* and the repair must be aware of it */
if (block_state == BLOCK_STATE_REP) {
failed[failed_count].is_bad = 0; /* it's not bad */
failed[failed_count].is_outofdate = 0;
failed[failed_count].index = j;
failed[failed_count].block = block;
failed[failed_count].disk = disk;
failed[failed_count].file = file;
failed[failed_count].file_pos = file_pos;
failed[failed_count].handle = &handle[j];
++failed_count;
continue;
}
}
/* now read and check the parity if requested */
if (!state->opt.auditonly) {
void* buffer_recov[LEV_MAX];
void* buffer_zero;
/* buffers for parity read and not computed */
for (l = 0; l < state->level; ++l)
buffer_recov[l] = buffer[diskmax + state->level + l];
for (; l < LEV_MAX; ++l)
buffer_recov[l] = 0;
/* the zero buffer is the last one */
buffer_zero = buffer[buffermax - 1];
/* read the parity */
for (l = 0; l < state->level; ++l) {
if (parity[l]) {
ret = parity_read(parity[l], i, buffer_recov[l], state->block_size, log_error);
if (ret == -1) {
buffer_recov[l] = 0; /* no parity to use */
log_tag("parity_error:%u:%s: Read error\n", i, lev_config_name(l));
++error;
}
} else {
buffer_recov[l] = 0;
}
}
/* try all the recovering strategies */
ret = repair(state, rehash, i, diskmax, failed, failed_map, failed_count, buffer, buffer_recov, buffer_zero);
if (ret != 0) {
/* increment the number of errors */
if (ret > 0)
error += ret;
++unrecoverable_error;
/* print a list of all the errors in files */
for (j = 0; j < failed_count; ++j) {
if (failed[j].is_bad)
log_tag("unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].disk->name, esc_tag(failed[j].file->sub, esc_buffer), failed[j].file_pos);
}
/* keep track of damaged files */
for (j = 0; j < failed_count; ++j) {
if (failed[j].is_bad)
file_flag_set(failed[j].file, FILE_IS_DAMAGED);
}
} else {
/* now counts partial recovers */
/* note that this could happen only when we have an incomplete 'sync' */
/* and that we have recovered is the state before the 'sync' */
int partial_recover_error = 0;
/* print a list of all the errors in files */
for (j = 0; j < failed_count; ++j) {
if (failed[j].is_bad && failed[j].is_outofdate) {
++partial_recover_error;
log_tag("unrecoverable:%u:%s:%s: Unrecoverable unsynced error at position %u\n", i, failed[j].disk->name, esc_tag(failed[j].file->sub, esc_buffer), failed[j].file_pos);
}
}
if (partial_recover_error != 0) {
error += partial_recover_error;
++unrecoverable_error;
}
/*
* Check parities, but only if all the blocks have it computed and it's used.
*
* If you check/fix after a partial sync, it's OK to have parity errors
* on the blocks with invalid parity and doesn't make sense to try to fix it.
*
* It's also OK to have data errors on unused parity, because sync doesn't
* update it.
*/
if (used_parity && valid_parity) {
/* check the parity */
for (l = 0; l < state->level; ++l) {
if (buffer_recov[l] != 0 && memcmp(buffer_recov[l], buffer[diskmax + l], state->block_size) != 0) {
unsigned diff = memdiff(buffer_recov[l], buffer[diskmax + l], state->block_size);
/* mark that the read parity is wrong, setting ptr to 0 */
buffer_recov[l] = 0;
log_tag("parity_error:%u:%s: Data error, diff bits %u/%u\n", i, lev_config_name(l), diff, state->block_size * 8);
++error;
}
}
}
/* now write recovered files */
if (fix) {
/* update the fixed files */
for (j = 0; j < failed_count; ++j) {
/* nothing to do if it doesn't need recovering */
if (!failed[j].is_bad)
continue;
/* do not fix if the file is excluded */
if (file_flag_has(failed[j].file, FILE_IS_EXCLUDED)
|| (state->opt.syncedonly && file_flag_has(failed[j].file, FILE_IS_UNSYNCED)))
continue;
ret = handle_write(failed[j].handle, failed[j].file_pos, buffer[failed[j].index], state->block_size);
if (ret == -1) {
/* LCOV_EXCL_START */
/* mark the file as damaged */
file_flag_set(failed[j].file, FILE_IS_DAMAGED);
if (errno == EACCES) {
log_fatal("WARNING! Please give write permission to the file.\n");
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
}
log_fatal("Stopping at block %u\n", i);
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* if we are not sure that the recovered content is uptodate */
if (failed[j].is_outofdate) {
/* mark the file as damaged */
file_flag_set(failed[j].file, FILE_IS_DAMAGED);
continue;
}
/* mark the file as containing some fixes */
/* note that it could be also marked as damaged in other iterations */
file_flag_set(failed[j].file, FILE_IS_FIXED);
log_tag("fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].disk->name, esc_tag(failed[j].file->sub, esc_buffer), failed[j].file_pos);
++recovered_error;
}
/*
* Update parity only if all the blocks have it computed and it's used.
*
* If you check/fix after a partial sync, you do not want to fix parity
* for blocks that are going to have it computed in the sync completion.
*
* For unused parity there is no need to write it, because when fixing
* we already have allocated space for it on parity file creation,
* and its content doesn't matter.
*/
if (used_parity && valid_parity) {
/* update the parity */
for (l = 0; l < state->level; ++l) {
/* if the parity on disk is wrong */
if (buffer_recov[l] == 0
/* and we have access at the parity */
&& parity[l] != 0
/* and the parity is not excluded */
&& !state->parity[l].is_excluded_by_filter
) {
ret = parity_write(parity[l], i, buffer[diskmax + l], state->block_size);
if (ret == -1) {
/* LCOV_EXCL_START */
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
log_fatal("WARNING! Without a working %s disk, it isn't possible to fix errors on it.\n", lev_name(l));
log_fatal("Stopping at block %u\n", i);
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
log_tag("parity_fixed:%u:%s: Fixed data error\n", i, lev_config_name(l));
++recovered_error;
}
}
}
} else {
/* if we are not fixing, we just set the FIXED flag */
/* meaning that we could fix this file if we try */
for (j = 0; j < failed_count; ++j) {
if (failed[j].is_bad) {
file_flag_set(failed[j].file, FILE_IS_FIXED);
}
}
}
}
} else {
/* if we are not checking, we just set the DAMAGED flag */
/* to report that the file is damaged, and we don't know if we can fix it */
for (j = 0; j < failed_count; ++j) {
if (failed[j].is_bad) {
file_flag_set(failed[j].file, FILE_IS_DAMAGED);
}
}
}
/* post process the files */
ret = file_post(state, fix, i, handle, diskmax);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("Stopping at block %u\n", i);
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* count the number of processed block */
++countpos;
/* progress */
if (state_progress(state, 0, i, countpos, countmax, countsize)) {
/* LCOV_EXCL_START */
break;
/* LCOV_EXCL_STOP */
}
}
/* for each disk, recover empty files, symlinks and empty dirs */
for (i = 0; i < diskmax; ++i) {
tommy_node* node;
struct snapraid_disk* disk;
if (!handle[i].disk)
continue;
/* for each empty file in the disk */
disk = handle[i].disk;
node = disk->filelist;
while (node) {
char path[PATH_MAX];
struct stat st;
struct snapraid_file* file;
int unsuccessful = 0;
file = node->data;
node = node->next; /* next node */
/* if not empty, it's already checked and continue to the next one */
if (file->size != 0) {
continue;
}
/* if excluded continue to the next one */
if (file_flag_has(file, FILE_IS_EXCLUDED)) {
continue;
}
/* stat the file */
pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
ret = stat(path, &st);
if (ret == -1) {
unsuccessful = 1;
log_error("Error stating empty file '%s'. %s.\n", path, strerror(errno));
log_tag("error:%s:%s: Empty file stat error\n", disk->name, esc_tag(file->sub, esc_buffer));
++error;
} else if (!S_ISREG(st.st_mode)) {
unsuccessful = 1;
log_tag("error:%s:%s: Empty file error for not regular file\n", disk->name, esc_tag(file->sub, esc_buffer));
++error;
} else if (st.st_size != 0) {
unsuccessful = 1;
log_tag("error:%s:%s: Empty file error for size '%" PRIu64 "'\n", disk->name, esc_tag(file->sub, esc_buffer), (uint64_t)st.st_size);
++error;
}
if (fix && unsuccessful) {
int f;
/* create the ancestor directories */
ret = mkancestor(path);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* create it */
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
f = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_NOFOLLOW, 0600);
if (f == -1) {
/* LCOV_EXCL_START */
log_fatal("Error creating empty file '%s'. %s.\n", path, strerror(errno));
if (errno == EACCES) {
log_fatal("WARNING! Please give write permission to the file.\n");
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
}
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* set the original modification time */
ret = fmtime(f, file->mtime_sec, file->mtime_nsec);
if (ret != 0) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Error timing file '%s'. %s.\n", file->sub, strerror(errno));
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* close it */
ret = close(f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
log_tag("fixed:%s:%s: Fixed empty file\n", disk->name, esc_tag(file->sub, esc_buffer));
++recovered_error;
log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
msg_info("recovered %s\n", fmt_term(disk, file->sub, esc_buffer));
}
}
/* for each link in the disk */
disk = handle[i].disk;
node = disk->linklist;
while (node) {
char path[PATH_MAX];
char pathto[PATH_MAX];
char linkto[PATH_MAX];
struct stat st;
struct stat stto;
struct snapraid_link* slink;
int unsuccessful = 0;
int unrecoverable = 0;
slink = node->data;
node = node->next; /* next node */
/* if excluded continue to the next one */
if (link_flag_has(slink, FILE_IS_EXCLUDED)) {
continue;
}
if (link_flag_has(slink, FILE_IS_HARDLINK)) {
/* stat the link */
pathprint(path, sizeof(path), "%s%s", disk->dir, slink->sub);
ret = stat(path, &st);
if (ret == -1) {
unsuccessful = 1;
log_error("Error stating hardlink '%s'. %s.\n", path, strerror(errno));
log_tag("hardlink_error:%s:%s:%s: Hardlink stat error\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
++error;
} else if (!S_ISREG(st.st_mode)) {
unsuccessful = 1;
log_tag("hardlink_error:%s:%s:%s: Hardlink error for not regular file\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
++error;
}
/* stat the "to" file */
pathprint(pathto, sizeof(pathto), "%s%s", disk->dir, slink->linkto);
ret = stat(pathto, &stto);
if (ret == -1) {
unsuccessful = 1;
if (errno == ENOENT) {
unrecoverable = 1;
if (fix) {
/* if the target doesn't exist, it's unrecoverable */
/* because we cannot create an hardlink of a file that */
/* doesn't exists */
++unrecoverable_error;
} else {
/* but in check, we can assume that fixing will recover */
/* such missing file, so we assume a less drastic error */
++error;
}
}
log_error("Error stating hardlink-to '%s'. %s.\n", pathto, strerror(errno));
log_tag("hardlink_error:%s:%s:%s: Hardlink to stat error\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
++error;
} else if (!S_ISREG(stto.st_mode)) {
unsuccessful = 1;
log_tag("hardlink_error:%s:%s:%s: Hardlink-to error for not regular file\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
++error;
} else if (!unsuccessful && st.st_ino != stto.st_ino) {
unsuccessful = 1;
log_error("Mismatch hardlink '%s' and '%s'. Different inode.\n", path, pathto);
log_tag("hardlink_error:%s:%s:%s: Hardlink mismatch for different inode\n", disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
++error;
}
} else {
/* read the symlink */
pathprint(path, sizeof(path), "%s%s", disk->dir, slink->sub);
ret = readlink(path, linkto, sizeof(linkto));
if (ret < 0) {
unsuccessful = 1;
log_error("Error reading symlink '%s'. %s.\n", path, strerror(errno));
log_tag("symlink_error:%s:%s: Symlink read error\n", disk->name, esc_tag(slink->sub, esc_buffer));
++error;
} else if (ret >= PATH_MAX) {
unsuccessful = 1;
log_error("Error reading symlink '%s'. Symlink too long.\n", path);
log_tag("symlink_error:%s:%s: Symlink read error\n", disk->name, esc_tag(slink->sub, esc_buffer));
++error;
} else {
linkto[ret] = 0;
if (strcmp(linkto, slink->linkto) != 0) {
unsuccessful = 1;
log_tag("symlink_error:%s:%s: Symlink data error '%s' instead of '%s'\n", disk->name, esc_tag(slink->sub, esc_buffer), linkto, slink->linkto);
++error;
}
}
}
if (fix && unsuccessful && !unrecoverable) {
/* create the ancestor directories */
ret = mkancestor(path);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* if it exists, it must be deleted before recreating */
ret = remove(path);
if (ret != 0 && errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error removing '%s'. %s.\n", path, strerror(errno));
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* create it */
if (link_flag_has(slink, FILE_IS_HARDLINK)) {
ret = hardlink(pathto, path);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error writing hardlink '%s' to '%s'. %s.\n", path, pathto, strerror(errno));
if (errno == EACCES) {
log_fatal("WARNING! Please give write permission to the hardlink.\n");
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
}
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
log_tag("hardlink_fixed:%s:%s: Fixed hardlink error\n", disk->name, esc_tag(slink->sub, esc_buffer));
++recovered_error;
} else {
ret = symlink(slink->linkto, path);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error writing symlink '%s' to '%s'. %s.\n", path, slink->linkto, strerror(errno));
if (errno == EACCES) {
log_fatal("WARNING! Please give write permission to the symlink.\n");
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
}
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
log_tag("symlink_fixed:%s:%s: Fixed symlink error\n", disk->name, esc_tag(slink->sub, esc_buffer));
++recovered_error;
}
log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(slink->sub, esc_buffer));
msg_info("recovered %s\n", fmt_term(disk, slink->sub, esc_buffer));
}
}
/* for each dir in the disk */
disk = handle[i].disk;
node = disk->dirlist;
while (node) {
char path[PATH_MAX];
struct stat st;
struct snapraid_dir* dir;
int unsuccessful = 0;
dir = node->data;
node = node->next; /* next node */
/* if excluded continue to the next one */
if (dir_flag_has(dir, FILE_IS_EXCLUDED)) {
continue;
}
/* stat the dir */
pathprint(path, sizeof(path), "%s%s", disk->dir, dir->sub);
ret = stat(path, &st);
if (ret == -1) {
unsuccessful = 1;
log_error("Error stating dir '%s'. %s.\n", path, strerror(errno));
log_tag("dir_error:%s:%s: Dir stat error\n", disk->name, esc_tag(dir->sub, esc_buffer));
++error;
} else if (!S_ISDIR(st.st_mode)) {
unsuccessful = 1;
log_tag("dir_error:%s:%s: Dir error for not directory\n", disk->name, esc_tag(dir->sub, esc_buffer));
++error;
}
if (fix && unsuccessful) {
/* create the ancestor directories */
ret = mkancestor(path);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* create it */
ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error creating dir '%s'. %s.\n", path, strerror(errno));
if (errno == EACCES) {
log_fatal("WARNING! Please give write permission to the dir.\n");
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
}
log_fatal("Stopping\n");
++unrecoverable_error;
goto bail;
/* LCOV_EXCL_STOP */
}
log_tag("dir_fixed:%s:%s: Fixed dir error\n", disk->name, esc_tag(dir->sub, esc_buffer));
++recovered_error;
log_tag("status:recovered:%s:%s\n", disk->name, esc_tag(dir->sub, esc_buffer));
msg_info("recovered %s\n", fmt_term(disk, dir->sub, esc_buffer));
}
}
}
state_progress_end(state, countpos, countmax, countsize);
bail:
/* close all the files left open */
for (j = 0; j < diskmax; ++j) {
struct snapraid_file* file = handle[j].file;
struct snapraid_disk* disk = handle[j].disk;
ret = handle_close(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
++unrecoverable_error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
/* remove all the files created from scratch that have not finished the processing */
/* it happens only when aborting pressing Ctrl+C or other reason. */
if (fix) {
/* for each disk */
for (i = 0; i < diskmax; ++i) {
tommy_node* node;
struct snapraid_disk* disk;
if (!handle[i].disk)
continue;
/* for each file in the disk */
disk = handle[i].disk;
node = disk->filelist;
while (node) {
char path[PATH_MAX];
struct snapraid_file* file;
file = node->data;
node = node->next; /* next node */
/* if the file was not created, meaning that it was already existing */
if (!file_flag_has(file, FILE_IS_CREATED)) {
/* nothing to do */
continue;
}
/* if processing was finished */
if (file_flag_has(file, FILE_IS_FINISHED)) {
/* nothing to do */
continue;
}
/* if the file was originally missing, and processing not yet finished */
/* we have to throw it away to ensure that at the next run we will retry */
/* to fix it, in case we select to undelete missing files */
pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
ret = remove(path);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error removing '%s'. %s.\n", path, strerror(errno));
log_fatal("WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
++unrecoverable_error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
}
}
if (error || recovered_error || unrecoverable_error) {
msg_status("\n");
msg_status("%8u errors\n", error);
if (fix) {
msg_status("%8u recovered errors\n", recovered_error);
}
if (unrecoverable_error) {
msg_status("%8u UNRECOVERABLE errors\n", unrecoverable_error);
} else {
/* without checking, we don't know if they are really recoverable or not */
if (!state->opt.auditonly)
msg_status("%8u unrecoverable errors\n", unrecoverable_error);
if (fix)
msg_status("Everything OK\n");
}
} else {
msg_status("Everything OK\n");
}
if (error && !fix)
log_fatal("WARNING! There are errors!\n");
if (unrecoverable_error)
log_fatal("DANGER! There are unrecoverable errors!\n");
log_tag("summary:error:%u\n", error);
if (fix)
log_tag("summary:error_recovered:%u\n", recovered_error);
if (!state->opt.auditonly)
log_tag("summary:error_unrecoverable:%u\n", unrecoverable_error);
if (fix) {
if (error + recovered_error + unrecoverable_error == 0)
log_tag("summary:exit:ok\n");
else if (unrecoverable_error == 0)
log_tag("summary:exit:recovered\n");
else
log_tag("summary:exit:unrecoverable\n");
} else if (!state->opt.auditonly) {
if (error + unrecoverable_error == 0)
log_tag("summary:exit:ok\n");
else if (unrecoverable_error == 0)
log_tag("summary:exit:recoverable\n");
else
log_tag("summary:exit:unrecoverable\n");
} else { /* audit only */
if (error == 0)
log_tag("summary:exit:ok\n");
else
log_tag("summary:exit:error\n");
}
log_flush();
free(failed);
free(failed_map);
free(block_enabled);
free(handle);
free(buffer_alloc);
free(buffer);
/* fail if some error are present after the run */
if (fix) {
if (state->opt.expect_unrecoverable) {
if (unrecoverable_error == 0)
return -1;
} else {
if (unrecoverable_error != 0)
return -1;
}
} else {
if (state->opt.expect_unrecoverable) {
if (unrecoverable_error == 0)
return -1;
} else if (state->opt.expect_recoverable) {
if (unrecoverable_error != 0 || error == 0)
return -1;
} else {
if (error != 0 || unrecoverable_error != 0)
return -1;
}
}
return 0;
}
int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, block_off_t blockcount)
{
block_off_t blockmax;
data_off_t size;
int ret;
struct snapraid_parity_handle parity[LEV_MAX];
struct snapraid_parity_handle* parity_ptr[LEV_MAX];
unsigned error;
unsigned l;
msg_progress("Initializing...\n");
blockmax = parity_allocated_size(state);
size = blockmax * (data_off_t)state->block_size;
if (blockstart > blockmax) {
/* LCOV_EXCL_START */
log_fatal("Error in the specified starting block %u. It's bigger than the parity size %u.\n", blockstart, blockmax);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* adjust the number of block to process */
if (blockcount != 0 && blockstart + blockcount < blockmax) {
blockmax = blockstart + blockcount;
}
if (fix) {
/* if fixing, create the file and open for writing */
/* if it fails, we cannot continue */
for (l = 0; l < state->level; ++l) {
/* skip parity disks that are not accessible */
if (state->parity[l].skip_access) {
parity_ptr[l] = 0;
continue;
}
parity_ptr[l] = &parity[l];
/* if the parity is excluded */
if (state->parity[l].is_excluded_by_filter) {
/* open for reading, and ignore error */
ret = parity_open(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
/* continue anyway */
parity_ptr[l] = 0;
}
} else {
/* open for writing */
ret = parity_create(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without an accessible %s file, it isn't possible to fix any error.\n", lev_name(l));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = parity_chsize(parity_ptr[l], &state->parity[l], 0, size, state->block_size, state->opt.skip_fallocate, state->opt.skip_space_holder);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without an accessible %s file, it isn't possible to sync.\n", lev_name(l));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
} else if (!state->opt.auditonly) {
/* if checking, open the file for reading */
/* it may fail if the file doesn't exist, in this case we continue to check the files */
for (l = 0; l < state->level; ++l) {
parity_ptr[l] = &parity[l];
ret = parity_open(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
msg_status("No accessible %s file, only files will be checked.\n", lev_name(l));
/* continue anyway */
parity_ptr[l] = 0;
}
}
} else {
/* otherwise don't use any parity */
for (l = 0; l < state->level; ++l)
parity_ptr[l] = 0;
}
error = 0;
/* skip degenerated cases of empty parity, or skipping all */
if (blockstart < blockmax) {
ret = state_check_process(state, fix, parity_ptr, blockstart, blockmax);
if (ret == -1) {
/* LCOV_EXCL_START */
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
/* try to close only if opened */
for (l = 0; l < state->level; ++l) {
if (parity_ptr[l]) {
/* if fixing and not excluded, truncate parity not valid */
if (fix && !state->parity[l].is_excluded_by_filter) {
ret = parity_truncate(parity_ptr[l]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Unexpected truncate error in %s disk.\n", lev_name(l));
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
ret = parity_close(parity_ptr[l]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Unexpected close error in %s disk.\n", lev_name(l));
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
}
/* abort if error are present */
if (error != 0)
return -1;
return 0;
}
snapraid-12.1/cmdline/device.c 0000664 0000000 0000000 00000100514 14166610522 0016260 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2015 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "state.h"
#include "raid/raid.h"
/**
* The following are Failure Rate tables computed from the data that
* BackBlaze made available at:
*
* Reliability Data Set For 41,000 Hard Drives Now Open Source
* https://www.backblaze.com/blog/hard-drive-data-feb2015/
*
* Hard Drive Data Sets
* https://www.backblaze.com/hard-drive-test-data.html
*
* Note that in this data:
* - Disks all passed the load-testing and have made it to production,
* and then Dead On Arrival (DOA) failures are excluded.
* - Disks that are predicted to fail by BackBlaze are removed before
* they really fail, not counting as a failure.
*
* The following tables are computed using the data from 2014-02-14 to 2014-12-31
* because it's the period when more SMART attributes were gathered.
*
* In this period there are 47322 disk seen, with 1988 removed because failed,
* and with 4121 removed because predicted to fail.
*/
/**
* Number of data point in each table.
*/
#define SMART_MEASURES 256
/*
* Divider for SMART attribute 5
*/
static unsigned SMART_5_STEP = 1;
/*
* Failure rate for 30 days, for a disk
* with SMART attribute 5 at a given value.
*/
static double SMART_5_R[SMART_MEASURES] = {
0.0026, 0.0748, 0.0919, 0.1013, 0.1079,
0.1137, 0.1194, 0.1235, 0.1301, 0.1398,
0.1453, 0.1490, 0.1528, 0.1566, 0.1595,
0.1635, 0.1656, 0.1701, 0.1718, 0.1740,
0.1762, 0.1787, 0.1808, 0.1833, 0.1858,
0.1885, 0.1901, 0.1915, 0.1934, 0.1958,
0.1975, 0.1993, 0.2014, 0.2048, 0.2068,
0.2088, 0.2109, 0.2120, 0.2137, 0.2160,
0.2173, 0.2214, 0.2226, 0.2237, 0.2262,
0.2277, 0.2292, 0.2304, 0.2338, 0.2369,
0.2381, 0.2396, 0.2411, 0.2427, 0.2445,
0.2462, 0.2472, 0.2488, 0.2496, 0.2504,
0.2514, 0.2525, 0.2535, 0.2544, 0.2554,
0.2571, 0.2583, 0.2601, 0.2622, 0.2631,
0.2635, 0.2644, 0.2659, 0.2675, 0.2682,
0.2692, 0.2701, 0.2707, 0.2712, 0.2726,
0.2745, 0.2767, 0.2778, 0.2784, 0.2800,
0.2814, 0.2834, 0.2839, 0.2851, 0.2877,
0.2883, 0.2891, 0.2900, 0.2907, 0.2916,
0.2934, 0.2950, 0.2969, 0.2975, 0.2983,
0.2999, 0.3006, 0.3013, 0.3021, 0.3033,
0.3054, 0.3066, 0.3074, 0.3082, 0.3094,
0.3106, 0.3112, 0.3120, 0.3137, 0.3141,
0.3145, 0.3151, 0.3159, 0.3169, 0.3174,
0.3181, 0.3194, 0.3215, 0.3219, 0.3231,
0.3234, 0.3237, 0.3242, 0.3255, 0.3270,
0.3283, 0.3286, 0.3289, 0.3304, 0.3315,
0.3322, 0.3347, 0.3361, 0.3382, 0.3384,
0.3395, 0.3398, 0.3401, 0.3405, 0.3411,
0.3431, 0.3435, 0.3442, 0.3447, 0.3450,
0.3455, 0.3464, 0.3472, 0.3486, 0.3497,
0.3501, 0.3509, 0.3517, 0.3531, 0.3535,
0.3540, 0.3565, 0.3569, 0.3576, 0.3579,
0.3584, 0.3590, 0.3594, 0.3599, 0.3621,
0.3627, 0.3642, 0.3649, 0.3655, 0.3658,
0.3667, 0.3683, 0.3699, 0.3704, 0.3707,
0.3711, 0.3715, 0.3718, 0.3721, 0.3727,
0.3740, 0.3744, 0.3748, 0.3753, 0.3756,
0.3761, 0.3766, 0.3775, 0.3794, 0.3801,
0.3804, 0.3813, 0.3817, 0.3823, 0.3831,
0.3847, 0.3875, 0.3881, 0.3886, 0.3890,
0.3893, 0.3896, 0.3900, 0.3907, 0.3923,
0.3925, 0.3933, 0.3936, 0.3961, 0.3971,
0.3981, 0.3989, 0.4007, 0.4012, 0.4018,
0.4023, 0.4027, 0.4041, 0.4048, 0.4056,
0.4073, 0.4079, 0.4086, 0.4104, 0.4107,
0.4109, 0.4112, 0.4118, 0.4133, 0.4139,
0.4144, 0.4146, 0.4148, 0.4164, 0.4165,
0.4174, 0.4191, 0.4197, 0.4201, 0.4204,
0.4210, 0.4213, 0.4216, 0.4221, 0.4231,
0.4235, 0.4237, 0.4239, 0.4241, 0.4244,
0.4249,
};
/*
* Divider for SMART attribute 187
*/
static unsigned SMART_187_STEP = 1;
/*
* Failure rate for 30 days, for a disk
* with SMART attribute 187 at a given value.
*/
static double SMART_187_R[SMART_MEASURES] = {
0.0039, 0.1287, 0.1579, 0.1776, 0.1905,
0.2013, 0.2226, 0.3263, 0.3612, 0.3869,
0.4086, 0.4292, 0.4559, 0.5278, 0.5593,
0.5847, 0.6124, 0.6345, 0.6517, 0.6995,
0.7308, 0.7541, 0.7814, 0.8122, 0.8306,
0.8839, 0.9100, 0.9505, 0.9906, 1.0254,
1.0483, 1.1060, 1.1280, 1.1624, 1.1895,
1.2138, 1.2452, 1.2864, 1.3120, 1.3369,
1.3705, 1.3894, 1.4055, 1.4218, 1.4434,
1.4670, 1.4834, 1.4993, 1.5174, 1.5400,
1.5572, 1.5689, 1.5808, 1.6198, 1.6346,
1.6405, 1.6570, 1.6618, 1.6755, 1.6877,
1.7100, 1.7258, 1.7347, 1.7814, 1.7992,
1.8126, 1.8225, 1.8269, 1.8341, 1.8463,
1.8765, 1.8850, 1.9005, 1.9281, 1.9398,
1.9618, 1.9702, 1.9905, 2.0099, 2.0480,
2.0565, 2.0611, 2.0709, 2.0846, 2.0895,
2.0958, 2.1008, 2.1055, 2.1097, 2.1235,
2.1564, 2.1737, 2.1956, 2.1989, 2.2015,
2.2148, 2.2355, 2.2769, 2.2940, 2.3045,
2.3096, 2.3139, 2.3344, 2.3669, 2.3779,
2.3941, 2.4036, 2.4396, 2.4473, 2.4525,
2.4656, 2.4762, 2.4787, 2.5672, 2.5732,
2.5755, 2.5794, 2.5886, 2.6100, 2.6144,
2.6341, 2.6614, 2.6679, 2.6796, 2.6847,
2.6872, 2.6910, 2.6934, 2.6995, 2.7110,
2.7179, 2.7204, 2.7232, 2.7282, 2.7355,
2.7375, 2.7422, 2.7558, 2.7580, 2.7643,
2.7767, 2.7770, 2.8016, 2.9292, 2.9294,
2.9337, 2.9364, 2.9409, 2.9436, 2.9457,
2.9466, 2.9498, 2.9543, 2.9570, 2.9573,
2.9663, 2.9708, 2.9833, 2.9859, 2.9895,
2.9907, 2.9932, 2.9935, 3.0021, 3.0035,
3.0079, 3.0103, 3.0126, 3.0151, 3.0266,
3.0288, 3.0320, 3.0330, 3.0343, 3.0373,
3.0387, 3.0438, 3.0570, 3.0579, 3.0616,
3.0655, 3.0728, 3.0771, 3.0794, 3.0799,
3.0812, 3.1769, 3.1805, 3.1819, 3.1860,
3.1869, 3.2004, 3.2016, 3.2025, 3.2070,
3.2129, 3.2173, 3.2205, 3.2254, 3.2263,
3.2300, 3.2413, 3.2543, 3.2580, 3.2595,
3.2611, 3.2624, 3.2787, 3.2798, 3.2809,
3.2823, 3.2833, 3.2834, 3.2853, 3.2866,
3.3332, 3.3580, 3.3595, 3.3625, 3.3631,
3.3667, 3.3702, 3.3737, 3.3742, 3.3747,
3.3769, 3.3775, 3.3791, 3.3809, 3.3813,
3.3814, 3.3822, 3.3827, 3.3828, 3.3833,
3.3833, 3.3843, 3.3882, 3.3963, 3.4047,
3.4057, 3.4213, 3.4218, 3.4230, 3.4231,
3.4240, 3.4262, 3.4283, 3.4283, 3.4288,
3.4293, 3.4302, 3.4317, 3.4478, 3.4486,
3.4520,
};
/*
* Divider for SMART attribute 188
*/
static unsigned SMART_188_STEP = 1;
/*
* Failure rate for 30 days, for a disk
* with SMART attribute 188 at a given value.
*/
static double SMART_188_R[SMART_MEASURES] = {
0.0025, 0.0129, 0.0182, 0.0215, 0.0236,
0.0257, 0.0279, 0.0308, 0.0341, 0.0382,
0.0430, 0.0491, 0.0565, 0.0658, 0.0770,
0.0906, 0.1037, 0.1197, 0.1355, 0.1525,
0.1686, 0.1864, 0.2011, 0.2157, 0.2281,
0.2404, 0.2505, 0.2591, 0.2676, 0.2766,
0.2827, 0.2913, 0.2999, 0.3100, 0.3185,
0.3298, 0.3361, 0.3446, 0.3506, 0.3665,
0.3699, 0.3820, 0.3890, 0.4059, 0.4108,
0.4255, 0.4290, 0.4424, 0.4473, 0.4617,
0.4667, 0.4770, 0.4829, 0.4977, 0.4997,
0.5102, 0.5137, 0.5283, 0.5316, 0.5428,
0.5480, 0.5597, 0.5634, 0.5791, 0.5826,
0.5929, 0.5945, 0.6025, 0.6102, 0.6175,
0.6245, 0.6313, 0.6421, 0.6468, 0.6497,
0.6557, 0.6570, 0.6647, 0.6698, 0.6769,
0.6849, 0.6884, 0.6925, 0.7025, 0.7073,
0.7161, 0.7223, 0.7256, 0.7280, 0.7411,
0.7445, 0.7530, 0.7628, 0.7755, 0.7900,
0.8006, 0.8050, 0.8098, 0.8132, 0.8192,
0.8230, 0.8293, 0.8356, 0.8440, 0.8491,
0.8672, 0.8766, 0.8907, 0.8934, 0.8992,
0.9062, 0.9111, 0.9209, 0.9290, 0.9329,
0.9378, 0.9385, 0.9402, 0.9427, 0.9448,
0.9459, 0.9568, 0.9626, 0.9628, 0.9730,
0.9765, 0.9797, 0.9825, 0.9873, 0.9902,
0.9926, 0.9991, 1.0031, 1.0044, 1.0062,
1.0120, 1.0148, 1.0188, 1.0218, 1.0231,
1.0249, 1.0277, 1.0335, 1.0355, 1.0417,
1.0467, 1.0474, 1.0510, 1.0529, 1.0532,
1.0562, 1.0610, 1.0702, 1.0708, 1.0800,
1.0804, 1.0845, 1.1120, 1.1191, 1.1225,
1.1264, 1.1265, 1.1335, 1.1347, 1.1479,
1.1479, 1.1519, 1.1545, 1.1645, 1.1646,
1.1647, 1.1649, 1.1678, 1.1713, 1.1723,
1.1733, 1.1736, 1.1736, 1.1738, 1.1739,
1.1739, 1.1741, 1.1741, 1.1746, 1.1746,
1.1748, 1.1750, 1.1760, 1.1794, 1.1854,
1.1908, 1.1912, 1.1912, 1.1971, 1.2033,
1.2033, 1.2120, 1.2166, 1.2185, 1.2185,
1.2189, 1.2211, 1.2226, 1.2234, 1.2320,
1.2345, 1.2345, 1.2347, 1.2350, 1.2350,
1.2407, 1.2408, 1.2408, 1.2408, 1.2409,
1.2460, 1.2518, 1.2519, 1.2519, 1.2519,
1.2520, 1.2520, 1.2521, 1.2521, 1.2521,
1.2593, 1.2745, 1.2760, 1.2772, 1.2831,
1.2833, 1.2890, 1.2906, 1.3166, 1.3201,
1.3202, 1.3202, 1.3202, 1.3204, 1.3204,
1.3314, 1.3422, 1.3423, 1.3441, 1.3491,
1.3583, 1.3602, 1.3606, 1.3636, 1.3650,
1.3661, 1.3703, 1.3708, 1.3716, 1.3730,
1.3731,
};
/*
* Divider for SMART attribute 193
*/
static unsigned SMART_193_STEP = 649;
/*
* Failure rate for 30 days, for a disk
* with SMART attribute 193 at a given value.
*/
static double SMART_193_R[SMART_MEASURES] = {
0.0000, 0.0016, 0.0032, 0.0036, 0.0039,
0.0042, 0.0046, 0.0049, 0.0052, 0.0054,
0.0057, 0.0060, 0.0062, 0.0065, 0.0068,
0.0071, 0.0074, 0.0077, 0.0080, 0.0083,
0.0086, 0.0091, 0.0094, 0.0098, 0.0101,
0.0104, 0.0108, 0.0111, 0.0119, 0.0122,
0.0127, 0.0130, 0.0134, 0.0137, 0.0141,
0.0144, 0.0146, 0.0152, 0.0155, 0.0159,
0.0163, 0.0165, 0.0168, 0.0172, 0.0176,
0.0179, 0.0184, 0.0188, 0.0190, 0.0194,
0.0197, 0.0201, 0.0204, 0.0207, 0.0209,
0.0213, 0.0215, 0.0219, 0.0221, 0.0225,
0.0229, 0.0234, 0.0241, 0.0246, 0.0253,
0.0263, 0.0278, 0.0286, 0.0293, 0.0298,
0.0302, 0.0306, 0.0311, 0.0315, 0.0319,
0.0322, 0.0329, 0.0334, 0.0338, 0.0343,
0.0348, 0.0352, 0.0358, 0.0362, 0.0367,
0.0371, 0.0374, 0.0378, 0.0383, 0.0388,
0.0393, 0.0397, 0.0401, 0.0404, 0.0410,
0.0416, 0.0422, 0.0428, 0.0436, 0.0443,
0.0449, 0.0454, 0.0457, 0.0462, 0.0468,
0.0473, 0.0479, 0.0483, 0.0488, 0.0491,
0.0493, 0.0497, 0.0500, 0.0504, 0.0507,
0.0510, 0.0514, 0.0519, 0.0523, 0.0528,
0.0533, 0.0538, 0.0542, 0.0547, 0.0551,
0.0556, 0.0560, 0.0565, 0.0572, 0.0577,
0.0584, 0.0590, 0.0594, 0.0599, 0.0603,
0.0607, 0.0611, 0.0616, 0.0621, 0.0626,
0.0632, 0.0639, 0.0647, 0.0655, 0.0661,
0.0669, 0.0676, 0.0683, 0.0691, 0.0699,
0.0708, 0.0713, 0.0719, 0.0724, 0.0730,
0.0736, 0.0745, 0.0751, 0.0759, 0.0769,
0.0779, 0.0787, 0.0796, 0.0804, 0.0815,
0.0825, 0.0833, 0.0840, 0.0847, 0.0854,
0.0859, 0.0865, 0.0873, 0.0881, 0.0890,
0.0900, 0.0912, 0.0919, 0.0929, 0.0942,
0.0956, 0.0965, 0.0976, 0.0986, 0.0995,
0.1006, 0.1019, 0.1031, 0.1038, 0.1045,
0.1051, 0.1058, 0.1066, 0.1072, 0.1077,
0.1084, 0.1091, 0.1099, 0.1104, 0.1111,
0.1118, 0.1127, 0.1135, 0.1142, 0.1149,
0.1157, 0.1163, 0.1168, 0.1173, 0.1179,
0.1184, 0.1189, 0.1195, 0.1203, 0.1208,
0.1213, 0.1223, 0.1231, 0.1240, 0.1246,
0.1252, 0.1260, 0.1269, 0.1276, 0.1287,
0.1303, 0.1311, 0.1319, 0.1328, 0.1335,
0.1341, 0.1348, 0.1362, 0.1373, 0.1380,
0.1387, 0.1392, 0.1398, 0.1403, 0.1408,
0.1412, 0.1418, 0.1422, 0.1428, 0.1434,
0.1439, 0.1445, 0.1451, 0.1457, 0.1464,
0.1469, 0.1475, 0.1480, 0.1486, 0.1491,
0.1498,
};
/*
* Divider for SMART attribute 197
*/
static unsigned SMART_197_STEP = 1;
/*
* Failure rate for 30 days, for a disk
* with SMART attribute 197 at a given value.
*/
static double SMART_197_R[SMART_MEASURES] = {
0.0028, 0.2972, 0.3883, 0.4363, 0.4644,
0.4813, 0.4948, 0.5051, 0.5499, 0.8535,
0.8678, 0.8767, 0.8882, 0.8933, 0.9012,
0.9076, 0.9368, 1.1946, 1.2000, 1.2110,
1.2177, 1.2305, 1.2385, 1.2447, 1.2699,
1.4713, 1.4771, 1.4802, 1.4887, 1.5292,
1.5384, 1.5442, 1.5645, 1.7700, 1.7755,
1.7778, 1.7899, 1.7912, 1.7991, 1.7998,
1.8090, 1.9974, 1.9992, 2.0088, 2.0132,
2.0146, 2.0161, 2.0171, 2.0273, 2.1845,
2.1866, 2.1877, 2.1900, 2.1922, 2.1944,
2.1974, 2.2091, 2.3432, 2.3459, 2.3463,
2.3468, 2.3496, 2.3503, 2.3533, 2.3593,
2.4604, 2.4606, 2.4609, 2.4612, 2.4620,
2.4626, 2.4638, 2.4689, 2.5575, 2.5581,
2.5586, 2.5586, 2.5588, 2.5602, 2.5602,
2.5648, 2.6769, 2.6769, 2.6769, 2.6794,
2.6805, 2.6811, 2.6814, 2.6862, 2.7742,
2.7755, 2.7771, 2.7780, 2.7790, 2.7797,
2.7807, 2.7871, 2.9466, 2.9478, 2.9492,
2.9612, 2.9618, 2.9624, 2.9628, 2.9669,
3.1467, 3.1481, 3.1494, 3.1499, 3.1504,
3.1507, 3.1509, 3.1532, 3.2675, 3.2681,
3.2703, 3.2712, 3.2714, 3.2726, 3.2726,
3.2743, 3.3376, 3.3379, 3.3382, 3.3397,
3.3403, 3.3410, 3.3410, 3.3429, 3.4052,
3.4052, 3.4052, 3.4052, 3.4052, 3.4053,
3.4053, 3.4075, 3.4616, 3.4616, 3.4616,
3.4616, 3.4616, 3.4616, 3.4620, 3.4634,
3.4975, 3.4975, 3.4975, 3.4975, 3.4979,
3.4979, 3.4979, 3.4998, 3.5489, 3.5489,
3.5489, 3.5489, 3.5489, 3.5493, 3.5497,
3.5512, 3.5827, 3.5828, 3.5828, 3.5828,
3.5828, 3.5828, 3.5828, 3.5844, 3.6251,
3.6251, 3.6251, 3.6267, 3.6267, 3.6271,
3.6271, 3.6279, 3.6562, 3.6562, 3.6563,
3.7206, 3.7242, 3.7332, 3.7332, 3.7346,
3.7548, 3.7548, 3.7553, 3.7576, 3.7581,
3.7586, 3.7587, 3.7600, 3.7773, 3.7812,
3.7836, 3.7841, 3.7842, 3.7851, 3.7856,
3.7876, 3.8890, 3.8890, 3.8890, 3.8890,
3.8890, 3.8890, 3.8890, 3.8897, 3.9111,
3.9114, 3.9114, 3.9114, 3.9114, 3.9114,
3.9114, 3.9126, 3.9440, 3.9440, 3.9440,
3.9440, 3.9440, 3.9498, 3.9498, 3.9509,
3.9783, 3.9783, 3.9784, 3.9784, 3.9784,
3.9784, 4.0012, 4.0019, 4.0406, 4.0413,
4.0413, 4.0413, 4.0413, 4.0414, 4.0414,
4.0421, 4.0552, 4.0552, 4.0558, 4.0558,
4.0558, 4.0558, 4.0558, 4.0563, 4.0753,
4.0753, 4.0760, 4.1131, 4.1131, 4.1131,
4.1131,
};
/*
* Divider for SMART attribute 198
*/
static unsigned SMART_198_STEP = 1;
/*
* Failure rate for 30 days, for a disk
* with SMART attribute 198 at a given value.
*/
static double SMART_198_R[SMART_MEASURES] = {
0.0030, 0.5479, 0.5807, 0.5949, 0.6046,
0.6086, 0.6139, 0.6224, 0.6639, 1.0308,
1.0329, 1.0364, 1.0371, 1.0387, 1.0399,
1.0421, 1.0675, 1.3730, 1.3733, 1.3741,
1.3741, 1.3752, 1.3794, 1.3800, 1.3985,
1.6291, 1.6303, 1.6309, 1.6352, 1.6384,
1.6448, 1.6464, 1.6645, 1.8949, 1.8951,
1.8962, 1.9073, 1.9073, 1.9152, 1.9161,
1.9240, 2.1308, 2.1315, 2.1328, 2.1328,
2.1328, 2.1328, 2.1329, 2.1439, 2.3203,
2.3205, 2.3205, 2.3205, 2.3205, 2.3205,
2.3205, 2.3265, 2.4729, 2.4729, 2.4729,
2.4729, 2.4729, 2.4729, 2.4729, 2.4778,
2.5900, 2.5900, 2.5901, 2.5901, 2.5901,
2.5901, 2.5901, 2.5949, 2.6964, 2.6965,
2.6965, 2.6965, 2.6965, 2.6965, 2.6965,
2.7010, 2.8328, 2.8328, 2.8328, 2.8329,
2.8329, 2.8329, 2.8329, 2.8366, 2.9405,
2.9405, 2.9405, 2.9405, 2.9405, 2.9405,
2.9405, 2.9442, 3.1344, 3.1344, 3.1346,
3.1463, 3.1463, 3.1463, 3.1463, 3.1493,
3.3076, 3.3076, 3.3076, 3.3076, 3.3076,
3.3077, 3.3077, 3.3097, 3.4456, 3.4456,
3.4456, 3.4456, 3.4456, 3.4456, 3.4456,
3.4473, 3.5236, 3.5236, 3.5236, 3.5236,
3.5236, 3.5236, 3.5236, 3.5249, 3.6004,
3.6004, 3.6004, 3.6004, 3.6004, 3.6004,
3.6004, 3.6026, 3.6684, 3.6684, 3.6684,
3.6684, 3.6684, 3.6684, 3.6684, 3.6697,
3.7121, 3.7121, 3.7121, 3.7121, 3.7121,
3.7121, 3.7121, 3.7136, 3.7744, 3.7744,
3.7744, 3.7744, 3.7744, 3.7745, 3.7745,
3.7756, 3.8151, 3.8151, 3.8151, 3.8151,
3.8151, 3.8151, 3.8151, 3.8163, 3.8673,
3.8673, 3.8673, 3.8673, 3.8673, 3.8673,
3.8673, 3.8680, 3.9044, 3.9044, 3.9044,
3.9044, 3.9044, 3.9044, 3.9044, 3.9056,
3.9297, 3.9297, 3.9297, 3.9297, 3.9297,
3.9297, 3.9297, 3.9305, 3.9494, 3.9494,
3.9494, 3.9494, 3.9494, 3.9494, 3.9494,
3.9514, 4.0725, 4.0725, 4.0725, 4.0725,
4.0725, 4.0725, 4.0725, 4.0731, 4.0990,
4.0993, 4.0993, 4.0993, 4.0993, 4.0993,
4.0993, 4.1004, 4.1385, 4.1385, 4.1385,
4.1386, 4.1386, 4.1387, 4.1387, 4.1398,
4.1732, 4.2284, 4.2284, 4.2284, 4.2284,
4.2284, 4.2284, 4.2290, 4.2781, 4.2781,
4.2963, 4.2963, 4.2963, 4.2963, 4.2963,
4.2971, 4.3141, 4.3141, 4.3141, 4.3141,
4.3141, 4.3141, 4.3141, 4.3146, 4.3393,
4.3393, 4.3393, 4.3393, 4.3393, 4.3393,
4.3393,
};
/**
* Computes the estimated Annual Failure Rate from the specified table.
*/
static double smart_afr_value(double* tab, unsigned step, uint64_t value)
{
value /= step;
if (value >= SMART_MEASURES)
value = SMART_MEASURES - 1;
/* table rates are for a month, so we scale to a year */
return 365.0 / 30.0 * tab[value];
}
/**
* Computes the estimated Annual Failure Rate of a set of SMART attributes.
*
* We define the Annual Failure Rate as the average number of
* failures you expect in a year from a disk slot:
*
* AFR = 8760/MTBF (Mean Time Between Failures in hours).
*
* Note that this definition is different from the one given
* by Seagate, that defines AFR = 1 - exp(-8760/MTBF), that
* instead represents the probability of a failure in the next
* year.
*
* To combine the different AFR from different SMART attributes,
* we use the maximum rate reported, and we do not sum them,
* because the attributes are not independent.
*/
static double smart_afr(uint64_t* smart, const char* model)
{
double afr = 0;
uint64_t mask32 = 0xffffffffU;
uint64_t mask16 = 0xffffU;
if (smart[5] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_5_R, SMART_5_STEP, smart[5] & mask32);
if (afr < r)
afr = r;
}
if (smart[187] != SMART_UNASSIGNED) {
/* with some disks, only the lower 16 bits are significative */
/* See: http://web.archive.org/web/20130507072056/http://media.kingston.com/support/downloads/MKP_306_SMART_attribute.pdf */
double r = smart_afr_value(SMART_187_R, SMART_187_STEP, smart[187] & mask16);
if (afr < r)
afr = r;
}
if (
/**
* Don't check Command_Timeout (188) for Seagate disks.
*
* It's reported by users that for Archive SMR (Shingled Magnetic Recording)
* and IronWolf disks to be a not significative test as
* this value increases too often also on sane disks.
*/
strncmp(model, "ST", 2) != 0 && smart[188] != SMART_UNASSIGNED
) {
/* with Seagate disks, there are three different 16 bits value reported */
/* the lowest one is the most significant */
double r = smart_afr_value(SMART_188_R, SMART_188_STEP, smart[188] & mask16);
if (afr < r)
afr = r;
}
if (smart[193] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_193_R, SMART_193_STEP, smart[193] & mask32);
if (afr < r)
afr = r;
}
if (smart[197] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_197_R, SMART_197_STEP, smart[197] & mask32);
if (afr < r)
afr = r;
}
if (smart[198] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_198_R, SMART_198_STEP, smart[198] & mask32);
if (afr < r)
afr = r;
}
return afr;
}
/**
* Factorial.
*/
static double fact(unsigned n)
{
double v = 1;
while (n > 1)
v *= n--;
return v;
}
/**
* Probability of having exactly ::n events in a Poisson
* distribution with rate ::rate in a time unit.
*/
static double poisson_prob_n_failures(double rate, unsigned n)
{
return pow(rate, n) * exp(-rate) / fact(n);
}
/**
* Probability of having ::n or more events in a Poisson
* distribution with rate ::rate in a time unit.
*/
static double poisson_prob_n_or_more_failures(double rate, unsigned n)
{
double p_neg = 0;
unsigned i;
for (i = 0; i < n; ++i)
p_neg += poisson_prob_n_failures(rate, n - 1 - i);
return 1 - p_neg;
}
/**
* Probability of having data loss in a RAID system with the specified ::redundancy
* supposing the specified ::array_failure_rate, and ::replace_rate.
*/
static double raid_prob_of_one_or_more_failures(double array_failure_rate, double replace_rate, unsigned n, unsigned redundancy)
{
unsigned i;
double MTBF;
double MTTR;
double MTTDL;
double raid_failure_rate;
/*
* Use the MTTDL model (Mean Time To Data Loss) to estimate the
* failure rate of the array.
*
* See:
* Garth Alan Gibson, "Redundant Disk Arrays: Reliable, Parallel Secondary Storage", 1990
*/
/* avoid division by zero */
if (array_failure_rate == 0)
return 0;
/* get the Mean Time Between Failure of a single disk */
/* from the array failure rate */
MTBF = n / array_failure_rate;
/* get the Mean Time Between Repair (the time that a failed disk is replaced) */
/* from the repair rate */
MTTR = 1.0 / replace_rate;
/* use the approximated MTTDL equation */
MTTDL = pow(MTBF, redundancy + 1) / pow(MTTR, redundancy);
for (i = 0; i < redundancy + 1; ++i)
MTTDL /= n - i;
/* the raid failure rate is just the inverse of the MTTDL */
raid_failure_rate = 1.0 / MTTDL;
/* probability of at least one RAID failure */
/* note that is almost equal at the probability of */
/* the first failure. */
return poisson_prob_n_or_more_failures(raid_failure_rate, 1);
}
static void state_smart(unsigned n, tommy_list* low)
{
tommy_node* i;
unsigned j;
size_t device_pad;
size_t serial_pad;
int have_parent;
double array_failure_rate;
double p_at_least_one_failure;
int make_it_fail = 0;
uint64_t mask32 = 0xffffffffU;
uint64_t mask16 = 0xffffU;
char esc_buffer[ESC_MAX];
/* compute lengths for padding */
device_pad = 0;
serial_pad = 0;
have_parent = 0;
for (i = tommy_list_head(low); i != 0; i = i->next) {
size_t len;
devinfo_t* devinfo = i->data;
len = strlen(devinfo->file);
if (len > device_pad)
device_pad = len;
len = strlen(devinfo->smart_serial);
if (len > serial_pad)
serial_pad = len;
if (devinfo->parent != 0)
have_parent = 1;
}
printf("SnapRAID SMART report:\n");
printf("\n");
printf(" Temp");
printf(" Power");
printf(" Error");
printf(" FP");
printf(" Size");
printf("\n");
printf(" C");
printf(" OnDays");
printf(" Count");
printf(" ");
printf(" TB");
printf(" "); printl("Serial", serial_pad);
printf(" "); printl("Device", device_pad);
printf(" Disk");
printf("\n");
/* |<##################################################################72>|####80>| */
printf(" -----------------------------------------------------------------------\n");
array_failure_rate = 0;
for (i = tommy_list_head(low); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
double afr;
uint64_t flag;
if (devinfo->smart[SMART_TEMPERATURE_CELSIUS] != SMART_UNASSIGNED)
printf("%7" PRIu64, devinfo->smart[SMART_TEMPERATURE_CELSIUS] & mask16);
else if (devinfo->smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS] != SMART_UNASSIGNED)
printf("%7" PRIu64, devinfo->smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS] & mask16);
else
printf(" -");
if (devinfo->smart[SMART_POWER_ON_HOURS] != SMART_UNASSIGNED)
printf("%7" PRIu64, (devinfo->smart[SMART_POWER_ON_HOURS] & mask32) / 24);
else
printf(" -");
if (devinfo->smart[SMART_FLAGS] != SMART_UNASSIGNED)
flag = devinfo->smart[SMART_FLAGS];
else
flag = 0;
if (flag & SMARTCTL_FLAG_FAIL)
printf(" FAIL");
else if (flag & SMARTCTL_FLAG_PREFAIL)
printf(" PREFAIL");
else if (flag & SMARTCTL_FLAG_PREFAIL_LOGGED)
printf(" logfail");
else if (devinfo->smart[SMART_ERROR] != SMART_UNASSIGNED
&& devinfo->smart[SMART_ERROR] != 0)
printf("%8" PRIu64, devinfo->smart[SMART_ERROR]);
else if (flag & SMARTCTL_FLAG_ERROR)
printf(" logerr");
else if (flag & SMARTCTL_FLAG_ERROR_LOGGED)
printf(" selferr");
else if (devinfo->smart[SMART_ERROR] == 0)
printf(" 0");
else
printf(" -");
/* if some fail/prefail attribute, make the command to fail */
if (flag & (SMARTCTL_FLAG_FAIL | SMARTCTL_FLAG_PREFAIL))
make_it_fail = 1;
/* note that in older smartmontools, like 5.x, rotation rate is not present */
/* and then it could remain unassigned */
if (flag & (SMARTCTL_FLAG_UNSUPPORTED | SMARTCTL_FLAG_OPEN)) {
/* if error running smartctl, skip AFR estimation */
afr = 0;
printf(" n/a");
} else if (devinfo->smart[SMART_ROTATION_RATE] == 0) {
/* if SSD, skip AFR estimation as data is from not SSD disks */
afr = 0;
printf(" SSD");
} else {
afr = smart_afr(devinfo->smart, devinfo->smart_model);
if (afr == 0) {
/* this happens only if no data */
printf(" -");
} else {
/* use only the disks in the array */
if (devinfo->parent != 0 || !have_parent)
array_failure_rate += afr;
printf("%4.0f%%", poisson_prob_n_or_more_failures(afr, 1) * 100);
}
}
if (devinfo->smart[SMART_SIZE] != SMART_UNASSIGNED)
printf(" %4.1f", devinfo->smart[SMART_SIZE] / 1E12);
else
printf(" -");
printf(" ");
if (*devinfo->smart_serial)
printl(devinfo->smart_serial, serial_pad);
else
printl("-", serial_pad);
printf(" ");
if (*devinfo->file)
printl(devinfo->file, device_pad);
else
printl("-", device_pad);
printf(" ");
if (*devinfo->name)
printf("%s", devinfo->name);
else
printf("-");
printf("\n");
log_tag("smart:%s:%s\n", devinfo->file, devinfo->name);
if (devinfo->smart_serial[0])
log_tag("attr:%s:%s:serial:%s\n", devinfo->file, devinfo->name, esc_tag(devinfo->smart_serial, esc_buffer));
if (devinfo->smart_vendor[0])
log_tag("attr:%s:%s:vendor:%s\n", devinfo->file, devinfo->name, esc_tag(devinfo->smart_vendor, esc_buffer));
if (devinfo->smart_model[0])
log_tag("attr:%s:%s:model:%s\n", devinfo->file, devinfo->name, esc_tag(devinfo->smart_model, esc_buffer));
if (afr != 0)
log_tag("attr:%s:%s:afr:%g:%g\n", devinfo->file, devinfo->name, afr, poisson_prob_n_or_more_failures(afr, 1));
if (devinfo->smart[SMART_SIZE] != SMART_UNASSIGNED)
log_tag("attr:%s:%s:size:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_SIZE]);
if (devinfo->smart[SMART_ERROR] != SMART_UNASSIGNED)
log_tag("attr:%s:%s:error:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_ERROR]);
if (devinfo->smart[SMART_ROTATION_RATE] != SMART_UNASSIGNED)
log_tag("attr:%s:%s:rotationrate:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_ROTATION_RATE]);
if (devinfo->smart[SMART_FLAGS] != SMART_UNASSIGNED)
log_tag("attr:%s:%s:flags:%" PRIu64 ":%" PRIx64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_FLAGS], devinfo->smart[SMART_FLAGS]);
for (j = 0; j < 256; ++j)
if (devinfo->smart[j] != SMART_UNASSIGNED)
log_tag("attr:%s:%s:%u:%" PRIu64 ":%" PRIx64 "\n", devinfo->file, devinfo->name, j, devinfo->smart[j], devinfo->smart[j]);
}
printf("\n");
/* |<##################################################################72>|####80>| */
printf("The FP column is the estimated probability (in percentage) that the disk\n");
printf("is going to fail in the next year.\n");
printf("\n");
/*
* The probability of one and of at least one failure is computed assuming
* a Poisson distribution with the estimated array failure rate.
*/
p_at_least_one_failure = poisson_prob_n_or_more_failures(array_failure_rate, 1);
printf("Probability that at least one disk is going to fail in the next year is %.0f%%.\n", p_at_least_one_failure * 100);
log_tag("summary:array_failure:%g:%g\n", array_failure_rate, p_at_least_one_failure);
/* print extra stats only in verbose mode */
if (msg_level < MSG_VERBOSE)
goto bail;
/* |<##################################################################72>|####80>| */
printf("\n");
printf("Probability of data loss in the next year for different parity and\n");
printf("combined scrub and repair time:\n");
printf("\n");
printf(" Parity 1 Week 1 Month 3 Months\n");
printf(" -----------------------------------------------------------------------\n");
for (j = 0; j < RAID_PARITY_MAX; ++j) {
printf("%6u", j + 1);
printf(" ");
printp(raid_prob_of_one_or_more_failures(array_failure_rate, 365.0 / 7, n, j + 1) * 100, 19);
printf(" ");
printp(raid_prob_of_one_or_more_failures(array_failure_rate, 365.0 / 30, n, j + 1) * 100, 17);
printf(" ");
printp(raid_prob_of_one_or_more_failures(array_failure_rate, 365.0 / 90, n, j + 1) * 100, 13);
printf("\n");
}
printf("\n");
/* |<##################################################################72>|####80>| */
printf("These values are the probabilities that in the next year you'll have a\n");
printf("sequence of failures that the parity WON'T be able to recover, assuming\n");
printf("that you regularly scrub, and in case repair, the array in the specified\n");
printf("time.\n");
bail:
if (make_it_fail) {
printf("\n");
printf("DANGER! SMART is reporting that one or more disks are FAILING!\n");
printf("Please take immediate action!\n");
exit(EXIT_FAILURE);
}
}
/**
* Fill with fake data the device list.
*/
static int devtest(tommy_list* low, int operation)
{
unsigned c;
if (operation != DEVICE_SMART)
return -1;
/* add some fake data */
for (c = 0; c < 16; ++c) {
devinfo_t* entry;
int j;
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = c;
tommy_list_insert_tail(low, &entry->node, entry);
for (j = 0; j < 256; ++j) {
switch (c) {
case 0 : entry->smart[j] = 0; break;
case 1 : entry->smart[j] = SMART_UNASSIGNED; break;
default :
if (j == 193)
entry->smart[j] = c - 2;
else
entry->smart[j] = 0;
break;
}
}
if (c == 0) {
entry->smart_serial[0] = 0;
entry->smart_vendor[0] = 0;
entry->smart_model[0] = 0;
entry->file[0] = 0;
entry->name[0] = 0;
entry->smart[SMART_SIZE] = SMART_UNASSIGNED;
entry->smart[SMART_ROTATION_RATE] = 0;
} else {
snprintf(entry->smart_serial, sizeof(entry->smart_serial), "S%u", c);
snprintf(entry->smart_vendor, sizeof(entry->smart_vendor), "V%u", c);
snprintf(entry->smart_model, sizeof(entry->smart_model), "M%u", c);
pathcpy(entry->file, sizeof(entry->name), "file");
pathcpy(entry->name, sizeof(entry->name), "name");
entry->smart[SMART_SIZE] = c * TERA;
entry->smart[SMART_ROTATION_RATE] = 7200;
}
entry->smart[SMART_ERROR] = 0;
entry->smart[SMART_FLAGS] = SMART_UNASSIGNED;
switch (c) {
case 3 : entry->smart[SMART_ERROR] = 1; break;
case 4 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_UNSUPPORTED; break;
case 5 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_COMMAND; break;
case 6 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_OPEN; break;
case 7 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_FAIL; break;
case 8 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_PREFAIL; break;
case 9 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_PREFAIL_LOGGED; break;
case 10 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_ERROR; break;
case 11 : entry->smart[SMART_FLAGS] = SMARTCTL_FLAG_ERROR_LOGGED; break;
}
}
return 0;
}
void state_device(struct snapraid_state* state, int operation, tommy_list* filterlist_disk)
{
tommy_node* i;
unsigned j;
tommy_list high;
tommy_list low;
int ret;
switch (operation) {
case DEVICE_UP : msg_progress("Spinup...\n"); break;
case DEVICE_DOWN : msg_progress("Spindown...\n"); break;
}
tommy_list_init(&high);
tommy_list_init(&low);
/* for all disks */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
devinfo_t* entry;
if (filterlist_disk != 0 && filter_path(filterlist_disk, 0, disk->name, 0) != 0)
continue;
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = disk->device;
pathcpy(entry->name, sizeof(entry->name), disk->name);
pathcpy(entry->mount, sizeof(entry->mount), disk->dir);
pathcpy(entry->smartctl, sizeof(entry->smartctl), disk->smartctl);
tommy_list_insert_tail(&high, &entry->node, entry);
}
/* for all parities */
for (j = 0; j < state->level; ++j) {
devinfo_t* entry;
unsigned s;
if (filterlist_disk != 0 && filter_path(filterlist_disk, 0, lev_config_name(j), 0) != 0)
continue;
for (s = 0; s < state->parity[j].split_mac; ++s) {
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = state->parity[j].split_map[s].device;
pathcpy(entry->name, sizeof(entry->name), lev_config_name(j));
pathcpy(entry->mount, sizeof(entry->mount), state->parity[j].split_map[s].path);
pathcpy(entry->smartctl, sizeof(entry->smartctl), state->parity[j].smartctl);
pathcut(entry->mount); /* remove the parity file */
tommy_list_insert_tail(&high, &entry->node, entry);
}
}
if (state->opt.fake_device) {
ret = devtest(&low, operation);
} else {
int others = operation == DEVICE_SMART;
ret = devquery(&high, &low, operation, others);
}
/* if the list is empty, it's not supported in this platform */
if (ret == 0 && tommy_list_empty(&low))
ret = -1;
if (ret != 0) {
const char* ope = 0;
switch (operation) {
case DEVICE_UP : ope = "Spinup"; break;
case DEVICE_DOWN : ope = "Spindown"; break;
case DEVICE_LIST : ope = "Device listing"; break;
case DEVICE_SMART : ope = "Smart"; break;
}
log_fatal("%s is unsupported in this platform.\n", ope);
} else {
if (operation == DEVICE_LIST) {
for (i = tommy_list_head(&low); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
devinfo_t* parent = devinfo->parent;
#ifdef _WIN32
printf("%" PRIu64 "\t%s\t%08" PRIx64 "\t%s\t%s\n", devinfo->device, devinfo->wfile, parent->device, parent->wfile, parent->name);
#else
printf("%u:%u\t%s\t%u:%u\t%s\t%s\n", major(devinfo->device), minor(devinfo->device), devinfo->file, major(parent->device), minor(parent->device), parent->file, parent->name);
#endif
}
}
if (operation == DEVICE_SMART)
state_smart(state->level + tommy_list_count(&state->disklist), &low);
}
tommy_list_foreach(&high, free);
tommy_list_foreach(&low, free);
}
snapraid-12.1/cmdline/dry.c 0000664 0000000 0000000 00000032252 14166610522 0015622 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "util.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
#include "io.h"
#include "raid/raid.h"
/****************************************************************************/
/* dry */
static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task* task)
{
struct snapraid_io* io = worker->io;
struct snapraid_state* state = io->state;
struct snapraid_handle* handle = worker->handle;
struct snapraid_disk* disk = handle->disk;
block_off_t blockcur = task->position;
unsigned char* buffer = task->buffer;
int ret;
char esc_buffer[ESC_MAX];
/* if the disk position is not used */
if (!disk) {
/* use an empty block */
memset(buffer, 0, state->block_size);
task->state = TASK_STATE_DONE;
return;
}
/* get the block */
task->block = fs_par2block_find(disk, blockcur);
/* if the block is not used */
if (!block_has_file(task->block)) {
/* use an empty block */
memset(buffer, 0, state->block_size);
task->state = TASK_STATE_DONE;
return;
}
/* get the file of this block */
task->file = fs_par2file_get(disk, blockcur, &task->file_pos);
/* if the file is different than the current one, close it */
if (handle->file != 0 && handle->file != task->file) {
/* keep a pointer at the file we are going to close for error reporting */
struct snapraid_file* report = handle->file;
ret = handle_close(handle);
if (ret == -1) {
/* LCOV_EXCL_START */
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
if (errno == EIO) {
log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to dry.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_IOERROR;
return;
}
log_tag("error:%u:%s:%s: Close error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to dry.\n");
log_fatal("Ensure that file '%s' can be accessed.\n", handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_ERROR;
return;
/* LCOV_EXCL_STOP */
}
}
ret = handle_open(handle, task->file, state->file_mode, log_error, 0);
if (ret == -1) {
if (errno == EIO) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Open EIO error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output open error in a data disk, it isn't possible to dry.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_IOERROR;
return;
/* LCOV_EXCL_STOP */
}
log_tag("error:%u:%s:%s: Open error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
if (task->read_size == -1) {
if (errno == EIO) {
log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
log_error("Input/Output error in file '%s' at position '%u'\n", handle->path, task->file_pos);
task->state = TASK_STATE_IOERROR_CONTINUE;
return;
}
log_tag("error:%u:%s:%s: Read error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
/* store the path of the opened file */
pathcpy(task->path, sizeof(task->path), handle->path);
task->state = TASK_STATE_DONE;
}
static void dry_parity_reader(struct snapraid_worker* worker, struct snapraid_task* task)
{
struct snapraid_io* io = worker->io;
struct snapraid_state* state = io->state;
struct snapraid_parity_handle* parity_handle = worker->parity_handle;
unsigned level = parity_handle->level;
block_off_t blockcur = task->position;
unsigned char* buffer = task->buffer;
int ret;
/* read the parity */
ret = parity_read(parity_handle, blockcur, buffer, state->block_size, log_error);
if (ret == -1) {
if (errno == EIO) {
log_tag("parity_error:%u:%s: Read EIO error. %s\n", blockcur, lev_config_name(level), strerror(errno));
log_error("Input/Output error in parity '%s' at position '%u'\n", lev_config_name(level), blockcur);
task->state = TASK_STATE_IOERROR_CONTINUE;
return;
}
log_tag("parity_error:%u:%s: Read error. %s\n", blockcur, lev_config_name(level), strerror(errno));
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
task->state = TASK_STATE_DONE;
}
static int state_dry_process(struct snapraid_state* state, struct snapraid_parity_handle* parity_handle, block_off_t blockstart, block_off_t blockmax)
{
struct snapraid_io io;
struct snapraid_handle* handle;
unsigned diskmax;
block_off_t blockcur;
unsigned j;
unsigned buffermax;
int ret;
data_off_t countsize;
block_off_t countpos;
block_off_t countmax;
unsigned error;
unsigned io_error;
unsigned l;
unsigned* waiting_map;
unsigned waiting_mac;
char esc_buffer[ESC_MAX];
handle = handle_mapping(state, &diskmax);
/* we need 1 * data + 2 * parity */
buffermax = diskmax + 2 * state->level;
/* initialize the io threads */
io_init(&io, state, state->opt.io_cache, buffermax, dry_data_reader, handle, diskmax, dry_parity_reader, 0, parity_handle, state->level);
/* possibly waiting disks */
waiting_mac = diskmax > RAID_PARITY_MAX ? diskmax : RAID_PARITY_MAX;
waiting_map = malloc_nofail(waiting_mac * sizeof(unsigned));
error = 0;
io_error = 0;
/* drop until now */
state_usage_waste(state);
countmax = blockmax - blockstart;
countsize = 0;
countpos = 0;
/* start all the worker threads */
io_start(&io, blockstart, blockmax, 0);
state_progress_begin(state, blockstart, blockmax, countmax);
while (1) {
void** buffer;
/* go to the next block */
blockcur = io_read_next(&io, &buffer);
if (blockcur >= blockmax)
break;
/* until now is scheduling */
state_usage_sched(state);
/* for each disk, process the block */
for (j = 0; j < diskmax; ++j) {
struct snapraid_task* task;
int read_size;
struct snapraid_block* block;
struct snapraid_disk* disk;
unsigned diskcur;
/* until now is misc */
state_usage_misc(state);
/* get the next task */
task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac);
/* until now is disk */
state_usage_disk(state, handle, waiting_map, waiting_mac);
/* get the task results */
disk = task->disk;
block = task->block;
read_size = task->read_size;
/* if the disk position is not used */
if (!disk)
continue;
state_usage_file(state, disk, task->file);
/* if the block is not used */
if (!block_has_file(block))
continue;
/* handle error conditions */
if (task->state == TASK_STATE_IOERROR) {
/* LCOV_EXCL_START */
++io_error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR) {
/* LCOV_EXCL_START */
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR_CONTINUE) {
++error;
continue;
}
if (task->state == TASK_STATE_IOERROR_CONTINUE) {
++io_error;
if (io_error >= state->opt.io_error_limit) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Too many input/output read error in a data disk, it isn't possible to scrub.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, task->path);
log_fatal("Stopping at block %u\n", blockcur);
goto bail;
/* LCOV_EXCL_STOP */
}
/* otherwise continue */
continue;
}
if (task->state != TASK_STATE_DONE) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in task state\n");
os_abort();
/* LCOV_EXCL_STOP */
}
countsize += read_size;
}
/* until now is misc */
state_usage_misc(state);
/* read the parity */
for (l = 0; l < state->level; ++l) {
struct snapraid_task* task;
unsigned levcur;
task = io_parity_read(&io, &levcur, waiting_map, &waiting_mac);
/* until now is parity */
state_usage_parity(state, waiting_map, waiting_mac);
/* handle error conditions */
if (task->state == TASK_STATE_IOERROR) {
/* LCOV_EXCL_START */
++io_error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR) {
/* LCOV_EXCL_START */
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR_CONTINUE) {
++error;
continue;
}
if (task->state == TASK_STATE_IOERROR_CONTINUE) {
++io_error;
if (io_error >= state->opt.io_error_limit) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Too many input/output read error in the %s disk, it isn't possible to scrub.\n", lev_name(levcur));
log_fatal("Ensure that disk '%s' is sane and can be read.\n", lev_config_name(levcur));
log_fatal("Stopping at block %u\n", blockcur);
goto bail;
/* LCOV_EXCL_STOP */
}
continue;
}
if (task->state != TASK_STATE_DONE) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in task state\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
/* count the number of processed block */
++countpos;
/* progress */
if (state_progress(state, &io, blockcur, countpos, countmax, countsize)) {
/* LCOV_EXCL_START */
break;
/* LCOV_EXCL_STOP */
}
}
state_progress_end(state, countpos, countmax, countsize);
state_usage_print(state);
bail:
/* stop all the worker threads */
io_stop(&io);
for (j = 0; j < diskmax; ++j) {
struct snapraid_file* file = handle[j].file;
struct snapraid_disk* disk = handle[j].disk;
ret = handle_close(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
if (error || io_error) {
msg_status("\n");
msg_status("%8u file errors\n", error);
msg_status("%8u io errors\n", io_error);
} else {
msg_status("Everything OK\n");
}
if (error)
log_fatal("DANGER! Unexpected errors!\n");
if (io_error)
log_fatal("DANGER! Unexpected input/output errors!\n");
free(handle);
free(waiting_map);
io_done(&io);
if (error + io_error != 0)
return -1;
return 0;
}
void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount)
{
block_off_t blockmax;
int ret;
struct snapraid_parity_handle parity_handle[LEV_MAX];
unsigned error;
unsigned l;
msg_progress("Drying...\n");
blockmax = parity_allocated_size(state);
if (blockstart > blockmax) {
/* LCOV_EXCL_START */
log_fatal("Error in the specified starting block %u. It's bigger than the parity size %u.\n", blockstart, blockmax);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* adjust the number of block to process */
if (blockcount != 0 && blockstart + blockcount < blockmax) {
blockmax = blockstart + blockcount;
}
/* open the file for reading */
/* it may fail if the file doesn't exist, in this case we continue to dry the files */
for (l = 0; l < state->level; ++l) {
ret = parity_open(&parity_handle[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without an accessible %s file, it isn't possible to dry.\n", lev_name(l));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
error = 0;
/* skip degenerated cases of empty parity, or skipping all */
if (blockstart < blockmax) {
ret = state_dry_process(state, parity_handle, blockstart, blockmax);
if (ret == -1) {
/* LCOV_EXCL_START */
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
/* try to close only if opened */
for (l = 0; l < state->level; ++l) {
ret = parity_close(&parity_handle[l]);
if (ret == -1) {
/* LCOV_EXCL_START */
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
/* abort if required */
if (error != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
snapraid-12.1/cmdline/dup.c 0000664 0000000 0000000 00000010063 14166610522 0015610 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "util.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
/****************************************************************************/
/* dup */
struct snapraid_hash {
struct snapraid_disk* disk; /**< Disk. */
struct snapraid_file* file; /**< File. */
unsigned char hash[HASH_MAX]; /**< Hash of the whole file. */
/* nodes for data structures */
tommy_hashdyn_node node;
};
struct snapraid_hash* hash_alloc(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
{
struct snapraid_hash* hash;
block_off_t i;
unsigned char* buf;
size_t hash_size = BLOCK_HASH_SIZE;
hash = malloc_nofail(sizeof(struct snapraid_hash));
hash->disk = disk;
hash->file = file;
buf = malloc_nofail(file->blockmax * hash_size);
/* set the back pointer */
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_block* block = fs_file2block_get(file, i);
memcpy(buf + i * hash_size, block->hash, hash_size);
if (!block_has_updated_hash(block)) {
free(buf);
free(hash);
return 0;
}
}
memhash(state->besthash, state->hashseed, hash->hash, buf, file->blockmax * hash_size);
free(buf);
return hash;
}
static inline tommy_uint32_t hash_hash(struct snapraid_hash* hash)
{
return tommy_hash_u32(0, hash->hash, HASH_MAX);
}
void hash_free(struct snapraid_hash* hash)
{
free(hash);
}
int hash_compare(const void* void_arg, const void* void_data)
{
const char* arg = void_arg;
const struct snapraid_hash* hash = void_data;
return memcmp(arg, hash->hash, HASH_MAX);
}
void state_dup(struct snapraid_state* state)
{
tommy_hashdyn hashset;
tommy_node* i;
unsigned count;
data_off_t size;
char esc_buffer[ESC_MAX];
char esc_buffer_alt[ESC_MAX];
tommy_hashdyn_init(&hashset);
count = 0;
size = 0;
msg_progress("Comparing...\n");
/* for each disk */
for (i = state->disklist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_disk* disk = i->data;
/* for each file */
for (j = disk->filelist; j != 0; j = j->next) {
struct snapraid_file* file = j->data;
struct snapraid_hash* hash;
tommy_hash_t hash32;
/* if empty, skip it */
if (file->size == 0)
continue;
hash = hash_alloc(state, disk, file);
/* if no hash, skip it */
if (!hash)
continue;
hash32 = hash_hash(hash);
struct snapraid_hash* found = tommy_hashdyn_search(&hashset, hash_compare, hash->hash, hash32);
if (found) {
++count;
size += found->file->size;
log_tag("dup:%s:%s:%s:%s:%" PRIu64 ": dup\n", disk->name, esc_tag(file->sub, esc_buffer), found->disk->name, esc_tag(found->file->sub, esc_buffer_alt), found->file->size);
printf("%12" PRIu64 " %s = %s\n", file->size, fmt_term(disk, file->sub, esc_buffer), fmt_term(found->disk, found->file->sub, esc_buffer_alt));
hash_free(hash);
} else {
tommy_hashdyn_insert(&hashset, &hash->node, hash, hash32);
}
}
}
tommy_hashdyn_foreach(&hashset, (tommy_foreach_func*)hash_free);
tommy_hashdyn_done(&hashset);
msg_status("\n");
msg_status("%8u duplicates, for %" PRIu64 " GB\n", count, size / GIGA);
if (count)
msg_status("There are duplicates!\n");
else
msg_status("No duplicates\n");
log_tag("summary:dup_count:%u\n", count);
log_tag("summary:dup_size:%" PRIu64 "\n", size);
if (count == 0) {
log_tag("summary:exit:unique\n");
} else {
log_tag("summary:exit:dup\n");
}
log_flush();
}
snapraid-12.1/cmdline/elem.c 0000664 0000000 0000000 00000111050 14166610522 0015740 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "elem.h"
#include "support.h"
#include "util.h"
/****************************************************************************/
/* snapraid */
int BLOCK_HASH_SIZE = HASH_MAX;
struct snapraid_content* content_alloc(const char* path, uint64_t dev)
{
struct snapraid_content* content;
content = malloc_nofail(sizeof(struct snapraid_content));
pathimport(content->content, sizeof(content->content), path);
content->device = dev;
return content;
}
void content_free(struct snapraid_content* content)
{
free(content);
}
struct snapraid_filter* filter_alloc_file(int direction, const char* pattern)
{
struct snapraid_filter* filter;
char* i;
char* first;
char* last;
int token_is_valid;
int token_is_filled;
filter = malloc_nofail(sizeof(struct snapraid_filter));
pathimport(filter->pattern, sizeof(filter->pattern), pattern);
filter->direction = direction;
/* find first and last slash */
first = 0;
last = 0;
/* reject invalid tokens, like "", ".", ".." and more dots */
token_is_valid = 0;
token_is_filled = 0;
for (i = filter->pattern; *i; ++i) {
if (*i == '/') {
/* reject invalid tokens, but accept an empty one as first */
if (!token_is_valid && (first != 0 || token_is_filled)) {
free(filter);
return 0;
}
token_is_valid = 0;
token_is_filled = 0;
/* update slash position */
if (!first)
first = i;
last = i;
} else if (*i != '.') {
token_is_valid = 1;
token_is_filled = 1;
} else {
token_is_filled = 1;
}
}
/* reject invalid tokens, but accept an empty one as last, but not if it's the only one */
if (!token_is_valid && (first == 0 || token_is_filled)) {
free(filter);
return 0;
}
/* it's a file filter */
filter->is_disk = 0;
if (first == 0) {
/* no slash */
filter->is_path = 0;
filter->is_dir = 0;
} else if (first == last && last[1] == 0) {
/* one slash at the end */
filter->is_path = 0;
filter->is_dir = 1;
last[0] = 0;
} else {
/* at least a slash not at the end */
filter->is_path = 1;
if (last[1] == 0) {
filter->is_dir = 1;
last[0] = 0;
} else {
filter->is_dir = 0;
}
/* a slash must be the first char, as we don't support PATH/FILE and PATH/DIR/ */
if (filter->pattern[0] != '/') {
free(filter);
return 0;
}
}
return filter;
}
struct snapraid_filter* filter_alloc_disk(int direction, const char* pattern)
{
struct snapraid_filter* filter;
filter = malloc_nofail(sizeof(struct snapraid_filter));
pathimport(filter->pattern, sizeof(filter->pattern), pattern);
filter->direction = direction;
/* it's a disk filter */
filter->is_disk = 1;
filter->is_path = 0;
filter->is_dir = 0;
/* no slash allowed in disk names */
if (strchr(filter->pattern, '/') != 0) {
/* LCOV_EXCL_START */
free(filter);
return 0;
/* LCOV_EXCL_STOP */
}
return filter;
}
void filter_free(struct snapraid_filter* filter)
{
free(filter);
}
const char* filter_type(struct snapraid_filter* filter, char* out, size_t out_size)
{
const char* direction;
if (filter->direction < 0)
direction = "exclude";
else
direction = "include";
if (filter->is_disk)
pathprint(out, out_size, "%s %s:", direction, filter->pattern);
else if (filter->is_dir)
pathprint(out, out_size, "%s %s/", direction, filter->pattern);
else
pathprint(out, out_size, "%s %s", direction, filter->pattern);
return out;
}
static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* path, const char* name, int is_dir)
{
int ret = 0;
/* match dirs with dirs and files with files */
if (filter->is_dir && !is_dir)
return 0;
if (!filter->is_dir && is_dir)
return 0;
if (filter->is_path) {
/* skip initial slash, as always missing from the path */
if (fnmatch(filter->pattern + 1, path, FNM_PATHNAME | FNM_CASEINSENSITIVE_FOR_WIN) == 0)
ret = filter->direction;
} else {
if (fnmatch(filter->pattern, name, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
ret = filter->direction;
}
if (reason != 0 && ret < 0)
*reason = filter;
return ret;
}
static int filter_recurse(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* const_path, int is_dir)
{
char path[PATH_MAX];
char* name;
unsigned i;
pathcpy(path, sizeof(path), const_path);
/* filter for all the directories */
name = path;
for (i = 0; path[i] != 0; ++i) {
if (path[i] == '/') {
/* set a terminator */
path[i] = 0;
/* filter the directory */
if (filter_apply(filter, reason, path, name, 1) != 0)
return filter->direction;
/* restore the slash */
path[i] = '/';
/* next name */
name = path + i + 1;
}
}
/* filter the final file */
if (filter_apply(filter, reason, path, name, is_dir) != 0)
return filter->direction;
return 0;
}
static int filter_element(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub, int is_dir, int is_def_include)
{
tommy_node* i;
int direction = 1; /* by default include all */
/* for each filter */
for (i = tommy_list_head(filterlist); i != 0; i = i->next) {
int ret;
struct snapraid_filter* filter = i->data;
if (filter->is_disk) {
if (fnmatch(filter->pattern, disk, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
ret = filter->direction;
else
ret = 0;
if (reason != 0 && ret < 0)
*reason = filter;
} else {
ret = filter_recurse(filter, reason, sub, is_dir);
}
if (ret > 0) {
/* include the file */
return 0;
} else if (ret < 0) {
/* exclude the file */
return -1;
} else {
/* default is opposite of the last filter */
direction = -filter->direction;
if (reason != 0 && direction < 0)
*reason = filter;
/* continue with the next one */
}
}
/* directories are always included by default, otherwise we cannot apply rules */
/* to the contained files */
if (is_def_include)
return 0;
/* files are excluded/included depending of the last rule processed */
if (direction < 0)
return -1;
return 0;
}
int filter_path(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub)
{
return filter_element(filterlist, reason, disk, sub, 0, 0);
}
int filter_subdir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub)
{
return filter_element(filterlist, reason, disk, sub, 1, 1);
}
int filter_emptydir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub)
{
return filter_element(filterlist, reason, disk, sub, 1, 0);
}
int filter_existence(int filter_missing, const char* dir, const char* sub)
{
char path[PATH_MAX];
struct stat st;
if (!filter_missing)
return 0;
/* we directly check if in the disk the file is present or not */
pathprint(path, sizeof(path), "%s%s", dir, sub);
if (lstat(path, &st) != 0) {
/* if the file doesn't exist, we don't filter it out */
if (errno == ENOENT)
return 0;
/* LCOV_EXCL_START */
log_fatal("Error in stat file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* the file is present, so we filter it out */
return 1;
}
int filter_correctness(int filter_error, tommy_arrayblkof* infoarr, struct snapraid_disk* disk, struct snapraid_file* file)
{
unsigned i;
if (!filter_error)
return 0;
/* check each block of the file */
for (i = 0; i < file->blockmax; ++i) {
block_off_t parity_pos = fs_file2par_get(disk, file, i);
snapraid_info info = info_get(infoarr, parity_pos);
/* if the file has a bad block, don't exclude it */
if (info_get_bad(info))
return 0;
}
/* the file is correct, so we filter it out */
return 1;
}
int filter_content(tommy_list* contentlist, const char* path)
{
tommy_node* i;
for (i = tommy_list_head(contentlist); i != 0; i = i->next) {
struct snapraid_content* content = i->data;
char tmp[PATH_MAX];
if (pathcmp(content->content, path) == 0)
return -1;
/* exclude also the ".tmp" copy used to save it */
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
if (pathcmp(tmp, path) == 0)
return -1;
/* exclude also the ".lock" file */
pathprint(tmp, sizeof(tmp), "%s.lock", content->content);
if (pathcmp(tmp, path) == 0)
return -1;
}
return 0;
}
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical)
{
struct snapraid_file* file;
block_off_t i;
file = malloc_nofail(sizeof(struct snapraid_file));
file->sub = strdup_nofail(sub);
file->size = size;
file->blockmax = (size + block_size - 1) / block_size;
file->mtime_sec = mtime_sec;
file->mtime_nsec = mtime_nsec;
file->inode = inode;
file->physical = physical;
file->flag = 0;
file->blockvec = malloc_nofail(file->blockmax * block_sizeof());
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_block* block = file_block(file, i);
block_state_set(block, BLOCK_STATE_CHG);
hash_invalid_set(block->hash);
}
return file;
}
struct snapraid_file* file_dup(struct snapraid_file* copy)
{
struct snapraid_file* file;
block_off_t i;
file = malloc_nofail(sizeof(struct snapraid_file));
file->sub = strdup_nofail(copy->sub);
file->size = copy->size;
file->blockmax = copy->blockmax;
file->mtime_sec = copy->mtime_sec;
file->mtime_nsec = copy->mtime_nsec;
file->inode = copy->inode;
file->physical = copy->physical;
file->flag = copy->flag;
file->blockvec = malloc_nofail(file->blockmax * block_sizeof());
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_block* block = file_block(file, i);
struct snapraid_block* copy_block = file_block(copy, i);
block->state = copy_block->state;
memcpy(block->hash, copy_block->hash, BLOCK_HASH_SIZE);
}
return file;
}
void file_free(struct snapraid_file* file)
{
free(file->sub);
file->sub = 0;
free(file->blockvec);
file->blockvec = 0;
free(file);
}
void file_rename(struct snapraid_file* file, const char* sub)
{
free(file->sub);
file->sub = strdup_nofail(sub);
}
void file_copy(struct snapraid_file* src_file, struct snapraid_file* dst_file)
{
block_off_t i;
if (src_file->size != dst_file->size) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in copy file with different size\n");
os_abort();
/* LCOV_EXCL_STOP */
}
if (src_file->mtime_sec != dst_file->mtime_sec) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in copy file with different mtime_sec\n");
os_abort();
/* LCOV_EXCL_STOP */
}
if (src_file->mtime_nsec != dst_file->mtime_nsec) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in copy file with different mtime_nsec\n");
os_abort();
/* LCOV_EXCL_STOP */
}
for (i = 0; i < dst_file->blockmax; ++i) {
/* set a block with hash computed but without parity */
block_state_set(file_block(dst_file, i), BLOCK_STATE_REP);
/* copy the hash */
memcpy(file_block(dst_file, i)->hash, file_block(src_file, i)->hash, BLOCK_HASH_SIZE);
}
file_flag_set(dst_file, FILE_IS_COPY);
}
const char* file_name(const struct snapraid_file* file)
{
const char* r = strrchr(file->sub, '/');
if (!r)
r = file->sub;
else
++r;
return r;
}
unsigned file_block_size(struct snapraid_file* file, block_off_t file_pos, unsigned block_size)
{
/* if it's the last block */
if (file_pos + 1 == file->blockmax) {
unsigned block_remainder;
if (file->size == 0)
return 0;
block_remainder = file->size % block_size;
if (block_remainder == 0)
block_remainder = block_size;
return block_remainder;
}
return block_size;
}
int file_block_is_last(struct snapraid_file* file, block_off_t file_pos)
{
if (file_pos == 0 && file->blockmax == 0)
return 1;
if (file_pos >= file->blockmax) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file block position\n");
os_abort();
/* LCOV_EXCL_STOP */
}
return file_pos == file->blockmax - 1;
}
int file_inode_compare_to_arg(const void* void_arg, const void* void_data)
{
const uint64_t* arg = void_arg;
const struct snapraid_file* file = void_data;
if (*arg < file->inode)
return -1;
if (*arg > file->inode)
return 1;
return 0;
}
int file_inode_compare(const void* void_a, const void* void_b)
{
const struct snapraid_file* file_a = void_a;
const struct snapraid_file* file_b = void_b;
if (file_a->inode < file_b->inode)
return -1;
if (file_a->inode > file_b->inode)
return 1;
return 0;
}
int file_path_compare(const void* void_a, const void* void_b)
{
const struct snapraid_file* file_a = void_a;
const struct snapraid_file* file_b = void_b;
return strcmp(file_a->sub, file_b->sub);
}
int file_physical_compare(const void* void_a, const void* void_b)
{
const struct snapraid_file* file_a = void_a;
const struct snapraid_file* file_b = void_b;
if (file_a->physical < file_b->physical)
return -1;
if (file_a->physical > file_b->physical)
return 1;
return 0;
}
int file_path_compare_to_arg(const void* void_arg, const void* void_data)
{
const char* arg = void_arg;
const struct snapraid_file* file = void_data;
return strcmp(arg, file->sub);
}
int file_name_compare(const void* void_a, const void* void_b)
{
const struct snapraid_file* file_a = void_a;
const struct snapraid_file* file_b = void_b;
const char* name_a = file_name(file_a);
const char* name_b = file_name(file_b);
return strcmp(name_a, name_b);
}
int file_stamp_compare(const void* void_a, const void* void_b)
{
const struct snapraid_file* file_a = void_a;
const struct snapraid_file* file_b = void_b;
if (file_a->size < file_b->size)
return -1;
if (file_a->size > file_b->size)
return 1;
if (file_a->mtime_sec < file_b->mtime_sec)
return -1;
if (file_a->mtime_sec > file_b->mtime_sec)
return 1;
if (file_a->mtime_nsec < file_b->mtime_nsec)
return -1;
if (file_a->mtime_nsec > file_b->mtime_nsec)
return 1;
return 0;
}
int file_namestamp_compare(const void* void_a, const void* void_b)
{
int ret;
ret = file_name_compare(void_a, void_b);
if (ret != 0)
return ret;
return file_stamp_compare(void_a, void_b);
}
int file_pathstamp_compare(const void* void_a, const void* void_b)
{
int ret;
ret = file_path_compare(void_a, void_b);
if (ret != 0)
return ret;
return file_stamp_compare(void_a, void_b);
}
struct snapraid_extent* extent_alloc(block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count)
{
struct snapraid_extent* extent;
if (count == 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when allocating empty extent for file '%s' at position '%u/%u'\n", file->sub, file_pos, file->blockmax);
os_abort();
/* LCOV_EXCL_STOP */
}
if (file_pos + count > file->blockmax) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when allocating overflowing extent for file '%s' at position '%u:%u/%u'\n", file->sub, file_pos, count, file->blockmax);
os_abort();
/* LCOV_EXCL_STOP */
}
extent = malloc_nofail(sizeof(struct snapraid_extent));
extent->parity_pos = parity_pos;
extent->file = file;
extent->file_pos = file_pos;
extent->count = count;
return extent;
}
void extent_free(struct snapraid_extent* extent)
{
free(extent);
}
int extent_parity_compare(const void* void_a, const void* void_b)
{
const struct snapraid_extent* arg_a = void_a;
const struct snapraid_extent* arg_b = void_b;
if (arg_a->parity_pos < arg_b->parity_pos)
return -1;
if (arg_a->parity_pos > arg_b->parity_pos)
return 1;
return 0;
}
int extent_file_compare(const void* void_a, const void* void_b)
{
const struct snapraid_extent* arg_a = void_a;
const struct snapraid_extent* arg_b = void_b;
if (arg_a->file < arg_b->file)
return -1;
if (arg_a->file > arg_b->file)
return 1;
if (arg_a->file_pos < arg_b->file_pos)
return -1;
if (arg_a->file_pos > arg_b->file_pos)
return 1;
return 0;
}
struct snapraid_link* link_alloc(const char* sub, const char* linkto, unsigned link_flag)
{
struct snapraid_link* slink;
slink = malloc_nofail(sizeof(struct snapraid_link));
slink->sub = strdup_nofail(sub);
slink->linkto = strdup_nofail(linkto);
slink->flag = link_flag;
return slink;
}
void link_free(struct snapraid_link* slink)
{
free(slink->sub);
free(slink->linkto);
free(slink);
}
int link_name_compare_to_arg(const void* void_arg, const void* void_data)
{
const char* arg = void_arg;
const struct snapraid_link* slink = void_data;
return strcmp(arg, slink->sub);
}
int link_alpha_compare(const void* void_a, const void* void_b)
{
const struct snapraid_link* slink_a = void_a;
const struct snapraid_link* slink_b = void_b;
return strcmp(slink_a->sub, slink_b->sub);
}
struct snapraid_dir* dir_alloc(const char* sub)
{
struct snapraid_dir* dir;
dir = malloc_nofail(sizeof(struct snapraid_dir));
dir->sub = strdup_nofail(sub);
dir->flag = 0;
return dir;
}
void dir_free(struct snapraid_dir* dir)
{
free(dir->sub);
free(dir);
}
int dir_name_compare(const void* void_arg, const void* void_data)
{
const char* arg = void_arg;
const struct snapraid_dir* dir = void_data;
return strcmp(arg, dir->sub);
}
struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev, const char* uuid, int skip_access)
{
struct snapraid_disk* disk;
disk = malloc_nofail(sizeof(struct snapraid_disk));
pathcpy(disk->name, sizeof(disk->name), name);
pathimport(disk->dir, sizeof(disk->dir), dir);
pathcpy(disk->uuid, sizeof(disk->uuid), uuid);
/* ensure that the dir terminate with "/" if it isn't empty */
pathslash(disk->dir, sizeof(disk->dir));
#if HAVE_THREAD
thread_mutex_init(&disk->fs_mutex);
disk->fs_mutex_enabled = 0; /* lock will be enabled at threads start */
#endif
disk->smartctl[0] = 0;
disk->device = dev;
disk->tick = 0;
disk->cached_blocks = 0;
disk->progress_file = 0;
disk->total_blocks = 0;
disk->free_blocks = 0;
disk->first_free_block = 0;
disk->has_volatile_inodes = 0;
disk->has_volatile_hardlinks = 0;
disk->has_unreliable_physical = 0;
disk->has_different_uuid = 0;
disk->has_unsupported_uuid = *uuid == 0; /* empty UUID means unsupported */
disk->had_empty_uuid = 0;
disk->mapping_idx = -1;
disk->skip_access = skip_access;
tommy_list_init(&disk->filelist);
tommy_list_init(&disk->deletedlist);
tommy_hashdyn_init(&disk->inodeset);
tommy_hashdyn_init(&disk->pathset);
tommy_hashdyn_init(&disk->stampset);
tommy_list_init(&disk->linklist);
tommy_hashdyn_init(&disk->linkset);
tommy_list_init(&disk->dirlist);
tommy_hashdyn_init(&disk->dirset);
tommy_tree_init(&disk->fs_parity, extent_parity_compare);
tommy_tree_init(&disk->fs_file, extent_file_compare);
disk->fs_last = 0;
return disk;
}
void disk_free(struct snapraid_disk* disk)
{
tommy_list_foreach(&disk->filelist, (tommy_foreach_func*)file_free);
tommy_list_foreach(&disk->deletedlist, (tommy_foreach_func*)file_free);
tommy_tree_foreach(&disk->fs_file, (tommy_foreach_func*)extent_free);
tommy_hashdyn_done(&disk->inodeset);
tommy_hashdyn_done(&disk->pathset);
tommy_hashdyn_done(&disk->stampset);
tommy_list_foreach(&disk->linklist, (tommy_foreach_func*)link_free);
tommy_hashdyn_done(&disk->linkset);
tommy_list_foreach(&disk->dirlist, (tommy_foreach_func*)dir_free);
tommy_hashdyn_done(&disk->dirset);
#if HAVE_THREAD
thread_mutex_destroy(&disk->fs_mutex);
#endif
free(disk);
}
void disk_start_thread(struct snapraid_disk* disk)
{
#if HAVE_THREAD
disk->fs_mutex_enabled = 1;
#else
(void)disk;
#endif
}
static inline void fs_lock(struct snapraid_disk* disk)
{
#if HAVE_THREAD
if (disk->fs_mutex_enabled)
thread_mutex_lock(&disk->fs_mutex);
#else
(void)disk;
#endif
}
static inline void fs_unlock(struct snapraid_disk* disk)
{
#if HAVE_THREAD
if (disk->fs_mutex_enabled)
thread_mutex_unlock(&disk->fs_mutex);
#else
(void)disk;
#endif
}
struct extent_disk_empty {
block_off_t blockmax;
};
/**
* Compare the extent if inside the specified blockmax.
*/
static int extent_disk_empty_compare_unlock(const void* void_a, const void* void_b)
{
const struct extent_disk_empty* arg_a = void_a;
const struct snapraid_extent* arg_b = void_b;
/* if the block is inside the specified blockmax, it's found */
if (arg_a->blockmax > arg_b->parity_pos)
return 0;
/* otherwise search for a smaller one */
return -1;
}
int fs_is_empty(struct snapraid_disk* disk, block_off_t blockmax)
{
struct extent_disk_empty arg = { blockmax };
/* if there is an element, it's not empty */
/* even if links and dirs have no block allocation */
if (!tommy_list_empty(&disk->filelist))
return 0;
if (!tommy_list_empty(&disk->linklist))
return 0;
if (!tommy_list_empty(&disk->dirlist))
return 0;
fs_lock(disk);
/* search for any extent inside blockmax */
if (tommy_tree_search_compare(&disk->fs_parity, extent_disk_empty_compare_unlock, &arg) != 0) {
fs_unlock(disk);
return 0;
}
/* finally, it's empty */
fs_unlock(disk);
return 1;
}
struct extent_disk_size {
block_off_t size;
};
/**
* Compare the extent by highest parity position.
*
* The maximum parity position is stored as size.
*/
static int extent_disk_size_compare_unlock(const void* void_a, const void* void_b)
{
struct extent_disk_size* arg_a = (void*)void_a;
const struct snapraid_extent* arg_b = void_b;
/* get the maximum size */
if (arg_a->size < arg_b->parity_pos + arg_b->count)
arg_a->size = arg_b->parity_pos + arg_b->count;
/* search always for a bigger one */
return 1;
}
block_off_t fs_size(struct snapraid_disk* disk)
{
struct extent_disk_size arg = { 0 };
fs_lock(disk);
tommy_tree_search_compare(&disk->fs_parity, extent_disk_size_compare_unlock, &arg);
fs_unlock(disk);
return arg.size;
}
struct extent_check {
const struct snapraid_extent* prev;
int result;
};
static void extent_parity_check_foreach_unlock(void* void_arg, void* void_obj)
{
struct extent_check* arg = void_arg;
const struct snapraid_extent* obj = void_obj;
const struct snapraid_extent* prev = arg->prev;
/* set the next previous block */
arg->prev = obj;
/* stop reporting if too many errors */
if (arg->result > 100) {
/* LCOV_EXCL_START */
return;
/* LCOV_EXCL_STOP */
}
if (obj->count == 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in parity count zero for file '%s' at '%u'\n",
obj->file->sub, obj->parity_pos);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
/* check only if there is a previous block */
if (!prev)
return;
/* check the order */
if (prev->parity_pos >= obj->parity_pos) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in parity order for files '%s' at '%u:%u' and '%s' at '%u:%u'\n",
prev->file->sub, prev->parity_pos, prev->count, obj->file->sub, obj->parity_pos, obj->count);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
/* check that the extents don't overlap */
if (prev->parity_pos + prev->count > obj->parity_pos) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for parity overlap for files '%s' at '%u:%u' and '%s' at '%u:%u'\n",
prev->file->sub, prev->parity_pos, prev->count, obj->file->sub, obj->parity_pos, obj->count);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
}
static void extent_file_check_foreach_unlock(void* void_arg, void* void_obj)
{
struct extent_check* arg = void_arg;
const struct snapraid_extent* obj = void_obj;
const struct snapraid_extent* prev = arg->prev;
/* set the next previous block */
arg->prev = obj;
/* stop reporting if too many errors */
if (arg->result > 100) {
/* LCOV_EXCL_START */
return;
/* LCOV_EXCL_STOP */
}
if (obj->count == 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file count zero for file '%s' at '%u'\n",
obj->file->sub, obj->file_pos);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
/* note that for deleted files, some extents may be missing */
/* if the files are different */
if (!prev || prev->file != obj->file) {
if (prev != 0) {
if (file_flag_has(prev->file, FILE_IS_DELETED)) {
/* check that the extent doesn't overflow the file */
if (prev->file_pos + prev->count > prev->file->blockmax) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in delete end for file '%s' at '%u:%u' overflowing size '%u'\n",
prev->file->sub, prev->file_pos, prev->count, prev->file->blockmax);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
} else {
/* check that the extent ends the file */
if (prev->file_pos + prev->count != prev->file->blockmax) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file end for file '%s' at '%u:%u' instead of size '%u'\n",
prev->file->sub, prev->file_pos, prev->count, prev->file->blockmax);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
}
}
if (file_flag_has(obj->file, FILE_IS_DELETED)) {
/* check that the extent doesn't overflow the file */
if (obj->file_pos + obj->count > obj->file->blockmax) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in delete start for file '%s' at '%u:%u' overflowing size '%u'\n",
obj->file->sub, obj->file_pos, obj->count, obj->file->blockmax);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
} else {
/* check that the extent starts the file */
if (obj->file_pos != 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file start for file '%s' at '%u:%u'\n",
obj->file->sub, obj->file_pos, obj->count);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
}
} else {
/* check the order */
if (prev->file_pos >= obj->file_pos) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file order for file '%s' at '%u:%u' and at '%u:%u'\n",
prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
if (file_flag_has(obj->file, FILE_IS_DELETED)) {
/* check that the extents don't overlap */
if (prev->file_pos + prev->count > obj->file_pos) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in delete sequence for file '%s' at '%u:%u' and at '%u:%u'\n",
prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
} else {
/* check that the extents are sequential */
if (prev->file_pos + prev->count != obj->file_pos) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file sequence for file '%s' at '%u:%u' and at '%u:%u'\n",
prev->file->sub, prev->file_pos, prev->count, obj->file_pos, obj->count);
++arg->result;
return;
/* LCOV_EXCL_STOP */
}
}
}
}
int fs_check(struct snapraid_disk* disk)
{
struct extent_check arg;
/* error count starts from 0 */
arg.result = 0;
fs_lock(disk);
/* check parity sequence */
arg.prev = 0;
tommy_tree_foreach_arg(&disk->fs_parity, extent_parity_check_foreach_unlock, &arg);
/* check file sequence */
arg.prev = 0;
tommy_tree_foreach_arg(&disk->fs_file, extent_file_check_foreach_unlock, &arg);
fs_unlock(disk);
if (arg.result != 0)
return -1;
return 0;
}
struct extent_parity_inside {
block_off_t parity_pos;
};
/**
* Compare the extent if containing the specified parity position.
*/
static int extent_parity_inside_compare_unlock(const void* void_a, const void* void_b)
{
const struct extent_parity_inside* arg_a = void_a;
const struct snapraid_extent* arg_b = void_b;
if (arg_a->parity_pos < arg_b->parity_pos)
return -1;
if (arg_a->parity_pos >= arg_b->parity_pos + arg_b->count)
return 1;
return 0;
}
/**
* Search the extent at the specified parity position.
* The search is optimized for sequential accesses.
* \return If not found return 0
*/
static struct snapraid_extent* fs_par2extent_get_unlock(struct snapraid_disk* disk, struct snapraid_extent** fs_last, block_off_t parity_pos)
{
struct snapraid_extent* extent;
/* check if the last accessed extent matches */
if (*fs_last
&& parity_pos >= (*fs_last)->parity_pos
&& parity_pos < (*fs_last)->parity_pos + (*fs_last)->count
) {
extent = *fs_last;
} else {
struct extent_parity_inside arg = { parity_pos };
extent = tommy_tree_search_compare(&disk->fs_parity, extent_parity_inside_compare_unlock, &arg);
}
if (!extent)
return 0;
/* store the last accessed extent */
*fs_last = extent;
return extent;
}
struct extent_file_inside {
struct snapraid_file* file;
block_off_t file_pos;
};
/**
* Compare the extent if containing the specified file position.
*/
static int extent_file_inside_compare_unlock(const void* void_a, const void* void_b)
{
const struct extent_file_inside* arg_a = void_a;
const struct snapraid_extent* arg_b = void_b;
if (arg_a->file < arg_b->file)
return -1;
if (arg_a->file > arg_b->file)
return 1;
if (arg_a->file_pos < arg_b->file_pos)
return -1;
if (arg_a->file_pos >= arg_b->file_pos + arg_b->count)
return 1;
return 0;
}
/**
* Search the extent at the specified file position.
* The search is optimized for sequential accesses.
* \return If not found return 0
*/
static struct snapraid_extent* fs_file2extent_get_unlock(struct snapraid_disk* disk, struct snapraid_extent** fs_last, struct snapraid_file* file, block_off_t file_pos)
{
struct snapraid_extent* extent;
/* check if the last accessed extent matches */
if (*fs_last
&& file == (*fs_last)->file
&& file_pos >= (*fs_last)->file_pos
&& file_pos < (*fs_last)->file_pos + (*fs_last)->count
) {
extent = *fs_last;
} else {
struct extent_file_inside arg = { file, file_pos };
extent = tommy_tree_search_compare(&disk->fs_file, extent_file_inside_compare_unlock, &arg);
}
if (!extent)
return 0;
/* store the last accessed extent */
*fs_last = extent;
return extent;
}
struct snapraid_file* fs_par2file_find(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
{
struct snapraid_extent* extent;
struct snapraid_file* file;
fs_lock(disk);
extent = fs_par2extent_get_unlock(disk, &disk->fs_last, parity_pos);
if (!extent) {
fs_unlock(disk);
return 0;
}
if (file_pos)
*file_pos = extent->file_pos + (parity_pos - extent->parity_pos);
file = extent->file;
fs_unlock(disk);
return file;
}
block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
{
struct snapraid_extent* extent;
block_off_t ret;
fs_lock(disk);
extent = fs_file2extent_get_unlock(disk, &disk->fs_last, file, file_pos);
if (!extent) {
fs_unlock(disk);
return POS_NULL;
}
ret = extent->parity_pos + (file_pos - extent->file_pos);
fs_unlock(disk);
return ret;
}
void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos)
{
struct snapraid_extent* extent;
struct snapraid_extent* parity_extent;
struct snapraid_extent* file_extent;
fs_lock(disk);
if (file_pos > 0) {
/* search an existing extent for the previous file_pos */
extent = fs_file2extent_get_unlock(disk, &disk->fs_last, file, file_pos - 1);
if (extent != 0 && parity_pos == extent->parity_pos + extent->count) {
/* ensure that we are extending the extent at the end */
if (file_pos != extent->file_pos + extent->count) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when allocating file '%s' at position '%u/%u' in the middle of extent '%u:%u' in disk '%s'\n", file->sub, file_pos, file->blockmax, extent->file_pos, extent->count, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
/* extend the existing extent */
++extent->count;
fs_unlock(disk);
return;
}
}
/* a extent doesn't exist, and we have to create a new one */
extent = extent_alloc(parity_pos, file, file_pos, 1);
/* insert the extent in the trees */
parity_extent = tommy_tree_insert(&disk->fs_parity, &extent->parity_node, extent);
file_extent = tommy_tree_insert(&disk->fs_file, &extent->file_node, extent);
if (parity_extent != extent || file_extent != extent) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when allocating file '%s' at position '%u/%u' for existing extent '%u:%u' in disk '%s'\n", file->sub, file_pos, file->blockmax, extent->file_pos, extent->count, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
/* store the last accessed extent */
disk->fs_last = extent;
fs_unlock(disk);
}
void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos)
{
struct snapraid_extent* extent;
struct snapraid_extent* second_extent;
struct snapraid_extent* parity_extent;
struct snapraid_extent* file_extent;
block_off_t first_count, second_count;
fs_lock(disk);
extent = fs_par2extent_get_unlock(disk, &disk->fs_last, parity_pos);
if (!extent) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when deallocating parity position '%u' for not existing extent in disk '%s'\n", parity_pos, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
/* if it's the only block of the extent, delete it */
if (extent->count == 1) {
/* remove from the trees */
tommy_tree_remove(&disk->fs_parity, extent);
tommy_tree_remove(&disk->fs_file, extent);
/* deallocate */
extent_free(extent);
/* clear the last accessed extent */
disk->fs_last = 0;
fs_unlock(disk);
return;
}
/* if it's at the start of the extent, shrink the extent */
if (parity_pos == extent->parity_pos) {
++extent->parity_pos;
++extent->file_pos;
--extent->count;
fs_unlock(disk);
return;
}
/* if it's at the end of the extent, shrink the extent */
if (parity_pos == extent->parity_pos + extent->count - 1) {
--extent->count;
fs_unlock(disk);
return;
}
/* otherwise it's in the middle */
first_count = parity_pos - extent->parity_pos;
second_count = extent->count - first_count - 1;
/* adjust the first extent */
extent->count = first_count;
/* allocate the second extent */
second_extent = extent_alloc(extent->parity_pos + first_count + 1, extent->file, extent->file_pos + first_count + 1, second_count);
/* insert the extent in the trees */
parity_extent = tommy_tree_insert(&disk->fs_parity, &second_extent->parity_node, second_extent);
file_extent = tommy_tree_insert(&disk->fs_file, &second_extent->file_node, second_extent);
if (parity_extent != second_extent || file_extent != second_extent) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when deallocating parity position '%u' for splitting extent '%u:%u' in disk '%s'\n", parity_pos, second_extent->file_pos, second_extent->count, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
/* store the last accessed extent */
disk->fs_last = second_extent;
fs_unlock(disk);
}
struct snapraid_block* fs_file2block_get(struct snapraid_file* file, block_off_t file_pos)
{
if (file_pos >= file->blockmax) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when dereferencing file '%s' at position '%u/%u'\n", file->sub, file_pos, file->blockmax);
os_abort();
/* LCOV_EXCL_STOP */
}
return file_block(file, file_pos);
}
struct snapraid_block* fs_par2block_find(struct snapraid_disk* disk, block_off_t parity_pos)
{
struct snapraid_file* file;
block_off_t file_pos;
file = fs_par2file_find(disk, parity_pos, &file_pos);
if (file == 0)
return BLOCK_NULL;
return fs_file2block_get(file, file_pos);
}
struct snapraid_map* map_alloc(const char* name, unsigned position, block_off_t total_blocks, block_off_t free_blocks, const char* uuid)
{
struct snapraid_map* map;
map = malloc_nofail(sizeof(struct snapraid_map));
pathcpy(map->name, sizeof(map->name), name);
map->position = position;
map->total_blocks = total_blocks;
map->free_blocks = free_blocks;
pathcpy(map->uuid, sizeof(map->uuid), uuid);
return map;
}
void map_free(struct snapraid_map* map)
{
free(map);
}
int time_compare(const void* void_a, const void* void_b)
{
const time_t* time_a = void_a;
const time_t* time_b = void_b;
if (*time_a < *time_b)
return -1;
if (*time_a > *time_b)
return 1;
return 0;
}
/****************************************************************************/
/* format */
int FMT_MODE = FMT_FILE;
/**
* Format a file path for poll reference
*/
const char* fmt_poll(const struct snapraid_disk* disk, const char* str, char* buffer)
{
(void)disk;
return esc_shell(str, buffer);
}
/**
* Format a path name for terminal reference
*/
const char* fmt_term(const struct snapraid_disk* disk, const char* str, char* buffer)
{
const char* out[3];
switch (FMT_MODE) {
case FMT_FILE :
default :
return esc_shell(str, buffer);
case FMT_DISK :
out[0] = disk->name;
out[1] = ":";
out[2] = str;
return esc_shell_multi(out, 3, buffer);
case FMT_PATH :
out[0] = disk->dir;
out[1] = str;
return esc_shell_multi(out, 2, buffer);
}
}
snapraid-12.1/cmdline/elem.h 0000664 0000000 0000000 00000103361 14166610522 0015753 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __ELEM_H
#define __ELEM_H
#include "util.h"
#include "support.h"
#include "tommyds/tommyhash.h"
#include "tommyds/tommylist.h"
#include "tommyds/tommytree.h"
#include "tommyds/tommyhashdyn.h"
#include "tommyds/tommyarray.h"
#include "tommyds/tommyarrayblkof.h"
/****************************************************************************/
/* snapraid */
/**
* Number of measures of the operation progress.
*/
#define PROGRESS_MAX 100
/**
* Max UUID length.
*/
#define UUID_MAX 128
/**
* Invalid position.
*/
#define POS_NULL ((block_off_t)-1)
/**
* Content file specification.
*/
struct snapraid_content {
char content[PATH_MAX]; /**< Path of the content file. */
uint64_t device; /**< Device identifier. */
void* context; /**< Context used for multithread operations. */
tommy_node node; /**< Next node in the list. */
};
/**
* Filter for paths.
*/
struct snapraid_filter {
char pattern[PATH_MAX]; /**< Filter pattern. */
int is_disk; /**< If the pattern is a disk one. */
int is_path; /**< If the pattern is only for the complete path. */
int is_dir; /**< If the pattern is only for dir. */
int direction; /**< If it's an inclusion (=1) or an exclusion (=-1). */
tommy_node node; /**< Next node in the list. */
};
/**
* Block pointer used to represent unused blocks.
*/
#define BLOCK_NULL 0
/**
* This block is an empty one.
* Note that an empty block is represent with ::BLOCK_NULL.
*/
#define BLOCK_STATE_EMPTY 0
/**
* The block has both the hash and the parity computed.
* This is the normal state of a saved block.
*
* The block hash field IS set.
* The parity for this disk is updated.
*/
#define BLOCK_STATE_BLK 1
/**
* The block is new and not yet hashed.
* This happens when a new block overwrite a just removed block, or an empty space.
*
* The block hash field MAY be set and it represents the hash of the OLD data.
* The hash may be also INVALID or ZERO.
*
* If the OLD block was empty, the hash is set to the special ZERO value.
* If the OLD block was lost, the hash is set to the special INVALID value.
*
* The parity for this disk is not updated, but it contains the old data referenced by the hash.
*
* If the state is read from an incomplete sync, we don't really know if the hash is referring at the
* data used to compute the parity, because the sync process was interrupted at an unknown point,
* and the parity may or may not be updated.
*
* For this reason we clear all such hashes when reading the state from an incomplete sync before
* starting a new sync, because sync is affected by such hashes, as sync updates the parity, only
* if the new data read for CHG blocks has a mismatching hash.
* Clearing is done setting the ::clear_past_hash flag before reading the state.
* No clearing is done in other commands, as check and fix are instead able to work with unsynced
* hashes, and scrub ignores CHG/DELETED blocks.
*/
#define BLOCK_STATE_CHG 2
/**
* The block is new and hashed.
* This happens when a new block overwrite a just removed block, or an empty space.
*
* Note that when the file copy heuristic is enabled, the REP blocks may be set
* using this heuristic, meaning that the hash may be wrong.
*
* For this reason, when the ::force_nocopy flag is enabled in sync, we convert all the REP blocks
* to CHG, invalidating the stored hash.
* Clearing is done setting the ::clear_past_hash flag before reading the state.
* No clearing is done in other commands, as they don't stop the process like in sync
* when there is a false silent error.
*
* The block hash field IS set, and it represents the hash of the new data.
* The parity for this disk is not updated.
*/
#define BLOCK_STATE_REP 3
/**
* This block is a deleted one.
* This happens when a file is deleted.
*
* The block hash field IS set, and it represents the hash of the previous data,
* but only if it's different by all 0.
* The parity for this disk is not updated, but it contains the old data referenced by the hash.
*
* If the state is read from an incomplete sync, we don't really know if the hash is referring at the
* data used to compute the parity, because the sync process was interrupted at an unknown point,
* and the parity may or may not be updated.
*
* A now the sync process is not affected by DELETED hash, so clearing won't be really needed,
* but considering that we have to do it for CHG blocks, we do it also for DELETED ones,
* clearing all the past hashes.
* Clearing is done setting the ::clear_past_hash flag before reading the state.
* No clearing is done in other commands, as check and fix are instead able to work with unsynced
* hashes, and scrub ignores CHG/DELETED blocks.
*/
#define BLOCK_STATE_DELETED 4
/**
* Block hash size.
*
* At max HASH_MAX.
*/
extern int BLOCK_HASH_SIZE;
/**
* Block of a file.
*/
struct snapraid_block {
unsigned char state; /**< State of the block. */
/**
* Hash of the block.
*
* The effective stored size is BLOCK_HASH_SIZE.
*/
unsigned char hash[HASH_MAX];
};
/**
* If a file is present in the disk.
* It's used only in scan to detect present and missing files.
*/
#define FILE_IS_PRESENT 0x01
/**
* If it's an excluded file from the processing.
* It's used in both check and fix to mark files to exclude from the processing.
*/
#define FILE_IS_EXCLUDED 0x02
/**
* If a fix was attempted but it failed.
* It's used only in fix to mark that some data is unrecoverable.
*/
#define FILE_IS_DAMAGED 0x04
/**
* If a fix was done.
* It's used only in fix to mark that some data was recovered.
*/
#define FILE_IS_FIXED 0x08
/**
* If the file was originally missing, and it was created in the fix process.
* It's used only in fix to mark files recovered from scratch,
* meaning that they don't have any previous content.
* This is important because it means that deleting them, you are not going
* to lose something that cannot be recovered.
* Note that excluded files won't ever get this flag.
*/
#define FILE_IS_CREATED 0x10
/**
* If the file has completed its processing, meaning that it won't be opened anymore.
* It's used only in fix to mark when we finish processing one file.
* Note that excluded files won't ever get this flag.
*/
#define FILE_IS_FINISHED 0x20
/**
* If the file hash was obtained from a file copy
* identified by the same name, size and stamp.
*/
#define FILE_IS_COPY 0x40
/**
* If the file was opened.
* It's used in fix to detect if it's the first time a file is opened.
*/
#define FILE_IS_OPENED 0x80
/**
* If the file is modified from the latest sync.
* It's used in fix to store if the state of the file before being modified.
*/
#define FILE_IS_UNSYNCED 0x100
/**
* If the file is without inode.
* It could happen in file-system where inodes are not persistent,
* or when restoring a full disk with "fix".
* In such cases we have to clear any stored duplicate inode.
* After the scan process completes, no file should have this flag set.
*/
#define FILE_IS_WITHOUT_INODE 0x200
/**
* The file is deleted.
* This happens when a file is deleted from the array,
* but it's keep inside the parity until the next sync.
*
* During the file-system check we needs this information,
* because deleted files may be present only partially.
*/
#define FILE_IS_DELETED 0x400
/**
* The file is missing.
* This happens in fix/check when a file is cannot be opened,
* and marking it as such prevents to retry to open it again.
*/
#define FILE_IS_MISSING 0x800
#define FILE_IS_HARDLINK 0x1000 /**< If it's an hardlink. */
#define FILE_IS_SYMLINK 0x2000 /**< If it's a file symlink. */
#define FILE_IS_SYMDIR 0x4000 /**< If it's a dir symlink for Windows. Not yet supported. */
#define FILE_IS_JUNCTION 0x8000 /**< If it's a junction for Windows. Not yet supported. */
#define FILE_IS_LINK_MASK 0xF000 /**< Mask for link type. */
/**
* File.
*/
struct snapraid_file {
int64_t mtime_sec; /**< Modification time. */
uint64_t inode; /**< Inode. */
uint64_t physical; /**< Physical offset of the file. */
data_off_t size; /**< Size of the file. */
struct snapraid_block* blockvec; /**< All the blocks of the file. */
int mtime_nsec; /**< Modification time nanoseconds. In the range 0 <= x < 1,000,000,000, or STAT_NSEC_INVALID if not present. */
block_off_t blockmax; /**< Number of blocks. */
unsigned flag; /**< FILE_IS_* flags. */
char* sub; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
/* nodes for data structures */
tommy_node nodelist;
tommy_hashdyn_node nodeset;
tommy_hashdyn_node pathset;
tommy_hashdyn_node stampset;
};
/**
* Symbolic Link.
*/
struct snapraid_link {
unsigned flag; /**< FILE_IS_* flags. */
char* sub; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
char* linkto; /**< Link to. */
/* nodes for data structures */
tommy_node nodelist;
tommy_hashdyn_node nodeset;
};
/**
* Dir.
*/
struct snapraid_dir {
unsigned flag; /**< FILE_IS_* flags. */
char* sub; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
/* nodes for data structures */
tommy_node nodelist;
tommy_hashdyn_node nodeset;
};
/**
* Chunk.
*
* A extent represents a fragment of a file mapped into the parity.
*/
struct snapraid_extent {
struct snapraid_file* file; /**< File containing this extent. */
block_off_t parity_pos; /**< Parity position. */
block_off_t file_pos; /**< Position in the file. */
block_off_t count; /**< Number of sequential blocks in the file and parity. */
tommy_tree_node parity_node; /**< Tree sorted by . */
tommy_tree_node file_node; /**< Tree sorter by . */
};
/**
* Disk.
*/
struct snapraid_disk {
char name[PATH_MAX]; /**< Name of the disk. */
char dir[PATH_MAX]; /**< Mount point of the disk. It always terminates with /. */
char smartctl[PATH_MAX]; /**< Custom command for smartctl. Empty means auto. */
char uuid[UUID_MAX]; /**< UUID of the disk. */
uint64_t device; /**< Device identifier. */
block_off_t total_blocks; /**< Number of total blocks. */
block_off_t free_blocks; /**< Number of free blocks at the last sync. */
uint64_t tick; /**< Usage time. */
uint64_t progress_tick[PROGRESS_MAX]; /**< Last ticks of progress. */
unsigned cached_blocks; /**< Number of IO blocks cached. */
struct snapraid_file* progress_file; /**< File in progress. */
/**
* First free searching block.
* Note that it doesn't necessarily point at the first free block,
* but it just tell you that no free block is present before this position.
*/
block_off_t first_free_block;
int has_volatile_inodes; /**< If the underline file-system has not persistent inodes. */
int has_volatile_hardlinks; /**< If the underline file-system has not synchronized metadata for hardlink (NTFS). */
int has_unreliable_physical; /**< If the physical offset of files has duplicates. */
int has_different_uuid; /**< If the disk has a different UUID, meaning that it is not the same file-system. */
int has_unsupported_uuid; /**< If the disk doesn't report UUID, meaning it's not supported. */
int had_empty_uuid; /**< If the disk had an empty UUID, meaning that it's a new disk. */
int mapping_idx; /**< Index in the mapping vector. Used only as buffer when writing the content file. */
int skip_access; /**< If the disk is inaccessible and it should be skipped. */
#if HAVE_THREAD
/**
* Mutex for protecting the filesystem structure.
*
* Specifically, this protects ::fs_parity, ::fs_file, and ::fs_last,
* meaning that it protects only extents.
*
* Files, links and dirs are not protected as they are not expected to
* change during multithread processing.
*/
thread_mutex_t fs_mutex;
int fs_mutex_enabled; /*< If the lock has to be used. */
/**
* Mutex for protecting the scan process.
*
* It's used during the scan process to protect the stampset to identity copy of files
*/
thread_mutex_t stamp_mutex;
#endif
/**
* Mapping of extents in the parity.
* Sorted by and by
*/
tommy_tree fs_parity;
tommy_tree fs_file;
/**
* Last extent we accessed.
* It's used to optimize access of sequential blocks.
*/
struct snapraid_extent* fs_last;
/**
* List of all the snapraid_file for the disk.
*/
tommy_list filelist;
/**
* List of all the deleted file for the disk.
*
* These files are kept allocated, because the blocks are still referenced in
* the ::blockarr.
*/
tommy_list deletedlist;
tommy_hashdyn inodeset; /**< Hashtable by inode of all the files. */
tommy_hashdyn pathset; /**< Hashtable by path of all the files. */
tommy_hashdyn stampset; /**< Hashtable by stamp (size and time) of all the files. */
tommy_list linklist; /**< List of all the links. */
tommy_hashdyn linkset; /**< Hashtable by name of all the links. */
tommy_list dirlist; /**< List of all the empty dirs. */
tommy_hashdyn dirset; /**< Hashtable by name of all the empty dirs. */
/* nodes for data structures */
tommy_node node;
};
/**
* Disk mapping.
*/
struct snapraid_map {
char name[PATH_MAX]; /**< Name of the disk. */
char uuid[UUID_MAX]; /**< UUID of the disk. Empty if unknown. */
block_off_t total_blocks; /**< Number of total blocks. */
block_off_t free_blocks; /**< Number of free blocks at last 'sync'. */
unsigned position; /**< Position of the disk in the parity. */
/* nodes for data structures */
tommy_node node;
};
/**
* Max number of parity split.
*/
#define SPLIT_MAX 8
/**
* Invalid parity size.
*
* This value is used to identify new parities,
* like when you alter the configuration adding
* a new parity level, creating it with 'fix'.
* Given that 'fix' doesn't write the content file,
* the new size will be written only at the next
* 'sync'.
*/
#define PARITY_SIZE_INVALID -1LL
/**
* Parity split.
*/
struct snapraid_split {
char path[PATH_MAX]; /**< Path of the parity file. */
char uuid[UUID_MAX]; /**< UUID of the disk. Empty if unknown. */
/**
* Size of the parity split.
* Only the latest not zero size is allowed to grow.
* If the value is unset, it's PARITY_SIZE_INVALID.
*/
data_off_t size;
uint64_t device; /**< Device identifier of the parity. */
};
/**
* Parity.
*/
struct snapraid_parity {
struct snapraid_split split_map[SPLIT_MAX]; /**< Parity splits. */
unsigned split_mac; /**< Number of parity splits. */
char smartctl[PATH_MAX]; /**< Custom command for smartctl. Empty means auto. */
block_off_t total_blocks; /**< Number of total blocks. */
block_off_t free_blocks; /**< Number of free blocks at the last sync. */
int is_excluded_by_filter; /**< If the parity is excluded by filters. */
int skip_access; /**< If at least one of the parity disk is inaccessible and it should be skipped. */
uint64_t tick; /**< Usage time. */
uint64_t progress_tick[PROGRESS_MAX]; /**< Last cpu ticks of progress. */
unsigned cached_blocks; /**< Number of IO blocks cached. */
};
/**
* Info.
*/
typedef uint32_t snapraid_info;
/**
* Allocate a content.
*/
struct snapraid_content* content_alloc(const char* path, uint64_t dev);
/**
* Deallocate a content.
*/
void content_free(struct snapraid_content* content);
/**
* Allocate a filter pattern for files and directories.
*/
struct snapraid_filter* filter_alloc_file(int is_include, const char* pattern);
/**
* Allocate a filter pattern for disks.
*/
struct snapraid_filter* filter_alloc_disk(int is_include, const char* pattern);
/**
* Deallocate an exclusion.
*/
void filter_free(struct snapraid_filter* filter);
/**
* Filter type description.
*/
const char* filter_type(struct snapraid_filter* filter, char* out, size_t out_size);
/**
* Filter hidden files.
* Return !=0 if it matches and it should be excluded.
*/
static inline int filter_hidden(int enable, struct dirent* dd)
{
if (enable && dirent_hidden(dd)) {
return 1; /* filter out */
}
return 0;
}
/**
* Filter a path using a list of filters.
* For each element of the path all the filters are applied, until the first one that matches.
* Return !=0 if it should be excluded.
*/
int filter_path(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub);
/**
* Filter a file/link/dir if missing.
* This call imply a disk check for the file presence.
* Return !=0 if the file is present and it should be excluded.
*/
int filter_existence(int filter_missing, const char* dir, const char* sub);
/**
* Filter a file if bad.
* Return !=0 if the file is correct and it should be excluded.
*/
int filter_correctness(int filter_error, tommy_arrayblkof* infoarr, struct snapraid_disk* disk, struct snapraid_file* file);
/**
* Filter a dir using a list of filters.
* For each element of the path all the filters are applied, until the first one that matches.
* Thesesdir are always by included by default, to allow to apply rules at the contained files.
* Return !=0 if should be excluded.
*/
int filter_subdir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub);
/**
* Filter a dir using a list of filters.
* For each element of the path all the filters are applied, until the first one that matches.
* Return !=0 if should be excluded.
*/
int filter_emptydir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub);
/**
* Filter a path if it's a content file.
* Return !=0 if should be excluded.
*/
int filter_content(tommy_list* contentlist, const char* path);
/**
* Check if the specified hash is invalid.
*
* An invalid hash is represented with all bytes at 0x00.
*
* If working with reduced hash lengths, this function always return 0.
*/
static inline int hash_is_invalid(const unsigned char* hash)
{
int i;
/* if the hash is reduced, we cannot grant that it's a specific kind of hash */
if (BLOCK_HASH_SIZE != HASH_MAX)
return 0;
for (i = 0; i < BLOCK_HASH_SIZE; ++i)
if (hash[i] != 0x00)
return 0;
return 1;
}
/**
* Check if the specified hash represent the zero block.
*
* A zero hash is represented with all bytes at 0xFF.
*
* If working with reduced hash lengths, this function always return 0.
*/
static inline int hash_is_zero(const unsigned char* hash)
{
int i;
/* if the hash is reduced, we cannot grant that it's a specific kind of hash */
if (BLOCK_HASH_SIZE != HASH_MAX)
return 0;
for (i = 0; i < BLOCK_HASH_SIZE; ++i)
if (hash[i] != 0xFF)
return 0;
return 1;
}
/**
* Check if the specified hash is unequivocally representing the data.
*
* If working with reduced hash lengths, this function always return 0.
*/
static inline int hash_is_unique(const unsigned char* hash)
{
/* if the hash is reduced, we cannot grant that it's a specific kind of hash */
if (BLOCK_HASH_SIZE != HASH_MAX)
return 0;
return !hash_is_zero(hash) && !hash_is_invalid(hash);
}
/**
* Set the hash to the special INVALID value.
*/
static inline void hash_invalid_set(unsigned char* hash)
{
memset(hash, 0x00, BLOCK_HASH_SIZE);
}
/**
* Set the hash to the special ZERO value.
*/
static inline void hash_zero_set(unsigned char* hash)
{
memset(hash, 0xFF, BLOCK_HASH_SIZE);
}
/**
* Allocated space for block.
*/
static inline size_t block_sizeof(void)
{
return 1 + BLOCK_HASH_SIZE;
}
/**
* Get the state of the block.
*
* For this function, it's allowed to pass a NULL block
* pointer than results in the BLOCK_STATE_EMPTY state.
*/
static inline unsigned block_state_get(const struct snapraid_block* block)
{
if (block == BLOCK_NULL)
return BLOCK_STATE_EMPTY;
return block->state;
}
/**
* Set the state of the block.
*/
static inline void block_state_set(struct snapraid_block* block, unsigned state)
{
block->state = state;
}
/**
* Check if the specified block has an updated hash.
*
* Note that EMPTY / CHG / DELETED return 0.
*/
static inline int block_has_updated_hash(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_BLK || state == BLOCK_STATE_REP;
}
/**
* Check if the specified block has a past hash,
* i.e. the hash of the data that it's now overwritten or lost.
*
* Note that EMPTY / BLK / REP return 0.
*/
static inline int block_has_past_hash(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_CHG || state == BLOCK_STATE_DELETED;
}
/**
* Check if the specified block is part of a file.
*
* Note that EMPTY / DELETED return 0.
*/
static inline int block_has_file(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_BLK
|| state == BLOCK_STATE_CHG || state == BLOCK_STATE_REP;
}
/**
* Check if the block has an invalid parity than needs to be updated.
*
* Note that EMPTY / BLK return 0.
*/
static inline int block_has_invalid_parity(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_DELETED
|| state == BLOCK_STATE_CHG || state == BLOCK_STATE_REP;
}
/**
* Check if the block is part of a file with valid parity.
*
* Note that anything different than BLK return 0.
*/
static inline int block_has_file_and_valid_parity(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_BLK;
}
static inline int file_flag_has(const struct snapraid_file* file, unsigned mask)
{
return (file->flag & mask) == mask;
}
static inline void file_flag_set(struct snapraid_file* file, unsigned mask)
{
file->flag |= mask;
}
static inline void file_flag_clear(struct snapraid_file* file, unsigned mask)
{
file->flag &= ~mask;
}
/**
* Allocate a file.
*/
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical);
/**
* Duplicate a file.
*/
struct snapraid_file* file_dup(struct snapraid_file* copy);
/**
* Deallocate a file.
*/
void file_free(struct snapraid_file* file);
/**
* Rename a file.
*/
void file_rename(struct snapraid_file* file, const char* sub);
/**
* Copy a file.
*/
void file_copy(struct snapraid_file* src_file, struct snapraid_file* dest_file);
/**
* Return the block at the specified position.
*
* Note that the block size if a runtime value.
*/
static inline struct snapraid_block* file_block(struct snapraid_file* file, size_t pos)
{
unsigned char* ptr = (unsigned char*)file->blockvec;
return (struct snapraid_block*)(ptr + pos * block_sizeof());
}
/**
* Return the name of the file, without the dir.
*/
const char* file_name(const struct snapraid_file* file);
/**
* Check if the block is the last in the file.
*/
int file_block_is_last(struct snapraid_file* file, block_off_t file_pos);
/**
* Get the size in bytes of the block.
* If it's the last block of a file it could be less than block_size.
*/
unsigned file_block_size(struct snapraid_file* file, block_off_t file_pos, unsigned block_size);
/**
* Compare a file with an inode.
*/
int file_inode_compare_to_arg(const void* void_arg, const void* void_data);
/**
* Compare files by inode.
*/
int file_inode_compare(const void* void_a, const void* void_b);
/**
* Compare files by path.
*/
int file_path_compare(const void* void_a, const void* void_b);
/**
* Compare files by physical address.
*/
int file_physical_compare(const void* void_a, const void* void_b);
/**
* Compute the hash of a file inode.
*/
static inline tommy_uint32_t file_inode_hash(uint64_t inode)
{
return (tommy_uint32_t)tommy_inthash_u64(inode);
}
/**
* Compare a file with a path.
*/
int file_path_compare_to_arg(const void* void_arg, const void* void_data);
/**
* Compare a file with another file for name, stamp, and both.
*/
int file_name_compare(const void* void_a, const void* void_b);
int file_stamp_compare(const void* void_a, const void* void_b);
int file_namestamp_compare(const void* void_a, const void* void_b);
int file_pathstamp_compare(const void* void_a, const void* void_b);
/**
* Compute the hash of a file path.
*/
static inline tommy_uint32_t file_path_hash(const char* sub)
{
return tommy_hash_u32(0, sub, strlen(sub));
}
/**
* Compute the hash of a file stamp.
*/
static inline tommy_uint32_t file_stamp_hash(data_off_t size, int64_t mtime_sec, int mtime_nsec)
{
return tommy_inthash_u32((tommy_uint32_t)size ^ tommy_inthash_u32(mtime_sec ^ tommy_inthash_u32(mtime_nsec)));
}
/**
* Allocate a extent.
*/
struct snapraid_extent* extent_alloc(block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count);
/**
* Deallocate a extent.
*/
void extent_free(struct snapraid_extent* extent);
/**
* Compare extent by parity position.
*/
int extent_parity_compare(const void* void_a, const void* void_b);
/**
* Compare extent by file and file position.
*/
int extent_file_compare(const void* void_a, const void* void_b);
static inline int link_flag_has(const struct snapraid_link* slink, unsigned mask)
{
return (slink->flag & mask) == mask;
}
static inline void link_flag_set(struct snapraid_link* slink, unsigned mask)
{
slink->flag |= mask;
}
static inline void link_flag_clear(struct snapraid_link* slink, unsigned mask)
{
slink->flag &= ~mask;
}
static inline void link_flag_let(struct snapraid_link* slink, unsigned flag, unsigned mask)
{
slink->flag &= ~mask;
slink->flag |= flag & mask;
}
static inline unsigned link_flag_get(struct snapraid_link* slink, unsigned mask)
{
return slink->flag & mask;
}
/**
* Allocate a link.
*/
struct snapraid_link* link_alloc(const char* name, const char* slink, unsigned link_flag);
/**
* Deallocate a link.
*/
void link_free(struct snapraid_link* slink);
/**
* Compare a link with a name.
*/
int link_name_compare_to_arg(const void* void_arg, const void* void_data);
/**
* Compare links by path.
*/
int link_alpha_compare(const void* void_a, const void* void_b);
/**
* Compute the hash of a link name.
*/
static inline tommy_uint32_t link_name_hash(const char* name)
{
return tommy_hash_u32(0, name, strlen(name));
}
static inline int dir_flag_has(const struct snapraid_dir* dir, unsigned mask)
{
return (dir->flag & mask) == mask;
}
static inline void dir_flag_set(struct snapraid_dir* dir, unsigned mask)
{
dir->flag |= mask;
}
static inline void dir_flag_clear(struct snapraid_dir* dir, unsigned mask)
{
dir->flag &= ~mask;
}
/**
* Allocate a dir.
*/
struct snapraid_dir* dir_alloc(const char* name);
/**
* Deallocate a dir.
*/
void dir_free(struct snapraid_dir* dir);
/**
* Compare a dir with a name.
*/
int dir_name_compare(const void* void_arg, const void* void_data);
/**
* Compute the hash of a dir name.
*/
static inline tommy_uint32_t dir_name_hash(const char* name)
{
return tommy_hash_u32(0, name, strlen(name));
}
/**
* Allocate a disk.
*/
struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev, const char* uuid, int skip_access);
/**
* Deallocate a disk.
*/
void disk_free(struct snapraid_disk* disk);
/**
* Enable multithread support for the disk.
*/
void disk_start_thread(struct snapraid_disk* disk);
/**
* Get the size of the disk in blocks.
*/
block_off_t fs_size(struct snapraid_disk* disk);
/**
* Check if a disk is totally empty and can be discarded from the content file.
* A disk is empty if it doesn't contain any file, symlink, hardlink or dir
* and without any DELETED block.
* The blockmax is used to limit the search of DELETED block up to blockmax.
*/
int fs_is_empty(struct snapraid_disk* disk, block_off_t blockmax);
/**
* Check the file-system for errors.
* Return 0 if it's OK.
*/
int fs_check(struct snapraid_disk* disk);
/**
* Allocate a parity position for the specified file position.
*
* After this call you can use the par2file/par2block operations
* to query the relation.
*
* \note This function is NOT thread-safe as it uses the disk cache. +
*/
void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos);
/**
* Deallocate the parity position.
*
* After this call the par2file/par2block operations
* won't find anymore the parity association.
*
* \note This function is NOT thread-safe as it uses the disk cache.
*/
void fs_deallocate(struct snapraid_disk* disk, block_off_t pos);
/**
* Get the block from the file position.
*/
struct snapraid_block* fs_file2block_get(struct snapraid_file* file, block_off_t file_pos);
/**
* Get the file position from the parity position.
* Return 0 if no file is using it.
*/
struct snapraid_file* fs_par2file_find(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos);
/**
* Get the file position from the parity position.
*/
static inline struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
{
struct snapraid_file* ret;
ret = fs_par2file_find(disk, parity_pos, file_pos);
if (ret == 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when deresolving parity to file at position '%u' in disk '%s'\n", parity_pos, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
return ret;
}
/**
* Get the parity position from the file position.
* Return POS_NULL if no parity is allocated.
*/
block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos);
/**
* Get the parity position from the file position.
*/
static inline block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
{
block_off_t ret;
ret = fs_file2par_find(disk, file, file_pos);
if (ret == POS_NULL) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when resolving file '%s' at position '%u/%u' in disk '%s'\n", file->sub, file_pos, file->blockmax, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
return ret;
}
/**
* Get the block from the parity position.
* Return BLOCK_NULL==0 if the block is over the end of the disk or not used.
*/
struct snapraid_block* fs_par2block_find(struct snapraid_disk* disk, block_off_t parity_pos);
/**
* Get the block from the parity position.
*/
static inline struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos)
{
struct snapraid_block* ret;
ret = fs_par2block_find(disk, parity_pos);
if (ret == BLOCK_NULL) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency when deresolving parity to block at position '%u' in disk '%s'\n", parity_pos, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
return ret;
}
/**
* Allocate a disk mapping.
* Uses uuid="" if not available.
*/
struct snapraid_map* map_alloc(const char* name, unsigned position, block_off_t total_blocks, block_off_t free_blocks, const char* uuid);
/**
* Deallocate a disk mapping.
*/
void map_free(struct snapraid_map* map);
/**
* Mask used to store additional information in the info bits.
*
* These bits reduce the granularity of the time in the memory representation.
*/
#define INFO_MASK 0x7
/**
* Make an info.
*/
static inline snapraid_info info_make(time_t last_access, int error, int rehash, int justsynced)
{
/* clear the lowest bits as reserved for other information */
snapraid_info info = last_access & ~INFO_MASK;
if (error != 0)
info |= 0x1;
if (rehash != 0)
info |= 0x2;
if (justsynced != 0)
info |= 0x4;
return info;
}
/**
* Extract the time information.
* This is the last time when the block was know to be correct.
* The "scrubbed" info tells if the time is referring at the latest sync or scrub.
*/
static inline time_t info_get_time(snapraid_info info)
{
return info & ~INFO_MASK;
}
/**
* Extract the error information.
* Report if the block address had some problem.
*/
static inline int info_get_bad(snapraid_info info)
{
return (info & 0x1) != 0;
}
/**
* Extract the rehash information.
* Report if the block address is using the old hash and needs to be rehashed.
*/
static inline int info_get_rehash(snapraid_info info)
{
return (info & 0x2) != 0;
}
/**
* Extract the scrubbed information.
* Report if the block address was never scrubbed.
*/
static inline int info_get_justsynced(snapraid_info info)
{
return (info & 0x4) != 0;
}
/**
* Mark the block address as with error.
*/
static inline snapraid_info info_set_bad(snapraid_info info)
{
return info | 0x1;
}
/**
* Mark the block address as with rehash.
*/
static inline snapraid_info info_set_rehash(snapraid_info info)
{
return info | 0x2;
}
/**
* Set the info at the specified position.
* The position is allocated if not yet done.
*/
static inline void info_set(tommy_arrayblkof* array, block_off_t pos, snapraid_info info)
{
tommy_arrayblkof_grow(array, pos + 1);
memcpy(tommy_arrayblkof_ref(array, pos), &info, sizeof(snapraid_info));
}
/**
* Get the info at the specified position.
* For not allocated position, 0 is returned.
*/
static inline snapraid_info info_get(tommy_arrayblkof* array, block_off_t pos)
{
snapraid_info info;
if (pos >= tommy_arrayblkof_size(array))
return 0;
memcpy(&info, tommy_arrayblkof_ref(array, pos), sizeof(snapraid_info));
return info;
}
/**
* Compare times
*/
int time_compare(const void* void_a, const void* void_b);
/****************************************************************************/
/* format */
#define FMT_FILE 0 /**< Print only the file. */
#define FMT_DISK 1 /**< Print the disk name and the file. */
#define FMT_PATH 2 /**< Print the full path. */
extern int FMT_MODE;
/**
* Format a file path for poll reference
*/
const char* fmt_poll(const struct snapraid_disk* disk, const char* str, char* buffer);
/**
* Format a path name for terminal reference
*/
const char* fmt_term(const struct snapraid_disk* disk, const char* str, char* buffer);
#endif
snapraid-12.1/cmdline/fnmatch.c 0000664 0000000 0000000 00000031302 14166610522 0016437 0 ustar 00root root 0000000 0000000 /* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
This file is part of the GNU C Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#if HAVE_CONFIG_H
# include
#endif
#if !HAVE_FNMATCH
/* Enable GNU extensions in fnmatch.h. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#include
#if HAVE_FNMATCH_H
#include
#else
#include "fnmatch.h"
#endif
#include
#if HAVE_STRING_H || defined _LIBC
# include
#else
# include
#endif
#if defined STDC_HEADERS || defined _LIBC
# include
#endif
/* For platforms which support the ISO C amendment 1 functionality we
support user defined character classes. */
#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
/* Solaris 2.5 has a bug: must be included before . */
# include
# include
#endif
/* Comment out all this code if we are using the GNU C Library, and are not
actually compiling the library itself. This code is part of the GNU C
Library, but also included in many other GNU distributions. Compiling
and linking in this code is a waste when using the GNU C library
(especially if it is a shared library). Rather than having every GNU
program understand `configure --with-gnu-libc' and omit the object files,
it is simpler to just do this in the source for each such file. */
#if defined _LIBC || !defined __GNU_LIBRARY__
# if defined STDC_HEADERS || !defined isascii
# define ISASCII(c) 1
# else
# define ISASCII(c) isascii(c)
# endif
# ifdef isblank
# define ISBLANK(c) (ISASCII (c) && isblank (c))
# else
# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
# endif
# ifdef isgraph
# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
# else
# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
# endif
# define ISPRINT(c) (ISASCII (c) && isprint (c))
# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
# define ISALNUM(c) (ISASCII (c) && isalnum (c))
# define ISALPHA(c) (ISASCII (c) && isalpha (c))
# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
# define ISLOWER(c) (ISASCII (c) && islower (c))
# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
# define ISSPACE(c) (ISASCII (c) && isspace (c))
# define ISUPPER(c) (ISASCII (c) && isupper (c))
# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
/* The GNU C library provides support for user-defined character classes
and the functions from ISO C amendment 1. */
# ifdef CHARCLASS_NAME_MAX
# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
# else
/* This shouldn't happen but some implementation might still have this
problem. Use a reasonable default value. */
# define CHAR_CLASS_MAX_LENGTH 256
# endif
# ifdef _LIBC
# define IS_CHAR_CLASS(string) __wctype (string)
# else
# define IS_CHAR_CLASS(string) wctype (string)
# endif
# else
# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
# define IS_CHAR_CLASS(string) \
(STREQ (string, "alpha") || STREQ (string, "upper") \
|| STREQ (string, "lower") || STREQ (string, "digit") \
|| STREQ (string, "alnum") || STREQ (string, "xdigit") \
|| STREQ (string, "space") || STREQ (string, "print") \
|| STREQ (string, "punct") || STREQ (string, "graph") \
|| STREQ (string, "cntrl") || STREQ (string, "blank"))
# endif
/* Avoid depending on library functions or files
whose names are inconsistent. */
# if !defined _LIBC && !defined getenv
extern char *getenv ();
# endif
# ifndef errno
extern int errno;
# endif
/* This function doesn't exist on most systems. */
# if !defined HAVE___STRCHRNUL && !defined _LIBC
static char *
__strchrnul (s, c)
const char *s;
int c;
{
char *result = strchr (s, c);
if (result == NULL)
result = strchr (s, '\0');
return result;
}
# endif
# ifndef internal_function
/* Inside GNU libc we mark some function in a special way. In other
environments simply ignore the marking. */
# define internal_function
# endif
/* Match STRING against the filename pattern PATTERN, returning zero if
it matches, nonzero if not. */
static int internal_fnmatch __P ((const char *pattern, const char *string,
int no_leading_period, int flags))
internal_function;
static int
internal_function
internal_fnmatch (pattern, string, no_leading_period, flags)
const char *pattern;
const char *string;
int no_leading_period;
int flags;
{
register const char *p = pattern, *n = string;
register unsigned char c;
/* Note that this evaluates C many times. */
# ifdef _LIBC
# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
# else
# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
# endif
while ((c = *p++) != '\0')
{
c = FOLD (c);
switch (c)
{
case '?':
if (*n == '\0')
return FNM_NOMATCH;
else if (*n == '/' && (flags & FNM_FILE_NAME))
return FNM_NOMATCH;
else if (*n == '.' && no_leading_period
&& (n == string
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
return FNM_NOMATCH;
break;
case '\\':
if (!(flags & FNM_NOESCAPE))
{
c = *p++;
if (c == '\0')
/* Trailing \ loses. */
return FNM_NOMATCH;
c = FOLD (c);
}
if (FOLD ((unsigned char) *n) != c)
return FNM_NOMATCH;
break;
case '*':
if (*n == '.' && no_leading_period
&& (n == string
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
return FNM_NOMATCH;
for (c = *p++; c == '?' || c == '*'; c = *p++)
{
if (*n == '/' && (flags & FNM_FILE_NAME))
/* A slash does not match a wildcard under FNM_FILE_NAME. */
return FNM_NOMATCH;
else if (c == '?')
{
/* A ? needs to match one character. */
if (*n == '\0')
/* There isn't another character; no match. */
return FNM_NOMATCH;
else
/* One character of the string is consumed in matching
this ? wildcard, so *??? won't match if there are
less than three characters. */
++n;
}
}
if (c == '\0')
/* The wildcard(s) is/are the last element of the pattern.
If the name is a file name and contains another slash
this does mean it cannot match. */
return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL
? FNM_NOMATCH : 0);
else
{
const char *endp;
endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');
if (c == '[')
{
int flags2 = ((flags & FNM_FILE_NAME)
? flags : (flags & ~FNM_PERIOD));
for (--p; n < endp; ++n)
if (internal_fnmatch (p, n,
(no_leading_period
&& (n == string
|| (n[-1] == '/'
&& (flags
& FNM_FILE_NAME)))),
flags2)
== 0)
return 0;
}
else if (c == '/' && (flags & FNM_FILE_NAME))
{
while (*n != '\0' && *n != '/')
++n;
if (*n == '/'
&& (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
flags) == 0))
return 0;
}
else
{
int flags2 = ((flags & FNM_FILE_NAME)
? flags : (flags & ~FNM_PERIOD));
if (c == '\\' && !(flags & FNM_NOESCAPE))
c = *p;
c = FOLD (c);
for (--p; n < endp; ++n)
if (FOLD ((unsigned char) *n) == c
&& (internal_fnmatch (p, n,
(no_leading_period
&& (n == string
|| (n[-1] == '/'
&& (flags
& FNM_FILE_NAME)))),
flags2) == 0))
return 0;
}
}
/* If we come here no match is possible with the wildcard. */
return FNM_NOMATCH;
case '[':
{
/* Nonzero if the sense of the character class is inverted. */
static int posixly_correct;
register int not;
char cold;
if (posixly_correct == 0)
posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
if (*n == '\0')
return FNM_NOMATCH;
if (*n == '.' && no_leading_period && (n == string
|| (n[-1] == '/'
&& (flags
& FNM_FILE_NAME))))
return FNM_NOMATCH;
if (*n == '/' && (flags & FNM_FILE_NAME))
/* `/' cannot be matched. */
return FNM_NOMATCH;
not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
if (not)
++p;
c = *p++;
for (;;)
{
unsigned char fn = FOLD ((unsigned char) *n);
if (!(flags & FNM_NOESCAPE) && c == '\\')
{
if (*p == '\0')
return FNM_NOMATCH;
c = FOLD ((unsigned char) *p);
++p;
if (c == fn)
goto matched;
}
else if (c == '[' && *p == ':')
{
/* Leave room for the null. */
char str[CHAR_CLASS_MAX_LENGTH + 1];
size_t c1 = 0;
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
wctype_t wt;
# endif
const char *startp = p;
for (;;)
{
if (c1 == CHAR_CLASS_MAX_LENGTH)
/* The name is too long and therefore the pattern
is ill-formed. */
return FNM_NOMATCH;
c = *++p;
if (c == ':' && p[1] == ']')
{
p += 2;
break;
}
if (c < 'a' || c >= 'z')
{
/* This cannot possibly be a character class name.
Match it as a normal range. */
p = startp;
c = '[';
goto normal_bracket;
}
str[c1++] = c;
}
str[c1] = '\0';
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
wt = IS_CHAR_CLASS (str);
if (wt == 0)
/* Invalid character class name. */
return FNM_NOMATCH;
if (__iswctype (__btowc ((unsigned char) *n), wt))
goto matched;
# else
if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n))
|| (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n))
|| (STREQ (str, "blank") && ISBLANK ((unsigned char) *n))
|| (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n))
|| (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n))
|| (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n))
|| (STREQ (str, "lower") && ISLOWER ((unsigned char) *n))
|| (STREQ (str, "print") && ISPRINT ((unsigned char) *n))
|| (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n))
|| (STREQ (str, "space") && ISSPACE ((unsigned char) *n))
|| (STREQ (str, "upper") && ISUPPER ((unsigned char) *n))
|| (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n)))
goto matched;
# endif
}
else if (c == '\0')
/* [ (unterminated) loses. */
return FNM_NOMATCH;
else
{
normal_bracket:
if (FOLD (c) == fn)
goto matched;
cold = c;
c = *p++;
if (c == '-' && *p != ']')
{
/* It is a range. */
unsigned char cend = *p++;
if (!(flags & FNM_NOESCAPE) && cend == '\\')
cend = *p++;
if (cend == '\0')
return FNM_NOMATCH;
if (cold <= fn && fn <= FOLD (cend))
goto matched;
c = *p++;
}
}
if (c == ']')
break;
}
if (!not)
return FNM_NOMATCH;
break;
matched:
/* Skip the rest of the [...] that already matched. */
while (c != ']')
{
if (c == '\0')
/* [... (unterminated) loses. */
return FNM_NOMATCH;
c = *p++;
if (!(flags & FNM_NOESCAPE) && c == '\\')
{
if (*p == '\0')
return FNM_NOMATCH;
/* XXX 1003.2d11 is unclear if this is right. */
++p;
}
else if (c == '[' && *p == ':')
{
do
if (*++p == '\0')
return FNM_NOMATCH;
while (*p != ':' || p[1] == ']');
p += 2;
c = *p;
}
}
if (not)
return FNM_NOMATCH;
}
break;
default:
if (c != FOLD ((unsigned char) *n))
return FNM_NOMATCH;
}
++n;
}
if (*n == '\0')
return 0;
if ((flags & FNM_LEADING_DIR) && *n == '/')
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
return 0;
return FNM_NOMATCH;
# undef FOLD
}
int
fnmatch (pattern, string, flags)
const char *pattern;
const char *string;
int flags;
{
return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
}
#endif /* _LIBC or not __GNU_LIBRARY__. */
#else
/* avoid the warning: ISO C forbids an empty translation unit */
extern int make_iso_compilers_happy;
#endif
snapraid-12.1/cmdline/fnmatch.h 0000664 0000000 0000000 00000005440 14166610522 0016450 0 ustar 00root root 0000000 0000000 /* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#ifndef _FNMATCH_H
#define _FNMATCH_H 1
#ifdef __cplusplus
extern "C" {
#endif
#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
# if !defined __GLIBC__ || !defined __P
# undef __P
# define __P(protos) protos
# endif
#else /* Not C++ or ANSI C. */
# undef __P
# define __P(protos) ()
/* We can get away without defining `const' here only because in this file
it is used only inside the prototype for `fnmatch', which is elided in
non-ANSI C where `const' is problematical. */
#endif /* C++ or ANSI C. */
#ifndef const
# if (defined __STDC__ && __STDC__) || defined __cplusplus
# define __const const
# else
# define __const
# endif
#endif
/* We #undef these before defining them because some losing systems
(HP-UX A.08.07 for example) define these in . */
#undef FNM_PATHNAME
#undef FNM_NOESCAPE
#undef FNM_PERIOD
/* Bits set in the FLAGS argument to `fnmatch'. */
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
#endif
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
#define FNM_NOMATCH 1
/* This value is returned if the implementation does not support
`fnmatch'. Since this is not the case here it will never be
returned but the conformance test suites still require the symbol
to be defined. */
#ifdef _XOPEN_SOURCE
# define FNM_NOSYS (-1)
#endif
/* Match NAME against the filename pattern PATTERN,
returning zero if it matches, FNM_NOMATCH if not. */
extern int fnmatch __P ((__const char *__pattern, __const char *__name,
int __flags));
#ifdef __cplusplus
}
#endif
#endif /* fnmatch.h */
snapraid-12.1/cmdline/handle.c 0000664 0000000 0000000 00000024203 14166610522 0016254 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "elem.h"
#include "support.h"
#include "handle.h"
/****************************************************************************/
/* handle */
int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, int mode)
{
int ret;
int flags;
/* if it's the same file, and already opened, nothing to do */
if (handle->file == file && handle->f != -1) {
return 0;
}
advise_init(&handle->advise, mode);
pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
ret = mkancestor(handle->path);
if (ret != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
/* initial values, changed later if required */
handle->created = 0;
/* flags for opening */
/* O_BINARY: open as binary file (Windows only) */
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
/* open for read write */
handle->f = open(handle->path, flags | O_RDWR);
/* if failed for missing write permission */
if (handle->f == -1 && (errno == EACCES || errno == EROFS)) {
/* open for real-only */
handle->f = open(handle->path, flags | O_RDONLY);
}
/* if failed for missing file */
if (handle->f == -1 && errno == ENOENT) {
char path_from[PATH_MAX];
/* check if exists a .unrecoverable copy, and rename to the real one */
pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
if (rename(path_from, handle->path) == 0) {
/* open for read write */
handle->f = open(handle->path, flags | O_RDWR);
} else {
/* create it */
handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
if (handle->f != -1) {
/* mark it as created if really done */
handle->created = 1;
}
}
}
if (handle->f == -1) {
/* LCOV_EXCL_START */
/* invalidate for error */
handle->file = 0;
handle->f = -1;
handle->valid_size = 0;
log_fatal("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
/* just opened */
handle->file = file;
/* get the stat info */
ret = fstat(handle->f, &handle->st);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
/* get the size of the existing data */
handle->valid_size = handle->st.st_size;
ret = advise_open(&handle->advise, handle->f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
{
int ret;
ret = ftruncate(handle->f, file->size);
if (ret != 0) {
/* LCOV_EXCL_START */
if (errno == EACCES) {
log_fatal("Failed to truncate file '%s' for missing write permission.\n", handle->path);
} else {
log_fatal("Error truncating file '%s'. %s.\n", handle->path, strerror(errno));
}
return -1;
/* LCOV_EXCL_STOP */
}
/* adjust the size to the truncated size */
handle->valid_size = file->size;
return 0;
}
int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, fptr* out, fptr* out_missing)
{
int ret;
int flags;
if (!out_missing)
out_missing = out;
/* if already opened, nothing to do */
if (handle->file == file && handle->file != 0 && handle->f != -1) {
return 0;
}
advise_init(&handle->advise, mode);
pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
/* for sure not created */
handle->created = 0;
/* flags for opening */
/* O_BINARY: open as binary file (Windows only) */
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
/* open for read */
handle->f = open_noatime(handle->path, flags | O_RDONLY);
if (handle->f == -1) {
/* invalidate for error */
handle->file = 0;
handle->f = -1;
handle->valid_size = 0;
if (errno == ENOENT)
out_missing("Missing file '%s'.\n", handle->path);
else
out("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
}
/* just opened */
handle->file = file;
/* get the stat info */
ret = fstat(handle->f, &handle->st);
if (ret != 0) {
/* LCOV_EXCL_START */
out("Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
/* get the size of the existing data */
handle->valid_size = handle->st.st_size;
ret = advise_open(&handle->advise, handle->f);
if (ret != 0) {
/* LCOV_EXCL_START */
out("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int handle_close(struct snapraid_handle* handle)
{
int ret;
/* close if open */
if (handle->f != -1) {
ret = close(handle->f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
/* invalidate for error */
handle->file = 0;
handle->f = -1;
handle->valid_size = 0;
return -1;
/* LCOV_EXCL_STOP */
}
}
/* reset the descriptor */
handle->file = 0;
handle->f = -1;
handle->valid_size = 0;
return 0;
}
int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing)
{
ssize_t read_ret;
data_off_t offset;
unsigned read_size;
unsigned count;
int ret;
offset = file_pos * (data_off_t)block_size;
if (!out_missing)
out_missing = out;
/* check if we are going to read only not initialized data */
if (offset >= handle->valid_size) {
/* if the file is missing, it's at 0 size, or it's rebuilt while reading */
if (offset == handle->valid_size || handle->valid_size == 0)
out_missing("Reading data from missing file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
else
out("Reading missing data from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
return -1;
}
read_size = file_block_size(handle->file, file_pos, block_size);
count = 0;
do {
/* read the full block to support O_DIRECT */
read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
if (read_ret < 0) {
/* LCOV_EXCL_START */
out("Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", handle->path, offset + count, block_size - count, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
if (read_ret == 0) {
out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
return -1;
}
count += read_ret;
} while (count < read_size);
/* pad with 0 */
if (read_size < block_size) {
memset(block_buffer + read_size, 0, block_size - read_size);
}
ret = advise_read(&handle->advise, handle->f, offset, block_size);
if (ret != 0) {
/* LCOV_EXCL_START */
out("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return read_size;
}
int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size)
{
ssize_t write_ret;
data_off_t offset;
unsigned write_size;
int ret;
offset = file_pos * (data_off_t)block_size;
write_size = file_block_size(handle->file, file_pos, block_size);
write_ret = pwrite(handle->f, block_buffer, write_size, offset);
if (write_ret != (ssize_t)write_size) { /* conversion is safe because block_size is always small */
/* LCOV_EXCL_START */
log_fatal("Error writing file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
/* adjust the size of the valid data */
if (handle->valid_size < offset + write_size) {
handle->valid_size = offset + write_size;
}
ret = advise_write(&handle->advise, handle->f, offset, block_size);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int handle_utime(struct snapraid_handle* handle)
{
int ret;
/* do nothing if not opened */
if (handle->f == -1)
return 0;
ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
{
tommy_node* i;
unsigned j;
unsigned size = 0;
struct snapraid_handle* handle;
/* get the size of the mapping */
size = 0;
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
if (map->position > size)
size = map->position;
}
++size; /* size is one more than the max */
handle = malloc_nofail(size * sizeof(struct snapraid_handle));
for (j = 0; j < size; ++j) {
/* default for empty position */
handle[j].disk = 0;
handle[j].file = 0;
handle[j].f = -1;
handle[j].valid_size = 0;
}
/* set the vector */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_map* map;
struct snapraid_disk* disk = i->data;
tommy_node* k;
/* search the mapping for this disk */
map = 0;
for (k = state->maplist; k != 0; k = k->next) {
map = k->data;
if (strcmp(disk->name, map->name) == 0)
break;
}
if (!map) {
/* LCOV_EXCL_START */
log_fatal("Internal error for inconsistent disk mapping.\n");
os_abort();
/* LCOV_EXCL_STOP */
}
handle[map->position].disk = disk;
}
*handlemax = size;
return handle;
}
snapraid-12.1/cmdline/handle.h 0000664 0000000 0000000 00000005715 14166610522 0016270 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HANDLE_H
#define __HANDLE_H
#include "state.h"
#include "support.h"
/****************************************************************************/
/* handle */
struct snapraid_handle {
char path[PATH_MAX]; /**< Path of the file. */
struct snapraid_disk* disk; /**< Disk of the file. */
struct snapraid_file* file; /**< File opened. When the file is closed, it's set to 0. */
int f; /**< Handle of the file. */
struct stat st; /**< Stat info of the opened file. */
struct advise_struct advise; /**< Advise information. */
data_off_t valid_size; /**< Size of the valid data. */
int created; /**< If the file was created, otherwise it was already existing. */
};
/**
* Create a file.
* The file is created if missing, and opened with write access.
* If the file is created, the handle->created is set.
* The initial size of the file is stored in the file->st struct.
* If the file cannot be opened for write access, it's opened with read-only access.
* The read-only access works only if the file has already the correct size and doesn't need to be modified.
*/
int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, int mode);
/**
* Truncate a file if required.
*/
int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file);
/**
* Open a file.
* The file is opened for reading.
*/
int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, fptr* out, fptr* out_missing);
/**
* Close a file.
*/
int handle_close(struct snapraid_handle* handle);
/**
* Read a block from a file.
* If the read block is shorter, it's padded with 0.
*/
int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing);
/**
* Write a block to a file.
*/
int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size);
/**
* Change the modification time of the file to the saved value.
*/
int handle_utime(struct snapraid_handle* handle);
/**
* Map the unsorted list of disk to an ordered vector.
* \param diskmax The size of the vector.
* \return The allocated vector of pointers.
*/
struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* diskmax);
#endif
snapraid-12.1/cmdline/import.c 0000664 0000000 0000000 00000021441 14166610522 0016334 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "import.h"
/****************************************************************************/
/* import */
/**
* Compare the hash of two import blocks.
*/
int import_block_hash_compare(const void* void_arg, const void* void_data)
{
const unsigned char* arg = void_arg;
const struct snapraid_import_block* block = void_data;
return memcmp(arg, block->hash, BLOCK_HASH_SIZE);
}
int import_block_prevhash_compare(const void* void_arg, const void* void_data)
{
const unsigned char* arg = void_arg;
const struct snapraid_import_block* block = void_data;
return memcmp(arg, block->prevhash, BLOCK_HASH_SIZE);
}
/**
* Compute the hash of the hash for an import block.
* We just use the first 32 bit of the hash itself.
*/
static inline tommy_uint32_t import_block_hash(const unsigned char* hash)
{
/* the hash data is not aligned, and we cannot access it with a direct cast */
return hash[0] | ((uint32_t)hash[1] << 8) | ((uint32_t)hash[2] << 16) | ((uint32_t)hash[3] << 24);
}
static void import_file(struct snapraid_state* state, const char* path, uint64_t size)
{
struct snapraid_import_file* file;
block_off_t i;
data_off_t offset;
void* buffer;
int ret;
int f;
int flags;
unsigned block_size = state->block_size;
struct advise_struct advise;
file = malloc_nofail(sizeof(struct snapraid_import_file));
file->path = strdup_nofail(path);
file->size = size;
file->blockmax = (size + block_size - 1) / block_size;
file->blockimp = malloc_nofail(file->blockmax * sizeof(struct snapraid_import_block));
buffer = malloc_nofail(block_size);
advise_init(&advise, state->file_mode);
/* open for read */
flags = O_RDONLY | O_BINARY | advise_flags(&advise);
f = open(path, flags);
if (f == -1) {
/* LCOV_EXCL_START */
log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = advise_open(&advise, f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error advising file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
offset = 0;
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_import_block* block = &file->blockimp[i];
unsigned read_size = block_size;
if (read_size > size)
read_size = size;
ret = read(f, buffer, read_size);
if (ret < 0 || (unsigned)ret != read_size) {
/* LCOV_EXCL_START */
log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
block->file = file;
block->offset = offset;
block->size = read_size;
memhash(state->hash, state->hashseed, block->hash, buffer, read_size);
tommy_hashdyn_insert(&state->importset, &block->nodeset, block, import_block_hash(block->hash));
/* if we are in a rehash state */
if (state->prevhash != HASH_UNDEFINED) {
/* compute also the previous hash */
memhash(state->prevhash, state->prevhashseed, block->prevhash, buffer, read_size);
tommy_hashdyn_insert(&state->previmportset, &block->prevnodeset, block, import_block_hash(block->prevhash));
}
offset += read_size;
size -= read_size;
}
ret = close(f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
tommy_list_insert_tail(&state->importlist, &file->nodelist, file);
free(buffer);
}
void import_file_free(struct snapraid_import_file* file)
{
free(file->path);
free(file->blockimp);
free(file);
}
int state_import_fetch(struct snapraid_state* state, int rehash, struct snapraid_block* missing_block, unsigned char* buffer)
{
struct snapraid_import_block* block;
int ret;
int f;
const unsigned char* hash = missing_block->hash;
unsigned block_size = state->block_size;
unsigned read_size;
unsigned char buffer_hash[HASH_MAX];
const char* path;
if (rehash) {
block = tommy_hashdyn_search(&state->previmportset, import_block_prevhash_compare, hash, import_block_hash(hash));
} else {
block = tommy_hashdyn_search(&state->importset, import_block_hash_compare, hash, import_block_hash(hash));
}
if (!block)
return -1;
path = block->file->path;
read_size = block->size;
f = open(path, O_RDONLY | O_BINARY);
if (f == -1) {
/* LCOV_EXCL_START */
if (errno == ENOENT) {
log_fatal("DANGER! file '%s' disappeared.\n", path);
log_fatal("If you moved it, please rerun the same command.\n");
} else {
log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = pread(f, buffer, read_size, block->offset);
if (ret < 0 || (unsigned)ret != read_size) {
/* LCOV_EXCL_START */
log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = close(f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (read_size != block_size) {
/* fill the remaining with 0 */
memset(buffer + read_size, 0, block_size - read_size);
}
/* recheck the hash */
if (rehash)
memhash(state->prevhash, state->prevhashseed, buffer_hash, buffer, read_size);
else
memhash(state->hash, state->hashseed, buffer_hash, buffer, read_size);
if (memcmp(buffer_hash, hash, BLOCK_HASH_SIZE) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in data reading file '%s'.\n", path);
log_fatal("Please don't change imported files while running.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return 0;
}
static void import_dir(struct snapraid_state* state, const char* dir)
{
DIR* d;
d = opendir(dir);
if (!d) {
/* LCOV_EXCL_START */
log_fatal("Error opening directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
while (1) {
char path_next[PATH_MAX];
struct stat st;
const char* name;
struct dirent* dd;
/* clear errno to detect erroneous conditions */
errno = 0;
dd = readdir(d);
if (dd == 0 && errno != 0) {
/* LCOV_EXCL_START */
log_fatal("Error reading directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (dd == 0) {
break; /* finished */
}
/* skip "." and ".." files */
name = dd->d_name;
if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;
pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &st);
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
/* because we use a directory reading method that doesn't read info about ReparsePoint. */
/* Note that here we cannot call here lstat_sync(), because we don't know what kind */
/* of file is it, and lstat_sync() doesn't always work */
if (st.st_mode == 0) {
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#else
/* get lstat info about the file */
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
if (S_ISREG(st.st_mode)) {
import_file(state, path_next, st.st_size);
} else if (S_ISDIR(st.st_mode)) {
pathslash(path_next, sizeof(path_next));
import_dir(state, path_next);
}
}
if (closedir(d) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
void state_import(struct snapraid_state* state, const char* dir)
{
char path[PATH_MAX];
msg_progress("Importing...\n");
/* if the hash is not full */
if (BLOCK_HASH_SIZE != HASH_MAX) {
/* LCOV_EXCL_START */
log_fatal("You cannot import files when using a reduced hash.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* add the final slash */
pathimport(path, sizeof(path), dir);
pathslash(path, sizeof(path));
import_dir(state, path);
}
snapraid-12.1/cmdline/import.h 0000664 0000000 0000000 00000004377 14166610522 0016352 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __IMPORT_H
#define __IMPORT_H
#include "elem.h"
#include "state.h"
/****************************************************************************/
/* import */
/**
* Import block.
* Block used to import data external when recovering by hash.
*/
struct snapraid_import_block {
struct snapraid_import_file* file; /**< Back pointer to the file owning this block. */
unsigned size; /**< Size of the block. */
data_off_t offset; /**< Position of the block in the file. */
unsigned char hash[HASH_MAX]; /**< Hash of the block. */
unsigned char prevhash[HASH_MAX]; /**< Previous hash of the block. Valid only if we are in rehash state. */
/* nodes for data structures */
tommy_hashdyn_node nodeset;
tommy_hashdyn_node prevnodeset;
};
/**
* Import file.
* File used to import data external when recovering by hash.
*/
struct snapraid_import_file {
data_off_t size; /**< Size of the file. */
struct snapraid_import_block* blockimp; /**< All the blocks of the file. */
block_off_t blockmax; /**< Number of blocks. */
char* path; /**< Full path of the file. */
/* nodes for data structures */
tommy_node nodelist;
};
/**
* Deallocate an import file.
*/
void import_file_free(struct snapraid_import_file* file);
/**
* Fetch a block from the specified hash.
* Return ==0 if the block is found, and copied into buffer.
*/
int state_import_fetch(struct snapraid_state* state, int prevhash, struct snapraid_block* missing_block, unsigned char* buffer);
/**
* Import files from the specified directory.
*/
void state_import(struct snapraid_state* state, const char* dir);
#endif
snapraid-12.1/cmdline/io.c 0000664 0000000 0000000 00000064401 14166610522 0015434 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2016 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "io.h"
void (*io_start)(struct snapraid_io* io,
block_off_t blockstart, block_off_t blockmax,
bit_vect_t* block_enabled) = 0;
void (*io_stop)(struct snapraid_io* io) = 0;
block_off_t (*io_read_next)(struct snapraid_io* io, void*** buffer) = 0;
struct snapraid_task* (*io_data_read)(struct snapraid_io* io, unsigned* diskcur, unsigned* waiting_map, unsigned* waiting_mac) = 0;
struct snapraid_task* (*io_parity_read)(struct snapraid_io* io, unsigned* levcur, unsigned* waiting_map, unsigned* waiting_mac) = 0;
void (*io_parity_write)(struct snapraid_io* io, unsigned* levcur, unsigned* waiting_map, unsigned* waiting_mac) = 0;
void (*io_write_preset)(struct snapraid_io* io, block_off_t blockcur, int skip) = 0;
void (*io_write_next)(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error) = 0;
void (*io_refresh)(struct snapraid_io* io) = 0;
/**
* Get the next block position to process.
*/
static block_off_t io_position_next(struct snapraid_io* io)
{
block_off_t blockcur;
/* get the next position */
if (io->block_enabled) {
while (io->block_next < io->block_max && !bit_vect_test(io->block_enabled, io->block_next))
++io->block_next;
}
blockcur = io->block_next;
/* next block for the next call */
++io->block_next;
return blockcur;
}
/**
* Setup the next pending task for all readers.
*/
static void io_reader_sched(struct snapraid_io* io, int task_index, block_off_t blockcur)
{
unsigned i;
for (i = 0; i < io->reader_max; ++i) {
struct snapraid_worker* worker = &io->reader_map[i];
struct snapraid_task* task = &worker->task_map[task_index];
/* setup the new pending task */
if (blockcur < io->block_max)
task->state = TASK_STATE_READY;
else
task->state = TASK_STATE_EMPTY;
task->path[0] = 0;
if (worker->handle)
task->disk = worker->handle->disk;
else
task->disk = 0;
task->buffer = io->buffer_map[task_index][worker->buffer_skew + i];
task->position = blockcur;
task->block = 0;
task->file = 0;
task->file_pos = 0;
task->read_size = 0;
task->is_timestamp_different = 0;
}
}
/**
* Setup the next pending task for all writers.
*/
static void io_writer_sched(struct snapraid_io* io, int task_index, block_off_t blockcur)
{
unsigned i;
for (i = 0; i < io->writer_max; ++i) {
struct snapraid_worker* worker = &io->writer_map[i];
struct snapraid_task* task = &worker->task_map[task_index];
/* setup the new pending task */
task->state = TASK_STATE_READY;
task->path[0] = 0;
task->disk = 0;
task->buffer = io->buffer_map[task_index][worker->buffer_skew + i];
task->position = blockcur;
task->block = 0;
task->file = 0;
task->file_pos = 0;
task->read_size = 0;
task->is_timestamp_different = 0;
}
}
/**
* Setup an empty next pending task for all writers.
*/
static void io_writer_sched_empty(struct snapraid_io* io, int task_index, block_off_t blockcur)
{
unsigned i;
for (i = 0; i < io->writer_max; ++i) {
struct snapraid_worker* worker = &io->writer_map[i];
struct snapraid_task* task = &worker->task_map[task_index];
/* setup the new pending task */
task->state = TASK_STATE_EMPTY;
task->path[0] = 0;
task->disk = 0;
task->buffer = 0;
task->position = blockcur;
task->block = 0;
task->file = 0;
task->file_pos = 0;
task->read_size = 0;
task->is_timestamp_different = 0;
}
}
/*****************************************************************************/
/* mono thread */
static block_off_t io_read_next_mono(struct snapraid_io* io, void*** buffer)
{
block_off_t blockcur_schedule;
/* reset the index */
io->reader_index = 0;
blockcur_schedule = io_position_next(io);
/* schedule the next read */
io_reader_sched(io, 0, blockcur_schedule);
/* set the buffer to use */
*buffer = io->buffer_map[0];
return blockcur_schedule;
}
static void io_write_preset_mono(struct snapraid_io* io, block_off_t blockcur, int skip)
{
unsigned i;
/* reset the index */
io->writer_index = 0;
/* clear errors */
for (i = 0; i < IO_WRITER_ERROR_MAX; ++i)
io->writer_error[i] = 0;
if (skip) {
/* skip the next write */
io_writer_sched_empty(io, 0, blockcur);
} else {
/* schedule the next write */
io_writer_sched(io, 0, blockcur);
}
}
static void io_write_next_mono(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error)
{
unsigned i;
(void)blockcur;
(void)skip;
/* report errors */
for (i = 0; i < IO_WRITER_ERROR_MAX; ++i)
writer_error[i] = io->writer_error[i];
}
static void io_refresh_mono(struct snapraid_io* io)
{
(void)io;
}
static struct snapraid_task* io_task_read_mono(struct snapraid_io* io, unsigned base, unsigned count, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
struct snapraid_worker* worker;
struct snapraid_task* task;
unsigned i;
/* get the next task */
i = io->reader_index++;
assert(base <= i && i < base + count);
worker = &io->reader_map[i];
task = &worker->task_map[0];
/* do the work */
if (task->state != TASK_STATE_EMPTY)
worker->func(worker, task);
/* return the position */
*pos = i - base;
/* store the waiting index */
waiting_map[0] = i - base;
*waiting_mac = 1;
return task;
}
static struct snapraid_task* io_data_read_mono(struct snapraid_io* io, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
return io_task_read_mono(io, io->data_base, io->data_count, pos, waiting_map, waiting_mac);
}
static struct snapraid_task* io_parity_read_mono(struct snapraid_io* io, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
return io_task_read_mono(io, io->parity_base, io->parity_count, pos, waiting_map, waiting_mac);
}
static void io_parity_write_mono(struct snapraid_io* io, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
struct snapraid_worker* worker;
struct snapraid_task* task;
unsigned i;
/* get the next task */
i = io->writer_index++;
worker = &io->writer_map[i];
task = &worker->task_map[0];
io->writer_error[i] = 0;
/* do the work */
if (task->state != TASK_STATE_EMPTY)
worker->func(worker, task);
/* return the position */
*pos = i;
/* store the waiting index */
waiting_map[0] = i;
*waiting_mac = 1;
}
static void io_start_mono(struct snapraid_io* io,
block_off_t blockstart, block_off_t blockmax,
bit_vect_t* block_enabled)
{
io->block_start = blockstart;
io->block_max = blockmax;
io->block_enabled = block_enabled;
io->block_next = blockstart;
}
static void io_stop_mono(struct snapraid_io* io)
{
(void)io;
}
/*****************************************************************************/
/* multi thread */
/* disable multithread if pthread is not present */
#if HAVE_THREAD
/**
* Get the next task to work on for a reader.
*
* This is the synchronization point for workers with the io.
*/
static struct snapraid_task* io_reader_step(struct snapraid_worker* worker)
{
struct snapraid_io* io = worker->io;
/* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex);
while (1) {
unsigned next_index;
/* check if the worker has to exit */
/* even if there is work to do */
if (io->done) {
thread_mutex_unlock(&io->io_mutex);
return 0;
}
/* get the next pending task */
next_index = (worker->index + 1) % io->io_max;
/* if the queue of pending tasks is not empty */
if (next_index != io->reader_index) {
struct snapraid_task* task;
/* the index that the IO may be waiting for */
unsigned waiting_index = io->reader_index;
/* the index that worker just completed */
unsigned done_index = worker->index;
/* get the new working task */
worker->index = next_index;
task = &worker->task_map[worker->index];
/* if the just completed task is at this index */
if (done_index == waiting_index) {
/* notify the IO that a new read is complete */
thread_cond_signal_and_unlock(&io->read_done, &io->io_mutex);
} else {
thread_mutex_unlock(&io->io_mutex);
}
/* return the new task */
return task;
}
/* otherwise wait for a read_sched event */
thread_cond_wait(&io->read_sched, &io->io_mutex);
}
}
/**
* Get the next task to work on for a writer.
*
* This is the synchronization point for workers with the io.
*/
static struct snapraid_task* io_writer_step(struct snapraid_worker* worker, int state)
{
struct snapraid_io* io = worker->io;
int error_index;
/* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex);
/* counts the number of errors in the global state */
error_index = state - IO_WRITER_ERROR_BASE;
if (error_index >= 0 && error_index < IO_WRITER_ERROR_MAX)
++io->writer_error[error_index];
while (1) {
unsigned next_index;
/* get the next pending task */
next_index = (worker->index + 1) % io->io_max;
/* if the queue of pending tasks is not empty */
if (next_index != io->writer_index) {
struct snapraid_task* task;
/* the index that the IO may be waiting for */
unsigned waiting_index = (io->writer_index + 1) % io->io_max;
/* the index that worker just completed */
unsigned done_index = worker->index;
/* get the new working task */
worker->index = next_index;
task = &worker->task_map[worker->index];
/* if the just completed task is at this index */
if (done_index == waiting_index) {
/* notify the IO that a new write is complete */
thread_cond_signal_and_unlock(&io->write_done, &io->io_mutex);
} else {
thread_mutex_unlock(&io->io_mutex);
}
/* return the new task */
return task;
}
/* check if the worker has to exit */
/* but only if there is no work to do */
if (io->done) {
thread_mutex_unlock(&io->io_mutex);
return 0;
}
/* otherwise wait for a write_sched event */
thread_cond_wait(&io->write_sched, &io->io_mutex);
}
}
/**
* Get the next block position to operate on.
*
* This is the synchronization point for workers with the io.
*/
static block_off_t io_read_next_thread(struct snapraid_io* io, void*** buffer)
{
block_off_t blockcur_schedule;
block_off_t blockcur_caller;
unsigned i;
/* get the next parity position to process */
blockcur_schedule = io_position_next(io);
/* ensure that all data/parity was read */
assert(io->reader_list[0] == io->reader_max);
/* setup the list of workers to process */
for (i = 0; i <= io->reader_max; ++i)
io->reader_list[i] = i;
/* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex);
/* schedule the next read */
io_reader_sched(io, io->reader_index, blockcur_schedule);
/* set the index for the tasks to return to the caller */
io->reader_index = (io->reader_index + 1) % io->io_max;
/* get the position to operate at high level from one task */
blockcur_caller = io->reader_map[0].task_map[io->reader_index].position;
/* set the buffer to use */
*buffer = io->buffer_map[io->reader_index];
/* signal all the workers that there is a new pending task */
thread_cond_broadcast_and_unlock(&io->read_sched, &io->io_mutex);
return blockcur_caller;
}
static void io_write_preset_thread(struct snapraid_io* io, block_off_t blockcur, int skip)
{
(void)io;
(void)blockcur;
(void)skip;
}
static void io_write_next_thread(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error)
{
unsigned i;
/* ensure that all parity was written */
assert(io->writer_list[0] == io->writer_max);
/* setup the list of workers to process */
for (i = 0; i <= io->writer_max; ++i)
io->writer_list[i] = i;
/* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex);
/* report errors */
for (i = 0; i < IO_WRITER_ERROR_MAX; ++i) {
writer_error[i] = io->writer_error[i];
io->writer_error[i] = 0;
}
if (skip) {
/* skip the next write */
io_writer_sched_empty(io, io->writer_index, blockcur);
} else {
/* schedule the next write */
io_writer_sched(io, io->writer_index, blockcur);
}
/* at this point the writers must be in sync with the readers */
assert(io->writer_index == io->reader_index);
/* set the index to be used for the next write */
io->writer_index = (io->writer_index + 1) % io->io_max;
/* signal all the workers that there is a new pending task */
thread_cond_broadcast_and_unlock(&io->write_sched, &io->io_mutex);
}
static void io_refresh_thread(struct snapraid_io* io)
{
unsigned i;
/* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex);
/* for all readers, count the number of read blocks */
for (i = 0; i < io->reader_max; ++i) {
unsigned begin, end, cached;
struct snapraid_worker* worker = &io->reader_map[i];
/* the first block read */
begin = io->reader_index + 1;
/* the block in reading */
end = worker->index;
if (begin > end)
end += io->io_max;
cached = end - begin;
if (worker->parity_handle)
io->state->parity[worker->parity_handle->level].cached_blocks = cached;
else
worker->handle->disk->cached_blocks = cached;
}
/* for all writers, count the number of written blocks */
/* note that this is a kind of "opposite" of cached blocks */
for (i = 0; i < io->writer_max; ++i) {
unsigned begin, end, cached;
struct snapraid_worker* worker = &io->writer_map[i];
/* the first block written */
begin = io->writer_index + 1;
/* the block in writing */
end = worker->index;
if (begin > end)
end += io->io_max;
cached = end - begin;
io->state->parity[worker->parity_handle->level].cached_blocks = cached;
}
thread_mutex_unlock(&io->io_mutex);
}
static struct snapraid_task* io_task_read_thread(struct snapraid_io* io, unsigned base, unsigned count, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
unsigned waiting_cycle;
/* count the waiting cycle */
waiting_cycle = 0;
/* clear the waiting indexes */
*waiting_mac = 0;
/* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex);
while (1) {
unsigned char* let;
unsigned busy_index;
/* get the index the IO is using */
/* we must ensure that this index has not a read in progress */
/* to avoid a concurrent access */
busy_index = io->reader_index;
/* search for a worker that has already finished */
let = &io->reader_list[0];
while (1) {
unsigned i = *let;
/* if we are at the end */
if (i == io->reader_max)
break;
/* if it's in range */
if (base <= i && i < base + count) {
struct snapraid_worker* worker;
/* if it's the first cycle */
if (waiting_cycle == 0) {
/* store the waiting indexes */
waiting_map[(*waiting_mac)++] = i - base;
}
worker = &io->reader_map[i];
/* if the worker has finished this index */
if (busy_index != worker->index) {
struct snapraid_task* task;
task = &worker->task_map[io->reader_index];
thread_mutex_unlock(&io->io_mutex);
/* mark the worker as processed */
/* setting the previous one to point at the next one */
*let = io->reader_list[i + 1];
/* return the position */
*pos = i - base;
/* on the first cycle, no one is waiting */
if (waiting_cycle == 0)
*waiting_mac = 0;
return task;
}
}
/* next position to check */
let = &io->reader_list[i + 1];
}
/* if no worker is ready, wait for an event */
thread_cond_wait(&io->read_done, &io->io_mutex);
/* count the cycles */
++waiting_cycle;
}
}
static struct snapraid_task* io_data_read_thread(struct snapraid_io* io, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
return io_task_read_thread(io, io->data_base, io->data_count, pos, waiting_map, waiting_mac);
}
static struct snapraid_task* io_parity_read_thread(struct snapraid_io* io, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
return io_task_read_thread(io, io->parity_base, io->parity_count, pos, waiting_map, waiting_mac);
}
static void io_parity_write_thread(struct snapraid_io* io, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac)
{
unsigned waiting_cycle;
/* count the waiting cycle */
waiting_cycle = 0;
/* clear the waiting indexes */
*waiting_mac = 0;
/* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex);
while (1) {
unsigned char* let;
unsigned busy_index;
/* get the next index the IO is going to use */
/* we must ensure that this index has not a write in progress */
/* to avoid a concurrent access */
/* note that we are already sure that a write is not in progress */
/* at the index the IO is using at now */
busy_index = (io->writer_index + 1) % io->io_max;
/* search for a worker that has already finished */
let = &io->writer_list[0];
while (1) {
unsigned i = *let;
struct snapraid_worker* worker;
/* if we are at the end */
if (i == io->writer_max)
break;
/* if it's the first cycle */
if (waiting_cycle == 0) {
/* store the waiting indexes */
waiting_map[(*waiting_mac)++] = i;
}
worker = &io->writer_map[i];
/* the two indexes cannot be equal */
assert(io->writer_index != worker->index);
/* if the worker has finished this index */
if (busy_index != worker->index) {
thread_mutex_unlock(&io->io_mutex);
/* mark the worker as processed */
/* setting the previous one to point at the next one */
*let = io->writer_list[i + 1];
/* return the position */
*pos = i;
/* on the first cycle, no one is waiting */
if (waiting_cycle == 0)
*waiting_mac = 0;
return;
}
/* next position to check */
let = &io->writer_list[i + 1];
}
/* if no worker is ready, wait for an event */
thread_cond_wait(&io->write_done, &io->io_mutex);
/* count the cycles */
++waiting_cycle;
}
}
static void io_reader_worker(struct snapraid_worker* worker, struct snapraid_task* task)
{
/* if we reached the end */
if (task->position >= worker->io->block_max) {
/* complete a dummy task */
task->state = TASK_STATE_EMPTY;
} else {
worker->func(worker, task);
}
}
static void* io_reader_thread(void* arg)
{
struct snapraid_worker* worker = arg;
/* force completion of the first task */
io_reader_worker(worker, &worker->task_map[0]);
while (1) {
struct snapraid_task* task;
/* get the new task */
task = io_reader_step(worker);
/* if no task, it means to exit */
if (!task)
break;
/* nothing more to do */
if (task->state == TASK_STATE_EMPTY)
continue;
assert(task->state == TASK_STATE_READY);
/* work on the assigned task */
io_reader_worker(worker, task);
}
return 0;
}
static void* io_writer_thread(void* arg)
{
struct snapraid_worker* worker = arg;
int latest_state = TASK_STATE_DONE;
while (1) {
struct snapraid_task* task;
/* get the new task */
task = io_writer_step(worker, latest_state);
/* if no task, it means to exit */
if (!task)
break;
/* nothing more to do */
if (task->state == TASK_STATE_EMPTY) {
latest_state = TASK_STATE_DONE;
continue;
}
assert(task->state == TASK_STATE_READY);
/* work on the assigned task */
worker->func(worker, task);
/* save the resulting state */
latest_state = task->state;
}
return 0;
}
static void io_start_thread(struct snapraid_io* io,
block_off_t blockstart, block_off_t blockmax,
bit_vect_t* block_enabled)
{
unsigned i;
tommy_node* j;
/* enable the filesystem mutex in all disks */
for (j = io->state->disklist; j != 0; j = j->next) {
struct snapraid_disk* disk = j->data;
disk_start_thread(disk);
}
io->block_start = blockstart;
io->block_max = blockmax;
io->block_enabled = block_enabled;
io->block_next = blockstart;
io->done = 0;
io->reader_index = io->io_max - 1;
io->writer_index = 0;
/* clear writer errors */
for (i = 0; i < IO_WRITER_ERROR_MAX; ++i)
io->writer_error[i] = 0;
/* setup the initial read pending tasks, except the latest one, */
/* the latest will be initialized at the fist io_read_next() call */
for (i = 0; i < io->io_max - 1; ++i) {
block_off_t blockcur = io_position_next(io);
io_reader_sched(io, i, blockcur);
}
/* setup the lists of workers to process */
io->reader_list[0] = io->reader_max;
for (i = 0; i <= io->writer_max; ++i)
io->writer_list[i] = i;
/* start the reader threads */
for (i = 0; i < io->reader_max; ++i) {
struct snapraid_worker* worker = &io->reader_map[i];
worker->index = 0;
thread_create(&worker->thread, io_reader_thread, worker);
}
/* start the writer threads */
for (i = 0; i < io->writer_max; ++i) {
struct snapraid_worker* worker = &io->writer_map[i];
worker->index = io->io_max - 1;
thread_create(&worker->thread, io_writer_thread, worker);
}
}
static void io_stop_thread(struct snapraid_io* io)
{
unsigned i;
thread_mutex_lock(&io->io_mutex);
/* mark that we are stopping */
io->done = 1;
/* signal all the threads to recognize the new state */
thread_cond_broadcast(&io->read_sched);
thread_cond_broadcast(&io->write_sched);
thread_mutex_unlock(&io->io_mutex);
/* wait for all readers to terminate */
for (i = 0; i < io->reader_max; ++i) {
struct snapraid_worker* worker = &io->reader_map[i];
void* retval;
/* wait for thread termination */
thread_join(worker->thread, &retval);
}
/* wait for all writers to terminate */
for (i = 0; i < io->writer_max; ++i) {
struct snapraid_worker* worker = &io->writer_map[i];
void* retval;
/* wait for thread termination */
thread_join(worker->thread, &retval);
}
}
#endif
/*****************************************************************************/
/* global */
void io_init(struct snapraid_io* io, struct snapraid_state* state,
unsigned io_cache, unsigned buffer_max,
void (*data_reader)(struct snapraid_worker*, struct snapraid_task*),
struct snapraid_handle* handle_map, unsigned handle_max,
void (*parity_reader)(struct snapraid_worker*, struct snapraid_task*),
void (*parity_writer)(struct snapraid_worker*, struct snapraid_task*),
struct snapraid_parity_handle* parity_handle_map, unsigned parity_handle_max)
{
unsigned i;
size_t allocated_size;
size_t block_size = state->block_size;
io->state = state;
#if HAVE_THREAD
if (io_cache == 0) {
/* default is 16 MiB of cache */
/* this seems to be a good tradeoff between speed and memory usage */
io->io_max = 16 * 1024 * 1024 / state->block_size;
if (io->io_max < IO_MIN)
io->io_max = IO_MIN;
if (io->io_max > IO_MAX)
io->io_max = IO_MAX;
} else {
io->io_max = io_cache;
}
#else
(void)io_cache;
/* without pthread force the mono thread mode */
io->io_max = 1;
#endif
assert(io->io_max == 1 || (io->io_max >= IO_MIN && io->io_max <= IO_MAX));
io->buffer_max = buffer_max;
allocated_size = 0;
for (i = 0; i < io->io_max; ++i) {
if (state->file_mode != ADVISE_DIRECT)
io->buffer_map[i] = malloc_nofail_vector_align(handle_max, buffer_max, block_size, &io->buffer_alloc_map[i]);
else
io->buffer_map[i] = malloc_nofail_vector_direct(handle_max, buffer_max, block_size, &io->buffer_alloc_map[i]);
if (!state->opt.skip_self)
mtest_vector(io->buffer_max, state->block_size, io->buffer_map[i]);
allocated_size += block_size * buffer_max;
}
msg_progress("Using %u MiB of memory for %u cached blocks.\n", (unsigned)(allocated_size / MEBI), io->io_max);
if (parity_writer) {
io->reader_max = handle_max;
io->writer_max = parity_handle_max;
} else {
io->reader_max = handle_max + parity_handle_max;
io->writer_max = 0;
}
io->reader_map = malloc_nofail(sizeof(struct snapraid_worker) * io->reader_max);
io->reader_list = malloc_nofail(io->reader_max + 1);
io->writer_map = malloc_nofail(sizeof(struct snapraid_worker) * io->writer_max);
io->writer_list = malloc_nofail(io->writer_max + 1);
io->data_base = 0;
io->data_count = handle_max;
io->parity_base = handle_max;
io->parity_count = parity_handle_max;
for (i = 0; i < io->reader_max; ++i) {
struct snapraid_worker* worker = &io->reader_map[i];
worker->io = io;
if (i < handle_max) {
/* it's a data read */
worker->handle = &handle_map[i];
worker->parity_handle = 0;
worker->func = data_reader;
/* data read is put in lower buffer index */
worker->buffer_skew = 0;
} else {
/* it's a parity read */
worker->handle = 0;
worker->parity_handle = &parity_handle_map[i - handle_max];
worker->func = parity_reader;
/* parity read is put after data and computed parity */
worker->buffer_skew = parity_handle_max;
}
}
for (i = 0; i < io->writer_max; ++i) {
struct snapraid_worker* worker = &io->writer_map[i];
worker->io = io;
/* it's a parity write */
worker->handle = 0;
worker->parity_handle = &parity_handle_map[i];
worker->func = parity_writer;
/* parity to write is put after data */
worker->buffer_skew = handle_max;
}
#if HAVE_THREAD
if (io->io_max > 1) {
io_read_next = io_read_next_thread;
io_write_preset = io_write_preset_thread;
io_write_next = io_write_next_thread;
io_refresh = io_refresh_thread;
io_data_read = io_data_read_thread;
io_parity_read = io_parity_read_thread;
io_parity_write = io_parity_write_thread;
io_start = io_start_thread;
io_stop = io_stop_thread;
thread_mutex_init(&io->io_mutex);
thread_cond_init(&io->read_done);
thread_cond_init(&io->read_sched);
thread_cond_init(&io->write_done);
thread_cond_init(&io->write_sched);
} else
#endif
{
io_read_next = io_read_next_mono;
io_write_preset = io_write_preset_mono;
io_write_next = io_write_next_mono;
io_refresh = io_refresh_mono;
io_data_read = io_data_read_mono;
io_parity_read = io_parity_read_mono;
io_parity_write = io_parity_write_mono;
io_start = io_start_mono;
io_stop = io_stop_mono;
}
}
void io_done(struct snapraid_io* io)
{
unsigned i;
for (i = 0; i < io->io_max; ++i) {
free(io->buffer_map[i]);
free(io->buffer_alloc_map[i]);
}
free(io->reader_map);
free(io->reader_list);
free(io->writer_map);
free(io->writer_list);
#if HAVE_THREAD
if (io->io_max > 1) {
thread_mutex_destroy(&io->io_mutex);
thread_cond_destroy(&io->read_done);
thread_cond_destroy(&io->read_sched);
thread_cond_destroy(&io->write_done);
thread_cond_destroy(&io->write_sched);
}
#endif
}
snapraid-12.1/cmdline/io.h 0000664 0000000 0000000 00000025667 14166610522 0015454 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2016 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __IO_H
#define __IO_H
#include "state.h"
#include "support.h"
#include "handle.h"
#include "parity.h"
/**
* Number of read-ahead buffers.
*
* More buffers always result in better performance.
*
* This is the scrub performance on my machine with different buffers:
*
* 1 - 380 MB/s, CPU 26%, speed 100% [SnapRAID 9.2]
* 2 - 426 MB/s, CPU 46%, speed 112%
* 4 - 452 MB/s, CPU 54%, speed 118%
* 8 - 487 MB/s, CPU 60%, speed 128%
* 16 - 505 MB/s, CPU 63%, speed 132%
* 32 - 520 MB/s, CPU 64%, speed 136% [SnapRAID <= 12.0]
* 64 - 524 MB/s, CPU 65%, speed 137% [SnapRAID > 12.0]
* 128 - 525 MB/s, CPU 66%, speed 138%
*/
#define IO_MIN 3 /* required by writers, readers can work also with 2 */
#define IO_MAX 128
/**
* State of the task.
*/
#define TASK_STATE_IOERROR_CONTINUE -4 /**< IO error. Continuation requested. */
#define TASK_STATE_ERROR_CONTINUE -3 /**< Generic error. Continuation requested. */
#define TASK_STATE_IOERROR -2 /**< IO error. Failure requested. */
#define TASK_STATE_ERROR -1 /**< Generic error. Failure requested. */
#define TASK_STATE_EMPTY 0 /**< Nothing to do. */
#define TASK_STATE_READY 1 /**< Ready to start. */
#define TASK_STATE_DONE 2 /**< Task completed. */
/**
* Task of work.
*
* This represents the minimal element of work that worker threads are
* going to be asked to do.
*
* It consists in reading a block of data from a disk.
*
* Note that the disk to use is defined implicitly in the worker thread.
*/
struct snapraid_task {
int state; /**< State of the task. One of the TASK_STATE_*. */
char path[PATH_MAX]; /**< Path of the file. */
struct snapraid_disk* disk; /**< Disk of the file. */
unsigned char* buffer; /**< Where to read the data. */
block_off_t position; /**< Parity position to read. */
/**
* Result of the task.
*/
struct snapraid_block* block;
struct snapraid_file* file;
block_off_t file_pos;
int read_size; /**< Size of the data read. */
int is_timestamp_different; /**< Report if file has a changed timestamp. */
};
/**
* Worker for tasks.
*
* This represents a worker thread designated to read data
* from a specific disk.
*/
struct snapraid_worker {
#if HAVE_THREAD
thread_id_t thread; /**< Thread context for the worker. */
#endif
struct snapraid_io* io; /**< Parent pointer. */
void (*func)(struct snapraid_worker*, struct snapraid_task*);
/**
* Handle to data or parity.
*
* Only one of the two is valid, the other is 0.
*/
struct snapraid_handle* handle; /**< Handle at the file on the disk. */
struct snapraid_parity_handle* parity_handle; /**< Handle at the parity on the disk. */
/**
* Vector of tasks.
*
* It's a ring of tasks reused cycle after cycle.
*/
struct snapraid_task task_map[IO_MAX];
/**
* The task in progress by the worker thread.
*
* It's an index inside in the ::task_map vector.
*/
unsigned index;
/**
* Which buffer base index should be used for destination.
*/
unsigned buffer_skew;
};
/**
* Number of error kind for writers.
*/
#define IO_WRITER_ERROR_BASE TASK_STATE_IOERROR_CONTINUE
#define IO_WRITER_ERROR_MAX (-IO_WRITER_ERROR_BASE)
/**
* Reader.
*
* This represents the pool of worker threads dedicated to read
* data from the disks.
*/
struct snapraid_io {
struct snapraid_state* state;
/**
* Number of read-ahead buffers to use.
*
* Between IO_MIN and IO_MAX for thread use.
*
* If equal to 1, it means to work without any thread.
*/
unsigned io_max;
#if HAVE_THREAD
/**
* Mutex used to protect the synchronization
* between the io and the workers.
*/
thread_mutex_t io_mutex;
/**
* Condition for a new read is completed.
*
* The workers signal this condition when a new read is completed.
* The IO waits on this condition when it's waiting for
* a new read to be completed.
*/
thread_cond_t read_done;
/**
* Condition for a new read scheduled.
*
* The workers wait on this condition when they are waiting for a new
* read to process.
* The IO signals this condition when new reads are scheduled.
*/
thread_cond_t read_sched;
/**
* Condition for a new write is completed.
*
* The workers signal this condition when a new write is completed.
* The IO waits on this condition when it's waiting for
* a new write to be completed.
*/
thread_cond_t write_done;
/**
* Condition for a new write scheduled.
*
* The workers wait on this condition when they are waiting for a new
* write to process.
* The IO signals this condition when new writes are scheduled.
*/
thread_cond_t write_sched;
#endif
/**
* Base position for workers.
*
* It's the index in the ::worker_map[].
*/
unsigned data_base;
unsigned data_count;
unsigned parity_base;
unsigned parity_count;
/**
* Callbacks for workers.
*/
void (*data_reader)(struct snapraid_worker*, struct snapraid_task*);
void (*parity_reader)(struct snapraid_worker*, struct snapraid_task*);
void (*parity_writer)(struct snapraid_worker*, struct snapraid_task*);
/**
* Blocks mapping.
*
* This info is used to obtain the sequence of block
* positions to process.
*/
block_off_t block_start;
block_off_t block_max;
block_off_t block_next;
bit_vect_t* block_enabled;
/**
* Buffers for data.
*
* A pool of buffers used to store the data read.
*/
unsigned buffer_max; /**< Number of buffers. */
void* buffer_alloc_map[IO_MAX]; /**< Allocation map for buffers. */
void** buffer_map[IO_MAX]; /**< Buffers for data. */
/**
* Workers.
*
* A vector of readers, each one representing a different thread.
*/
unsigned reader_max; /**< Number of workers. */
struct snapraid_worker* reader_map; /**< Vector of workers. */
unsigned writer_max; /**< Number of workers. */
struct snapraid_worker* writer_map; /**< Vector of workers. */
/**
* List of not yet processed workers.
*
* The list has ::reader_max + 1 elements. Each element contains
* the number of the reader to process.
*
* At initialization the list is filled with [0..reader_max].
* To get the next element to process we use i = list[i + 1].
* The end is when i == reader_max.
*/
unsigned char* reader_list;
unsigned char* writer_list;
/**
* Exit condition for all threads.
*/
int done;
/**
* The task currently used by the caller.
*
* It's a rolling counter, when reaching ::io_max
* it goes again to 0.
*
* When the caller finish with the current index,
* it's incremented, and a read_sched() signal is sent.
*
* In monothread mode it isn't the task index,
* but the worker index.
*/
unsigned reader_index;
/**
* The task currently used by the caller.
*
* It's a rolling counter, when reaching ::io_max
* it goes again to 0.
*
* When the caller finish with the current index,
* it's incremented, and a write_sched() signal is sent.
*
* In monothread mode it isn't the task index,
* but the worker index.
*/
unsigned writer_index;
/**
* Counts the error happening in the writers.
*/
int writer_error[IO_WRITER_ERROR_MAX];
};
/**
* Initialize the InputOutput workers.
*
* \param io_cache The number of IO buffers for read-ahead and write-behind. 0 for default.
* \param buffer_max The number of data/parity buffers to allocate.
*/
void io_init(struct snapraid_io* io, struct snapraid_state* state,
unsigned io_cache, unsigned buffer_max,
void (*data_reader)(struct snapraid_worker*, struct snapraid_task*),
struct snapraid_handle* handle_map, unsigned handle_max,
void (*parity_reader)(struct snapraid_worker*, struct snapraid_task*),
void (*parity_writer)(struct snapraid_worker*, struct snapraid_task*),
struct snapraid_parity_handle* parity_handle_map, unsigned parity_handle_max);
/**
* Deinitialize the InputOutput workers.
*/
void io_done(struct snapraid_io* io);
/**
* Start all the worker threads.
*/
extern void (*io_start)(struct snapraid_io* io,
block_off_t blockstart, block_off_t blockmax,
bit_vect_t* block_enabled);
/**
* Stop all the worker threads.
*/
extern void (*io_stop)(struct snapraid_io* io);
/**
* Next read position.
*
* This call starts the reading process.
* It must be called before io_data_read() and io_parity_read().
*
* \param io InputOutput context.
* \param buffer The data buffers to use for this position.
* \return The parity position.
*/
extern block_off_t (*io_read_next)(struct snapraid_io* io, void*** buffer);
/**
* Read a data block.
*
* It must be called exactly ::handle_max times.
*
* \param io InputOutput context.
* \param diskcur The position of the data block in the ::handle_map vector.
* \return The completed task.
*/
extern struct snapraid_task* (*io_data_read)(struct snapraid_io* io, unsigned* diskcur, unsigned* waiting_map, unsigned* waiting_mac);
/**
* Read a parity block.
*
* It must be called exactly ::parity_handle_max times.
*
* \param io InputOutput context.
* \param levcur The position of the parity block in the ::parity_handle_map vector.
* \return The completed task.
*/
extern struct snapraid_task* (*io_parity_read)(struct snapraid_io* io, unsigned* levcur, unsigned* waiting_map, unsigned* waiting_mac);
/**
* Write of a parity block.
*
* It must be called exactly ::parity_handle_max times.
*
* \param io InputOutput context.
* \param levcur The position of the parity block in the ::parity_handle_map vector.
*/
extern void (*io_parity_write)(struct snapraid_io* io, unsigned* levcur, unsigned* waiting_map, unsigned* waiting_mac);
/**
* Preset the write position.
*
* This call starts the write process.
* It must be called before io_parity_write().
*
* \param io InputOutput context.
* \param blockcur The parity position to write.
* \param skip Skip the writes, in case parity doesn't need to be updated.
*/
extern void (*io_write_preset)(struct snapraid_io* io, block_off_t blockcur, int skip);
/**
* Next write position.
*
* This call ends the write process.
* It must be called after io_parity_write().
*
* \param io InputOutput context.
* \param blockcur The parity position to write.
* \param skip Skip the writes, in case parity doesn't need to be updated.
* \param writer_error Return the number of errors. Vector of IO_WRITER_ERROR_MAX elements.
*/
extern void (*io_write_next)(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error);
/**
* Refresh the number of cached blocks for all data and parity disks.
*/
extern void (*io_refresh)(struct snapraid_io* io);
#endif
snapraid-12.1/cmdline/list.c 0000664 0000000 0000000 00000006725 14166610522 0016005 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "util.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
/****************************************************************************/
/* list */
void state_list(struct snapraid_state* state)
{
tommy_node* i;
unsigned file_count;
data_off_t file_size;
unsigned link_count;
char esc_buffer[ESC_MAX];
char esc_buffer_alt[ESC_MAX];
file_count = 0;
file_size = 0;
link_count = 0;
msg_progress("Listing...\n");
/* for each disk */
for (i = state->disklist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_disk* disk = i->data;
/* sort by name */
tommy_list_sort(&disk->filelist, file_path_compare);
/* for each file */
for (j = disk->filelist; j != 0; j = j->next) {
struct snapraid_file* file = j->data;
#if HAVE_LOCALTIME_R
struct tm tm_res;
#endif
struct tm* tm;
time_t t;
++file_count;
file_size += file->size;
log_tag("file:%s:%s:%" PRIu64 ":%" PRIi64 ":%u:%" PRIi64 "\n", disk->name, esc_tag(file->sub, esc_buffer), file->size, file->mtime_sec, file->mtime_nsec, file->inode);
t = file->mtime_sec;
#if HAVE_LOCALTIME_R
tm = localtime_r(&t, &tm_res);
#else
tm = localtime(&t);
#endif
printf("%12" PRIu64 " ", file->size);
if (tm) {
printf("%04u/%02u/%02u %02u:%02u", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
if (msg_level >= MSG_VERBOSE)
printf(":%02u.%09u", tm->tm_sec, file->mtime_nsec);
printf(" ");
}
printf("%s\n", fmt_term(disk, file->sub, esc_buffer));
}
/* sort by name */
tommy_list_sort(&disk->linklist, link_alpha_compare);
/* for each link */
for (j = disk->linklist; j != 0; j = j->next) {
struct snapraid_link* slink = j->data;
const char* type;
switch (slink->flag & FILE_IS_LINK_MASK) {
case FILE_IS_HARDLINK : type = "hardlink"; break;
case FILE_IS_SYMLINK : type = "symlink"; break;
case FILE_IS_SYMDIR : type = "symdir"; break;
case FILE_IS_JUNCTION : type = "junction"; break;
/* LCOV_EXCL_START */
default : type = "unknown"; break;
/* LCOV_EXCL_STOP */
}
++link_count;
log_tag("link_%s:%s:%s:%s\n", type, disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt));
printf("%12s ", type);
printf(" ");
if (msg_level >= MSG_VERBOSE)
printf(" ");
printf("%s -> %s\n", fmt_term(disk, slink->sub, esc_buffer), fmt_term(disk, slink->linkto, esc_buffer_alt));
}
}
msg_status("\n");
msg_status("%8u files, for %" PRIu64 " GB\n", file_count, file_size / GIGA);
msg_status("%8u links\n", link_count);
log_tag("summary:file_count:%u\n", file_count);
log_tag("summary:file_size:%" PRIu64 "\n", file_size);
log_tag("summary:link_count:%u\n", link_count);
log_tag("summary:exit:ok\n");
log_flush();
}
snapraid-12.1/cmdline/metro.c 0000664 0000000 0000000 00000007101 14166610522 0016145 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2019 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* Derivative work from metrohash128.cpp
*
* metrohash128.cpp
*
* Copyright 2015-2018 J. Andrew Rogers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
static const uint64_t k0 = 0xC83A91E1;
static const uint64_t k1 = 0x8648DBDB;
static const uint64_t k2 = 0x7BDEC03B;
static const uint64_t k3 = 0x2F5870A5;
void MetroHash128(const void* data, size_t size, const uint8_t* seed, uint8_t* digest)
{
const uint8_t* ptr = data;
uint64_t v[4];
v[0] = (util_read64(seed) - k0) * k3;
v[1] = (util_read64(seed + 8) + k1) * k2;
if (size >= 32) {
v[2] = (util_read64(seed) + k0) * k2;
v[3] = (util_read64(seed + 8) - k1) * k3;
do {
v[0] += util_read64(ptr) * k0; ptr += 8; v[0] = util_rotr64(v[0], 29) + v[2];
v[1] += util_read64(ptr) * k1; ptr += 8; v[1] = util_rotr64(v[1], 29) + v[3];
v[2] += util_read64(ptr) * k2; ptr += 8; v[2] = util_rotr64(v[2], 29) + v[0];
v[3] += util_read64(ptr) * k3; ptr += 8; v[3] = util_rotr64(v[3], 29) + v[1];
size -= 32;
} while (size >= 32);
v[2] ^= util_rotr64(((v[0] + v[3]) * k0) + v[1], 21) * k1;
v[3] ^= util_rotr64(((v[1] + v[2]) * k1) + v[0], 21) * k0;
v[0] ^= util_rotr64(((v[0] + v[2]) * k0) + v[3], 21) * k1;
v[1] ^= util_rotr64(((v[1] + v[3]) * k1) + v[2], 21) * k0;
}
if (size >= 16) {
v[0] += util_read64(ptr) * k2; ptr += 8; v[0] = util_rotr64(v[0], 33) * k3;
v[1] += util_read64(ptr) * k2; ptr += 8; v[1] = util_rotr64(v[1], 33) * k3;
v[0] ^= util_rotr64((v[0] * k2) + v[1], 45) * k1;
v[1] ^= util_rotr64((v[1] * k3) + v[0], 45) * k0;
size -= 16;
}
if (size >= 8) {
v[0] += util_read64(ptr) * k2; ptr += 8; v[0] = util_rotr64(v[0], 33) * k3;
v[0] ^= util_rotr64((v[0] * k2) + v[1], 27) * k1;
size -= 8;
}
if (size >= 4) {
v[1] += util_read32(ptr) * k2; ptr += 4; v[1] = util_rotr64(v[1], 33) * k3;
v[1] ^= util_rotr64((v[1] * k3) + v[0], 46) * k0;
size -= 4;
}
if (size >= 2) {
v[0] += util_read16(ptr) * k2; ptr += 2; v[0] = util_rotr64(v[0], 33) * k3;
v[0] ^= util_rotr64((v[0] * k2) + v[1], 22) * k1;
size -= 2;
}
if (size >= 1) {
v[1] += util_read8(ptr) * k2; v[1] = util_rotr64(v[1], 33) * k3;
v[1] ^= util_rotr64((v[1] * k3) + v[0], 58) * k0;
}
v[0] += util_rotr64((v[0] * k0) + v[1], 13);
v[1] += util_rotr64((v[1] * k1) + v[0], 37);
v[0] += util_rotr64((v[0] * k2) + v[1], 13);
v[1] += util_rotr64((v[1] * k3) + v[0], 37);
util_write64(digest, v[0]);
util_write64(digest + 8, v[0]);
}
snapraid-12.1/cmdline/mingw.c 0000664 0000000 0000000 00000207717 14166610522 0016157 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#ifdef __MINGW32__ /* Only for MingW */
#include "support.h"
/**
* Exit codes.
*/
int exit_success = 0;
int exit_failure = 1;
int exit_sync_needed = 2;
/* Add missing Windows declaration */
/* For SetThreadExecutionState */
#define WIN32_ES_SYSTEM_REQUIRED 0x00000001L
#define WIN32_ES_DISPLAY_REQUIRED 0x00000002L
#define WIN32_ES_USER_PRESENT 0x00000004L
#define WIN32_ES_AWAYMODE_REQUIRED 0x00000040L
#define WIN32_ES_CONTINUOUS 0x80000000L
/* File Index */
#define FILE_INVALID_FILE_ID ((ULONGLONG)-1LL)
/**
* Direct access to RtlGenRandom().
* This function is accessible only with LoadLibrary() and it's available from Windows XP.
*/
static BOOLEAN (WINAPI* ptr_RtlGenRandom)(PVOID, ULONG);
/**
* Direct access to GetTickCount64().
* This function is available only from Windows Vista.
*/
static ULONGLONG (WINAPI* ptr_GetTickCount64)(void);
/**
* Description of the last error.
* It's stored in the thread local storage.
*/
static windows_key_t last_error;
/**
* Monotone tick counter
*/
static windows_mutex_t tick_lock;
static uint64_t tick_last;
/**
* If we are running in Wine.
*/
static int is_wine;
/**
* If we should use the legacy FindFirst/Next way to list directories.
*/
static int is_scan_winfind;
/**
* Loaded ADVAPI32.DLL.
*/
static HMODULE dll_advapi32;
/**
* Executable dir.
*
* Or empty or terminating with '\'.
*/
static WCHAR exedir[MAX_PATH];
/**
* Set the executable dir.
*/
static void exedir_init(void)
{
DWORD size;
WCHAR* slash;
size = GetModuleFileNameW(0, exedir, MAX_PATH);
if (size == 0 || size == MAX_PATH) {
/* use empty dir */
exedir[0] = 0;
return;
}
slash = wcsrchr(exedir, L'\\');
if (!slash) {
/* use empty dir */
exedir[0] = 0;
return;
}
/* cut exe name */
slash[1] = 0;
}
void os_init(int opt)
{
HMODULE ntdll, kernel32;
is_scan_winfind = opt != 0;
/* initialize the thread local storage for strerror(), using free() as destructor */
if (windows_key_create(&last_error, free) != 0) {
log_fatal("Error calling windows_key_create().\n");
exit(EXIT_FAILURE);
}
tick_last = 0;
if (windows_mutex_init(&tick_lock, 0) != 0) {
log_fatal("Error calling windows_mutex_init().\n");
exit(EXIT_FAILURE);
}
ntdll = GetModuleHandle("NTDLL.DLL");
if (!ntdll) {
log_fatal("Error loading the NTDLL module.\n");
exit(EXIT_FAILURE);
}
kernel32 = GetModuleHandle("KERNEL32.DLL");
if (!kernel32) {
log_fatal("Error loading the KERNEL32 module.\n");
exit(EXIT_FAILURE);
}
dll_advapi32 = LoadLibrary("ADVAPI32.DLL");
if (!dll_advapi32) {
log_fatal("Error loading the ADVAPI32 module.\n");
exit(EXIT_FAILURE);
}
/* check for Wine presence */
is_wine = GetProcAddress(ntdll, "wine_get_version") != 0;
/* setup the standard random generator used as fallback */
srand(GetTickCount());
/* get pointer to RtlGenRandom, note that it was reported missing in some cases */
ptr_RtlGenRandom = (void*)GetProcAddress(dll_advapi32, "SystemFunction036");
/* get pointer to RtlGenRandom, note that it was reported missing in some cases */
ptr_GetTickCount64 = (void*)GetProcAddress(kernel32, "GetTickCount64");
/* set the thread execution level to avoid sleep */
/* first try for Windows 7 */
if (SetThreadExecutionState(WIN32_ES_CONTINUOUS | WIN32_ES_SYSTEM_REQUIRED | WIN32_ES_AWAYMODE_REQUIRED) == 0) {
/* retry with the XP variant */
SetThreadExecutionState(WIN32_ES_CONTINUOUS | WIN32_ES_SYSTEM_REQUIRED);
}
exedir_init();
}
void os_done(void)
{
/* delete the thread local storage for strerror() */
windows_key_delete(last_error);
windows_mutex_destroy(&tick_lock);
/* restore the normal execution level */
SetThreadExecutionState(WIN32_ES_CONTINUOUS);
FreeLibrary(dll_advapi32);
}
void os_abort(void)
{
void* stack[32];
size_t size;
unsigned i;
printf("Stacktrace of " PACKAGE " v" VERSION);
printf(", mingw");
#ifdef __GNUC__
printf(", gcc " __VERSION__);
#endif
printf(", %d-bit", (int)sizeof(void *) * 8);
printf(", PATH_MAX=%d", PATH_MAX);
printf("\n");
/* get stackstrace, but without symbols */
size = CaptureStackBackTrace(0, 32, stack, NULL);
for (i = 0; i < size; ++i)
printf("[bt] %02u: %p\n", i, stack[i]);
printf("Please report this error to the SnapRAID Forum:\n");
printf("https://sourceforge.net/p/snapraid/discussion/1677233/\n");
/* use exit() and not abort to avoid the Windows abort dialog */
exit(EXIT_FAILURE);
}
void os_clear(void)
{
HANDLE console;
CONSOLE_SCREEN_BUFFER_INFO screen;
COORD coord;
DWORD written;
/* get the console */
console = GetStdHandle(STD_OUTPUT_HANDLE);
if (console == INVALID_HANDLE_VALUE)
return;
/* get the screen size */
if (!GetConsoleScreenBufferInfo(console, &screen))
return;
/* fill the screen with spaces */
coord.X = 0;
coord.Y = 0;
FillConsoleOutputCharacterA(console, ' ', screen.dwSize.X * screen.dwSize.Y, coord, &written);
/* set the cursor at the top left corner */
SetConsoleCursorPosition(console, coord);
}
/**
* Size in chars of conversion buffers for u8to16() and u16to8().
*/
#define CONV_MAX PATH_MAX
/**
* Convert a generic string from UTF8 to UTF16.
*/
static wchar_t* u8tou16(wchar_t* conv_buf, const char* src)
{
int ret;
ret = MultiByteToWideChar(CP_UTF8, 0, src, -1, conv_buf, CONV_MAX);
if (ret <= 0) {
log_fatal("Error converting name '%s' from UTF-8 to UTF-16\n", src);
exit(EXIT_FAILURE);
}
return conv_buf;
}
/**
* Convert a generic string from UTF16 to UTF8.
*/
static char* u16tou8ex(char* conv_buf, const wchar_t* src, size_t number_of_wchar, size_t* result_length_without_terminator)
{
int ret;
ret = WideCharToMultiByte(CP_UTF8, 0, src, number_of_wchar, conv_buf, CONV_MAX, 0, 0);
if (ret <= 0) {
log_fatal("Error converting from UTF-16 to UTF-8\n");
exit(EXIT_FAILURE);
}
*result_length_without_terminator = ret;
return conv_buf;
}
static char* u16tou8(char* conv_buf, const wchar_t* src)
{
size_t len;
/* convert also the 0 terminator */
return u16tou8ex(conv_buf, src, wcslen(src) + 1, &len);
}
/**
* Check if the char is a forward or back slash.
*/
static int is_slash(char c)
{
return c == '/' || c == '\\';
}
/**
* Convert a path to the Windows format.
*
* If only_is_required is 1, the extended-length format is used only if required.
*
* The exact operation done is:
* - If it's a '\\?\' or '\\.\' path, convert any '/' to '\'.
* - If it's a disk designator path, like 'D:\' or 'D:/', it prepends '\\?\' to the path and convert any '/' to '\'.
* - If it's a UNC path, like ''\\server'', it prepends '\\?\UNC\' to the path and convert any '/' to '\'.
* - Otherwise, only the UTF conversion is done. In this case Windows imposes a limit of 260 chars, and automatically convert any '/' to '\'.
*
* For more details see:
* Naming Files, Paths, and Namespaces
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#maxpath
*/
static wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_required)
{
int ret;
wchar_t* dst;
int count;
dst = conv_buf;
/* note that we always check for both / and \ because the path is blindly */
/* converted to unix format by path_import() */
if (only_if_required && strlen(src) < 260 - 12) {
/* it's a short path */
/* 260 is the MAX_PATH, note that it includes the space for the terminating NUL */
/* 12 is an additional space for filename, required when creating directory */
/* do nothing */
} else if (is_slash(src[0]) && is_slash(src[1]) && (src[2] == '?' || src[2] == '.') && is_slash(src[3])) {
/* if it's already a '\\?\' or '\\.\' path */
/* do nothing */
} else if (is_slash(src[0]) && is_slash(src[1])) {
/* if it is a UNC path, like '\\server' */
/* prefix with '\\?\UNC\' */
*dst++ = L'\\';
*dst++ = L'\\';
*dst++ = L'?';
*dst++ = L'\\';
*dst++ = L'U';
*dst++ = L'N';
*dst++ = L'C';
*dst++ = L'\\';
/* skip initial '\\' */
src += 2;
} else if (src[0] != 0 && src[1] == ':' && is_slash(src[2])) {
/* if it is a disk designator path, like 'D:\' or 'D:/' */
/* prefix with '\\?\' */
*dst++ = L'\\';
*dst++ = L'\\';
*dst++ = L'?';
*dst++ = L'\\';
}
/* chars already used */
count = dst - conv_buf;
ret = MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, CONV_MAX - count);
if (ret <= 0) {
log_fatal("Error converting name '%s' from UTF-8 to UTF-16\n", src);
exit(EXIT_FAILURE);
}
/* convert any / to \ */
/* note that in UTF-16, it's not possible to have '/' used as part */
/* of a pair of codes representing a single UNICODE char */
/* See: http://en.wikipedia.org/wiki/UTF-16 */
while (*dst) {
if (*dst == L'/')
*dst = L'\\';
++dst;
}
return conv_buf;
}
#define convert(buf, a) convert_arg(buf, a, 0)
#define convert_if_required(buf, a) convert_arg(buf, a, 1)
static BOOL GetReparseTagInfoByHandle(HANDLE hFile, FILE_ATTRIBUTE_TAG_INFO* lpFileAttributeTagInfo, DWORD dwFileAttributes)
{
/* if not a reparse point, return no info */
if ((dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
lpFileAttributeTagInfo->FileAttributes = dwFileAttributes;
lpFileAttributeTagInfo->ReparseTag = 0;
return TRUE;
}
/* do the real call */
return GetFileInformationByHandleEx(hFile, FileAttributeTagInfo, lpFileAttributeTagInfo, sizeof(FILE_ATTRIBUTE_TAG_INFO));
}
/**
* Convert Windows attr to the Unix stat format.
*/
static void windows_attr2stat(DWORD FileAttributes, DWORD ReparseTag, struct windows_stat* st)
{
/* Convert special attributes */
if ((FileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) {
st->st_mode = S_IFBLK;
st->st_desc = "device";
} else if ((FileAttributes & FILE_ATTRIBUTE_OFFLINE) != 0) { /* Offline */
st->st_mode = S_IFCHR;
st->st_desc = "offline";
} else if ((FileAttributes & FILE_ATTRIBUTE_TEMPORARY) != 0) { /* Temporary */
st->st_mode = S_IFCHR;
st->st_desc = "temporary";
} else if ((FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { /* Reparse point */
switch (ReparseTag) {
/* if we don't have the ReparseTag information */
case 0 :
/* don't set the st_mode, to set it later calling lstat_sync() */
st->st_mode = 0;
st->st_desc = "unknown";
break;
/* for deduplicated files, assume that they are regular ones */
case IO_REPARSE_TAG_DEDUP :
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFDIR;
st->st_desc = "directory-dedup";
} else {
st->st_mode = S_IFREG;
st->st_desc = "regular-dedup";
}
break;
case IO_REPARSE_TAG_SYMLINK :
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFLNKDIR;
st->st_desc = "reparse-point-symlink-dir";
} else {
st->st_mode = S_IFLNK;
st->st_desc = "reparse-point-symlink-file";
}
break;
/* all the other are skipped as reparse-point */
case IO_REPARSE_TAG_MOUNT_POINT :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point-mount";
break;
case IO_REPARSE_TAG_NFS :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point-nfs";
break;
default :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point";
break;
}
} else if ((FileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0) { /* System */
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFCHR;
st->st_desc = "system-directory";
} else {
st->st_mode = S_IFREG;
st->st_desc = "system-file";
}
} else {
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFDIR;
st->st_desc = "directory";
} else {
st->st_mode = S_IFREG;
st->st_desc = "regular";
}
}
/* store the HIDDEN attribute in a separate field */
st->st_hidden = (FileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
}
/**
* Convert Windows info to the Unix stat format.
*/
static int windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ATTRIBUTE_TAG_INFO* tag, struct windows_stat* st)
{
uint64_t mtime;
windows_attr2stat(info->dwFileAttributes, tag->ReparseTag, st);
st->st_size = info->nFileSizeHigh;
st->st_size <<= 32;
st->st_size |= info->nFileSizeLow;
mtime = info->ftLastWriteTime.dwHighDateTime;
mtime <<= 32;
mtime |= info->ftLastWriteTime.dwLowDateTime;
/*
* Convert to unix time
*
* How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
* http://support.microsoft.com/kb/167296
*/
mtime -= 116444736000000000LL;
st->st_mtime = mtime / 10000000;
st->st_mtimensec = (mtime % 10000000) * 100;
st->st_ino = info->nFileIndexHigh;
st->st_ino <<= 32;
st->st_ino |= info->nFileIndexLow;
st->st_nlink = info->nNumberOfLinks;
st->st_dev = info->dwVolumeSerialNumber;
/* GetFileInformationByHandle() ensures to return synced information */
st->st_sync = 1;
/**
* In ReFS the IDs are 128 bit, and the 64 bit interface may fail.
*
* From Microsoft "Application Compatibility with ReFS"
* http://download.microsoft.com/download/C/B/3/CB3561DC-6BF6-443D-B5B9-9676ACDF7F75/Application%20Compatibility%20with%20ReFS.docx
* "64-bit file identifier can be obtained from GetFileInformationByHandle in"
* "the nFileIndexHigh and nFileIndexLow members. This API is an extended version"
* "that includes 128-bit file identifiers. If GetFileInformationByHandle returns"
* "FILE_INVALID_FILE_ID, the identifier may only be described in 128 bit form."
*/
if (st->st_ino == FILE_INVALID_FILE_ID) {
log_fatal("Invalid inode number! Is this ReFS?\n");
errno = EINVAL;
return -1;
}
return 0;
}
/**
* Convert Windows info to the Unix stat format.
*/
static int windows_stream2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ID_BOTH_DIR_INFO* stream, struct windows_stat* st)
{
uint64_t mtime;
/* The FILE_ID_BOTH_DIR_INFO doesn't have the ReparseTag information */
/* we could use instead FILE_ID_EXTD_DIR_INFO, but it's available only */
/* from Windows Server 2012 */
windows_attr2stat(stream->FileAttributes, 0, st);
st->st_size = stream->EndOfFile.QuadPart;
mtime = stream->LastWriteTime.QuadPart;
/*
* Convert to unix time
*
* How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
* http://support.microsoft.com/kb/167296
*/
mtime -= 116444736000000000LL;
st->st_mtime = mtime / 10000000;
st->st_mtimensec = (mtime % 10000000) * 100;
st->st_ino = stream->FileId.QuadPart;
st->st_nlink = info->nNumberOfLinks;
st->st_dev = info->dwVolumeSerialNumber;
/* directory listing doesn't ensure to return synced information */
st->st_sync = 0;
/* in ReFS the IDs are 128 bit, and the 64 bit interface may fail */
if (st->st_ino == FILE_INVALID_FILE_ID) {
log_fatal("Invalid inode number! Is this ReFS?\n");
errno = EINVAL;
return -1;
}
return 0;
}
/**
* Convert Windows findfirst info to the Unix stat format.
*/
static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_stat* st)
{
uint64_t mtime;
windows_attr2stat(info->dwFileAttributes, info->dwReserved0, st);
st->st_size = info->nFileSizeHigh;
st->st_size <<= 32;
st->st_size |= info->nFileSizeLow;
mtime = info->ftLastWriteTime.dwHighDateTime;
mtime <<= 32;
mtime |= info->ftLastWriteTime.dwLowDateTime;
/*
* Convert to unix time
*
* How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
* http://support.microsoft.com/kb/167296
*/
mtime -= 116444736000000000LL;
st->st_mtime = mtime / 10000000;
st->st_mtimensec = (mtime % 10000000) * 100;
/* No inode information available */
st->st_ino = 0;
/* No link information available */
st->st_nlink = 0;
/* No device information available */
st->st_dev = 0;
/* directory listing doesn't ensure to return synced information */
st->st_sync = 0;
}
static void windows_finddata2dirent(const WIN32_FIND_DATAW* info, struct windows_dirent* dirent)
{
char conv_buf[CONV_MAX];
const char* name;
size_t len;
name = u16tou8ex(conv_buf, info->cFileName, wcslen(info->cFileName), &len);
if (len + 1 >= sizeof(dirent->d_name)) {
log_fatal("Name too long\n");
exit(EXIT_FAILURE);
}
memcpy(dirent->d_name, name, len);
dirent->d_name[len] = 0;
windows_finddata2stat(info, &dirent->d_stat);
}
static int windows_stream2dirent(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ID_BOTH_DIR_INFO* stream, struct windows_dirent* dirent)
{
char conv_buf[CONV_MAX];
const char* name;
size_t len;
name = u16tou8ex(conv_buf, stream->FileName, stream->FileNameLength / 2, &len);
if (len + 1 >= sizeof(dirent->d_name)) {
log_fatal("Name too long\n");
exit(EXIT_FAILURE);
}
memcpy(dirent->d_name, name, len);
dirent->d_name[len] = 0;
return windows_stream2stat(info, stream, &dirent->d_stat);
}
/**
* Convert Windows error to errno.
*/
static void windows_errno(DWORD error)
{
switch (error) {
case ERROR_INVALID_HANDLE :
/* we check for a bad handle calling _get_osfhandle() */
/* and in such case we return EBADF */
/* Other cases are here identified with EINVAL */
errno = EINVAL;
break;
case ERROR_HANDLE_EOF : /* in ReadFile() over the end of the file */
errno = EINVAL;
break;
case ERROR_FILE_NOT_FOUND :
case ERROR_PATH_NOT_FOUND : /* in GetFileAttributeW() if internal path not found */
errno = ENOENT;
break;
case ERROR_ACCESS_DENIED : /* in CreateDirectoryW() if dir is scheduled for deletion */
case ERROR_CURRENT_DIRECTORY : /* in RemoveDirectoryW() if removing the current directory */
case ERROR_SHARING_VIOLATION : /* in RemoveDirectoryW() if in use */
case ERROR_WRITE_PROTECT : /* when dealing with read-only media/snapshot and trying to write to them */
errno = EACCES;
break;
case ERROR_ALREADY_EXISTS : /* in CreateDirectoryW() if already exists */
errno = EEXIST;
break;
case ERROR_DISK_FULL :
errno = ENOSPC;
break;
case ERROR_BUFFER_OVERFLOW :
errno = ENAMETOOLONG;
break;
case ERROR_NOT_ENOUGH_MEMORY :
errno = ENOMEM;
break;
case ERROR_NOT_SUPPORTED : /* in CreateSymlinkW() if not present in kernel32 */
errno = ENOSYS;
break;
case ERROR_PRIVILEGE_NOT_HELD : /* in CreateSymlinkW() if no SeCreateSymbolicLinkPrivilige permission */
errno = EPERM;
break;
case ERROR_IO_DEVICE : /* in ReadFile() and WriteFile() */
case ERROR_CRC : /* in ReadFile() */
errno = EIO;
break;
default :
log_fatal("Unexpected Windows error %d.\n", (int)error);
errno = EIO;
break;
}
}
int windows_fstat(int fd, struct windows_stat* st)
{
BY_HANDLE_FILE_INFORMATION info;
FILE_ATTRIBUTE_TAG_INFO tag;
HANDLE h;
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
if (!GetFileInformationByHandle(h, &info)) {
windows_errno(GetLastError());
return -1;
}
if (!GetReparseTagInfoByHandle(h, &tag, info.dwFileAttributes)) {
windows_errno(GetLastError());
return -1;
}
return windows_info2stat(&info, &tag, st);
}
int windows_lstat(const char* file, struct windows_stat* st)
{
wchar_t conv_buf[CONV_MAX];
HANDLE h;
WIN32_FIND_DATAW data;
/* FindFirstFileW by default gets information of symbolic links and not of their targets */
h = FindFirstFileW(convert(conv_buf, file), &data);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
if (!FindClose(h)) {
windows_errno(GetLastError());
return -1;
}
windows_finddata2stat(&data, st);
return 0;
}
void windows_dirent_lstat(const struct windows_dirent* dd, struct windows_stat* st)
{
memcpy(st, &dd->d_stat, sizeof(struct windows_stat));
}
int windows_mkdir(const char* file)
{
wchar_t conv_buf[CONV_MAX];
if (!CreateDirectoryW(convert(conv_buf, file), 0)) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
int windows_rmdir(const char* file)
{
wchar_t conv_buf[CONV_MAX];
if (!RemoveDirectoryW(convert(conv_buf, file))) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
static BOOL GetFilePhysicalOffset(HANDLE h, uint64_t* physical)
{
STARTING_VCN_INPUT_BUFFER svib;
unsigned char rpb_buffer[sizeof(RETRIEVAL_POINTERS_BUFFER)];
RETRIEVAL_POINTERS_BUFFER* rpb = (RETRIEVAL_POINTERS_BUFFER*)&rpb_buffer;
BOOL ret;
DWORD n;
/* in Wine FSCTL_GET_RETRIVIAL_POINTERS is not supported */
if (is_wine) {
*physical = FILEPHY_UNREPORTED_OFFSET;
return TRUE;
}
/* set the output variable, just to be safe */
rpb->ExtentCount = 0;
/* read the physical address */
svib.StartingVcn.QuadPart = 0;
ret = DeviceIoControl(h, FSCTL_GET_RETRIEVAL_POINTERS, &svib, sizeof(svib), rpb_buffer, sizeof(rpb_buffer), &n, 0);
if (!ret) {
DWORD error = GetLastError();
if (error == ERROR_MORE_DATA) {
/* we ignore ERROR_MODE_DATA because we are interested only at the first entry */
/* and this is the expected error if the files has more entries */
} else if (error == ERROR_HANDLE_EOF) {
/* if the file is small, it can be stored in the Master File Table (MFT) */
/* and then it doesn't have a physical address */
/* In such case we report a specific fake address, to report this special condition */
/* that it's different from the 0 offset reported by the underline file system */
*physical = FILEPHY_WITHOUT_OFFSET;
return TRUE;
} else if (error == ERROR_NOT_SUPPORTED) {
/* for disks shared on network this operation is not supported */
*physical = FILEPHY_UNREPORTED_OFFSET;
return TRUE;
} else {
return FALSE;
}
}
if (rpb->ExtentCount < 1)
*physical = FILEPHY_UNREPORTED_OFFSET;
else
*physical = rpb->Extents[0].Lcn.QuadPart + FILEPHY_REAL_OFFSET;
return TRUE;
}
int lstat_sync(const char* file, struct windows_stat* st, uint64_t* physical)
{
wchar_t conv_buf[CONV_MAX];
BY_HANDLE_FILE_INFORMATION info;
FILE_ATTRIBUTE_TAG_INFO tag;
HANDLE h;
/*
* Open the handle of the file.
*
* Use FILE_FLAG_BACKUP_SEMANTICS to open directories (it's just ignored for files).
* Use FILE_FLAG_OPEN_REPARSE_POINT to open symbolic links and not the their target.
*
* Note that even with FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
* and FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT some paths
* cannot be opened like "C:\System Volume Information" resulting
* in error ERROR_ACCESS_DENIED.
*/
h = CreateFileW(convert(conv_buf, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
if (!GetFileInformationByHandle(h, &info)) {
DWORD error = GetLastError();
CloseHandle(h);
windows_errno(error);
return -1;
}
if (!GetReparseTagInfoByHandle(h, &tag, info.dwFileAttributes)) {
DWORD error = GetLastError();
CloseHandle(h);
windows_errno(error);
return -1;
}
/* read the physical offset, only if a pointer is provided */
if (physical != 0) {
if (!GetFilePhysicalOffset(h, physical)) {
DWORD error = GetLastError();
CloseHandle(h);
windows_errno(error);
return -1;
}
}
if (!CloseHandle(h)) {
windows_errno(GetLastError());
return -1;
}
return windows_info2stat(&info, &tag, st);
}
int windows_stat(const char* file, struct windows_stat* st)
{
wchar_t conv_buf[CONV_MAX];
BY_HANDLE_FILE_INFORMATION info;
FILE_ATTRIBUTE_TAG_INFO tag;
HANDLE h;
/*
* Open the handle of the file.
*
* Use FILE_FLAG_BACKUP_SEMANTICS to open directories (it's just ignored for files).
*/
h = CreateFileW(convert(conv_buf, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
if (!GetFileInformationByHandle(h, &info)) {
DWORD error = GetLastError();
CloseHandle(h);
windows_errno(error);
return -1;
}
if (!GetReparseTagInfoByHandle(h, &tag, info.dwFileAttributes)) {
DWORD error = GetLastError();
CloseHandle(h);
windows_errno(error);
return -1;
}
if (!CloseHandle(h)) {
windows_errno(GetLastError());
return -1;
}
return windows_info2stat(&info, &tag, st);
}
int windows_ftruncate(int fd, off64_t off)
{
HANDLE h;
LARGE_INTEGER pos;
if (fd == -1) {
errno = EBADF;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
pos.QuadPart = off;
if (!SetFilePointerEx(h, pos, 0, FILE_BEGIN)) {
windows_errno(GetLastError());
return -1;
}
/*
* Windows effectively reserves space, but it doesn't initialize it.
* It's then important to write starting from the begin to the end,
* to avoid to have Windows to fill the holes writing zeros.
*
* See:
* "Why does my single-byte write take forever?"
* http://blogs.msdn.com/b/oldnewthing/archive/2011/09/22/10215053.aspx
*/
if (!SetEndOfFile(h)) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
int windows_fallocate(int fd, int mode, off64_t off, off64_t len)
{
if (mode != 0)
return -1;
/* no difference with ftruncate because Windows doesn't use sparse files */
return windows_ftruncate(fd, off + len);
}
int windows_fsync(int fd)
{
HANDLE h;
if (fd == -1) {
errno = EBADF;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
/*
* "The FlushFileBuffers API can be used to flush all the outstanding data
* and metadata on a single file or a whole volume. However, frequent use
* of this API can cause reduced throughput. Internally, Windows uses the
* SCSI Synchronize Cache or the IDE/ATAPI Flush cache commands."
*
* From:
* "Windows Write Caching - Part 2 An overview for Application Developers"
* http://winntfs.com/2012/11/29/windows-write-caching-part-2-an-overview-for-application-developers/
*/
if (!FlushFileBuffers(h)) {
DWORD error = GetLastError();
switch (error) {
case ERROR_INVALID_HANDLE :
/*
* FlushFileBuffers returns this error if the handle
* doesn't support buffering, like the console output.
*
* We had a report that also ATA-over-Ethernet returns
* this error, but not enough sure to ignore it.
* So, we use now an extended error reporting.
*/
log_fatal("Unexpected Windows INVALID_HANDLE error in FlushFileBuffers().\n");
log_fatal("Are you using ATA-over-Ethernet ? Please report it.\n");
/* normal error processing */
windows_errno(error);
return -1;
case ERROR_ACCESS_DENIED :
/*
* FlushFileBuffers returns this error for read-only
* data, that cannot have to be flushed.
*/
return 0;
default :
windows_errno(error);
return -1;
}
}
return 0;
}
int windows_futimens(int fd, struct windows_timespec tv[2])
{
HANDLE h;
FILETIME ft;
uint64_t mtime;
if (fd == -1) {
errno = EBADF;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
/*
* Convert to windows time
*
* How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
* http://support.microsoft.com/kb/167296
*/
mtime = tv[0].tv_sec;
mtime *= 10000000;
mtime += tv[0].tv_nsec / 100;
mtime += 116444736000000000;
ft.dwHighDateTime = mtime >> 32;
ft.dwLowDateTime = mtime;
if (!SetFileTime(h, 0, 0, &ft)) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
int windows_utimensat(int fd, const char* file, struct windows_timespec tv[2], int flags)
{
wchar_t conv_buf[CONV_MAX];
HANDLE h;
FILETIME ft;
uint64_t mtime;
DWORD wflags;
/*
* Support only the absolute paths
*/
if (fd != AT_FDCWD) {
errno = EBADF;
return -1;
}
/*
* Open the handle of the file.
*
* Use FILE_FLAG_BACKUP_SEMANTICS to open directories (it's just ignored for files).
* Use FILE_FLAG_OPEN_REPARSE_POINT to open symbolic links and not the their target.
*
* Note that even with FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
* and FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT some paths
* cannot be opened like "C:\System Volume Information" resulting
* in error ERROR_ACCESS_DENIED.
*/
wflags = FILE_FLAG_BACKUP_SEMANTICS;
if ((flags & AT_SYMLINK_NOFOLLOW) != 0)
wflags |= FILE_FLAG_OPEN_REPARSE_POINT;
h = CreateFileW(convert(conv_buf, file), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, wflags, 0);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
/*
* Convert to windows time
*
* How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
* http://support.microsoft.com/kb/167296
*/
mtime = tv[0].tv_sec;
mtime *= 10000000;
mtime += tv[0].tv_nsec / 100;
mtime += 116444736000000000;
ft.dwHighDateTime = mtime >> 32;
ft.dwLowDateTime = mtime;
if (!SetFileTime(h, 0, 0, &ft)) {
windows_errno(GetLastError());
CloseHandle(h);
return -1;
}
if (!CloseHandle(h)) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
int windows_rename(const char* from, const char* to)
{
wchar_t conv_buf_from[CONV_MAX];
wchar_t conv_buf_to[CONV_MAX];
/*
* Implements an atomic rename in Windows.
* Not really atomic at now to support XP.
*
* Is an atomic file rename (with overwrite) possible on Windows?
* http://stackoverflow.com/questions/167414/is-an-atomic-file-rename-with-overwrite-possible-on-windows
*/
if (!MoveFileExW(convert(conv_buf_from, from), convert(conv_buf_to, to), MOVEFILE_REPLACE_EXISTING)) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
int windows_remove(const char* file)
{
wchar_t conv_buf[CONV_MAX];
if (!DeleteFileW(convert(conv_buf, file))) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
FILE* windows_fopen(const char* file, const char* mode)
{
wchar_t conv_buf_file[CONV_MAX];
wchar_t conv_buf_mode[CONV_MAX];
return _wfopen(convert(conv_buf_file, file), u8tou16(conv_buf_mode, mode));
}
int windows_open(const char* file, int flags, ...)
{
wchar_t conv_buf[CONV_MAX];
HANDLE h;
int f;
DWORD access;
DWORD share;
DWORD create;
DWORD attr;
switch (flags & O_ACCMODE) {
case O_RDONLY :
access = GENERIC_READ;
break;
case O_WRONLY :
access = GENERIC_WRITE;
break;
case O_RDWR :
access = GENERIC_READ | GENERIC_WRITE;
break;
default:
errno = EINVAL;
return -1;
}
share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
switch (flags & (O_CREAT | O_EXCL | O_TRUNC)) {
case 0 :
create = OPEN_EXISTING;
break;
case O_CREAT :
create = OPEN_ALWAYS;
break;
case O_CREAT | O_EXCL :
case O_CREAT | O_EXCL | O_TRUNC :
create = CREATE_NEW;
break;
case O_CREAT | O_TRUNC :
create = CREATE_ALWAYS;
break;
case O_TRUNC :
create = TRUNCATE_EXISTING;
break;
default:
errno = EINVAL;
return -1;
}
attr = FILE_ATTRIBUTE_NORMAL;
if ((flags & O_DIRECT) != 0)
attr |= FILE_FLAG_NO_BUFFERING;
if ((flags & O_DSYNC) != 0)
attr |= FILE_FLAG_WRITE_THROUGH;
if ((flags & O_RANDOM) != 0)
attr |= FILE_FLAG_RANDOM_ACCESS;
if ((flags & O_SEQUENTIAL) != 0)
attr |= FILE_FLAG_SEQUENTIAL_SCAN;
if ((flags & _O_SHORT_LIVED) != 0)
attr |= FILE_ATTRIBUTE_TEMPORARY;
if ((flags & O_TEMPORARY) != 0)
attr |= FILE_FLAG_DELETE_ON_CLOSE;
h = CreateFileW(convert(conv_buf, file), access, share, 0, create, attr, 0);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
/* mask out flags unknown by Windows */
flags &= ~(O_DIRECT | O_DSYNC);
f = _open_osfhandle((intptr_t)h, flags);
if (f == -1) {
CloseHandle(h);
return -1;
}
return f;
}
struct windows_dir_struct {
BY_HANDLE_FILE_INFORMATION info;
WIN32_FIND_DATAW find;
HANDLE h;
struct windows_dirent entry;
unsigned char* buffer;
unsigned buffer_size;
unsigned buffer_pos;
int state;
};
#define DIR_STATE_EOF -1 /**< End of the dir stream */
#define DIR_STATE_EMPTY 0 /**< The entry is empty. */
#define DIR_STATE_FILLED 1 /**< The entry is valid. */
static windows_dir* windows_opendir_find(const char* dir)
{
wchar_t conv_buf[CONV_MAX];
wchar_t* wdir;
windows_dir* dirstream;
size_t len;
dirstream = malloc(sizeof(windows_dir));
if (!dirstream) {
log_fatal("Low memory\n");
exit(EXIT_FAILURE);
}
wdir = convert(conv_buf, dir);
/* add final / and * */
len = wcslen(wdir);
if (len != 0 && wdir[len - 1] != '\\')
wdir[len++] = L'\\';
wdir[len++] = L'*';
wdir[len++] = 0;
dirstream->h = FindFirstFileW(wdir, &dirstream->find);
if (dirstream->h == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND) {
dirstream->state = DIR_STATE_EOF;
return dirstream;
}
free(dirstream);
windows_errno(error);
return 0;
}
windows_finddata2dirent(&dirstream->find, &dirstream->entry);
dirstream->state = DIR_STATE_FILLED;
return dirstream;
}
static struct windows_dirent* windows_readdir_find(windows_dir* dirstream)
{
if (dirstream->state == DIR_STATE_EMPTY) {
if (!FindNextFileW(dirstream->h, &dirstream->find)) {
DWORD error = GetLastError();
if (error != ERROR_NO_MORE_FILES) {
windows_errno(error);
return 0;
}
dirstream->state = DIR_STATE_EOF;
} else {
windows_finddata2dirent(&dirstream->find, &dirstream->entry);
dirstream->state = DIR_STATE_FILLED;
}
}
if (dirstream->state == DIR_STATE_FILLED) {
dirstream->state = DIR_STATE_EMPTY;
return &dirstream->entry;
}
/* otherwise it's the end of stream */
assert(dirstream->state == DIR_STATE_EOF);
errno = 0;
return 0;
}
static int windows_closedir_find(windows_dir* dirstream)
{
if (dirstream->h != INVALID_HANDLE_VALUE) {
if (!FindClose(dirstream->h)) {
DWORD error = GetLastError();
free(dirstream);
windows_errno(error);
return -1;
}
}
free(dirstream);
return 0;
}
static int windows_first_stream(windows_dir* dirstream)
{
FILE_ID_BOTH_DIR_INFO* fd;
if (!GetFileInformationByHandleEx(dirstream->h, FileIdBothDirectoryInfo, dirstream->buffer, dirstream->buffer_size)) {
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
dirstream->state = DIR_STATE_EOF;
return 0;
}
windows_errno(error);
return -1;
}
/* get the first entry */
dirstream->state = DIR_STATE_FILLED;
dirstream->buffer_pos = 0;
fd = (FILE_ID_BOTH_DIR_INFO*)dirstream->buffer;
return windows_stream2dirent(&dirstream->info, fd, &dirstream->entry);
}
static int windows_next_stream(windows_dir* dirstream)
{
FILE_ID_BOTH_DIR_INFO* fd;
/* last entry read */
fd = (FILE_ID_BOTH_DIR_INFO*)(dirstream->buffer + dirstream->buffer_pos);
/* check if there is a next one */
if (fd->NextEntryOffset == 0) {
/* if not, fill it up again */
if (windows_first_stream(dirstream) != 0)
return -1;
return 0;
}
/* go to the next one */
dirstream->state = DIR_STATE_FILLED;
dirstream->buffer_pos += fd->NextEntryOffset;
fd = (FILE_ID_BOTH_DIR_INFO*)(dirstream->buffer + dirstream->buffer_pos);
return windows_stream2dirent(&dirstream->info, fd, &dirstream->entry);
}
static windows_dir* windows_opendir_stream(const char* dir)
{
wchar_t conv_buf[CONV_MAX];
windows_dir* dirstream;
WCHAR* wdir;
dirstream = malloc(sizeof(windows_dir));
if (!dirstream) {
log_fatal("Low memory\n");
exit(EXIT_FAILURE);
}
wdir = convert(conv_buf, dir);
/* uses a 64 kB buffer for reading directory */
dirstream->buffer_size = 64 * 1024;
dirstream->buffer = malloc(dirstream->buffer_size);
if (!dirstream->buffer) {
log_fatal("Low memory\n");
exit(EXIT_FAILURE);
}
dirstream->h = CreateFileW(wdir, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (dirstream->h == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
free(dirstream->buffer);
free(dirstream);
windows_errno(error);
return 0;
}
/* get dir information for the VolumeSerialNumber */
/* this value is used for all the files in the dir */
if (!GetFileInformationByHandle(dirstream->h, &dirstream->info)) {
DWORD error = GetLastError();
CloseHandle(dirstream->h);
free(dirstream->buffer);
free(dirstream);
windows_errno(error);
return 0;
}
if (windows_first_stream(dirstream) != 0) {
CloseHandle(dirstream->h);
free(dirstream->buffer);
free(dirstream);
return 0;
}
return dirstream;
}
static struct windows_dirent* windows_readdir_stream(windows_dir* dirstream)
{
if (dirstream->state == DIR_STATE_EMPTY) {
if (windows_next_stream(dirstream) != 0) {
free(dirstream->buffer);
free(dirstream);
return 0;
}
}
if (dirstream->state == DIR_STATE_FILLED) {
dirstream->state = DIR_STATE_EMPTY;
return &dirstream->entry;
}
/* otherwise it's the end of stream */
assert(dirstream->state == DIR_STATE_EOF);
errno = 0;
return 0;
}
static int windows_closedir_stream(windows_dir* dirstream)
{
if (dirstream->h != INVALID_HANDLE_VALUE) {
if (!CloseHandle(dirstream->h)) {
DWORD error = GetLastError();
free(dirstream->buffer);
free(dirstream);
windows_errno(error);
return -1;
}
}
free(dirstream->buffer);
free(dirstream);
return 0;
}
windows_dir* windows_opendir(const char* dir)
{
if (!is_scan_winfind)
return windows_opendir_stream(dir);
else
return windows_opendir_find(dir);
}
struct windows_dirent* windows_readdir(windows_dir* dirstream)
{
if (!is_scan_winfind)
return windows_readdir_stream(dirstream);
else
return windows_readdir_find(dirstream);
}
int windows_closedir(windows_dir* dirstream)
{
if (!is_scan_winfind)
return windows_closedir_stream(dirstream);
else
return windows_closedir_find(dirstream);
}
int windows_dirent_hidden(struct dirent* dd)
{
return dd->d_stat.st_hidden;
}
const char* windows_stat_desc(struct stat* st)
{
return st->st_desc;
}
void windows_sleep(unsigned seconds)
{
Sleep(seconds * 1000);
}
int windows_link(const char* existing, const char* file)
{
wchar_t conv_buf_file[CONV_MAX];
wchar_t conv_buf_existing[CONV_MAX];
if (!CreateHardLinkW(convert(conv_buf_file, file), convert(conv_buf_existing, existing), 0)) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
/**
* In Windows 10 allow creation of symblink by not privileged user.
*
* See: Symlinks in Windows 10!
* https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/#cQG7cx48oGH86lkI.97
* "Specify this flag to allow creation of symbolic links when the process is not elevated"
*/
#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
#endif
int windows_symlink(const char* existing, const char* file)
{
wchar_t conv_buf_file[CONV_MAX];
wchar_t conv_buf_existing[CONV_MAX];
/* We must convert to the extended-length \\?\ format if the path is too long */
/* otherwise the link creation fails. */
/* But we don't want to always convert it, to avoid to recreate */
/* user symlinks different than they were before */
if (!CreateSymbolicLinkW(convert(conv_buf_file, file), convert_if_required(conv_buf_existing, existing),
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
) {
DWORD error = GetLastError();
if (GetLastError() != ERROR_INVALID_PARAMETER) {
windows_errno(error);
return -1;
}
/* retry without the new flag SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE */
if (!CreateSymbolicLinkW(convert(conv_buf_file, file), convert_if_required(conv_buf_existing, existing), 0)) {
windows_errno(GetLastError());
return -1;
}
}
return 0;
}
/* Adds missing definitions in MingW winnt.h */
#ifndef FSCTL_GET_REPARSE_POINT
#define FSCTL_GET_REPARSE_POINT 0x000900a8
#endif
#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
typedef struct _REPARSE_DATA_BUFFER {
DWORD ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
_ANONYMOUS_UNION union {
struct {
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
BYTE DataBuffer[1];
} GenericReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif
int windows_readlink(const char* file, char* buffer, size_t size)
{
wchar_t conv_buf_file[CONV_MAX];
char conv_buf_name[CONV_MAX];
HANDLE h;
const char* name;
size_t len;
unsigned char rdb_buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
REPARSE_DATA_BUFFER* rdb = (REPARSE_DATA_BUFFER*)rdb_buffer;
BOOL ret;
DWORD n;
/*
* Open the handle of the file.
*
* Use FILE_FLAG_BACKUP_SEMANTICS to open directories (it's just ignored for files).
* Use FILE_FLAG_OPEN_REPARSE_POINT to open symbolic links and not the their target.
*/
h = CreateFileW(convert(conv_buf_file, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
/* read the reparse point */
ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, 0, 0, rdb_buffer, sizeof(rdb_buffer), &n, 0);
if (!ret) {
windows_errno(GetLastError());
CloseHandle(h);
return -1;
}
CloseHandle(h);
/* check if it's really a symbolic link */
if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
errno = EINVAL;
return -1;
}
/* convert the name to UTF-8 */
name = u16tou8ex(conv_buf_name,
rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.PrintNameOffset,
rdb->SymbolicLinkReparseBuffer.PrintNameLength / 2, &len);
/* check for overflow */
if (len > size) {
len = size;
}
memcpy(buffer, name, len);
return len;
}
int devuuid(uint64_t device, char* uuid, size_t uuid_size)
{
/* just use the volume serial number returned in the device parameter */
snprintf(uuid, uuid_size, "%08x", (unsigned)device);
log_tag("uuid:windows:%u:%s:\n", (unsigned)device, uuid);
return 0;
}
int filephy(const char* file, uint64_t size, uint64_t* physical)
{
wchar_t conv_buf[CONV_MAX];
HANDLE h;
(void)size;
/* open the handle of the file */
h = CreateFileW(convert(conv_buf, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
if (!GetFilePhysicalOffset(h, physical)) {
DWORD error = GetLastError();
CloseHandle(h);
windows_errno(error);
return -1;
}
CloseHandle(h);
return 0;
}
int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_hardlinks, uint64_t* total_space, uint64_t* free_space)
{
wchar_t conv_buf[CONV_MAX];
/* all FAT/exFAT/NTFS when managed from Windows have persistent inodes */
if (has_persistent_inode)
*has_persistent_inode = 1;
/* NTFS doesn't synchronize hardlinks metadata */
if (has_syncronized_hardlinks)
*has_syncronized_hardlinks = 0;
if (free_space || total_space) {
ULARGE_INTEGER total_bytes;
ULARGE_INTEGER total_free_bytes;
DWORD attr;
char dir[PATH_MAX];
if (strlen(path) + 1 > sizeof(dir)) {
windows_errno(ERROR_BUFFER_OVERFLOW);
return -1;
}
strcpy(dir, path);
/* get the file attributes */
attr = GetFileAttributesW(convert(conv_buf, dir));
if (attr == INVALID_FILE_ATTRIBUTES) {
DWORD error = GetLastError();
if (error != ERROR_FILE_NOT_FOUND) {
windows_errno(error);
return -1;
}
/* if it doesn't exist, we assume a file */
/* and we check for the containing dir */
attr = 0;
}
/* if it's not a directory, truncate the file name */
if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
char* slash = strrchr(dir, '/');
/**
* Cut the file name, but leave the last slash.
*
* This is done because a MSDN comment about using of UNC paths.
*
* MSDN 'GetDiskFreeSpaceEx function'
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa364937%28v=vs.85%29.aspx
* If this parameter is a UNC name, it must include a trailing backslash,
* for example, "\\MyServer\MyShare\".
*/
if (slash)
slash[1] = 0;
}
/* get the free space of the directory */
/* note that it must be a directory */
if (!GetDiskFreeSpaceExW(convert(conv_buf, dir), 0, &total_bytes, &total_free_bytes)) {
windows_errno(GetLastError());
return -1;
}
if (total_space)
*total_space = total_bytes.QuadPart;
if (free_space)
*free_space = total_free_bytes.QuadPart;
}
return 0;
}
/* ensure to call the real C strerror() */
#undef strerror
const char* windows_strerror(int err)
{
/* get the normal C error from the specified err */
char* error;
char* previous;
const char* str = strerror(err);
size_t len = strlen(str);
/* adds space for GetLastError() */
len += 32;
/* allocate a new one */
error = malloc(len);
if (!error)
return str;
snprintf(error, len, "%s [%d/%u]", str, err, (unsigned)GetLastError());
/* get previous one, if any */
previous = windows_getspecific(last_error);
/* store in the thread local storage */
if (windows_setspecific(last_error, error) != 0) {
free(error);
return str;
}
free(previous);
return error;
}
ssize_t windows_read(int fd, void* buffer, size_t size)
{
HANDLE h;
DWORD count;
if (fd == -1) {
errno = EBADF;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
if (!ReadFile(h, buffer, size, &count, 0)) {
windows_errno(GetLastError());
return -1;
}
return count;
}
ssize_t windows_write(int fd, const void* buffer, size_t size)
{
HANDLE h;
DWORD count;
if (fd == -1) {
errno = EBADF;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
if (!WriteFile(h, buffer, size, &count, 0)) {
windows_errno(GetLastError());
return -1;
}
return count;
}
off_t windows_lseek(int fd, off_t offset, int whence)
{
HANDLE h;
LARGE_INTEGER pos;
LARGE_INTEGER ret;
if (fd == -1) {
errno = EBADF;
return -1;
}
/* we support only SEEK_SET */
if (whence != SEEK_SET) {
errno = EINVAL;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
pos.QuadPart = offset;
if (!SetFilePointerEx(h, pos, &ret, FILE_BEGIN)) {
windows_errno(GetLastError());
return -1;
}
return ret.QuadPart;
}
ssize_t windows_pread(int fd, void* buffer, size_t size, off_t offset)
{
HANDLE h;
OVERLAPPED overlapped;
DWORD count;
if (fd == -1) {
errno = EBADF;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
retry:
memset(&overlapped, 0, sizeof(overlapped));
overlapped.Offset = offset & 0xFFFFFFFF;
overlapped.OffsetHigh = offset >> 32;
if (!ReadFile(h, buffer, size, &count, &overlapped)) {
DWORD err = GetLastError();
/*
* If Windows is not able to allocate memory from the PagedPool for the disk cache
* it could return the ERROR_NO_SYSTEM_RESOURCES error.
* In this case, the only possibility is to retry after a wait of few milliseconds.
*
* SQL Server reports operating system error 1450 or 1452 or 665 (retries)
* http://blogs.msdn.com/b/psssql/archive/2008/07/10/sql-server-reports-operating-system-error-1450-or-1452-or-665-retries.aspx
*
* 03-12-09 - ERROR_NO_SYSTEM_RESOURCES
* http://cbloomrants.blogspot.it/2009/03/03-12-09-errornosystemresources.html
*
* From SnapRAID Discussion Forum:
*
* Error reading file
* https://sourceforge.net/p/snapraid/discussion/1677233/thread/6657fdbf/
*
* Unexpected Windows ERROR_NO_SYSTEM_RESOURCES in pwrite(), retrying...
* https://sourceforge.net/p/snapraid/discussion/1677233/thread/a7c25ba9/
*/
if (err == ERROR_NO_SYSTEM_RESOURCES) {
log_fatal("Unexpected Windows ERROR_NO_SYSTEM_RESOURCES in pread(), retrying...\n");
Sleep(50);
goto retry;
}
windows_errno(err);
return -1;
}
return count;
}
ssize_t windows_pwrite(int fd, const void* buffer, size_t size, off_t offset)
{
HANDLE h;
OVERLAPPED overlapped;
DWORD count;
if (fd == -1) {
errno = EBADF;
return -1;
}
h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
retry:
memset(&overlapped, 0, sizeof(overlapped));
overlapped.Offset = offset & 0xFFFFFFFF;
overlapped.OffsetHigh = offset >> 32;
if (!WriteFile(h, buffer, size, &count, &overlapped)) {
DWORD err = GetLastError();
/* See windows_pread() for comments on this error management */
if (err == ERROR_NO_SYSTEM_RESOURCES) {
log_fatal("Unexpected Windows ERROR_NO_SYSTEM_RESOURCES in pwrite(), retrying...\n");
Sleep(50);
goto retry;
}
windows_errno(err);
return -1;
}
return count;
}
size_t windows_direct_size(void)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
/*
* MSDN 'File Buffering'
* https://msdn.microsoft.com/en-us/library/windows/desktop/cc644950%28v=vs.85%29.aspx
*
* "Therefore, in most situations, page-aligned memory will also be sector-aligned,"
* "because the case where the sector size is larger than the page size is rare."
*/
return si.dwPageSize;
}
uint64_t tick(void)
{
LARGE_INTEGER t;
uint64_t r;
/*
* Ensure to return a strict monotone tick counter.
*
* We had reports of invalid stats due faulty High Precision Event Timer.
* See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/a2122fd6/
*/
windows_mutex_lock(&tick_lock);
/*
* MSDN 'QueryPerformanceCounter'
* "On systems that run Windows XP or later, the function"
* "will always succeed and will thus never return zero."
*/
r = 0;
if (QueryPerformanceCounter(&t))
r = t.QuadPart;
if (r < tick_last)
r = tick_last;
tick_last = r;
windows_mutex_unlock(&tick_lock);
return r;
}
uint64_t tick_ms(void)
{
/* GetTickCount64() isn't supported in Windows XP */
if (ptr_GetTickCount64 != 0)
return ptr_GetTickCount64();
return GetTickCount();
}
int randomize(void* void_ptr, size_t size)
{
size_t i;
unsigned char* ptr = void_ptr;
/* try RtlGenRandom */
if (ptr_RtlGenRandom != 0 && ptr_RtlGenRandom(ptr, size) != 0)
return 0;
/* try rand_s */
for (i = 0; i < size; ++i) {
unsigned v = 0;
if (rand_s(&v) != 0)
break;
ptr[i] = v;
}
if (i == size)
return 0;
/* fallback to standard rand */
for (i = 0; i < size; ++i)
ptr[i] = rand();
return 0;
}
/**
* Get the device file from a path inside the device.
*/
static int devresolve(const char* mount, char* file, size_t file_size, char* wfile, size_t wfile_size)
{
wchar_t conv_buf_mount[CONV_MAX];
char conv_buf_volume_guid[CONV_MAX];
WCHAR volume_mount[MAX_PATH];
WCHAR volume_guid[MAX_PATH];
DWORD i;
char* p;
/* get the volume mount point from the disk path */
if (!GetVolumePathNameW(convert(conv_buf_mount, mount), volume_mount, sizeof(volume_mount) / sizeof(WCHAR))) {
windows_errno(GetLastError());
return -1;
}
/* get the volume GUID path from the mount point */
if (!GetVolumeNameForVolumeMountPointW(volume_mount, volume_guid, sizeof(volume_guid) / sizeof(WCHAR))) {
windows_errno(GetLastError());
return -1;
}
/* remove the final slash, otherwise CreateFile() opens the file-system */
/* and not the volume */
i = 0;
while (volume_guid[i] != 0)
++i;
if (i != 0 && volume_guid[i - 1] == '\\')
volume_guid[i - 1] = 0;
pathcpy(wfile, wfile_size, u16tou8(conv_buf_volume_guid, volume_guid));
/* get the GUID start { */
p = strchr(wfile, '{');
if (!p)
p = wfile;
else
++p;
pathprint(file, file_size, "/dev/vol%s", p);
/* cut GUID end } */
p = strrchr(file, '}');
if (p)
*p = 0;
return 0;
}
/**
* Read a device tree filling the specified list of disk_t entries.
*/
static int devtree(const char* name, const char* custom, const char* wfile, devinfo_t* parent, tommy_list* list)
{
wchar_t conv_buf[CONV_MAX];
HANDLE h;
unsigned char vde_buffer[sizeof(VOLUME_DISK_EXTENTS)];
VOLUME_DISK_EXTENTS* vde = (VOLUME_DISK_EXTENTS*)&vde_buffer;
unsigned vde_size = sizeof(vde_buffer);
void* vde_alloc = 0;
BOOL ret;
DWORD n;
DWORD i;
/* open the volume */
h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (h == INVALID_HANDLE_VALUE) {
windows_errno(GetLastError());
return -1;
}
/* get the physical extents of the volume */
ret = DeviceIoControl(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 0, 0, vde, vde_size, &n, 0);
if (!ret) {
DWORD error = GetLastError();
if (error != ERROR_MORE_DATA) {
CloseHandle(h);
windows_errno(error);
}
/* more than one extends, allocate more space */
vde_size = sizeof(VOLUME_DISK_EXTENTS) + vde->NumberOfDiskExtents * sizeof(DISK_EXTENT);
vde_alloc = malloc_nofail(vde_size);
vde = vde_alloc;
/* retry with more space */
ret = DeviceIoControl(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 0, 0, vde, vde_size, &n, 0);
}
if (!ret) {
DWORD error = GetLastError();
CloseHandle(h);
windows_errno(error);
return -1;
}
for (i = 0; i < vde->NumberOfDiskExtents; ++i) {
devinfo_t* devinfo;
devinfo = calloc_nofail(1, sizeof(devinfo_t));
pathcpy(devinfo->name, sizeof(devinfo->name), name);
pathcpy(devinfo->smartctl, sizeof(devinfo->smartctl), custom);
devinfo->device = vde->Extents[i].DiskNumber;
pathprint(devinfo->file, sizeof(devinfo->file), "/dev/pd%" PRIu64, devinfo->device);
pathprint(devinfo->wfile, sizeof(devinfo->wfile), "\\\\.\\PhysicalDrive%" PRIu64, devinfo->device);
devinfo->parent = parent;
/* insert in the list */
tommy_list_insert_tail(list, &devinfo->node, devinfo);
}
if (!CloseHandle(h)) {
windows_errno(GetLastError());
return -1;
}
free(vde_alloc);
return 0;
}
/**
* Read smartctl --scan from a stream.
* Return 0 on success.
*/
static int smartctl_scan(FILE* f, tommy_list* list)
{
while (1) {
char buf[256];
char* s;
s = fgets(buf, sizeof(buf), f);
if (s == 0)
break;
/* remove extraneous chars */
s = strpolish(buf);
log_tag("smartctl:scan::text: %s\n", s);
if (*s == '/') {
char* sep = strchr(s, ' ');
if (sep) {
tommy_node* i;
const char* number;
uint64_t device;
/* clear everything after the first space */
*sep = 0;
/* get the device number from the device file */
/* note that this is Windows specific */
/* for the format /dev/pdX of smartmontools */
number = s;
while (*number != 0 && !isdigit(*number))
++number;
device = atoi(number);
/* check if already present */
/* comparing the device file */
for (i = tommy_list_head(list); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
if (devinfo->device == device)
break;
}
/* if not found */
if (i == 0) {
devinfo_t* devinfo;
devinfo = calloc_nofail(1, sizeof(devinfo_t));
devinfo->device = device;
pathprint(devinfo->file, sizeof(devinfo->file), "/dev/pd%" PRIu64, devinfo->device);
pathprint(devinfo->wfile, sizeof(devinfo->wfile), "\\\\.\\PhysicalDrive%" PRIu64, devinfo->device);
/* insert in the list */
tommy_list_insert_tail(list, &devinfo->node, devinfo);
}
}
}
}
return 0;
}
/**
* Scan all the devices.
*/
static int devscan(tommy_list* list)
{
char conv_buf[CONV_MAX];
WCHAR cmd[MAX_PATH + 128];
FILE* f;
int ret;
snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" --scan-open -d pd", exedir);
log_tag("smartctl:scan::run: %s\n", u16tou8(conv_buf, cmd));
f = _wpopen(cmd, L"rt");
if (!f) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from popen).\n", u16tou8(conv_buf, cmd));
return -1;
/* LCOV_EXCL_STOP */
}
if (smartctl_scan(f, list) != 0) {
/* LCOV_EXCL_START */
pclose(f);
return -1;
/* LCOV_EXCL_STOP */
}
ret = pclose(f);
log_tag("smartctl:scan::ret: %x\n", ret);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from pclose).\n", u16tou8(conv_buf, cmd));
return -1;
/* LCOV_EXCL_STOP */
}
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' with return code %xh.\n", u16tou8(conv_buf, cmd), ret);
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
/**
* Get SMART attributes.
*/
static int devsmart(uint64_t device, const char* name, const char* custom, uint64_t* smart, char* serial, char* vendor, char* model)
{
char conv_buf[CONV_MAX];
WCHAR cmd[MAX_PATH + 128];
char file[128];
FILE* f;
int ret;
int count;
snprintf(file, sizeof(file), "/dev/pd%" PRIu64, device);
/* if there is a custom command */
if (custom[0]) {
char option[128];
snprintf(option, sizeof(option), custom, file);
snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" -a %s", exedir, option);
} else {
snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" -a %s", exedir, file);
}
count = 0;
retry:
log_tag("smartctl:%s:%s:run: %s\n", file, name, u16tou8(conv_buf, cmd));
f = _wpopen(cmd, L"rt");
if (!f) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from popen).\n", u16tou8(conv_buf, cmd));
return -1;
/* LCOV_EXCL_STOP */
}
if (smartctl_attribute(f, file, name, smart, serial, vendor, model) != 0) {
/* LCOV_EXCL_START */
pclose(f);
return -1;
/* LCOV_EXCL_STOP */
}
ret = pclose(f);
log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from pclose).\n", u16tou8(conv_buf, cmd));
return -1;
/* LCOV_EXCL_STOP */
}
/* if first try without custom command */
if (count == 0 && custom[0] == 0) {
/*
* Handle some common cases in Windows.
*
* Sometimes the "type" autodetection is wrong, and the command fails at identification
* stage, returning with error 2, or even with error 0, and with no info at all.
* We detect this condition checking the PowerOnHours, Size and RotationRate attributes.
*
* In such conditions we retry using the "sat" type, that often allows to proceed.
*
* Note that getting error 4 is instead very common, even with full info gathering.
*/
if ((ret == 0 || ret == 2)
&& smart[9] == SMART_UNASSIGNED
&& smart[SMART_SIZE] == SMART_UNASSIGNED
&& smart[SMART_ROTATION_RATE] == SMART_UNASSIGNED
) {
/* retry using the "sat" type */
snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" -a -d sat %s", exedir, file);
++count;
goto retry;
}
}
/* store the smartctl return value */
smart[SMART_FLAGS] = ret;
return 0;
}
/**
* Spin down a specific device.
*/
static int devdown(uint64_t device, const char* name, const char* custom)
{
char conv_buf[CONV_MAX];
WCHAR cmd[MAX_PATH + 128];
char file[128];
FILE* f;
int ret;
int count;
snprintf(file, sizeof(file), "/dev/pd%" PRIu64, device);
/* if there is a custom command */
if (custom[0]) {
char option[128];
snprintf(option, sizeof(option), custom, file);
snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" -s standby,now %s", exedir, option);
} else {
snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" -s standby,now %s", exedir, file);
}
count = 0;
retry:
log_tag("smartctl:%s:%s:run: %s\n", file, name, u16tou8(conv_buf, cmd));
f = _wpopen(cmd, L"rt");
if (!f) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from popen).\n", u16tou8(conv_buf, cmd));
return -1;
/* LCOV_EXCL_STOP */
}
if (smartctl_flush(f, file, name) != 0) {
/* LCOV_EXCL_START */
pclose(f);
return -1;
/* LCOV_EXCL_STOP */
}
ret = pclose(f);
log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from pclose).\n", u16tou8(conv_buf, cmd));
return -1;
/* LCOV_EXCL_STOP */
}
/* if first try without custom command */
if (count == 0 && custom[0] == 0) {
/*
* Handle some common cases in Windows.
*
* Sometimes the "type" autodetection is wrong, and the command fails at identification
* stage, returning with error 2.
*
* In such conditions we retry using the "sat" type, that often allows to proceed.
*/
if (ret == 2) {
/* retry using the "sat" type */
snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" -s standby,now -d sat %s", exedir, file);
++count;
goto retry;
}
}
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' with return code %xh.\n", u16tou8(conv_buf, cmd), ret);
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
/**
* Spin up a device.
*
* There isn't a defined way to spin up a device,
* so we just do a generic write.
*/
static int devup(const char* mount)
{
wchar_t conv_buf[CONV_MAX];
int f;
char path[PATH_MAX];
/* add a temporary name used for writing */
pathprint(path, sizeof(path), "%s.snapraid-spinup.tmp", mount);
/* create a temporary file, automatically deleted on close */
f = _wopen(convert(conv_buf, path), _O_CREAT | _O_TEMPORARY | _O_RDWR, _S_IREAD | _S_IWRITE);
if (f != -1)
close(f);
return 0;
}
/**
* Thread for spinning up.
*
* Note that filling up the devinfo object is done inside this thread,
* to avoid to block the main thread if the device need to be spin up
* to handle stat/resolve requests.
*/
static void* thread_spinup(void* arg)
{
devinfo_t* devinfo = arg;
struct stat st;
uint64_t start;
start = tick_ms();
/* uses lstat_sync() that maps to CreateFile */
/* we cannot use FindFirstFile because it doesn't allow to open the root dir */
if (lstat_sync(devinfo->mount, &st, 0) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to stat path '%s'. %s.\n", devinfo->mount, strerror(errno));
return (void*)-1;
/* LCOV_EXCL_STOP */
}
/* set the device number */
devinfo->device = st.st_dev;
if (devresolve(devinfo->mount, devinfo->file, sizeof(devinfo->file), devinfo->wfile, sizeof(devinfo->wfile)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to resolve path '%s'.\n", devinfo->mount);
return (void*)-1;
/* LCOV_EXCL_STOP */
}
if (devup(devinfo->mount) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, tick_ms() - start);
return 0;
}
/**
* Thread for spinning down.
*/
static void* thread_spindown(void* arg)
{
devinfo_t* devinfo = arg;
uint64_t start;
start = tick_ms();
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, tick_ms() - start);
return 0;
}
/**
* Thread for getting smart info.
*/
static void* thread_smart(void* arg)
{
devinfo_t* devinfo = arg;
if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->smart_serial, devinfo->smart_vendor, devinfo->smart_model) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
return 0;
}
static int device_thread(tommy_list* list, void* (*func)(void* arg))
{
int fail = 0;
tommy_node* i;
/* starts all threads */
for (i = tommy_list_head(list); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
thread_create(&devinfo->thread, func, devinfo);
}
/* joins all threads */
for (i = tommy_list_head(list); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
void* retval;
thread_join(devinfo->thread, &retval);
if (retval != 0)
++fail;
}
if (fail != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int devquery(tommy_list* high, tommy_list* low, int operation, int others)
{
tommy_node* i;
void* (*func)(void* arg) = 0;
if (operation != DEVICE_UP) {
/* for each device */
for (i = tommy_list_head(high); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
if (devresolve(devinfo->mount, devinfo->file, sizeof(devinfo->file), devinfo->wfile, sizeof(devinfo->wfile)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to resolve path '%s'.\n", devinfo->mount);
return -1;
/* LCOV_EXCL_STOP */
}
/* expand the tree of devices */
if (devtree(devinfo->name, devinfo->smartctl, devinfo->wfile, devinfo, low) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to expand device '%s'.\n", devinfo->file);
return -1;
/* LCOV_EXCL_STOP */
}
}
}
if (operation == DEVICE_UP) {
/* duplicate the high */
for (i = tommy_list_head(high); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
devinfo_t* entry;
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = devinfo->device;
pathcpy(entry->name, sizeof(entry->name), devinfo->name);
pathcpy(entry->mount, sizeof(entry->mount), devinfo->mount);
/* insert in the high */
tommy_list_insert_tail(low, &entry->node, entry);
}
}
/* add other devices */
if (others) {
if (devscan(low) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to list other devices.\n");
return -1;
/* LCOV_EXCL_STOP */
}
}
switch (operation) {
case DEVICE_UP : func = thread_spinup; break;
case DEVICE_DOWN : func = thread_spindown; break;
case DEVICE_SMART : func = thread_smart; break;
}
if (!func)
return 0;
return device_thread(low, func);
}
/****************************************************************************/
/* pthread like interface */
int windows_mutex_init(windows_mutex_t* mutex, void* attr)
{
(void)attr;
InitializeCriticalSection(mutex);
return 0;
}
int windows_mutex_destroy(windows_mutex_t* mutex)
{
DeleteCriticalSection(mutex);
return 0;
}
int windows_mutex_lock(windows_mutex_t* mutex)
{
EnterCriticalSection(mutex);
return 0;
}
int windows_mutex_unlock(windows_mutex_t* mutex)
{
LeaveCriticalSection(mutex);
return 0;
}
int windows_cond_init(windows_cond_t* cond, void* attr)
{
(void)attr;
InitializeConditionVariable(cond);
return 0;
}
int windows_cond_destroy(windows_cond_t* cond)
{
/* note that in Windows there is no DeleteConditionVariable() to call */
(void)cond;
return 0;
}
int windows_cond_signal(windows_cond_t* cond)
{
WakeConditionVariable(cond);
return 0;
}
int windows_cond_broadcast(windows_cond_t* cond)
{
WakeAllConditionVariable(cond);
return 0;
}
int windows_cond_wait(windows_cond_t* cond, windows_mutex_t* mutex)
{
if (!SleepConditionVariableCS(cond, mutex, INFINITE))
return -1;
return 0;
}
struct windows_key_context {
void (* func)(void *);
DWORD key;
tommy_node node;
};
/* list of all keys with destructor */
static tommy_list windows_key_list = { 0 };
int windows_key_create(windows_key_t* key, void(* destructor)(void*))
{
struct windows_key_context* context;
context = malloc(sizeof(struct windows_key_context));
if (!context)
return -1;
context->func = destructor;
context->key = TlsAlloc();
if (context->key == 0xFFFFFFFF) {
windows_errno(GetLastError());
free(context);
return -1;
}
/* insert in the list of destructors */
if (context->func)
tommy_list_insert_tail(&windows_key_list, &context->node, context);
*key = context;
return 0;
}
int windows_key_delete(windows_key_t key)
{
struct windows_key_context* context = key;
/* remove from the list of destructors */
if (context->func)
tommy_list_remove_existing(&windows_key_list, &context->node);
TlsFree(context->key);
free(context);
return 0;
}
void* windows_getspecific(windows_key_t key)
{
struct windows_key_context* context = key;
return TlsGetValue(context->key);
}
int windows_setspecific(windows_key_t key, void* value)
{
struct windows_key_context* context = key;
if (!TlsSetValue(context->key, value)) {
windows_errno(GetLastError());
return -1;
}
return 0;
}
struct windows_thread_context {
HANDLE h;
unsigned id;
void* (* func)(void *);
void* arg;
void* ret;
};
/* forwarder to change the function declaration */
static unsigned __stdcall windows_thread_func(void* arg)
{
struct windows_thread_context* context = arg;
tommy_node* i;
context->ret = context->func(context->arg);
/* call the destructor of all the keys */
i = tommy_list_head(&windows_key_list);
while (i) {
struct windows_key_context* key = i->data;
if (key->func) {
void* value = windows_getspecific(key);
if (value)
key->func(value);
}
i = i->next;
}
return 0;
}
int windows_create(thread_id_t* thread, void* attr, void* (* func)(void *), void* arg)
{
struct windows_thread_context* context;
(void)attr;
context = malloc(sizeof(struct windows_thread_context));
if (!context)
return -1;
context->func = func;
context->arg = arg;
context->ret = 0;
context->h = (void*)_beginthreadex(0, 0, windows_thread_func, context, 0, &context->id);
if (context->h == 0) {
free(context);
return -1;
}
*thread = context;
return 0;
}
int windows_join(thread_id_t thread, void** retval)
{
struct windows_thread_context* context = thread;
if (WaitForSingleObject(context->h, INFINITE) != WAIT_OBJECT_0) {
windows_errno(GetLastError());
return -1;
}
if (!CloseHandle(context->h)) {
windows_errno(GetLastError());
return -1;
}
*retval = context->ret;
free(context);
return 0;
}
#endif
snapraid-12.1/cmdline/mingw.h 0000664 0000000 0000000 00000023576 14166610522 0016163 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __PORTABLE_MINGW_H
#define __PORTABLE_MINGW_H
#ifdef __MINGW32__ /* Only for MingW */
#include
/**
* Always assume that the assembler supports SSE2, SSSE3, SSE42 and AVX2 instructions in x86
*/
#ifdef CONFIG_X86
#define HAVE_SSE2 1
#define HAVE_SSSE3 1
#define HAVE_SSE42 1
#define HAVE_AVX2 1
#endif
/****************************************************************************/
/* file */
/**
* Redefines PATH_MAX to allow very long paths.
*/
#undef PATH_MAX
#define PATH_MAX 1024
/* Remap functions and types */
#undef fopen
#define fopen windows_fopen
#undef open
#define open windows_open
#define open_noatime windows_open
#undef stat
#define stat windows_stat
#undef lstat
#define lstat windows_lstat
#undef off_t
#define off_t off64_t
#undef fstat
#define fstat windows_fstat
#define HAVE_FTRUNCATE 1
#undef ftruncate
#define ftruncate windows_ftruncate
#define HAVE_FALLOCATE 1
#undef fallocate
#define fallocate windows_fallocate
#define HAVE_FSYNC 1
#undef fsync
#define fsync windows_fsync
#undef rename
#define rename windows_rename
#undef remove
#define remove windows_remove
#undef mkdir
#define mkdir(a, b) windows_mkdir(a)
#undef rmdir
#define rmdir windows_rmdir
#undef dirent
#define dirent windows_dirent
#undef DIR
#define DIR windows_dir
#undef opendir
#define opendir windows_opendir
#undef readdir
#define readdir windows_readdir
#undef closedir
#define closedir windows_closedir
#define HAVE_FUTIMENS 1
#undef futimens
#define futimens windows_futimens
#define HAVE_UTIMENSAT 1
#define AT_FDCWD -1
#define AT_SYMLINK_NOFOLLOW 1
#undef utimensat
#define utimensat windows_utimensat
#define O_NOFOLLOW 0
#define dirent_hidden windows_dirent_hidden
#define HAVE_STRUCT_DIRENT_D_STAT 1
#undef HAVE_STRUCT_DIRENT_D_INO
#define HAVE_STRUCT_STAT_ST_NLINK 1
#define dirent_lstat windows_dirent_lstat
#define stat_desc windows_stat_desc
#undef sleep
#define sleep windows_sleep
/* 4==DIR, 5,6,7=free, 8==REG */
#define S_IFLNK 0x5000 /* Symbolic link to file */
#define S_ISLNK(m) (((m) & _S_IFMT) == S_IFLNK)
#define S_IFLNKDIR 0x6000 /* Symbolic link to directory */
#define S_ISLNKDIR(m) (((m) & _S_IFMT) == S_IFLNKDIR)
#define S_IFJUN 0x7000 /* Junction */
#define S_ISJUN(m) (((m) & _S_IFMT) == S_IFJUN)
#undef readlink
#define readlink windows_readlink
#undef symlink
#define symlink windows_symlink
#undef link
#define link windows_link
#undef strerror
#define strerror windows_strerror
#undef read
#define read windows_read
#undef write
#define write windows_write
#undef lseek
#define lseek windows_lseek
#undef pread
#define pread windows_pread
#undef pwrite
#define pwrite windows_pwrite
#define direct_size windows_direct_size
#define HAVE_DIRECT_IO 1
#define O_DIRECT 0x10000000
#define O_DSYNC 0x20000000
/**
* If nanoseconds are not supported, we report the special STAT_NSEC_INVALID value,
* to mark that it's undefined.
*/
#define STAT_NSEC_INVALID -1
/* We have nano second support */
#define STAT_NSEC(st) ((int)(st)->st_mtimensec)
/**
* Generic stat information.
*/
struct windows_stat {
uint64_t st_ino;
int64_t st_size;
int64_t st_mtime;
int32_t st_mtimensec;
uint32_t st_mode;
uint32_t st_dev;
uint32_t st_nlink;
int st_hidden;
const char* st_desc;
int st_sync; /**< If the information are in sync with the file-system. */
};
/**
* Like the C fstat().
*/
int windows_fstat(int fd, struct windows_stat* st);
/**
* Like the C lstat(), but with some limitations.
*
* The st_ino field may be 0 if it's not possible to read it in a fast way.
* Specifically this always happens.
*
* In case of hardlinks, the size and the attributes of the file can
* be completely bogus, because changes made by other hardlinks are reported
* in the directory entry only when the file is opened.
*
* MSDN CreateHardLinks
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363860%28v=vs.85%29.aspx
* 'When you create a hard link on the NTFS file system, the file attribute information'
* 'in the directory entry is refreshed only when the file is opened, or when'
* 'GetFileInformationByHandle is called with the handle of a specific file.'
*
* MSDN HardLinks and Junctions
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa365006%28v=vs.85%29.aspx
* 'However, the directory entry size and attribute information is updated only'
* 'for the link through which the change was made.'
*
* Use lstat_sync() to override these limitations.
*/
int windows_lstat(const char* file, struct windows_stat* st);
/**
* Like the C stat().
*/
int windows_stat(const char* file, struct windows_stat* st);
/**
* Like the C mkdir().
*/
int windows_mkdir(const char* file);
/**
* Like rmdir().
*/
int windows_rmdir(const char* file);
/**
* Like the C lstat(), but with some limitations.
*
* This call fills all the st_* fields of the stat struct,
* and if provided the pointer, also the physical offset.
*
* It doesn't work for all kinds of files and directories.
* You must call it only for regular files.
* For example, "C:\System Volume Information" cannot be accessed
* with error ERROR_ACCESS_DENIED.
*
* Note that instead lstat() works for all the files.
*/
#define HAVE_LSTAT_SYNC 1
int lstat_sync(const char* file, struct windows_stat* st, uint64_t* physical);
/**
* Like the C ftruncate().
*/
int windows_ftruncate(int fd, off64_t off);
/**
* Like the C fallocate().
*/
int windows_fallocate(int fd, int mode, off64_t off, off64_t len);
/**
* Like the C fsync().
*/
int windows_fsync(int fd);
/**
* Like the C futimes().
*/
int windows_futimes(int fd, struct timeval tv[2]);
struct windows_timespec {
int64_t tv_sec;
int tv_nsec;
};
#define timespec windows_timespec
/**
* Like the C futimens().
*/
int windows_futimens(int fd, struct windows_timespec tv[2]);
/**
* Like the C utimensat().
*/
int windows_utimensat(int fd, const char* file, struct windows_timespec tv[2], int flags);
/**
* Like the C rename().
*/
int windows_rename(const char* a, const char* b);
/**
* Like the C remove().
*/
int windows_remove(const char* a);
/**
* Like the C fopen().
*/
FILE* windows_fopen(const char* file, const char* mode);
/**
* Like the C open().
*/
int windows_open(const char* file, int flags, ...);
/**
* Like the C dirent.
*/
struct windows_dirent {
char d_name[PATH_MAX];
struct windows_stat d_stat;
};
/**
* Like the C DIR.
*/
struct windows_dir_struct;
typedef struct windows_dir_struct windows_dir;
/**
* Like the C opendir().
*/
windows_dir* windows_opendir(const char* dir);
/**
* Like the C readdir().
*/
struct windows_dirent* windows_readdir(windows_dir* dirstream);
/**
* Like the C closedir().
*/
int windows_closedir(windows_dir* dirstream);
/**
* Convert a dirent record to a lstat record, but with some limitations.
*
* The st_mode field may be 0 if the file is a reparse point.
* Specifically this happens if we are using GetFileInformationByHandleEx()
* to read the directory stream.
*
* The st_ino field may be 0 if it's not possible to read it in a fast way.
* Specifically this happens if we are using FindFirst/FindNext to enumerate
* the directory.
*
* In such cases, call lstat_sync() to fill the missing fields.
*/
void windows_dirent_lstat(const struct windows_dirent* dd, struct windows_stat* st);
/**
* Like dirent_hidden().
*/
int windows_dirent_hidden(struct dirent* dd);
/**
* Like stat_desc().
*/
const char* windows_stat_desc(struct stat* st);
/**
* Like sleep().
*/
void windows_sleep(unsigned seconds);
/**
* Like readlink().
*/
int windows_readlink(const char* file, char* buffer, size_t size);
/**
* Like symlink().
* Return ENOSYS if symlinks are not supported.
*/
int windows_symlink(const char* existing, const char* file);
/**
* Like link().
*/
int windows_link(const char* existing, const char* file);
/**
* Like strerror().
*/
const char* windows_strerror(int err);
/**
* Like read().
*/
ssize_t windows_read(int f, void* buffer, size_t size);
/**
* Like write().
*/
ssize_t windows_write(int f, const void* buffer, size_t size);
/**
* Like lseek().
*/
off_t windows_lseek(int f, off_t offset, int whence);
/**
* Like pread().
*/
ssize_t windows_pread(int f, void* buffer, size_t size, off_t offset);
/**
* Like pwrite().
*/
ssize_t windows_pwrite(int f, const void* buffer, size_t size, off_t offset);
/**
* List direct_size().
*/
size_t windows_direct_size(void);
/****************************************************************************/
/* thread */
/**
* Like the pthread_* equivalent.
*/
int windows_mutex_init(windows_mutex_t* mutex, void* attr);
int windows_mutex_destroy(windows_mutex_t* mutex);
int windows_mutex_lock(windows_mutex_t* mutex);
int windows_mutex_unlock(windows_mutex_t* mutex);
int windows_cond_init(windows_cond_t* cond, void* attr);
int windows_cond_destroy(windows_cond_t* cond);
int windows_cond_signal(windows_cond_t* cond);
int windows_cond_broadcast(windows_cond_t* cond);
int windows_cond_wait(windows_cond_t* cond, windows_mutex_t* mutex);
int windows_key_create(windows_key_t* key, void(* destructor)(void*));
int windows_key_delete(windows_key_t key);
void* windows_getspecific(windows_key_t key);
int windows_setspecific(windows_key_t key, void* value);
int windows_create(thread_id_t* thread, void* attr, void* (* func)(void *), void *arg);
int windows_join(thread_id_t thread, void** retval);
#endif
#endif
snapraid-12.1/cmdline/mkstream.c 0000664 0000000 0000000 00000013051 14166610522 0016643 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2015 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "stream.h"
#include "support.h"
#define STREAM_MAX 8
#define BUFFER_MAX 64
#define STR_MAX 128
void test(void)
{
struct stream* s;
char file[32];
unsigned char buffer[BUFFER_MAX];
char str[STR_MAX];
unsigned i, j;
uint32_t u32 = -1L;
uint64_t u64 = -1LL;
uint32_t put_crc_stored;
uint32_t put_crc_computed;
crc32c_init();
s = sopen_multi_write(STREAM_MAX);
for (i = 0; i < STREAM_MAX; ++i) {
snprintf(file, sizeof(file), "stream%u.bin", i);
remove(file);
if (sopen_multi_file(s, i, file) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 0; j < 256; ++j) {
if (sputc(j, s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 0; j < 32; ++j) {
if (sputb32(u32 >> j, s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 0; j < 64; ++j) {
if (sputb64(u64 >> j, s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 0; j < BUFFER_MAX; ++j) {
memset(buffer, j, j);
if (swrite(buffer, j, s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 1; j < STR_MAX; ++j) {
memset(str, ' ' + j, j - 1);
str[j - 1] = 0;
if (sputbs(str, s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
put_crc_stored = scrc(s);
put_crc_computed = scrc_stream(s);
if (put_crc_stored != put_crc_computed) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (sputble32(put_crc_stored, s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (sclose(s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
for (i = 0; i < STREAM_MAX; ++i) {
uint32_t get_crc_stored;
uint32_t get_crc_computed;
snprintf(file, sizeof(file), "stream%u.bin", i);
s = sopen_read(file);
if (s == 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
for (j = 0; j < 256; ++j) {
int c = sgetc(s);
if (c == EOF || (unsigned char)c != j) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 0; j < 32; ++j) {
uint32_t v32;
if (sgetb32(s, &v32) != 0 || v32 != (u32 >> j)) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 0; j < 64; ++j) {
uint64_t v64;
if (sgetb64(s, &v64) != 0 || v64 != (u64 >> j)) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 1; j < BUFFER_MAX; ++j) {
char copy[BUFFER_MAX];
memset(buffer, j, j);
if (sread(s, copy, j) != 0 || memcmp(copy, buffer, j) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (j = 1; j < STR_MAX; ++j) {
char copy[STR_MAX];
memset(str, ' ' + j, j - 1);
str[j - 1] = 0;
if (sgetbs(s, copy, sizeof(copy)) != 0 || strcmp(copy, str) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* get the computed CRC *before* reading the stored one */
get_crc_computed = scrc(s);
if (sgetble32(s, &get_crc_stored) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (get_crc_stored != put_crc_stored) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (get_crc_stored != get_crc_computed) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (sclose(s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (i = 0; i < STREAM_MAX; ++i) {
uint32_t get_crc_stored;
uint32_t get_crc_computed;
unsigned char buf[4];
snprintf(file, sizeof(file), "stream%u.bin", i);
s = sopen_read(file);
if (s == 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (sdeplete(s, buf) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* get the stored crc from the last four bytes */
get_crc_stored = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
if (get_crc_stored != put_crc_stored) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* get the computed CRC *after* reading the stored one */
get_crc_computed = scrc(s);
/* adjust the stored crc to include itself */
get_crc_stored = crc32c(get_crc_stored, buf, 4);
if (get_crc_stored != get_crc_computed) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (sclose(s) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
int main(void)
{
unsigned i;
lock_init();
for (i = 1; i <= 16; ++i) {
/* test with different stream buffer size */
STREAM_SIZE = i;
printf("Test stream buffer size %u\n", i);
test();
}
return 0;
}
snapraid-12.1/cmdline/mktest.c 0000664 0000000 0000000 00000040061 14166610522 0016330 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
/****************************************************************************/
/* random */
/**
* Pseudo random number generator.
*/
unsigned long long seed = 0;
unsigned rnd(unsigned max)
{
seed = seed * 6364136223846793005LL + 1442695040888963407LL;
return (seed >> 32) % max;
}
unsigned rndnz(unsigned max)
{
if (max <= 1)
return 1;
else
return rnd(max - 1) + 1;
}
void rndnz_range(unsigned char* data, int size)
{
int i;
for (i = 0; i < size; ++i)
data[i] = rndnz(256);
}
void rndnz_damage(unsigned char* data, int size)
{
int i;
/* corrupt ensuring always different data */
for (i = 0; i < size; ++i) {
unsigned char c;
do {
c = rndnz(256);
} while (c == data[i]);
data[i] = c;
}
}
char CHARSET[] = "qwertyuiopasdfghjklzxcvbnm1234567890 .-+";
#define CHARSET_LEN (sizeof(CHARSET) - 1)
void rnd_name(char* file)
{
int l = 1 + rnd(20);
while (l) {
*file++ = CHARSET[rnd(CHARSET_LEN)];
--l;
}
*file = 0;
}
/****************************************************************************/
/* file */
int file_cmp(const void* a, const void* b)
{
return strcmp(a, b);
}
int fallback(int f, struct stat* st)
{
#if HAVE_FUTIMENS
struct timespec tv[2];
#else
struct timeval tv[2];
#endif
int ret;
#if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */
tv[0].tv_sec = st->st_mtime;
if (STAT_NSEC(st) != STAT_NSEC_INVALID)
tv[0].tv_nsec = STAT_NSEC(st);
else
tv[0].tv_nsec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_nsec = tv[0].tv_nsec;
ret = futimens(f, tv);
#elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */
tv[0].tv_sec = st->st_mtime;
if (STAT_NSEC(st) != STAT_NSEC_INVALID)
tv[0].tv_usec = STAT_NSEC(st) / 1000;
else
tv[0].tv_usec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_usec = tv[0].tv_usec;
ret = futimes(f, tv);
#elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
tv[0].tv_sec = st->st_mtime;
if (STAT_NSEC(st) != STAT_NSEC_INVALID)
tv[0].tv_usec = STAT_NSEC(st) / 1000;
else
tv[0].tv_usec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_usec = tv[0].tv_usec;
ret = futimesat(f, 0, tv);
#else
#error No function available to set file timestamps
#endif
return ret;
}
/****************************************************************************/
/* cmd */
/**
* Create a file with random content.
* - If the file exists it's rewritten, but avoiding to truncating it to 0.
*/
void cmd_generate_file(const char* path, int size)
{
unsigned char* data;
int f;
/* remove the existing file/symlink if any */
if (remove(path) != 0) {
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error removing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else {
/* don't truncate files to 0 size to avoid ZERO file size protection */
++size;
}
data = malloc(size);
/* We don't write zero bytes because we want to test */
/* the recovering of new files, after an aborted sync */
/* If the files contains full blocks at zero */
/* this is an impossible condition to recover */
/* because we cannot differentiate between an unused block */
/* and a file filled with 0 */
rndnz_range(data, size);
f = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_NOFOLLOW, 0600);
if (f < 0) {
/* LCOV_EXCL_START */
log_fatal("Error creating file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (write(f, data, size) != size) {
/* LCOV_EXCL_START */
log_fatal("Error writing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (close(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
free(data);
}
/**
* Create a symlink.
* - If the file already exists, it's removed.
*/
void cmd_generate_symlink(const char* path, const char* linkto)
{
/* remove the existing file/symlink if any */
if (remove(path) != 0) {
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error removing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
if (symlink(linkto, path) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error writing symlink %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/**
* Create a file or a symlink with a random name.
*/
void cmd_generate(int disk, int size)
{
char path[PATH_MAX];
char* file;
snprintf(path, sizeof(path), "bench/disk%d/", disk);
file = path + strlen(path);
/* add a directory */
*file++ = 'a' + rnd(2);
*file = 0;
/* create it */
if (mkdir(path, 0777) != 0) {
if (errno != EEXIST) {
/* LCOV_EXCL_START */
log_fatal("Error creating directory %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
*file++ = '/';
while (1) {
/* add a random file */
rnd_name(file);
/* skip some invalid file name, see http://en.wikipedia.org/wiki/Filename */
if (strcmp(file, ".") == 0
|| strcmp(file, "..") == 0
|| strcmp(file, "prn") == 0
|| strcmp(file, "con") == 0
|| strcmp(file, "nul") == 0
|| strcmp(file, "aux") == 0
|| file[0] == ' '
|| file[strlen(file) - 1] == ' '
|| file[strlen(file) - 1] == '.'
) {
continue;
}
break;
}
#ifndef WIN32 /* Windows XP doesn't support symlinks */
if (rnd(32) == 0) {
/* symlink */
char linkto[PATH_MAX];
rnd_name(linkto);
cmd_generate_symlink(path, linkto);
} else
#endif
{
/* file */
cmd_generate_file(path, size);
}
}
/**
* Write a partially a file.
* - The file must exist.
* - The file size is not changed.
* - The written data may be equal or not at the already existing one.
* - If it's a symlink nothing is done.
*/
void cmd_write(const char* path, int size)
{
struct stat st;
if (lstat(path, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (S_ISREG(st.st_mode)) {
unsigned char* data;
off_t off;
int f;
/* not over the end */
if (size > st.st_size)
size = st.st_size;
/* start at random position inside the file */
if (size < st.st_size)
off = rnd(st.st_size - size);
else
off = 0;
data = malloc(size);
rndnz_range(data, size);
f = open(path, O_WRONLY | O_BINARY | O_NOFOLLOW);
if (f < 0) {
/* LCOV_EXCL_START */
log_fatal("Error creating file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (lseek(f, off, SEEK_SET) != off) {
/* LCOV_EXCL_START */
log_fatal("Error seeking file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (write(f, data, size) != size) {
/* LCOV_EXCL_START */
log_fatal("Error writing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (close(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
free(data);
}
}
/**
* Damage a file.
* - The file must exist.
* - The file size is not changed.
* - The written data is SURELY different than the already existing one.
* - The file timestamp is NOT modified.
* - If it's a symlink nothing is done.
*/
void cmd_damage(const char* path, int size)
{
struct stat st;
/* here a 0 size means to change nothing */
/* as also the timestamp should not be changed */
if (!size)
return;
if (lstat(path, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (st.st_size == 0)
return;
if (S_ISREG(st.st_mode)) {
off_t off;
unsigned char* data;
int f;
/* not over the end */
if (size > st.st_size)
size = st.st_size;
/* start at random position inside the file */
if (size < st.st_size)
off = rnd(st.st_size - size);
else
off = 0;
data = malloc(size);
f = open(path, O_RDWR | O_BINARY | O_NOFOLLOW);
if (f < 0) {
/* LCOV_EXCL_START */
log_fatal("Error creating file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (lseek(f, off, SEEK_SET) != off) {
/* LCOV_EXCL_START */
log_fatal("Error seeking file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (read(f, data, size) != size) {
/* LCOV_EXCL_START */
log_fatal("Error writing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
rndnz_damage(data, size);
if (lseek(f, off, SEEK_SET) != off) {
/* LCOV_EXCL_START */
log_fatal("Error seeking file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (write(f, data, size) != size) {
/* LCOV_EXCL_START */
log_fatal("Error writing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (fallback(f, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error setting time for file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (close(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
free(data);
}
}
/**
* Append to a file.
* - The file must exist.
* - If it's a symlink nothing is done.
*/
void cmd_append(const char* path, int size)
{
struct stat st;
if (lstat(path, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (S_ISREG(st.st_mode)) {
unsigned char* data;
int f;
data = malloc(size);
rndnz_range(data, size);
f = open(path, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW);
if (f < 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (write(f, data, size) != size) {
/* LCOV_EXCL_START */
log_fatal("Error writing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (close(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
free(data);
}
}
/**
* Truncate a file.
* - The file must exist.
* - The file is NEVER truncated to 0.
* - If it's a symlink nothing is done.
*/
void cmd_truncate(const char* path, int size)
{
struct stat st;
if (lstat(path, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (S_ISREG(st.st_mode)) {
off_t off;
int f;
/* if file is empty, just rewrite it */
if (st.st_size == 0) {
size = 0;
} else {
/* don't truncate files to 0 size to avoid ZERO file size protection */
if (size >= st.st_size)
size = st.st_size - 1;
}
off = st.st_size - size;
f = open(path, O_WRONLY | O_BINARY | O_NOFOLLOW);
if (f < 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (ftruncate(f, off) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error truncating file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (close(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
/**
* Delete a file.
* - The file must exist.
*/
void cmd_delete(const char* path)
{
if (remove(path) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error removing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/**
* Change a file. Or deleted/truncated/append/created.
* - The file must exist.
*/
void cmd_change(const char* path, int size)
{
struct stat st;
if (!size)
return;
if (lstat(path, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (S_ISLNK(st.st_mode)) {
/* symlink */
if (rnd(2) == 0) {
/* delete */
if (remove(path) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error removing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else {
/* recreate */
char linkto[PATH_MAX];
if (remove(path) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error removing %s\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
rnd_name(linkto);
cmd_generate_symlink(path, linkto);
}
} else if (S_ISREG(st.st_mode)) {
int r;
r = rnd(4);
if (r == 0) {
cmd_write(path, size);
} else if (r == 1) {
cmd_append(path, size);
} else if (r == 2) {
cmd_truncate(path, size);
} else {
cmd_delete(path);
}
}
}
void help(void)
{
printf("Test for " PACKAGE " v" VERSION " by Andrea Mazzoleni, " PACKAGE_URL "\n");
printf("Usage:\n");
printf("\tmktest generate SEED DISK_NUM FILE_NUM FILE_SIZE\n");
printf("\tmktest damage SEED NUM SIZE FILE\n");
printf("\tmktest write SEED NUM SIZE FILE\n");
printf("\tmktest change SEED SIZE FILE\n");
printf("\tmktest append SEED SIZE FILE\n");
printf("\tmktest truncate SEED SIZE FILE\n");
}
int main(int argc, char* argv[])
{
int i, j, b;
lock_init();
if (argc < 2) {
help();
exit(EXIT_SUCCESS);
}
if (strcmp(argv[1], "generate") == 0) {
int disk, file, size;
if (argc != 6) {
/* LCOV_EXCL_START */
help();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
seed = atoi(argv[2]);
disk = atoi(argv[3]);
file = atoi(argv[4]);
size = atoi(argv[5]);
for (i = 0; i < disk; ++i) {
for (j = 0; j < file; ++j) {
if (j == 0)
/* create at least a big one */
cmd_generate(i + 1, size);
else if (j == 1)
/* create at least an empty one */
cmd_generate(i + 1, 0);
else
cmd_generate(i + 1, rnd(size));
}
}
} else if (strcmp(argv[1], "write") == 0) {
int fail, size;
if (argc < 6) {
/* LCOV_EXCL_START */
help();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
seed = atoi(argv[2]);
fail = atoi(argv[3]);
size = atoi(argv[4]);
b = 5;
/* sort the file names */
qsort(&argv[b], argc - b, sizeof(argv[b]), file_cmp);
for (i = b; i < argc; ++i)
for (j = 0; j < fail; ++j)
cmd_write(argv[i], rndnz(size));
} else if (strcmp(argv[1], "damage") == 0) {
int fail, size;
if (argc < 6) {
/* LCOV_EXCL_START */
help();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
seed = atoi(argv[2]);
fail = atoi(argv[3]);
size = atoi(argv[4]);
b = 5;
/* sort the file names */
qsort(&argv[b], argc - b, sizeof(argv[b]), file_cmp);
for (i = b; i < argc; ++i)
for (j = 0; j < fail; ++j)
cmd_damage(argv[i], rndnz(size)); /* at least one byte */
} else if (strcmp(argv[1], "append") == 0) {
int size;
if (argc < 5) {
/* LCOV_EXCL_START */
help();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
seed = atoi(argv[2]);
size = atoi(argv[3]);
b = 4;
/* sort the file names */
qsort(&argv[b], argc - b, sizeof(argv[b]), file_cmp);
for (i = b; i < argc; ++i)
cmd_append(argv[i], rndnz(size)); /* at least one byte */
} else if (strcmp(argv[1], "truncate") == 0) {
int size;
if (argc < 5) {
/* LCOV_EXCL_START */
help();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
seed = atoi(argv[2]);
size = atoi(argv[3]);
b = 4;
/* sort the file names */
qsort(&argv[b], argc - b, sizeof(argv[b]), file_cmp);
for (i = b; i < argc; ++i)
cmd_truncate(argv[i], rnd(size));
} else if (strcmp(argv[1], "change") == 0) {
int size;
if (argc < 5) {
/* LCOV_EXCL_START */
help();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
seed = atoi(argv[2]);
size = atoi(argv[3]);
b = 4;
/* sort the file names */
qsort(&argv[b], argc - b, sizeof(argv[b]), file_cmp);
for (i = b; i < argc; ++i)
cmd_change(argv[i], rnd(size));
} else {
/* LCOV_EXCL_START */
help();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
lock_done();
return 0;
}
snapraid-12.1/cmdline/murmur3.c 0000664 0000000 0000000 00000013065 14166610522 0016437 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* Derivative work from MurmorHash3.cpp revision r136
*
* SMHasher & MurmurHash
* http://code.google.com/p/smhasher/
*
* Exact source used as reference:
* http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp?spec=svn136&r=136
*/
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
/* Finalization mix - force all bits of a hash block to avalanche */
static inline uint32_t fmix32(uint32_t h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
/*
* Warning!
* Don't declare these variables static, otherwise the gcc optimizer
* may generate very slow code for multiplication with these constants,
* like:
-> .cpp
k1 *= c1;
-> .asm
152: 8d 14 80 lea (%eax,%eax,4),%edx
155: 8d 14 90 lea (%eax,%edx,4),%edx
158: c1 e2 03 shl $0x3,%edx
15b: 29 c2 sub %eax,%edx
15d: 8d 14 d2 lea (%edx,%edx,8),%edx
160: 8d 14 90 lea (%eax,%edx,4),%edx
163: 8d 14 d0 lea (%eax,%edx,8),%edx
166: 8d 14 90 lea (%eax,%edx,4),%edx
169: 8d 14 50 lea (%eax,%edx,2),%edx
16c: 8d 14 90 lea (%eax,%edx,4),%edx
16f: 8d 14 92 lea (%edx,%edx,4),%edx
172: 8d 14 50 lea (%eax,%edx,2),%edx
175: 8d 04 d0 lea (%eax,%edx,8),%eax
178: 8d 14 c5 00 00 00 00 lea 0x0(,%eax,8),%edx
17f: 29 d0 sub %edx,%eax
* resulting in speeds of 500 MB/s instead of 3000 MB/s.
*
* Verified with gcc 4.4.4 compiling with :
*
* g++ -g -c -O2 MurmurHash3.cpp -o MurmurHash3.o
*/
uint32_t c1 = 0x239b961b;
uint32_t c2 = 0xab0e9789;
uint32_t c3 = 0x38b34ae5;
uint32_t c4 = 0xa1e38b93;
void MurmurHash3_x86_128(const void* data, size_t size, const uint8_t* seed, void* digest)
{
size_t nblocks;
const uint32_t* blocks;
const uint32_t* end;
size_t size_remainder;
uint32_t h1, h2, h3, h4;
h1 = util_read32(seed + 0);
h2 = util_read32(seed + 4);
h3 = util_read32(seed + 8);
h4 = util_read32(seed + 12);
nblocks = size / 16;
blocks = data;
end = blocks + nblocks * 4;
/* body */
while (blocks < end) {
uint32_t k1 = blocks[0];
uint32_t k2 = blocks[1];
uint32_t k3 = blocks[2];
uint32_t k4 = blocks[3];
#if WORDS_BIGENDIAN
k1 = util_swap32(k1);
k2 = util_swap32(k2);
k3 = util_swap32(k3);
k4 = util_swap32(k4);
#endif
k1 *= c1; k1 = util_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
h1 = util_rotl32(h1, 19); h1 += h2; h1 = h1 * 5 + 0x561ccd1b;
k2 *= c2; k2 = util_rotl32(k2, 16); k2 *= c3; h2 ^= k2;
h2 = util_rotl32(h2, 17); h2 += h3; h2 = h2 * 5 + 0x0bcaa747;
k3 *= c3; k3 = util_rotl32(k3, 17); k3 *= c4; h3 ^= k3;
h3 = util_rotl32(h3, 15); h3 += h4; h3 = h3 * 5 + 0x96cd1c35;
k4 *= c4; k4 = util_rotl32(k4, 18); k4 *= c1; h4 ^= k4;
h4 = util_rotl32(h4, 13); h4 += h1; h4 = h4 * 5 + 0x32ac3b17;
blocks += 4;
}
/* tail */
size_remainder = size & 15;
if (size_remainder != 0) {
const uint8_t* tail = (const uint8_t*)blocks;
uint32_t k1 = 0;
uint32_t k2 = 0;
uint32_t k3 = 0;
uint32_t k4 = 0;
switch (size_remainder) {
case 15 : k4 ^= (uint32_t)tail[14] << 16; /* fallthrough */
case 14 : k4 ^= (uint32_t)tail[13] << 8; /* fallthrough */
case 13 : k4 ^= (uint32_t)tail[12] << 0; /* fallthrough */
k4 *= c4; k4 = util_rotl32(k4, 18); k4 *= c1; h4 ^= k4;
/* fallthrough */
case 12 : k3 ^= (uint32_t)tail[11] << 24; /* fallthrough */
case 11 : k3 ^= (uint32_t)tail[10] << 16; /* fallthrough */
case 10 : k3 ^= (uint32_t)tail[ 9] << 8; /* fallthrough */
case 9 : k3 ^= (uint32_t)tail[ 8] << 0; /* fallthrough */
k3 *= c3; k3 = util_rotl32(k3, 17); k3 *= c4; h3 ^= k3;
/* fallthrough */
case 8 : k2 ^= (uint32_t)tail[ 7] << 24; /* fallthrough */
case 7 : k2 ^= (uint32_t)tail[ 6] << 16; /* fallthrough */
case 6 : k2 ^= (uint32_t)tail[ 5] << 8; /* fallthrough */
case 5 : k2 ^= (uint32_t)tail[ 4] << 0; /* fallthrough */
k2 *= c2; k2 = util_rotl32(k2, 16); k2 *= c3; h2 ^= k2;
/* fallthrough */
case 4 : k1 ^= (uint32_t)tail[ 3] << 24; /* fallthrough */
case 3 : k1 ^= (uint32_t)tail[ 2] << 16; /* fallthrough */
case 2 : k1 ^= (uint32_t)tail[ 1] << 8; /* fallthrough */
case 1 : k1 ^= (uint32_t)tail[ 0] << 0; /* fallthrough */
k1 *= c1; k1 = util_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
/* fallthrough */
}
}
/* finalization */
h1 ^= size; h2 ^= size; h3 ^= size; h4 ^= size;
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
h1 = fmix32(h1);
h2 = fmix32(h2);
h3 = fmix32(h3);
h4 = fmix32(h4);
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
util_write32(digest + 0, h1);
util_write32(digest + 4, h2);
util_write32(digest + 8, h3);
util_write32(digest + 12, h4);
}
snapraid-12.1/cmdline/murmur3test.c 0000664 0000000 0000000 00000473067 14166610522 0017353 0 ustar 00root root 0000000 0000000 { "", 0, { 0x6d, 0xc8, 0xcf, 0x99, 0x79, 0xda, 0x82, 0x0b, 0x9d, 0xd0, 0x02, 0x56, 0x0e, 0x6a, 0x28, 0x0a } },
{ "a", 1, { 0x83, 0xa4, 0xc1, 0x6e, 0x1f, 0xcb, 0x8b, 0x30, 0x26, 0x59, 0x53, 0x64, 0x3b, 0x3f, 0xc1, 0xda } },
{ "abc", 3, { 0xb3, 0xfc, 0x85, 0x98, 0x5b, 0xe6, 0x5a, 0xa2, 0x4b, 0xe9, 0x91, 0xee, 0x71, 0x9f, 0x9f, 0x8d } },
{ "message digest", 14, { 0x20, 0xa2, 0x19, 0x39, 0xec, 0x22, 0x47, 0x6d, 0xe2, 0xec, 0x49, 0x9d, 0xc0, 0xd9, 0x9a, 0x3e } },
{ "abcdefghijklmnopqrstuvwxyz", 26, { 0xde, 0x14, 0xd1, 0x23, 0x69, 0xa3, 0x51, 0x40, 0xc2, 0x05, 0x6c, 0x02, 0xb1, 0xa5, 0x57, 0xf7 } },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, { 0xc9, 0x76, 0x9b, 0xc1, 0xaf, 0x21, 0x57, 0xbc, 0x1a, 0x37, 0xa3, 0xd0, 0xc5, 0x3e, 0x0c, 0xa7 } },
{ "The quick brown fox jumps over the lazy dog", 43, { 0x50, 0x2a, 0xbe, 0xaf, 0x34, 0xa9, 0x5a, 0x3d, 0x23, 0x32, 0x5f, 0x35, 0xf4, 0xbb, 0xae, 0xb6 } },
{ "\x00", 1, { 0xe6, 0x32, 0x4f, 0xfc, 0x34, 0xec, 0x2e, 0xa6, 0xd2, 0x78, 0x69, 0x5d, 0x02, 0x5a, 0x13, 0xf2 } },
{ "\x16\x27", 2, { 0x6f, 0xa3, 0xa2, 0x16, 0xbf, 0x09, 0x0c, 0x4c, 0xc7, 0xca, 0xc2, 0xbc, 0xd7, 0xb4, 0xed, 0xd4 } },
{ "\xe2\x56\xb4", 3, { 0xc1, 0x5f, 0x9c, 0x8d, 0x8b, 0xeb, 0x7a, 0x1e, 0xc3, 0xd2, 0x30, 0xb6, 0xc1, 0x45, 0x4b, 0xee } },
{ "\xc9\x4d\x9c\xda", 4, { 0x00, 0x7b, 0x90, 0x9a, 0x99, 0xbd, 0xc8, 0x6b, 0x70, 0x54, 0x3b, 0x17, 0xfa, 0xae, 0x7c, 0xca } },
{ "\x79\xf1\x29\x69\x5d", 5, { 0xb3, 0x8c, 0x03, 0x90, 0x92, 0x18, 0x1d, 0x76, 0xfe, 0x37, 0xb2, 0xb2, 0x49, 0x8f, 0x84, 0x5e } },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, { 0x0b, 0x4f, 0x0a, 0xa5, 0x2b, 0xaf, 0x0c, 0x3b, 0x6f, 0x80, 0xaa, 0xd8, 0xfb, 0x25, 0x09, 0xbf } },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, { 0xb6, 0xf1, 0x94, 0x8a, 0x57, 0x87, 0xfa, 0xe4, 0x18, 0x79, 0xab, 0x38, 0x5c, 0x4b, 0xc8, 0x5d } },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, { 0x9a, 0x13, 0x04, 0x08, 0x73, 0xb3, 0xe7, 0xd8, 0x22, 0xcb, 0x09, 0x11, 0xda, 0xce, 0xc2, 0x8b } },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, { 0x01, 0xd2, 0x9a, 0x1a, 0x6f, 0x81, 0x70, 0x76, 0xac, 0x74, 0xe9, 0xbc, 0x5e, 0x70, 0x76, 0xcb } },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, { 0x06, 0xd3, 0x6f, 0x5a, 0x8f, 0x86, 0x3b, 0xdd, 0x46, 0xa1, 0xa1, 0x5f, 0x75, 0xf0, 0x8f, 0xa3 } },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, { 0x16, 0x2f, 0xc0, 0x17, 0x48, 0x37, 0xdc, 0xe0, 0x06, 0xa1, 0x78, 0xd5, 0xa8, 0xb7, 0xae, 0x2f } },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, { 0xc0, 0xa5, 0x2e, 0xda, 0x4e, 0x6c, 0xed, 0x9f, 0x3e, 0x36, 0x79, 0xac, 0x5d, 0x65, 0xb6, 0x88 } },
{ "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 13, { 0xda, 0xa3, 0x53, 0xaa, 0x70, 0xb5, 0xa2, 0xcc, 0x01, 0x60, 0xbf, 0x90, 0x60, 0x76, 0x42, 0x15 } },
{ "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 14, { 0xa9, 0x13, 0x46, 0x19, 0xc6, 0x8f, 0xed, 0x22, 0xde, 0xbf, 0x77, 0xbd, 0xfb, 0x61, 0xa4, 0x0d } },
{ "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 15, { 0xe3, 0x99, 0xd9, 0x33, 0xc9, 0xc6, 0xf5, 0x16, 0xdf, 0x60, 0x39, 0x1a, 0xe7, 0x56, 0x3e, 0x30 } },
{ "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 16, { 0x92, 0x65, 0x17, 0x2d, 0xa5, 0xfb, 0x6d, 0x60, 0xdc, 0xd0, 0xce, 0x45, 0x52, 0x63, 0xad, 0x13 } },
{ "\x59\xa9\x9f\xa6\xdb\xb7\x02\xc5\x95\x65\x34\x17\xde\xe5\xbb\xdf\xc5", 17, { 0x6d, 0xae, 0x56, 0xbf, 0xa2, 0x8b, 0x0a, 0x62, 0x3c, 0xf7, 0x12, 0x95, 0xe0, 0x3d, 0x9e, 0xa4 } },
{ "\x0d\x52\x83\x32\x6a\x92\xf9\x9a\x6e\x3c\x3d\x5e\xc4\x25\xfe\xc1\xc4\xbe", 18, { 0xf2, 0x00, 0xea, 0x7b, 0x07, 0xe8, 0xf4, 0xf7, 0xbc, 0x36, 0xd5, 0x3b, 0x4a, 0x35, 0x01, 0x66 } },
{ "\x59\x84\x02\x3f\xbc\x20\x01\x70\xed\xa0\x05\x14\x23\x18\x06\xf2\x52\xc5\xc1", 19, { 0xfc, 0x1c, 0x08, 0x2b, 0x38, 0x0b, 0x86, 0xf9, 0x62, 0x01, 0x50, 0x17, 0x13, 0x70, 0x86, 0xe8 } },
{ "\x81\x84\xc3\xe8\x2f\x63\x65\x79\x4e\xd3\x34\x2c\x9c\xbc\x87\x05\x6d\xe5\xbc\x0a", 20, { 0xfb, 0xb8, 0x1e, 0xc4, 0x33, 0x0c, 0x81, 0x8b, 0x9f, 0x93, 0xf7, 0x33, 0xe4, 0xdf, 0x12, 0x0c } },
{ "\x77\xfc\x10\x0f\x3a\xb2\x20\xad\x0a\x03\xfd\x51\xba\x93\xe6\x70\xe7\x34\xa4\xd8\xde", 21, { 0x09, 0x77, 0x2c, 0xd4, 0xc8, 0x21, 0xce, 0xf3, 0x15, 0xee, 0x0a, 0xef, 0x1b, 0xdf, 0x79, 0x31 } },
{ "\x84\x87\x22\x2b\xb3\xf8\x44\xbf\x63\xbb\x43\xbd\xa8\xc4\x05\xe1\x2f\xb2\x44\x8d\x7a\xec", 22, { 0xc7, 0xb8, 0x65, 0x7e, 0xbc, 0x87, 0x1f, 0xbb, 0x56, 0x47, 0xde, 0x82, 0x6d, 0x23, 0xdc, 0x23 } },
{ "\x9f\x72\x49\x57\xca\xd7\x15\xb1\xe6\x89\xb5\x8d\xec\x5f\x24\xeb\x42\x69\x5a\x73\x70\xb5\x56", 23, { 0xc0, 0x92, 0x04, 0xcb, 0x56, 0x1c, 0x82, 0x54, 0x66, 0xff, 0x4d, 0x02, 0xa4, 0x75, 0xfb, 0xab } },
{ "\x4e\xec\xbd\x3d\xa2\x11\x70\x9c\xa8\x2e\xdb\xca\x6b\xbb\x74\x69\x1e\xa7\x03\x0b\x1b\xcd\x2e\xf0", 24, { 0x96, 0x49, 0x0a, 0x9c, 0xef, 0x7d, 0xe0, 0x15, 0x4d, 0x76, 0x7a, 0x39, 0x7e, 0x2d, 0xe8, 0x58 } },
{ "\x74\x84\x9f\xed\x38\x55\xd4\x69\x44\xc8\x82\x82\xc2\x57\xa7\x4d\x43\x84\x2b\x3a\x75\x06\x32\x95\x81", 25, { 0x7f, 0xaa, 0x9d, 0xbf, 0xa5, 0xb8, 0xad, 0xe4, 0xaa, 0x1e, 0x11, 0xb3, 0x83, 0x59, 0xec, 0x8f } },
{ "\x1e\x01\x28\x03\x09\x8a\xfe\xa7\x8e\x95\x42\x5d\xb7\x8d\x46\x38\x9c\xe5\xa1\xe8\x5a\x25\x70\xd2\x23\x95", 26, { 0xb2, 0x19, 0xf5, 0x35, 0xe2, 0x17, 0x94, 0xd8, 0x0c, 0x5c, 0x03, 0x78, 0x66, 0xd3, 0xe8, 0x94 } },
{ "\xbc\xc9\x70\x84\xfc\x6c\x51\x35\xb1\x1c\xe4\x67\x2f\x97\xe4\x80\x7c\x36\x59\x55\x51\xbf\x6d\x98\x3d\xe8\x5f", 27, { 0x79, 0xca, 0xd3, 0xb3, 0x5d, 0xee, 0x4d, 0xa4, 0x00, 0xa9, 0x5c, 0x20, 0x3b, 0x19, 0x7d, 0x16 } },
{ "\xdf\xe9\x69\x90\x4d\x76\xbc\xbb\xdf\x03\x74\x42\x55\x4a\x37\xa3\xba\x6a\x5a\x09\x92\xbf\x17\xff\xa0\xed\x6d\x3f", 28, { 0x7c, 0xca, 0x53, 0x89, 0xcd, 0x37, 0x11, 0x06, 0xf9, 0xd0, 0x8e, 0x41, 0x17, 0xd7, 0x42, 0xe4 } },
{ "\x51\xfc\x95\xa9\xc8\x9d\x1c\x4f\x87\x8b\xa2\xad\xb7\x0d\x2d\xf6\x14\x98\x2b\x89\x77\x91\x02\x83\x01\x2f\x56\x6e\xe1", 29, { 0xdb, 0x92, 0x9e, 0xe8, 0x1e, 0xf7, 0x3a, 0xc6, 0xfc, 0x66, 0x8b, 0x7f, 0x3b, 0x77, 0x56, 0x64 } },
{ "\x7f\xd6\x16\xc3\x81\xc3\x7c\xd6\x70\xff\xe4\x77\x1f\xcd\x09\x7f\x7f\x2b\x71\x26\x3d\xc9\xdb\x92\x88\xa5\xd4\x00\xf0\x44", 30, { 0x6d, 0x2e, 0x0a, 0x81, 0xbc, 0x6a, 0xd7, 0x2d, 0x04, 0xba, 0x4a, 0xcc, 0x0b, 0x3b, 0xc9, 0xf8 } },
{ "\x68\x8e\x6a\x8e\xf6\xa2\x70\x47\x1d\xfb\x45\x26\xd2\x52\x56\x94\x94\xac\xbc\x02\xb6\x3f\xde\xe7\xdb\xfe\x34\x55\x81\xc3\x26", 31, { 0xe3, 0xc8, 0xa6, 0x56, 0x3f, 0x8c, 0x6e, 0x64, 0x11, 0xb3, 0x8a, 0x09, 0x95, 0xcf, 0xa0, 0xe1 } },
{ "\x37\xb3\x18\x13\x29\xe2\xa2\x6d\xf4\xce\x2b\x01\xa5\x9f\x4b\x54\x48\x10\xb1\x29\x46\xcb\x13\x20\x58\xcf\xb0\x78\x27\x0d\x7e\xf5", 32, { 0xe0, 0x0a, 0x19, 0xff, 0x6e, 0xee, 0x06, 0x88, 0xea, 0x38, 0x6b, 0xc8, 0x68, 0x6a, 0x94, 0xa2 } },
{ "\x82\x13\xf6\xdd\x3b\xdf\x78\x1c\x9e\xd6\x5b\x87\x8f\xcd\x95\x3d\x3f\x43\x04\x2a\x8b\xb2\x57\xa5\xf1\xfa\x9c\x39\x2f\xfe\x66\x81\x7a", 33, { 0x2b, 0xba, 0x42, 0x8d, 0xb0, 0x96, 0xcd, 0x0c, 0xc3, 0x6a, 0x05, 0x7c, 0x81, 0x42, 0x6f, 0x4c } },
{ "\x4a\x9a\x2c\x58\xf1\xd6\x21\x1a\x76\x7f\xbc\xfa\xe0\x66\x35\xcd\xf1\x17\x22\x64\x6f\xbd\x13\x85\xa1\xb5\x5b\x81\xd6\xad\xee\x72\x1e\x79", 34, { 0x20, 0xdf, 0x47, 0x95, 0xc3, 0x0d, 0x81, 0x29, 0xdc, 0xd2, 0xf7, 0x28, 0x7c, 0xf8, 0xef, 0xa9 } },
{ "\x66\x13\x1c\x72\x20\x3c\xf3\x8b\x83\xf8\x5e\xf2\x60\x44\xdc\x5e\x75\x67\x08\xfb\x0c\xe9\x07\xf0\xc1\x31\x1a\x89\x60\xbf\x53\x06\xed\x02\x64", 35, { 0xe9, 0x2a, 0x0c, 0x85, 0xe6, 0xaa, 0x86, 0x00, 0xdd, 0xb1, 0x3e, 0x0f, 0xa4, 0xd2, 0x5d, 0xa1 } },
{ "\xff\xbd\xd6\x92\x72\xc1\x9e\xc9\x6b\xe3\xfb\xca\x4e\x88\x26\x7f\xc4\x36\xf0\x70\x40\x4f\x53\x4d\x2d\xfc\xb3\xab\xb6\x25\xb7\xcc\x31\x9c\xbf\x97", 36, { 0x90, 0x40, 0x16, 0xeb, 0xb9, 0x97, 0x22, 0xd2, 0x28, 0x99, 0xd8, 0x87, 0x57, 0x71, 0x58, 0x9c } },
{ "\x29\x59\x01\xa6\x06\xe0\x43\x7e\x5b\xbf\x37\xda\xcc\x33\x6e\x20\x9a\xeb\xfa\xf5\xcd\xe4\xa5\xec\xfd\x73\xc7\x59\xbc\x61\xc8\x44\xa3\x30\x33\x79\xf8", 37, { 0xf4, 0x09, 0x5a, 0x82, 0x2b, 0x32, 0x80, 0x9a, 0x0a, 0x71, 0x32, 0x74, 0x57, 0x18, 0xc5, 0x40 } },
{ "\x7c\x72\xbd\xf9\x7d\x2c\x1b\xc8\xa9\x38\x5c\xf2\x76\x3c\x94\x9d\x3d\xe7\xd9\x4e\x3a\xc7\x0f\x55\xb0\xc7\xdf\xd5\x29\x49\xc6\x74\xdb\xdc\x49\x9b\x27\x9a", 38, { 0x12, 0x88, 0x65, 0xe1, 0x77, 0x1a, 0x72, 0xd0, 0x39, 0xc4, 0xcd, 0xc6, 0x21, 0x0e, 0xa5, 0x13 } },
{ "\x46\x6a\x3b\x21\x66\x3c\xd6\x2c\xaf\xd2\x2b\xcd\x36\x1e\xdf\x49\xf2\xae\x2d\x8f\xab\xb3\x29\x57\x10\xae\x22\xd4\xe8\xb6\x20\x15\x61\x68\x8c\xfd\x85\x6b\xef", 39, { 0x31, 0xe0, 0x8f, 0xd8, 0xfd, 0x6b, 0x31, 0xa2, 0xc4, 0x03, 0x70, 0xb9, 0xe1, 0x0d, 0x95, 0x16 } },
{ "\xdd\xca\x9e\xd3\xab\xc8\xe1\xc5\xe2\x05\x7d\x7c\xf5\xec\x31\x14\x71\xdd\xda\x73\xae\x5e\xbd\xcd\x31\x6e\x74\x42\xa1\xfa\x74\xa4\x64\x69\x37\x57\xd6\x52\x0a\x51", 40, { 0x44, 0xa4, 0x8b, 0x8f, 0x05, 0x9d, 0x67, 0xeb, 0x05, 0xac, 0xed, 0x43, 0x92, 0x50, 0x91, 0xf9 } },
{ "\x26\x4d\x75\x42\xcc\xe7\x42\xcd\xaa\xaf\x2c\xf3\xbf\x6d\x26\x91\x82\x72\x44\x00\xe1\x0b\x4a\xda\xd5\x0f\xd0\xdc\xa2\x98\x0e\xe7\xa8\xce\x4a\x21\xe9\xdf\xdb\x38\x18", 41, { 0xae, 0x76, 0x17, 0x96, 0x45, 0x61, 0xbb, 0x31, 0xbb, 0xf5, 0xc4, 0x6e, 0xe9, 0xc6, 0x22, 0xf8 } },
{ "\x0b\xd8\xc7\x5a\xdb\x62\xd1\x9c\x71\x8b\x69\x90\xfb\xe2\x74\x45\x41\x9d\x87\x61\x5e\x61\xea\xfe\x8c\xb0\xbe\x1f\x18\xef\x3a\xce\x74\x13\x11\xd3\x6b\xe8\xf0\x12\x6f\xfe", 42, { 0x07, 0xd5, 0xac, 0x77, 0x53, 0xc4, 0x8c, 0xd9, 0xb4, 0xec, 0x2b, 0xa5, 0xa5, 0x57, 0x45, 0xaf } },
{ "\x23\x82\x0e\x30\x57\xc6\x83\xd2\x7a\x69\x69\x76\xbf\xbb\x8b\x5b\xbd\x42\xd6\x73\xb7\xdf\x70\x10\xf6\x7f\xe1\x2a\x53\x7a\x6f\x4f\xd1\x8b\x89\x27\x66\x6d\x59\xa3\xdb\x0e\x7c", 43, { 0x52, 0x80, 0xcc, 0x28, 0xb7, 0x1b, 0x2d, 0x6f, 0xcd, 0x73, 0xe1, 0xd7, 0xa9, 0xd0, 0xe6, 0xb4 } },
{ "\xc2\xc9\x72\xce\xff\xc3\x27\x5e\x5b\x92\x60\x81\x02\x9b\xb1\x02\x9d\x5d\x66\x77\x33\x17\x80\xaf\xbc\x86\x4d\x47\x44\xcc\xa3\x5b\xb9\x6c\xb9\x5a\x51\x72\xa1\xf3\x9a\xb0\x66\x2c", 44, { 0xe3, 0x96, 0x4f, 0xe1, 0x37, 0xb0, 0x7a, 0xc6, 0xeb, 0xbe, 0x59, 0x4f, 0xfa, 0xaa, 0x95, 0xf1 } },
{ "\x1f\x22\xbe\x91\xb4\x73\x6c\x89\x5f\x99\x35\xc4\xe3\x9b\x7c\xd0\xea\x73\x7c\xcb\xce\x0e\x22\x33\x04\x6f\x85\xd9\x25\xf4\x03\xf4\x9b\xb1\xf9\x52\xe4\xf8\x1e\xa9\x86\xfb\x7d\x0c\x8f", 45, { 0x6f, 0xb0, 0xa2, 0x69, 0x84, 0x9d, 0x97, 0x2b, 0x05, 0x8d, 0x15, 0x87, 0x7b, 0x2b, 0xa3, 0xc0 } },
{ "\x88\x0d\x79\xc8\xf2\xee\x8f\x76\x10\x42\x5d\xcc\x5f\xa6\x55\xf0\x43\x4c\x5f\xa8\x6b\xb7\x0a\xa1\x51\x11\xdd\x5c\xe1\x2c\xcc\xb6\x31\x13\xc6\x12\x6f\x4c\x6b\x24\xd9\xae\xdb\x5d\xe4\xd9", 46, { 0x8c, 0x08, 0x81, 0x71, 0xd6, 0xf1, 0x21, 0x47, 0xe1, 0xac, 0x8a, 0xed, 0x99, 0xa2, 0x4c, 0x1f } },
{ "\xaa\xee\x80\x55\xef\x7b\x73\x4a\x7c\x5e\xe2\xd9\x27\x95\xef\x17\x47\x49\x7b\xa4\x3b\xfc\x0c\xeb\x24\x4e\xca\xf8\xb2\x6b\xa4\xf0\x1c\x14\x90\x32\x29\x49\xef\x09\x6c\x91\x0e\x0a\x8d\xe5\xc9", 47, { 0xca, 0x4d, 0x13, 0x4c, 0xb6, 0xdf, 0xee, 0x5b, 0x9f, 0xb9, 0x05, 0x0d, 0xb6, 0x91, 0x7d, 0xd7 } },
{ "\x11\xc1\x3f\x4a\xd2\x5b\xba\xef\x1e\x7c\x66\x4a\xb5\xe6\xaa\x41\xc2\xef\x56\x56\xf7\x91\x45\x01\x45\x9e\xab\x3a\x38\x10\x01\x13\xee\x7f\x8a\x81\xb2\xab\x80\x3b\x39\x39\xe0\xba\x78\x7b\xcf\x61", 48, { 0xf5, 0x57, 0x8d, 0x27, 0xa0, 0xa1, 0xcb, 0xc7, 0xe8, 0x2f, 0x77, 0xec, 0xdd, 0xa3, 0x84, 0xf7 } },
{ "\x20\x68\xa7\xb7\xb8\xab\xa3\xcf\x55\xd7\x23\xc9\xf7\xb7\x9b\x71\x83\xc3\x1e\x04\x59\xaf\x83\x13\x91\x1e\x31\x81\xd7\x75\x8d\xa6\xe0\xca\xfc\x96\x88\xfa\x97\x7c\x8a\xd9\x6b\x1f\xdb\x85\x69\x87\xa3", 49, { 0x94, 0x10, 0x45, 0x76, 0xe9, 0xd7, 0x75, 0xce, 0x30, 0xcb, 0x28, 0xb7, 0xbc, 0xc0, 0x98, 0x94 } },
{ "\xd4\xf1\xb9\xd9\xeb\xf5\x9d\x7a\xf0\xcd\x01\x65\xd7\x98\xb6\xd6\x59\x49\xd8\x7d\x03\x55\x2c\x3a\x6b\xf7\xa1\x78\x7d\x1e\xf9\x23\xf3\xf5\x81\x47\xe3\x0c\xfc\x46\x72\x28\x9e\xb6\xa6\xa4\x34\xd5\x5a\x81", 50, { 0x8f, 0x3d, 0x45, 0x4f, 0x31, 0xdc, 0xf9, 0xdc, 0x48, 0xbe, 0x95, 0x53, 0x80, 0xf1, 0xc0, 0x74 } },
{ "\x61\xa9\x4a\x12\x02\xb8\x4e\x3d\xb3\x61\xa4\x6e\x6b\xd6\x66\x1e\x42\xb7\x1a\xfb\x54\x4b\x68\xb5\xbd\x5d\xe6\x65\xc3\xb1\x0f\x99\x13\x22\x53\x00\x24\x59\x48\xaf\xb8\x2c\xfe\x0d\x81\x90\x70\x62\xe0\x3c\x15", 51, { 0x5d, 0xe7, 0x06, 0x6a, 0xbc, 0x01, 0xd0, 0x64, 0x94, 0x29, 0xae, 0x04, 0xf1, 0xcc, 0xb5, 0xd6 } },
{ "\xf6\xc8\xdd\x96\xe9\xc2\xef\x9b\x8f\x09\x3f\xbf\x85\xd5\xfa\xa2\x55\xb5\x70\x1c\xc1\x15\x6b\x8e\xb0\xdf\x26\x55\xb2\x3e\xec\x58\x32\x7e\x4f\xc1\x37\x10\x01\xc8\xd6\xa9\x52\xab\x38\x89\x46\xba\x44\xd9\x52\x8e", 52, { 0x84, 0xae, 0x25, 0xbc, 0x36, 0x9c, 0x91, 0x9b, 0x44, 0x2e, 0xd6, 0xef, 0x9e, 0x1f, 0xe2, 0x8f } },
{ "\xe9\x2d\xb3\x5a\x09\x2a\x6c\x59\x05\x41\xda\x67\xe1\x99\xf5\xac\x14\x0b\x25\x73\xef\x47\xbe\x19\xa7\x14\x1a\x20\x01\xb4\x52\x63\x99\x65\x98\x64\xce\x04\x34\xc1\x4d\xcd\x19\xc5\x39\x3d\x24\x1b\xf4\x18\xf0\x9e\x8d", 53, { 0x1e, 0xa3, 0x3b, 0xad, 0x84, 0x80, 0xa6, 0x3e, 0x7a, 0xbe, 0xe1, 0xc2, 0xf6, 0x07, 0x6c, 0x8f } },
{ "\x86\x58\x4b\xc6\x59\x8a\x58\xc0\x6a\xc4\x5e\x45\x21\xaf\xb1\xb2\x12\x54\xd0\x7f\xc4\xbf\xf8\x6d\x8e\x2f\xd3\x4b\x9b\xf6\x4e\x64\x0c\xf3\x88\x88\x3c\xaa\xe6\xb5\x1f\xfd\x43\x63\xc3\x89\x45\x69\xf9\xa0\xcb\x8f\x0d\xde", 54, { 0x18, 0x55, 0xdc, 0x67, 0x81, 0x98, 0xdf, 0x9d, 0x59, 0x3d, 0xed, 0xb3, 0xf2, 0x14, 0x22, 0x43 } },
{ "\x82\xb1\xb0\x6a\x97\x8c\xf7\xcb\x86\x28\x7c\x64\x11\xe2\xa2\x8e\x4d\x15\xf7\x50\xd6\x64\xb2\xbd\x23\xa7\x5b\xeb\xf4\x70\x8a\x8b\xe8\x39\xc7\x2a\x2b\x2b\x91\x03\x4c\x8d\x7a\x7e\x2c\xc8\x6f\x49\x12\x13\x16\x12\xdc\xbf\x50", 55, { 0x7c, 0xd8, 0x1e, 0x1e, 0xfb, 0x01, 0xad, 0x5a, 0x4c, 0x0e, 0x83, 0xbd, 0x9d, 0x2e, 0xe1, 0xff } },
{ "\xde\x50\x21\xdd\x09\x91\x17\x1f\xda\xad\x39\xf7\xc2\x53\x9e\xcc\x32\xa2\x48\xaa\x16\x1c\x2d\x86\xf1\xb9\xe2\xa0\x96\x18\xe6\x01\x80\xd0\x89\x24\xcf\xe5\x77\xb1\xe0\x57\xe5\x64\x87\xd4\x57\x0d\xeb\x8d\x0b\xb0\xff\x25\x06\xcc", 56, { 0xb5, 0x25, 0x80, 0x30, 0x10, 0x74, 0x47, 0xb8, 0xac, 0x64, 0xa9, 0x31, 0x47, 0xd4, 0x6a, 0x69 } },
{ "\x99\xc6\xb8\xb9\x30\x4e\x4e\x2a\x37\xa7\x95\xd2\x10\x35\x86\x24\xaf\x68\x72\x32\xcc\x71\xcb\xb7\xc4\x11\x8c\x30\x0d\x8c\x46\x24\x54\xbd\xc8\x85\x35\x43\x6b\x2f\x96\xd9\xf7\x8d\xa0\xc4\x36\x5b\x5b\x65\x13\x85\xe6\xee\x6d\x2f\x4f", 57, { 0xf2, 0xeb, 0xbc, 0x0f, 0x7b, 0x22, 0x29, 0x53, 0xd2, 0xcb, 0x16, 0x52, 0x41, 0x8f, 0xf5, 0x27 } },
{ "\x31\x7e\xce\x15\x94\xe2\x73\x37\x21\x47\x28\xf8\xba\xb0\x73\x32\x69\x65\xf7\x54\xd6\x7b\x3a\xfa\xd2\xfc\x82\x28\x0f\xb2\xd8\x22\xf2\x95\x2e\xea\x81\xce\x9c\x57\x77\x67\xc6\xce\x77\x92\x5a\xa0\x5d\x93\xb6\x00\xaf\x4a\xa6\x9f\x32\x61", 58, { 0xb7, 0x47, 0x82, 0x40, 0x69, 0x9d, 0x2d, 0x25, 0xd5, 0xce, 0x1b, 0xd5, 0xeb, 0x8c, 0xbf, 0x71 } },
{ "\x4b\xe6\x99\x0e\x17\xcf\x58\x85\xf4\x60\x98\xfe\x84\xc6\x43\x5e\x84\x0a\x3e\x84\x4a\x72\xb7\x33\x6d\x77\x94\x97\x83\x5d\x0a\x76\x05\x75\x4f\xf4\x4f\xeb\x67\x02\xc4\x4c\x1b\x65\x0f\x99\x09\x5f\x84\x00\xd4\xfc\xa4\x3d\x7b\xfa\xbf\x8d\xc7", 59, { 0x82, 0x36, 0x57, 0xa2, 0xc1, 0xad, 0x78, 0x3f, 0xb9, 0xfb, 0xa2, 0xc6, 0x79, 0x7e, 0xca, 0x7a } },
{ "\x55\xb4\x4a\xde\x6f\xd4\x28\xf6\xd9\xe5\x0a\x23\xc1\x42\x82\x50\xf8\x20\xe4\x4c\x9e\xeb\x05\xfc\x6b\xce\x95\xaf\x9d\x52\x16\x8f\x7a\x1e\x78\x32\x81\xdd\x53\x50\x31\x3d\xc0\x8f\x75\x13\xd5\x7b\xaa\x9b\xbb\x91\x4f\xdd\x6c\x7a\x48\x2c\x0e\x55", 60, { 0x7a, 0x3f, 0x5c, 0x60, 0x63, 0x7e, 0x72, 0xdf, 0xcc, 0x23, 0x8a, 0xa3, 0x84, 0x14, 0x20, 0xe2 } },
{ "\xcf\xc9\xe7\x2d\xf7\xa4\xfd\x8a\xde\x42\x4b\x23\x6f\xba\xa1\xb1\xd1\xd9\x19\xde\x70\x65\x31\x5c\xa6\x7f\x8a\x13\x05\x21\x3c\x72\x43\xc7\xd4\xe7\xc3\x2b\xeb\x69\xbc\x28\x64\x32\x08\xc0\x41\x8b\x8b\x39\x22\xbf\x3e\x62\x5c\x31\xd7\xf3\xb2\x8a\x17", 61, { 0xe3, 0x74, 0x31, 0xe3, 0xd2, 0x5e, 0x01, 0x00, 0x51, 0xde, 0x8c, 0x48, 0x58, 0x76, 0xaa, 0xca } },
{ "\x12\xb3\x0a\xb0\xf2\xaf\x00\xd0\x96\x57\x55\x4e\xb4\xf0\x42\x70\xb4\x34\x21\x56\xd7\xc5\x61\x07\x75\x4f\x94\x17\x5e\x39\xa3\xf1\x62\x07\x21\xc7\xed\x4f\xbc\x13\xca\x55\xd0\xc8\x08\x46\x15\x1a\xfa\x0d\x79\xe7\x58\xd6\x09\xc0\x82\x1d\x08\x98\xe5\x72", 62, { 0x14, 0x7a, 0x4d, 0x78, 0xbe, 0xcf, 0x03, 0xf0, 0xa6, 0x57, 0x16, 0x8d, 0x7e, 0x07, 0xa8, 0x36 } },
{ "\x22\x09\x76\xcb\xba\x3d\x5c\x85\x60\x7f\xaa\xf9\x5e\x5e\x4a\xb7\x71\x7b\xf5\x62\x95\xf0\x5e\x28\xf9\x5d\x6e\xdb\x12\x90\xaa\xb1\xcc\xd0\xb2\x95\xdb\xc7\xe3\x27\x2f\x09\x1b\x57\x85\x45\x9c\x99\x1a\x07\x09\xc5\x7a\x27\x8e\x8f\x77\xd2\x1d\x9e\x36\x32\xd3", 63, { 0x8d, 0x97, 0x7a, 0xb1, 0x6f, 0x7a, 0x54, 0x80, 0x88, 0x10, 0x07, 0x3b, 0xb4, 0x6a, 0x19, 0xb9 } },
{ "\x4a\x64\xbd\x79\xf7\x86\x17\x25\x09\x6d\xa5\x01\x83\xc7\xaf\x23\x3f\xd2\x31\x9c\xcc\x2c\x3f\x8d\xdf\xc7\x12\x72\x78\xf8\xb3\x82\x93\xae\x42\x2f\x86\xbf\xe3\xc8\xfd\x5e\x46\x3f\x90\xa9\xd2\x04\xfe\x5f\x6e\x0f\x09\xaf\xeb\xfd\xed\x2b\x11\x52\x7e\xdc\x45\xdd", 64, { 0x15, 0xf4, 0x8b, 0xb9, 0x10, 0x92, 0xcd, 0x68, 0x96, 0xc1, 0x4c, 0x7d, 0x00, 0x47, 0x36, 0x34 } },
{ "\xdd\x6e\xd4\x39\xd6\x8c\xde\x6f\xda\x47\xe1\xb9\x94\xaf\xe1\x62\x29\x84\x32\xc9\x11\x06\xe2\x84\x8d\xe9\xc5\xa0\xc4\x65\x1d\x07\x7e\x69\xe6\xfb\x7c\xef\xbc\xbe\x71\x6b\x6a\x54\xa1\x5d\x10\x60\x15\x06\xf0\x2b\x78\x37\xd5\xc4\x92\x44\x20\x41\x5e\x18\x70\x23\xc9", 65, { 0x8e, 0x53, 0x6f, 0xfb, 0xda, 0xda, 0x44, 0x77, 0xb3, 0x44, 0x3e, 0x65, 0xec, 0xbc, 0x40, 0x2a } },
{ "\x62\xed\xd8\x0a\xed\x32\x59\x98\x0e\xd4\xf4\xcd\x36\x93\x24\x15\xa7\x1d\x9c\xd2\x44\x79\x63\xd0\x81\x16\x18\x60\x79\x71\x57\x13\x1e\x5d\x34\x15\x8a\xf2\xe4\x23\x75\x14\x7c\x2a\xc0\x9f\xd1\x7e\x2d\x2c\x7d\xb3\x32\x83\x03\x1c\xe2\x9d\x0a\xdd\x0b\x54\xc6\xaf\x5f\xa9", 66, { 0x3b, 0xd1, 0xd1, 0xc0, 0xfa, 0xcb, 0x9c, 0xdb, 0x86, 0x97, 0x8f, 0x09, 0x31, 0xa6, 0x10, 0xca } },
{ "\x74\x30\xca\xb2\x03\xe5\xe2\x1e\xd0\xcd\x7d\x66\x8a\xa2\x5c\x92\x35\xaf\x04\xa5\x4a\x49\xad\xa7\xfb\xeb\x54\x4d\x93\xf2\xeb\x46\xfc\xf1\xb1\x24\x5a\xb2\x9c\xe5\xd5\xca\xf1\x1e\x75\xd8\xf6\xc3\x26\x5a\xc0\x95\xda\x2b\x26\x28\xcd\x9d\xd6\x90\xe4\x2f\x85\xa8\x2f\xeb\x42", 67, { 0xfe, 0x03, 0x68, 0xe3, 0xc5, 0xf8, 0xb8, 0x4d, 0x9b, 0x29, 0x4b, 0x26, 0x36, 0x39, 0x34, 0xe6 } },
{ "\xfe\x8e\x5f\xe4\x5e\x6f\x35\xa2\xfa\xb5\x71\xc1\x33\xb4\x47\x68\x06\x59\x97\x8f\x16\x67\x06\x16\x52\xdc\xba\xf2\x42\x72\xb1\x82\xf3\x41\xbf\x00\x7d\x81\x22\x12\x2e\x6e\xc3\x80\x55\x44\x0c\x01\xac\x6a\x60\x2e\x63\xab\x3b\x98\xc5\x9f\xe8\xf5\x89\x28\xd9\x75\xb8\x18\xa6\x33", 68, { 0xb7, 0x0e, 0x24, 0x63, 0x59, 0x66, 0x0c, 0xa2, 0xfb, 0x46, 0xc6, 0xa1, 0x72, 0xd1, 0xd9, 0x3f } },
{ "\x35\xf0\xa5\xc6\xee\xc3\x22\xf8\x9e\x3a\x3b\x8e\x3c\xb0\x5b\x46\x03\x7d\x51\xdf\x1f\x50\x0c\x52\xf1\xb6\xf5\x1e\xff\xb7\xe9\xc1\x7b\x9f\xd2\x42\x6e\xda\xe1\xeb\x81\xf9\x69\x15\x68\xd1\x5b\x1b\xec\xc2\x5a\x71\x93\x15\x7a\xcd\xed\x7f\x9a\x83\x7e\xc2\x5b\xee\x43\x73\xbc\x0a\xe3", 69, { 0xd4, 0x3f, 0xb8, 0xeb, 0x1a, 0xb1, 0xed, 0xed, 0x3a, 0x26, 0x8f, 0x0b, 0x7d, 0x06, 0x0a, 0x64 } },
{ "\xd6\x54\x1f\x73\xe5\x2b\x98\xe6\x70\x34\xf2\x10\x5e\x6b\xd0\x55\x11\x87\x2b\xf2\xe8\x5d\xa6\xe7\xde\xd1\xff\x80\x4f\x79\xa2\x7f\xdb\xd0\x58\xe8\x63\x13\x1c\x69\x31\xbb\x0e\x63\x34\x98\x81\x99\x68\x87\x2f\x74\x54\xd0\x8c\xf0\xad\x1e\x37\x27\x18\x1b\x8d\x78\x0d\xa8\x58\x54\xd7\xb6", 70, { 0xbd, 0x93, 0x38, 0x13, 0x7b, 0x70, 0x0d, 0xa8, 0x1b, 0x1a, 0x7a, 0x2d, 0xbe, 0x11, 0x15, 0x38 } },
{ "\xa5\xa5\xb7\xc4\xb3\xda\x29\xf5\x07\x93\x3e\xcd\x9d\xe6\x28\x88\x38\x4c\xd4\x17\xdb\x2c\xb2\xb8\x50\x01\x90\x30\xe5\x2a\xa4\xa8\x37\x4e\xa8\x21\x26\x69\x20\x96\xe8\x78\xb7\xad\x39\xbd\xc7\x19\xb0\xaf\xf8\x8f\x2d\x82\x00\x62\x6f\x4e\x88\xbd\xc2\xdf\x2f\x7d\xd5\x18\xa7\xa9\xa9\x04\xfd", 71, { 0x7a, 0x7d, 0xc9, 0x94, 0x96, 0xaf, 0x51, 0x90, 0xc5, 0x48, 0x51, 0x9b, 0xeb, 0xd2, 0x6d, 0x3c } },
{ "\x8e\xa6\x7b\xf7\x7b\xc8\x6d\x96\x16\xf6\xce\xbb\x5a\x73\x1e\xe6\x2e\x6d\x93\x19\xa2\xb3\xe6\xdb\xc8\x84\xb7\x58\x2d\x83\x9e\xa6\xf3\xc6\xcf\x22\x8c\x0d\x69\xf9\x15\x0f\x5b\xc3\x4d\xf0\xf3\x49\x30\xd0\x05\x67\x34\x7d\xb4\x0d\x1f\x48\xe0\x5f\x83\xf5\x2c\xfc\xd7\xcc\xaa\x5c\x34\xcc\x26\x53", 72, { 0x82, 0x13, 0x1c, 0x29, 0xf6, 0x16, 0x5a, 0x23, 0xba, 0xc4, 0x25, 0x2c, 0x5a, 0x9e, 0x9b, 0xde } },
{ "\xa9\xff\x0c\xb2\x3b\xe9\xf7\x0c\x7c\x1a\xd6\x96\xc6\xe3\xbd\x28\x15\x25\x46\x60\x7e\x45\xa1\xe6\x50\x3b\x3f\xc9\xc6\xca\x17\x08\x65\xca\x94\x2e\xf8\xa1\x1b\x14\x26\xd0\x3c\xca\xad\xa9\x74\x91\x3d\x1e\x9d\xff\xf3\xb5\xf3\x45\x70\x99\xc3\x8e\x96\xca\xb2\x7d\xdf\x73\xa7\xd9\x59\x5c\x56\x45\x42", 73, { 0x92, 0x2d, 0x15, 0x76, 0x2c, 0xb6, 0xf5, 0x81, 0x60, 0xcc, 0x0e, 0xd5, 0x09, 0xff, 0x39, 0xb8 } },
{ "\x76\x6a\x28\x3c\x94\x4e\xab\x2c\x39\x99\xe0\xb5\x8c\x14\x00\x4c\x68\x68\xcd\xf8\x90\x69\x16\xa9\xff\xf9\x59\x32\x5e\x9a\x82\xe3\x8f\x94\xd2\xc7\x66\xbf\xdb\x77\x78\xee\xa7\x99\x0b\xf8\xf9\xf9\x2d\x64\xab\x41\xe0\xb0\x36\x58\x72\x3e\x50\x55\xec\xdc\xb0\x1f\xa0\xee\xed\xa3\xe5\x3a\x84\xb0\xe0\x50", 74, { 0xa5, 0x43, 0x80, 0x60, 0x02, 0x56, 0x20, 0x34, 0xdf, 0x73, 0x76, 0xbf, 0xe5, 0x73, 0xcb, 0xbc } },
{ "\xf8\x72\x78\x94\x14\x89\x6b\xea\x4c\xc5\x7f\xfc\x42\x80\xd0\x2a\x64\x98\x48\x0f\xb2\x08\x2e\xad\x9d\x23\xd5\x34\x0b\x6c\x74\x74\x78\x14\x16\xdc\x36\x11\xa8\x2e\xd4\xd7\x30\xc1\x9d\xb5\x1d\x72\x38\xcf\xb9\x28\x6d\xb4\xba\x03\x07\x9b\xf8\x21\xd4\x3f\x3a\x0c\xc9\x54\x29\xc3\x42\xa4\x66\x64\x68\xcb\xb7", 75, { 0xf9, 0x8f, 0xcc, 0xd8, 0x9b, 0xcd, 0x41, 0x55, 0x5b, 0xb0, 0xb5, 0xa2, 0xf4, 0x53, 0x3a, 0x88 } },
{ "\x73\xac\x8b\x5c\xc9\x6f\xcd\x80\x1b\xc4\x35\xcb\x26\x4d\x60\x14\x74\x42\xbd\x34\xce\x90\x05\xf6\x3f\x1d\x58\x2a\xc0\x2a\xf6\xd9\x85\xd2\x96\x92\xe3\xaf\x66\xe3\xdc\x9c\xa0\xe3\x49\x94\xc6\xec\xa2\xb9\x7a\x67\x6d\x71\xfc\xc0\x41\xab\x40\x78\x76\x40\xab\x58\x6c\x74\x6a\xc3\x74\x9b\x6f\x25\x5e\xd8\x09\x88", 76, { 0xaa, 0x3b, 0xd6, 0x8d, 0x86, 0x26, 0x1f, 0x65, 0xe7, 0x23, 0x96, 0x84, 0xda, 0x55, 0xa7, 0x8d } },
{ "\x9f\x01\x1f\x45\x1f\x9e\x09\xd7\x9d\x10\x0c\xcb\xc0\xcf\x6f\xd3\xbb\xcc\x3e\xfb\x9b\xf5\x9b\xf3\x30\xd7\xbb\x8a\x96\x5d\x84\x3c\x5f\xb6\xc2\xe2\x5e\x55\x00\x87\x12\x12\x09\x5b\xae\x6e\xfb\xc1\xc1\xf3\x04\x83\x87\xba\xbc\x25\xcb\x06\x1f\x57\x77\x0e\x25\x83\xba\xd8\x00\x87\xd4\x41\xf2\x7d\x81\x19\xc8\xb5\x00", 77, { 0xe5, 0xee, 0xc1, 0x56, 0xbb, 0x5a, 0x4f, 0xa7, 0x4c, 0x76, 0x0d, 0x17, 0x27, 0xfb, 0xc2, 0x3e } },
{ "\x6b\xf6\xc4\xbe\xda\x50\xb6\xa4\x26\x63\x5e\xfa\xce\x6e\xcf\xf6\xd0\x00\xe3\xe5\x8c\x2f\xf8\xf5\xc7\x72\xbb\x9d\xca\x1c\x1c\xa9\xaa\x76\x61\xe5\xa3\x77\x56\x65\x9e\x22\xa2\x70\xd2\x7c\x36\x07\x86\x29\xde\x15\xf4\xa3\xfa\x34\x4a\x15\x46\x19\xca\xda\xd4\x9e\x11\x8d\x1c\x6b\x74\xcc\x2a\x3f\x86\x8e\xf5\xe0\xb6\x1e", 78, { 0x78, 0xa5, 0x61, 0x3a, 0x59, 0x4a, 0x02, 0xa5, 0xa3, 0xa6, 0x94, 0x54, 0x0b, 0xe8, 0x94, 0x0f } },
{ "\xd0\x9d\x15\xb4\xf9\x7d\x36\xae\x4f\x06\x2e\x70\x21\xab\xbb\xd5\x22\xd3\x81\x50\x78\x82\x6f\xa4\xf3\xa6\x41\x3c\x5b\x00\x1e\x27\xe2\xad\x61\xb4\xb8\x51\x75\xa7\xbe\xdf\xd9\x15\x52\x06\x43\xe1\x49\x3b\xe1\xd3\xfa\x5f\xba\x52\x77\x0e\x33\xbe\xd7\x84\xae\x6c\xaf\x2b\x4d\x3f\x9f\xc5\xd4\xdf\x08\x32\xac\x99\xdb\xdf\x04", 79, { 0x23, 0x58, 0xdd, 0x9f, 0x9e, 0xdd, 0x10, 0xda, 0xc2, 0x01, 0x1b, 0xaa, 0xb3, 0xec, 0x5e, 0x13 } },
{ "\xd3\x8d\xcb\x12\x2a\x27\x37\x78\x2a\x79\xea\xc1\xa9\x6e\x1e\x0a\xc0\x7b\x0a\xbd\x67\x31\x0d\x79\xac\xf3\x74\x0e\x90\xc1\xe8\xa4\x7e\x32\x77\xfb\x69\x80\x3d\xcc\x81\xbd\xae\x4f\x91\x24\x83\xd6\xf8\x8b\x3f\x96\x41\xf4\x5d\x86\x62\x42\x18\x61\x8e\x08\xd6\xc5\xb7\x7d\x32\x7d\xa3\x8c\xa5\xdc\xaa\x08\xd0\x40\x0a\xfd\x68\xb6", 80, { 0xe2, 0xa8, 0x37, 0xb5, 0xf7, 0x9d, 0x34, 0x76, 0x8c, 0x62, 0x65, 0xbe, 0x61, 0xb3, 0x81, 0xfb } },
{ "\xe6\x21\xbe\xbc\xe1\x6a\xea\x21\x53\xb9\x93\x09\x27\xb4\x5e\x0d\x51\xb1\xf1\x7f\xee\xcd\xa3\xcb\xf9\x1d\xc2\xf8\xa7\xfc\x80\x4e\x61\x84\x7a\x12\x5c\x18\xe6\x6d\xd6\x1d\x59\x6b\xee\xc8\x33\x1e\x20\x12\xa8\xe3\x5d\xc9\x6b\xbc\xc3\xc0\xf9\xdd\xfe\x27\x7f\x42\x3a\xf5\x68\x7d\xc4\x81\x87\x8e\x3c\x30\xde\xea\x79\x49\x09\x9e\xf9", 81, { 0x56, 0x9a, 0x78, 0x23, 0xde, 0x2b, 0xe3, 0xea, 0x51, 0x96, 0xff, 0x8d, 0xac, 0x0f, 0x6e, 0x95 } },
{ "\xe5\x4a\x8d\x03\x02\x32\xd5\x5c\xb6\xe8\x50\xa1\x80\x19\x36\x47\xba\x7c\xe0\x2d\x2a\x00\xa7\xdb\xb9\x95\xeb\x5a\x3b\x94\x30\xaf\x6c\xcc\x62\xf5\xfc\x2a\x39\x16\xc1\x04\xb7\x26\x0c\x02\xcf\x5c\x16\xa9\x20\xad\x98\x85\xde\x07\x79\xf3\xd2\x27\xa2\x88\x78\x17\xee\x22\x46\x48\x3d\x89\x0c\x47\xa7\xc1\x76\x1f\xce\xee\xaf\x4c\x4d\xe4", 82, { 0x2e, 0x98, 0xba, 0x76, 0xed, 0xff, 0xf1, 0x24, 0x93, 0x11, 0x1d, 0xae, 0xa9, 0x3c, 0x2c, 0x19 } },
{ "\x20\x57\x4d\x4b\x56\x13\x36\x6b\x02\x72\x81\x9a\x19\x04\xac\x3f\x1c\x0e\x47\x82\x72\x43\x2c\x92\xf1\x22\xcc\x92\x7b\xeb\xa3\x21\xc5\x3f\xcd\x84\x33\x53\xb3\x73\xdf\xd0\xdd\x7d\xb9\x4b\xec\x18\xc8\x5c\x9f\x49\x8f\x5d\x12\xec\x8a\xc1\x08\xa1\xd7\x0e\x5d\x53\xf2\x78\x9e\x66\x99\x1c\xb4\x7c\xce\xd5\xbb\xe4\xfb\xbf\xf0\xa1\x2f\x46\xc9", 83, { 0xc1, 0x06, 0x74, 0x68, 0xa6, 0x97, 0x98, 0x95, 0xee, 0x75, 0x92, 0x71, 0xe1, 0xe8, 0x5a, 0x0a } },
{ "\x12\x87\xe9\x1f\x42\xa8\x91\x87\xce\x68\x88\xbe\x6f\x8c\x18\x42\x24\xac\xc1\xfd\xfd\x33\x15\x1c\x85\x83\x34\xff\xcd\x35\xe0\x49\x11\x7e\x3c\x16\x0a\xad\x26\x4c\xcf\xd0\x2d\x0b\x69\x76\x31\x3e\xf2\x90\x96\x53\x68\x71\x24\x07\x13\xf5\x7f\x5a\x91\xbd\x3e\xa2\x7b\x98\xa9\xbc\xb6\xfd\x14\x5d\x58\xb4\xd2\x86\x34\x95\xd8\x16\x04\x69\xf0\x79", 84, { 0x0b, 0x29, 0x0b, 0x0f, 0xff, 0xe0, 0x89, 0x9c, 0xea, 0x43, 0x82, 0xda, 0xea, 0x48, 0x69, 0x79 } },
{ "\x32\xed\x96\x7e\x43\xf4\x73\x47\xe0\xac\x73\x59\x83\xf9\x85\xd4\xba\xfd\xef\xbc\x1b\xb9\x4b\x75\xc5\xcd\x21\x4e\x8e\x5b\x22\x34\xce\x0a\xff\x87\xc7\x26\xe4\x09\xaf\xe5\x95\xf2\x5c\xc5\x23\x22\x8c\x10\x4a\x6e\xcd\x81\x6a\x1b\x0f\x01\x20\x69\xc4\x8a\x81\x46\xb1\x2d\xf3\x24\x55\x2e\xa7\xe4\xf2\x4d\xb6\xf1\x3c\x20\xd7\x6a\x9d\x9b\xbb\x77\x23", 85, { 0xd1, 0xd8, 0x5e, 0xa5, 0x95, 0xa7, 0x49, 0xac, 0x47, 0x06, 0x57, 0x0d, 0x28, 0x00, 0xf4, 0xa0 } },
{ "\xb2\x05\xbd\x07\x15\xe9\xec\xdb\x1a\x60\x75\x4f\xb9\x05\x59\x9f\xb0\x90\x1d\x9d\xc7\xec\xda\x56\x2e\xf6\x70\x64\x02\xd4\xdb\xd0\xee\xf0\x9e\xa1\xee\x90\x5b\x06\x2f\x14\x84\x1b\x13\xcb\x2c\x5b\x50\x71\xf7\xa3\x38\x49\x30\xdf\x13\xd3\xf9\x53\xa8\x2b\x9d\x88\xdb\xfe\x02\xd1\x70\x29\x3a\x78\xed\xf0\x38\x8a\x9e\xd7\x9f\x3c\xb2\x20\xb6\xf9\x83\xe2", 86, { 0xf1, 0x5f, 0x79, 0x80, 0x57, 0x2d, 0x9a, 0xb6, 0xc1, 0x7f, 0x79, 0xf3, 0xc9, 0x37, 0x04, 0x63 } },
{ "\x78\x09\xf3\xc3\xc8\xdf\xf2\x52\xc2\xff\x1b\x03\x2d\x8a\x7e\xc8\x0c\x67\x79\x48\x54\x10\xbf\xbe\xb7\xf6\x7a\x71\x9f\x92\x8d\xcb\x36\x0a\xf2\x19\xa2\x7c\x43\xde\x4e\xe9\x54\xd4\x64\xc1\xfd\x80\x57\x07\xf5\x71\x9c\x20\xd9\x15\x78\x3e\x4d\x37\xf4\x64\x39\x19\xee\x15\x35\x29\x4a\x9d\xff\x64\x45\xfb\x29\x44\xe9\x69\xd0\x67\x9d\x8a\x86\xbe\xd4\x2f\x51", 87, { 0x1b, 0xd6, 0xd5, 0xe4, 0x5a, 0xec, 0x0f, 0xb0, 0x32, 0x9f, 0x48, 0xd3, 0x30, 0xa0, 0x5c, 0xc7 } },
{ "\x6e\xe6\xc6\xcd\x46\xfb\x60\x6c\xdd\x23\x5d\xde\x48\x84\xb7\x8c\x76\xab\xe7\x3d\x28\x03\x77\xdb\x8f\x63\x26\x50\x83\xc1\xb1\x8b\x5e\x04\x44\x9f\x73\xf8\x7d\x0a\x2e\x5b\x19\x12\xca\x14\x3d\x4b\xa9\x83\x63\x36\x53\xf3\xdf\x04\x0d\x2c\x0d\x78\x15\x26\x19\xea\x79\xd7\x6b\x67\x91\x2d\xad\xf6\x1f\x18\x7a\xf6\x01\xbe\xa4\xa3\x90\xd2\x22\xb7\x99\xff\x95\x2c", 88, { 0x10, 0x9b, 0xe4, 0xa6, 0xe5, 0x6b, 0x8b, 0xa9, 0x93, 0x3d, 0x1f, 0x2b, 0x86, 0xee, 0x43, 0x6f } },
{ "\xf6\x7e\x75\x20\xc8\xc7\xdc\xd2\x52\x63\xef\xc9\x75\xe0\xe5\x14\xc4\xde\xb5\xac\x43\x47\x60\xf5\xc1\x9c\xd8\x63\xcf\x6a\x8c\x3a\x5c\xdb\x91\x6e\xee\x68\x6e\xa8\x7f\xac\x84\x6a\xf2\x54\x18\x49\x30\x33\xff\x59\xe4\x72\xe5\xa8\xcf\xe5\x39\xe0\xc8\x78\xb8\x10\x54\xc8\x95\x84\xde\xce\x42\xd0\x93\xb6\xde\xec\xdc\xce\x3b\x79\x79\x99\x8a\x22\x37\x04\xa6\x1d\x4d", 89, { 0xc5, 0x4b, 0x08, 0x71, 0x93, 0xf9, 0x29, 0x50, 0xdf, 0x2e, 0xf2, 0xdb, 0xd3, 0xc0, 0x45, 0x8e } },
{ "\xb0\xfa\xd1\x59\x6e\x0f\x92\xf2\xc9\xb5\x87\xe9\xbc\xcd\xad\x21\x84\xb2\xf4\x08\x90\x42\x87\xb1\x96\x8c\x29\x90\xbf\x18\xbb\xd1\x02\xcd\xda\x55\x8f\x83\xa5\x4a\x61\x26\x9c\x65\xf6\x83\xa4\xbf\x1b\xb2\x27\x49\xe0\x20\x58\xee\xac\x38\x94\x44\x69\xae\xe4\xed\xdf\x68\x9f\x90\x1f\x09\x2c\x6f\x15\x1a\xe5\xa6\x41\xd7\x18\xff\x7f\x94\x27\x74\x02\xca\xcf\x42\x5f\xc2", 90, { 0x87, 0x4d, 0x37, 0x7f, 0xf8, 0xc5, 0xf8, 0xbc, 0xa4, 0x91, 0x41, 0xf0, 0xcb, 0xc4, 0x24, 0xfc } },
{ "\x85\xa6\xc5\xdd\xfc\x37\x4b\x7e\xc4\xdf\x62\x94\x0c\x77\x6a\xc7\x88\xfe\x60\x3e\x6d\x76\xb4\x0b\x99\x5c\x38\x34\xd1\xc2\x35\x5f\x22\x0a\x98\x19\x68\x41\x4a\xc3\xed\x15\x9d\x19\x29\x75\xe1\x60\xa8\xc4\x17\x2c\x09\xb9\xbd\xcb\x22\xd5\xc8\x51\x41\x82\xb0\x41\x8d\xc5\xa5\xd5\x8c\xa0\xdf\xeb\xbe\x07\x13\x8c\x66\x7c\x01\x19\x68\x49\x2a\x14\x4d\x5a\xa0\x88\x64\xb4\xf5", 91, { 0x9a, 0x75, 0xb0, 0x83, 0xa7, 0xef, 0xdc, 0xd1, 0xec, 0x55, 0x02, 0xe0, 0xc3, 0x21, 0x11, 0xcb } },
{ "\x17\x04\x96\x55\x9e\xec\x2a\xb0\x86\xcb\xe8\x0f\xaa\xdb\x88\x08\xe0\xa7\xa1\x4d\x26\xee\xe3\x5e\x6b\xa0\xee\x5f\xf7\x05\xcc\x04\x77\xfd\x12\xa8\xa9\x62\x8e\x7d\xa0\x89\xad\xde\xb4\xe8\x6c\xc1\x0a\xd1\xf9\x48\xbc\x5c\xe6\x76\xa4\x64\x08\xfe\xca\xa7\xf2\x37\x68\x11\x09\x2d\x96\x12\x44\xd1\x96\x2b\x83\x50\xbe\x89\x88\x80\xfe\xc8\x96\x77\x0d\xd8\xa4\x36\x35\xfd\x3e\x83", 92, { 0x76, 0x5a, 0x38, 0x22, 0x4b, 0x74, 0x03, 0xca, 0xef, 0x81, 0xf9, 0x95, 0x2f, 0x9a, 0x61, 0x09 } },
{ "\xb7\xf0\xf9\xd4\xdc\x23\x91\x67\x99\x22\xa1\x6f\xdd\x15\xfd\x68\x7b\x62\xaf\x8c\xaf\x6f\x8d\x5e\x4f\x85\x74\x63\x8f\xd0\x23\xf0\x71\x83\xb6\x1f\xc7\x18\xbc\xdb\xe6\xb5\xf8\x1b\xe9\xaa\xb1\x2c\x9f\x17\xb6\x26\x12\xab\xf2\x80\xb5\x87\xa7\xc0\x29\x40\x4b\x4a\xac\x03\xfc\x5a\x1e\xd3\xa0\x0d\xb1\xef\x9d\x99\x2d\x4f\x32\x9e\xde\x4c\x70\x94\x1b\x5f\xd9\x63\x73\x01\x93\x11\x75", 93, { 0xd5, 0x80, 0x2e, 0xe7, 0x75, 0x2b, 0x14, 0x45, 0xe7, 0x79, 0xbb, 0x8a, 0x9e, 0x25, 0xbe, 0x59 } },
{ "\xb2\x49\x95\x1c\x16\x9f\xcb\xd5\x39\x4f\x81\x2f\xf3\xe5\xae\x1e\x67\x13\xc2\xec\xa5\xb2\x19\xbd\x70\x56\x89\xbd\x19\x8c\x1a\xe8\x8a\xb2\xd5\x75\x5f\x73\xb5\xe0\x2e\xdf\xba\x93\xe7\x23\x1d\x30\x57\xe9\x9e\x7e\xc7\x4f\xf9\xed\xc1\x7c\x77\xc7\xf2\xe5\xa0\xf5\x2b\x1b\x0e\x6c\x81\x4e\x5c\x32\x95\x62\x3e\x11\xee\xac\xab\xd6\x82\x49\x34\x46\x54\x1b\x78\x9f\x37\x61\xb0\xc6\xb0\xf2", 94, { 0xe7, 0x95, 0xef, 0x83, 0xc5, 0xa8, 0xfd, 0x49, 0x2c, 0xea, 0xcc, 0xb6, 0x5a, 0xd5, 0xfa, 0xf8 } },
{ "\x66\x12\xf9\x16\x43\xb3\x22\x01\x05\xc0\x53\xed\x8f\xdd\x2c\xd2\x1a\x85\x4d\xc5\x8a\xfa\xf1\xeb\xdb\xc6\xb5\x18\xba\xec\x77\x08\x30\xb0\x59\x38\x79\x00\xe5\x62\xf3\x7e\x4b\x94\x32\x2b\xc5\xec\x30\xb1\x1b\xf2\xa2\x4d\x42\x92\x65\x57\x20\xb4\x17\x6f\x6b\x7a\xc3\x46\xaf\x15\xb4\x06\x6e\x44\x6a\x5f\x61\x6b\xaa\x2f\xdd\x4c\xb0\xcd\x51\x64\x80\xf3\xec\xe4\x5b\x45\x5b\x4a\x4c\x82\xb0", 95, { 0x78, 0xba, 0x8a, 0x34, 0xdc, 0x24, 0xb8, 0xf7, 0x78, 0xb9, 0x19, 0xfa, 0x5e, 0x19, 0xcd, 0x1d } },
{ "\x9c\xe1\x46\x5e\x41\xf7\x79\x61\x7c\xaf\x58\x7a\x85\x7d\x1e\x98\x46\x5f\x4e\x53\xaf\x2a\xa0\x91\xb6\xff\x86\x4b\xef\xdd\x59\x6a\xc3\x50\x25\xcb\x50\x48\x0d\x62\xdd\x04\x5d\x7a\xf8\x2d\x2a\x1a\x2e\xff\xa8\x83\x67\xa2\x08\xd7\xdb\x97\x0b\xd4\xb5\x4e\x28\xbd\x42\xd4\x56\x59\xd6\xa9\x77\x15\x3c\xb0\x61\x44\xd7\x3f\x89\x87\x59\x56\x4e\x48\x1f\xed\xe0\x60\x28\x66\xcb\xd9\xb1\x22\x24\xcd", 96, { 0x4c, 0x22, 0xab, 0x04, 0x6c, 0xee, 0xab, 0xc7, 0x2b, 0x36, 0x9d, 0x34, 0x1e, 0x33, 0x3e, 0x9d } },
{ "\x6e\x2f\x4e\x6d\x66\x1f\x69\x1e\x6c\xf4\x59\xbd\x35\x37\x6e\x03\x1b\x28\xb5\x04\x6d\x0f\xda\xd1\x70\xb1\xb8\x3e\xf7\xde\x7f\xbe\x9a\x27\x81\x6a\x96\xae\x99\x20\x41\x16\x38\xc8\x28\x98\x0d\xb2\xc7\x37\x51\x1b\xe4\x0c\xac\xcf\x88\xbb\xc7\xe8\x9b\x06\x6f\xb4\x1b\x80\x62\xb5\xf8\x59\xba\xc2\x90\x58\xf3\x4a\xc2\xe8\x9b\xb8\xb1\x49\x95\xf3\x17\xe7\x09\xfd\x43\xc8\xec\xbb\xb0\x1e\x27\xd3\x44", 97, { 0xf5, 0xdd, 0x03, 0x8a, 0xea, 0x22, 0xdd, 0xfb, 0x84, 0x4d, 0xe1, 0x47, 0x01, 0x83, 0x5f, 0xfa } },
{ "\xfc\x66\xb1\x22\x26\x80\xa5\x2c\x01\x7e\x26\x18\xa9\x4a\x3a\x2d\x21\xf6\xed\x9b\xa5\xa5\xfe\x75\x1c\x1a\x9a\x4c\xa9\xdb\x40\x69\x61\x01\xc3\xa6\x17\x9f\x6e\xe9\x52\xcd\x41\x3e\x60\x50\x5a\x91\x84\xe7\x6f\x05\x4a\x34\x78\xf3\xfc\x46\x69\x82\x2d\xbf\x1d\xdf\x72\xa7\x4e\x19\x30\xcc\xc1\x9c\x91\xd3\x7f\x48\x91\xe0\x3d\x2e\x34\xdd\xdd\xe7\xda\x6b\x0d\x90\xaa\x63\x0e\x65\x2c\x07\x8f\xf9\xf3\xcb", 98, { 0x3d, 0x89, 0x3a, 0x16, 0x78, 0x2f, 0xa7, 0x44, 0x52, 0x47, 0x8e, 0x03, 0xaf, 0x55, 0xc5, 0x4e } },
{ "\xb7\x0f\x77\xb9\x42\x19\x01\x85\xc3\x4a\xbf\x2d\x07\x5a\xf7\x4c\x9a\xd6\x59\xfd\x63\x21\x6d\x66\x6a\x0b\x2b\xe8\xba\x0b\xa9\xe1\x62\x1c\xef\x1c\x95\x32\xe6\xd6\xf1\x58\x6f\x6e\xdd\x68\x91\xa8\x0b\x66\xca\x9d\x1d\x79\x86\x43\x64\x86\xad\x95\xc3\x6a\x57\x5f\xb2\xd4\x9b\xa8\x5e\xbe\x85\xbb\xee\x86\x25\xd4\xe1\xad\xc4\x64\x93\x2b\x32\x01\xde\x6c\x3b\xed\xdd\xeb\x38\x41\xcc\x6a\xc1\xb7\x0b\xcb\x80", 99, { 0x19, 0x77, 0x92, 0x3c, 0x8c, 0xe5, 0xc6, 0xcc, 0x04, 0xed, 0xe0, 0x02, 0x69, 0xa4, 0xb0, 0x14 } },
{ "\x11\x42\x8e\x21\x28\x9e\xe8\x65\x94\x38\x7c\x56\xf5\x97\xb8\x95\xdc\x4c\xcf\xcf\x5a\xbc\x4e\x4e\x22\xfa\x5d\xab\x5d\xd2\x95\xa2\x0b\xe3\x78\xfe\x10\xe4\x91\xb3\xe3\x07\x29\x0a\xce\x7a\xa3\xf7\xe2\x0a\x75\x86\x7b\xe2\xc3\x74\x6c\x72\x9a\xda\xc6\x6b\xc5\x04\x4a\xe6\x50\x60\x69\xa4\x97\x92\xf3\x70\x09\xa4\x64\xa1\x7f\x5b\xc8\xbc\x33\xa5\x47\x36\x5e\x2e\x51\x56\x72\x11\x66\x39\xf1\xab\x82\xef\xe8\x8c", 100, { 0x55, 0x0f, 0x1a, 0x5e, 0x93, 0x15, 0x75, 0xf2, 0xb7, 0x6b, 0x67, 0x79, 0x45, 0x36, 0x12, 0xe1 } },
{ "\x24\xc3\x0c\x87\xfa\x99\x6f\xe9\x2b\x6f\xdf\xc6\x40\x06\xdc\x6f\xb0\x9a\x33\xff\x06\xcc\x3b\x15\xb1\xa1\xab\x9c\x68\xa3\x17\xc5\x38\x25\x05\x16\x2b\xa4\xb0\x9c\x97\x49\x58\x3c\x00\x8c\x44\x28\xab\xf2\x56\x6d\xc0\xf6\x49\xa8\x1a\x06\x89\xd8\xaf\xa4\xae\x4f\x97\xad\x97\xc3\xba\xb7\x1f\x81\x1b\x93\x7f\xc1\xbb\xf1\x40\x14\x2b\x23\xda\xfa\xb2\xfa\xee\x90\x16\xcd\x1d\x66\x0b\x98\x83\x6f\x35\x40\xd4\x11\xdc", 101, { 0x7e, 0x9b, 0xb1, 0xbf, 0xc2, 0xf0, 0xd6, 0xa9, 0xd1, 0xa3, 0x5d, 0x1f, 0x74, 0xae, 0xd6, 0xc5 } },
{ "\xf4\x11\x5d\x2b\xb0\xe6\x59\x25\xdb\xa3\x78\x25\x10\xd5\x2b\x10\xfa\x81\x90\x19\x59\xfc\x49\x30\x21\x6a\x68\x08\xc1\xd1\x86\xc3\x81\xd9\xf3\x3d\x04\x22\x07\xf2\xed\x42\x97\x8d\x9e\x59\x83\x0e\x1c\x53\x6d\x96\x0f\xba\x84\xd2\xe5\x36\x7e\x02\x15\xa2\xa4\xa5\x81\xcb\x07\xc3\xf4\xc7\xd0\xd6\xcb\xf0\xb5\x56\xb1\x7c\x88\xe0\x83\xba\xe9\x02\x52\xeb\x1c\xa5\x86\xbb\xf5\x18\x1f\x5a\xf0\xb5\x17\x3c\x5d\x32\xe4\x69", 102, { 0x0a, 0x37, 0xdb, 0x00, 0x9a, 0x07, 0x39, 0xfd, 0xd4, 0x36, 0x02, 0x15, 0x7e, 0xce, 0x60, 0x3c } },
{ "\xb8\x86\x19\x81\x17\x75\x2c\x50\xbc\xba\x52\x0a\xca\xd0\x87\x23\x15\x5e\xaa\xcd\x12\x3d\xc4\x0f\xed\x27\x69\x7f\xba\xfd\x37\x9c\xc3\xb4\x7b\xca\xff\xde\xf4\xea\xa4\xd1\xaf\x95\xd4\x76\x11\xf6\x72\xb0\xf3\x41\x18\x66\xe7\xd4\x21\x2f\x9a\xe8\xe1\x99\x52\xf4\xf4\x16\x96\x9e\xb4\x84\xec\x5e\xc4\x57\xbd\xb2\xc9\x66\x55\xf8\x39\x4e\x07\xeb\x4b\x87\x6d\xbc\x0e\x01\xcd\xcc\x96\x7a\x98\x04\x50\x5f\x0c\xd2\x77\x5c\xf9", 103, { 0x5f, 0x1c, 0x52, 0x55, 0xd4, 0xde, 0x34, 0x35, 0x0d, 0xbb, 0xf3, 0x57, 0xa6, 0x15, 0x3f, 0x6f } },
{ "\xc0\x9f\x82\xa0\xb2\x6f\x14\xb6\xbc\x6c\xee\x89\x9e\xf0\x93\x3a\xdd\x39\xd1\x28\x7f\x9d\x13\x6d\x62\xdb\x43\x82\x39\x8e\x5a\xfe\x24\xbb\x85\x7b\xeb\x89\xf5\x99\xd8\x10\x44\x1c\x2e\x49\x7f\x31\x49\x26\xd4\xce\x53\xbb\xd9\x37\xa0\x99\x76\x9c\xa7\x3c\x71\xdf\x9e\x9a\x94\x74\xb1\xdb\x7d\x62\xde\x28\x02\x66\x70\x9d\x3e\x36\x15\x58\x3d\xe3\x9e\xb3\x40\x57\x54\x8e\xad\xfe\xf1\xc5\x96\x82\x42\xc1\xe4\xae\x57\x2a\x20\x74", 104, { 0x3c, 0xd5, 0x22, 0xa8, 0x37, 0xc4, 0xe8, 0xad, 0x2a, 0xf4, 0xf6, 0x6d, 0x03, 0x38, 0x4e, 0xa9 } },
{ "\x63\xba\xdd\x0a\x37\x2d\xc6\x2e\x56\x54\x40\x8b\xb4\x49\xbb\x34\x10\xbd\x9a\x4d\xeb\x6d\xbc\xc8\x23\xb4\xa7\x4d\x31\x39\x51\x33\xfc\xc8\x8a\xab\x57\x3f\x09\xfd\x1e\x9f\x11\xea\xe8\x42\xbf\x2f\x69\x2b\xf3\x2b\x67\x79\xd9\x98\x57\xf6\x13\x75\x94\xcc\x85\x04\x3b\x57\xbc\xef\xa5\x16\xfb\x86\x82\xba\xe4\x23\x8e\x61\xc0\xaf\xf3\xdc\x6b\x3d\x3b\x2c\xdf\xd4\xf0\x0c\x5b\x4a\xd3\xa8\x2f\x4b\xb5\x6b\x27\x79\xf5\xf6\x91\xae\x96", 105, { 0x34, 0x7e, 0x78, 0x3a, 0xaa, 0xd6, 0xde, 0xb7, 0xf4, 0x1f, 0xf8, 0x13, 0x4d, 0xe7, 0xba, 0x0f } },
{ "\x2c\xed\x5c\xfb\x6a\x31\x16\x42\xd4\xb6\x27\x3b\xcb\xc2\x60\x04\x7a\xb3\xf0\x42\x90\xc4\x6b\xfe\x08\x7f\xed\x19\x23\xbf\x58\x6d\x78\x61\xb8\x82\x21\x87\xc8\xea\x17\x88\x8e\x3a\x98\x77\x21\xa5\xc4\x4f\x8b\x36\x48\xb8\xc9\xaa\x31\x78\xef\xe7\xe2\x79\x68\x1d\x21\x72\x5b\x78\x4b\x35\x2a\x7f\xa8\x95\x14\x0c\xd9\xf2\xfa\xe8\x6e\x63\x3f\x02\x94\x7e\xc8\x4c\xeb\xc7\x23\x33\x76\xb2\xc4\xb9\xac\x56\x6a\x30\xab\xb1\xa0\x95\x8c\x92", 106, { 0x56, 0x75, 0xe1, 0xc5, 0x9d, 0xa4, 0xf9, 0xec, 0x3b, 0xf3, 0x7d, 0x8f, 0xb3, 0x9a, 0xef, 0xb5 } },
{ "\x5f\x86\xd1\x27\xd0\xe1\xfb\x23\x30\xfb\x39\x8b\xcd\x7a\x3a\x1e\x2d\xd0\x23\x5f\x4d\x54\x9d\x40\x07\xfe\x05\x6d\x8d\xbf\xc7\x32\x11\x7b\xc5\x09\x87\xa4\xf0\xc4\x82\x74\xfa\x53\x3b\xc7\x22\x33\xb1\x92\x2e\x74\xea\x04\x77\x64\x57\x37\x1e\xdd\x55\x93\x5c\x28\xd0\xc0\xf8\x8d\x02\x45\xd1\x79\x59\xc2\x9b\x49\x77\xc6\xa7\xb9\x53\x4e\xda\xe4\x7c\xdb\xbf\xf7\x7e\x2e\xb9\x76\x5d\xa3\x51\x2a\x3e\x28\xae\xa6\x26\xd8\x22\x75\xd9\x38\xe0", 107, { 0x4e, 0xe1, 0xda, 0x97, 0xf7, 0x4d, 0xce, 0x3c, 0xae, 0xd0, 0x5a, 0x17, 0xa8, 0x9d, 0xea, 0x3f } },
{ "\xe3\xe4\x0c\xcc\x64\xb7\xd7\x6b\xa4\xea\xee\xfa\x2b\xa0\x38\x9a\xac\x09\x84\xa8\xeb\x01\x87\x2b\x4a\xd6\x71\x67\xee\x27\x2a\x8e\x92\xe7\x2e\x96\xe8\x81\x02\x26\xa7\x16\x51\xa9\x36\xe1\xa8\x85\xf3\xbc\xcb\x66\x20\xbb\xb2\xb4\x6a\xf6\x23\x23\x18\x65\xed\x68\xcd\xe3\xbe\x09\xf9\x55\xa2\x4d\xe2\xe4\x18\x53\x4b\x66\xd3\x3f\xbe\xda\x0a\x8f\x7b\x12\x7c\x8b\xfd\x6b\x04\xdb\x25\xb8\xd4\x33\x06\x3d\x51\x29\x4c\xd7\x8b\x26\x49\x39\x57\x7b", 108, { 0x56, 0x6e, 0x56, 0xba, 0xee, 0x28, 0x17, 0xeb, 0x10, 0xc1, 0x9b, 0xf4, 0x7b, 0xbe, 0xf1, 0x99 } },
{ "\x9d\x51\x0b\x8b\x82\xc9\xd6\x26\xb2\xa9\xb9\xf7\xf5\x19\x26\x13\x4a\x44\x33\xb1\xb1\x59\x7b\xf9\x93\xab\x92\xf5\xcf\xf8\x22\xa4\x63\xc7\xa7\x2d\xb2\xea\xc3\x31\x77\x23\x3e\x39\x47\xe3\x9e\x4e\xbc\x2e\x5f\xa8\x44\x9a\xd0\x7e\x84\x75\x8a\xfc\x6a\x06\x8d\x53\xce\xec\xf8\xea\xc4\xa4\x65\xb2\x80\x26\xdf\x97\xe6\xae\x81\x12\xae\x98\xac\xdb\x31\x64\xe4\xbc\xd2\xda\x47\x81\x0b\x47\xac\x0c\x13\xe5\x54\x85\x29\x58\x4f\xae\x80\x55\x6f\x54\xc7", 109, { 0xca, 0x20, 0x60, 0x9f, 0x8f, 0x6d, 0x8f, 0x91, 0x97, 0x72, 0x2c, 0xe3, 0xfd, 0x2a, 0x05, 0xa5 } },
{ "\x5d\xe1\xe1\x54\xa7\x6d\x0f\xec\x1c\x4a\xb7\x31\x7c\x9e\xc7\xa9\x9e\x92\x52\x67\xd4\x0f\xc2\x58\x6d\x17\x28\x2c\x54\xc2\xb4\xde\xd5\xd3\x40\xe2\x80\xf6\x06\xeb\x26\x98\x73\x56\x00\x63\x13\x36\xf0\x55\xab\xfc\xdf\x7c\x65\xc3\x45\x50\x26\x36\xc6\xac\xfc\x1f\xfa\x6b\xb3\x8c\x45\x9b\x86\xa0\xe5\x61\xf3\xf3\x0b\x69\xa7\xa7\x20\x07\x99\x08\x28\xef\x33\xdf\x44\x8d\xaa\x54\x51\x02\x6f\x7d\xae\xbc\xbd\x87\x1c\xb1\x53\x7f\xbe\x38\x3c\xbe\x3f\x84", 110, { 0x51, 0x82, 0x43, 0x6a, 0x40, 0xa6, 0x76, 0xb1, 0x58, 0xa4, 0x3d, 0xb1, 0xd3, 0xcd, 0xde, 0x96 } },
{ "\xc9\xef\x12\xbd\xa1\xbe\xd5\xbd\xef\x1f\xcf\x64\xb9\x38\x98\x98\x51\xec\xd8\xdc\xe4\x05\x27\x8c\x2f\xfd\x14\xb2\x52\x69\x41\x89\xbd\x03\xac\x8c\x47\x52\x08\x39\x5d\xf8\x49\x67\x57\x98\x3f\x41\xe6\x62\x5a\xde\xaa\x3c\x8c\x7e\xe0\x8e\x4c\x64\x39\xaa\xb6\x4b\xc5\xd7\xcf\x86\x0e\xf9\xe7\xb7\x42\xde\x17\x2b\x87\x27\xea\xd1\x73\xd1\x18\xd5\x94\x5f\x6d\xde\x29\xa6\xc9\xe0\xf4\x34\x40\x9e\x27\x5e\x61\xc0\x7b\xe5\x94\x8c\x60\x44\x9d\x44\x4f\x99\x3d", 111, { 0xbc, 0x5f, 0x18, 0x15, 0x64, 0x22, 0x74, 0x18, 0xa4, 0xa0, 0x96, 0xdb, 0x00, 0x70, 0x9d, 0xb4 } },
{ "\x1c\x3b\x2d\xf7\xd7\x25\xa2\xf0\xfd\xcf\xb8\xf0\xbb\x88\xbc\x85\x57\x26\x8d\x46\x4e\x12\x4c\x35\x0b\x7d\xa0\x3e\x46\xb1\xa2\xdc\xf8\x82\x6c\xdb\xf6\xe3\x39\x38\x31\x95\x39\x24\x89\xf9\xf4\x27\x49\x07\x58\x62\x43\x57\x61\xed\x89\x5d\x63\x5e\xc5\xb2\xf7\x6b\x36\x8d\x80\xa7\x48\x50\x59\x68\xce\x3e\x9d\xca\xde\x4b\x92\xcc\x49\x0c\xb2\x97\xb5\xce\x58\xdf\x59\xc2\x04\x62\x55\x56\x4b\x8e\xac\x9e\x5e\x40\xdf\xf1\x34\xa6\x27\x91\x57\x45\x4e\x82\x48\x16", 112, { 0x3e, 0x3d, 0x87, 0x58, 0x0e, 0x01, 0x69, 0x78, 0x5c, 0x50, 0xd4, 0xb4, 0xf2, 0x87, 0x15, 0xf9 } },
{ "\x2a\x7b\x90\xcc\xb7\xfa\x65\x31\xd0\x72\xf5\xae\x8a\xa0\x51\xe9\x2d\xfc\xf9\x89\xd0\x4a\x00\x15\x90\x4f\xdc\xfa\x6c\xa1\xcc\xab\xc0\x98\xe6\xe3\x5c\x61\xbc\x06\x41\x30\xaa\xa5\xf7\x95\xbf\x20\x8e\xe8\x46\x66\x2f\xdf\xf0\xd9\x5d\x3e\x9f\x4c\xce\xad\xd1\x2e\xe0\xa5\xa7\xc0\xba\x84\x91\x82\x00\xc1\x99\xac\x32\x39\x48\xd8\xa2\xa8\x38\xbd\x10\x33\x38\x15\xe3\x21\x15\xa0\x06\xaa\x0b\x42\x5d\xe8\xc8\x48\xe3\xea\x19\xc8\x62\xe8\x34\x26\xcd\x90\xa1\xb3\x3d", 113, { 0x19, 0x09, 0x55, 0xc0, 0xf8, 0xda, 0x05, 0x7f, 0xa9, 0x81, 0xbc, 0xa1, 0xcc, 0x3f, 0xe1, 0x11 } },
{ "\x7f\xc5\x6f\x87\xcb\x0c\xef\x76\xbd\xb2\x5a\xaa\x9c\x2f\x8d\x0c\xa4\x3d\x5f\xec\x16\x87\xfe\xba\x69\xef\x78\x5e\x9e\x7c\x56\x34\xb1\xdf\x63\xa7\x2b\xa0\x8a\x69\xd4\xea\xdd\x4c\x86\xef\xb2\xc0\x1d\xf9\xe8\xea\x8b\x0f\x47\x5d\x08\x40\x05\x77\x66\x8f\x65\x5a\x82\x7a\x7a\x86\xd7\x29\x0a\x10\x2c\x30\x8d\x81\x6e\x01\x55\x4e\x98\xf1\xc7\xef\xce\xe5\xc7\x9e\x8a\x99\x32\xad\xed\x8c\x85\x84\x37\x8c\x9b\x36\x52\xd9\x93\xc0\x89\xf9\xd0\xdd\x56\x18\x19\x89\x58\x19", 114, { 0x4e, 0x98, 0x6a, 0xa5, 0x5f, 0x57, 0xb4, 0x40, 0xd2, 0xf5, 0xf2, 0x3b, 0x19, 0xa7, 0x14, 0xa0 } },
{ "\xe1\x10\x63\x3d\xa2\xd1\xb2\x6e\x62\x94\x37\x29\x58\x85\x13\x06\xa7\xcd\x21\xe6\x49\xcc\xad\xb8\x07\xf4\x43\xe7\xa4\x45\xa1\x64\x1a\x61\xce\x4b\xfc\x4b\x44\x35\xfa\xc0\x48\x19\x83\x32\x5b\xdf\x85\x5d\xc8\x83\x50\x88\x5e\xe2\x98\x5a\x38\x25\x99\x57\xb8\xc7\x55\xf5\x92\x44\xf9\x5f\x04\x5f\x5e\xc5\x24\x10\xab\x5e\x51\x09\x17\xfb\xcb\xe4\xcc\x49\x5f\xeb\xe7\xa3\x3b\x83\x9c\x92\xe0\x35\x77\xe2\x34\x5a\xbd\x62\xb7\x63\xf1\x37\xce\xc3\x72\xdd\x3b\x79\x41\xbc\xae", 115, { 0x73, 0x08, 0xff, 0x52, 0x04, 0x9f, 0x74, 0x4f, 0x24, 0x7d, 0x90, 0x31, 0xe7, 0xa5, 0x0d, 0x41 } },
{ "\x3d\xe1\x62\x74\x46\x57\x63\x4e\xb6\x51\xca\x5d\xa3\x63\x3b\x38\xc3\x6c\xa7\x20\xb3\x17\xaa\x4b\xe4\x7d\x84\x5c\x23\xe8\xb2\xf4\xc3\xb3\x28\x62\x68\x4d\x2e\x76\x73\x5c\xd4\x73\x05\xfe\x13\x22\xb2\x2a\x82\x03\xc4\x35\xb1\x9f\x29\x71\x26\xf9\xfa\xf0\xf2\x22\xa8\x66\xee\xec\xc5\x2c\x97\xb6\x6d\x61\x83\x67\x4f\x2b\x80\x76\x5b\x1b\x48\x25\x0a\xaf\xe2\xcd\x45\xf0\x97\x55\xf3\x3c\x8f\xbc\x22\x1e\x09\xd6\xd1\x59\x34\x14\x57\x04\xac\x7b\x74\xcf\x94\xb7\xf3\x63\x4c\x49", 116, { 0x57, 0xd9, 0xf8, 0x21, 0x2c, 0xc8, 0x6d, 0xe5, 0xdc, 0xbf, 0x77, 0x8f, 0x8b, 0x15, 0x62, 0xfd } },
{ "\xf8\xe8\xcf\xdb\xec\xa0\xac\xb4\x01\xb0\x9f\x46\x64\xff\xcc\xe5\xff\x37\x97\x92\xe7\xe9\x22\xf6\x69\xcd\x64\x6a\xac\x27\xe3\x33\x03\x44\x0e\xcb\xd2\x23\x39\x5a\x19\x31\x35\x44\xa2\x2d\x8b\xdb\xc3\x2b\x55\x35\xd1\xb4\xba\x19\x21\x0a\x04\x13\xbc\x89\x60\xa7\x9e\x28\x31\xa2\xab\x1f\x10\x8c\x2f\xa3\x65\x39\x10\xcd\x9b\x7e\x93\x99\x03\x01\xc7\x09\x47\x2a\x92\x69\x88\x36\x56\xae\x17\x6a\x3f\xf8\xcd\x64\x2b\x37\x08\x8c\x37\xe9\x42\xaa\xe2\x01\x4f\x92\xe1\xe3\x33\xfa\x7f", 117, { 0x88, 0x12, 0x61, 0x94, 0x21, 0xa3, 0xcb, 0x31, 0xa8, 0x60, 0x2d, 0xff, 0x13, 0x02, 0x40, 0xa5 } },
{ "\x8c\x9d\x6c\xad\xcf\x04\x56\xad\xba\x5d\x3f\x57\x17\x76\x14\x07\x0e\xf2\xa1\x24\xe8\xe1\x1b\x4d\xee\xfb\xd9\x21\x70\x7a\x23\xab\xe1\x91\x23\x69\x20\x8c\xf9\xf8\xd2\x85\xea\x5d\xea\xc0\xb8\xf2\x4a\xa4\x0c\xeb\x83\x57\x10\x84\xb9\xf4\x19\xc9\xa2\x6c\x82\x01\xad\xf6\x94\xb8\x3f\x34\xa1\x68\x18\xe4\x30\xc3\xd4\x3f\x52\xa0\x8e\xf2\x13\x7f\x9f\xb6\x0c\xba\x84\x8e\x15\x4b\xdd\x9c\x19\x34\x92\xa1\x02\x8f\x10\x10\xd2\x32\xb1\xcd\xd3\xfe\x3a\x87\xe7\xc5\x7e\xae\xf9\xd5\x1f\x13", 118, { 0x1f, 0x41, 0x60, 0x14, 0x09, 0xd9, 0xff, 0x1c, 0x0b, 0x49, 0x8c, 0xd7, 0xbe, 0xa9, 0x80, 0x08 } },
{ "\x94\x25\x74\x1c\x92\x1b\x86\xa0\xec\xf8\x35\x65\xb1\xe1\x78\x33\x12\x8b\xc2\x94\x9a\x81\x7f\x2b\x7a\x15\xbd\xaf\x02\xe1\xe8\x82\x2c\xf9\xae\xf2\x53\xaf\x01\x0b\x01\x01\x3b\x16\xe5\xa3\x5b\xb3\xe3\xa5\x6d\x8e\x46\xc2\x08\xc1\x11\x44\xf1\xc6\x73\x96\xdd\x17\x58\x68\x54\x64\x1c\x79\xb1\x70\x5a\x04\x46\x89\xe3\xc9\x9c\xa2\xcb\xb6\xd8\x0e\x9d\x32\x39\xdb\xae\x07\xbd\xc9\x8f\xe9\xe3\xe6\x9c\xa7\x8c\xc7\xb1\xed\xfb\x65\xfc\xb3\xfb\x91\xee\x46\x20\x15\x4b\xf1\x25\x69\x62\x48\x74", 119, { 0x58, 0x70, 0xa6, 0x06, 0xac, 0xb1, 0xee, 0x1f, 0x53, 0xe8, 0x72, 0x20, 0x3c, 0x05, 0x3b, 0x77 } },
{ "\x0f\x56\x10\xc5\x8c\x9a\xce\xde\x03\x7b\xeb\x78\x6f\xd7\x81\x42\x7c\xeb\xc4\xff\x03\x4a\x0f\xca\x20\xea\x8a\x7a\xf2\x59\x76\x20\xef\x0d\x15\xad\xe1\xd8\x39\xb1\x81\x7a\x67\x3e\xae\x50\xa6\xeb\x4a\x2b\xea\xf4\xb2\x3c\x18\x7f\xd8\x2b\xb6\xf9\xfe\x46\x31\x9f\x10\xd6\xc9\x19\x9f\x8e\x1d\x40\x76\x1d\x4e\x00\xdb\xe3\xd3\x59\x63\xbf\xd9\x7f\x72\x07\x55\x22\x4f\x91\xa7\xc8\xe0\xee\xec\x55\x06\xb7\xe0\xad\x97\xff\x6e\x70\xf4\xe8\xd7\xe4\x47\x51\x7a\xf1\x5c\xad\x45\x45\x18\xef\xb9\x98", 120, { 0x26, 0x76, 0x51, 0x74, 0x00, 0x2d, 0x40, 0xe3, 0x3e, 0x70, 0x71, 0xc5, 0xf9, 0x19, 0xf1, 0x8b } },
{ "\x9a\xa1\x93\x09\xec\x14\x1c\xa7\x65\xb2\x0f\x0d\x9f\x6f\x22\x25\x11\x5e\x33\x0d\x48\x60\x10\x16\xc7\xf7\xe3\xe9\x77\x38\x38\xc4\xcc\xdf\xad\xe2\x77\x7c\x35\xf9\xc1\xcc\x08\xdd\x8b\x23\x2b\x42\xdf\x04\x97\x9e\x32\xd3\x09\x2d\x38\xa1\x65\x0e\x64\x27\xc8\x8b\xfb\xcf\x29\x76\xd4\xeb\xaa\xae\xae\x08\x81\xc1\x2e\x5d\x7d\xab\x73\x5e\x38\xbd\x58\x6e\xd8\x99\x45\xb1\x81\x5a\xb2\xff\x5b\xd0\x3a\xf4\x3b\xe8\x57\x24\xf0\x2b\xc0\x6c\x2d\x5d\x5c\x64\x0e\x45\xe1\xf0\x48\x8d\x0e\xf6\xf2\xbf\x81", 121, { 0x3a, 0xfd, 0x49, 0x99, 0x51, 0xab, 0x70, 0xe1, 0xaf, 0x2e, 0xa2, 0x10, 0x8f, 0x52, 0x9c, 0x6e } },
{ "\x29\xea\x6f\x36\x04\xe5\x78\x9c\xd3\x17\x5e\x55\xeb\x7b\xd3\x8b\xfa\xbf\x55\xea\x79\xd0\xf4\x3d\x3e\xfd\x31\xa8\x2d\xca\x02\x7f\x0f\x54\xf3\xc2\x7b\x5c\x66\x37\xf0\xf1\xfb\x22\x05\xbe\x0b\xa2\xb7\xee\x4d\xab\xe2\xb7\x9b\x9b\xcb\x8a\xcf\x7b\xda\xd5\xc7\xd5\x65\x73\x89\x2d\xa6\xb2\x7f\x1d\xcf\xfe\xe3\x10\x34\x2e\x36\x9b\xa7\x6b\xe9\x73\xe2\xb9\x1f\x0f\x1c\x23\x8a\xdb\xbe\x87\x72\x15\x2f\xfb\xd4\x48\xcc\xdb\xa7\x63\xf3\x71\x3a\x76\x3e\x3f\xb9\x08\xce\xeb\xce\x17\xbd\xc8\x63\xad\xb5\xfd", 122, { 0x1a, 0xfc, 0xd7, 0x74, 0xe1, 0x55, 0x51, 0x67, 0x6b, 0xe7, 0x1b, 0xc8, 0xd4, 0xab, 0x39, 0x47 } },
{ "\x86\x1b\x9f\x54\x66\xd5\x73\xa1\x7a\xe9\x2e\xcc\xc1\x1d\xe2\x7e\x24\xa5\xe7\x64\xf7\x7e\x2f\x23\x9e\x6a\xb7\xd8\x4c\x88\x1a\x4a\xe7\x8f\x40\xaf\x08\xa7\x33\x17\x1e\xe4\x12\x79\xb1\x60\x1e\x59\xc4\xf3\xf1\x12\x55\x91\xcf\xc5\xfe\x41\x15\xde\xbd\xb6\xce\x40\xd1\x8c\x65\x0d\xbb\x20\x74\x13\x64\x0c\xbb\xd6\x5d\x3e\x2c\x36\x40\xb3\x22\xbd\x36\xd5\xb2\x87\x93\x6f\x1a\xce\x9b\x49\x57\x12\x68\x65\xd2\xe1\xe3\xd4\x3a\x48\xef\x35\x6d\xd6\xa6\xcb\x8f\x49\xbb\x3a\x3d\xd8\xff\xde\xdc\x1c\xff\xc9\x0b", 123, { 0x31, 0x66, 0x6a, 0xcb, 0x7f, 0xb2, 0x21, 0x3b, 0x4c, 0xb3, 0xbc, 0x28, 0xe1, 0x8b, 0x32, 0x58 } },
{ "\x9c\xca\x57\x37\xf3\xd0\x6e\x4c\xa0\xe5\x57\x89\x6a\x65\x34\x6c\x6e\x72\x1d\xcd\xc7\x59\xdb\x07\xd8\x13\x40\x50\x39\xe7\x21\x2a\x3b\x2d\xf2\xf2\x1a\x2d\xfc\x96\xbe\x25\x3d\x64\x2e\x69\xdc\xfd\x92\xa5\x47\x68\xe2\x3e\xeb\x43\x31\xd7\x8f\x14\x90\xf0\x4e\xbd\xa0\xa8\x2f\x0e\xb8\xa3\x62\x75\xae\x06\x1a\xd0\x46\x9f\x01\x63\x35\x22\x5d\xe5\xd0\x8e\xbd\xb5\x56\xaf\x5f\x2a\xd6\xbc\x22\x07\xbf\x20\x22\x0d\x02\x56\xf5\xab\xe6\xed\x81\xd1\x68\xab\x78\xe2\x4c\xce\x72\xc8\xc4\x6d\xa5\x21\xbc\xfe\x43\x97", 124, { 0x69, 0x78, 0xc0, 0x8c, 0x6b, 0x92, 0x3d, 0x79, 0x26, 0x06, 0x37, 0x64, 0x96, 0x5e, 0x9a, 0x6b } },
{ "\xc0\xf1\x71\x4d\x8b\x79\xdf\x75\x2d\x6a\x08\xfe\xd7\x3d\x08\x6b\x46\x31\x15\xbf\xca\x8c\x9b\x94\xf2\x00\xf8\x4c\xd6\x28\xd1\x5e\x01\x31\x0f\xd2\xf9\x96\x7a\xc8\x6b\x03\xf0\x31\xf8\x5b\x41\xa1\x96\xd5\xaa\x3d\xa4\x41\xed\xcf\x8f\x69\x09\xf8\x1a\x92\x9b\x85\x4d\x22\xd1\xda\xfc\x5b\x07\x8a\xf2\x45\x00\x09\xbb\xaa\xc2\x79\x0b\x3b\x0e\xa0\xce\xd0\x7a\xfb\xcd\xcd\x2d\xeb\xfa\xa0\x37\x0e\x58\x66\x8a\xa9\x89\xad\x99\x41\xf5\x54\x8c\x49\x94\x8f\x1d\xf5\x59\x07\x12\x2d\x3c\x1e\x57\x9d\xe2\x50\xb7\xe9\xea", 125, { 0x5a, 0x5a, 0xd4, 0x98, 0xee, 0x0c, 0xa8, 0xdd, 0xef, 0x60, 0xed, 0xc3, 0xf6, 0xd3, 0x95, 0x0f } },
{ "\xb5\x37\xfe\xd6\xa3\x0f\x84\x94\x70\x46\x6f\xa9\x55\xe9\xb5\xf9\x6e\xe7\x1a\x35\xdd\x8c\x26\xe1\x0f\x98\x38\x00\x16\xfc\xb5\x5f\x36\x30\x59\x7c\x7b\x33\xad\x11\x87\x20\x99\x40\x6a\x6a\x11\x5c\xaa\xb4\xeb\x51\x62\x50\xd1\xb2\x86\x51\x52\x5d\x44\x4e\x13\xcd\x86\xb6\x22\xfc\x94\xcb\x6b\xf3\xd7\x3d\x43\xef\xb8\x64\x22\x32\xa7\x18\x6e\x63\x38\x30\x72\xa2\x67\x96\x6d\x2c\xfc\x04\xc7\xa8\x0a\x5d\x5e\x0c\x91\xaa\xff\x2f\x43\xaf\xf1\xeb\x64\x29\xab\xee\xca\xa7\xa5\x1e\x04\x02\x4b\xa6\x97\x7b\x0e\xa2\x63\x6f", 126, { 0x17, 0xca, 0x2b, 0xf4, 0x91, 0x4e, 0x34, 0x11, 0xb0, 0x08, 0x52, 0xdc, 0x95, 0x8c, 0xfb, 0x2c } },
{ "\x95\x63\x25\xb9\x12\x5f\x16\xa4\xaf\xb8\xb0\x8b\x26\x67\x90\x10\x70\x05\x76\xf5\x95\x36\x6a\x9a\xa2\xb2\xfa\x13\xb9\xf1\x9e\xe5\x42\x73\x3c\x5e\x3f\xa9\xc6\x8e\xbe\x83\x01\xe5\x67\x97\x61\x6b\x35\xea\x11\x96\x42\x5f\x0e\xcb\xba\xba\x73\x74\xf2\x4f\xcf\xba\x91\x4b\xb2\xdf\xec\x9e\x47\x3b\x70\x84\x1b\xd2\x38\xaf\xfc\x8e\xbf\x13\xfc\x1d\xaf\x4d\x95\x69\xd8\xb1\xe6\xb0\x3c\xee\x1c\x41\x47\x60\xec\xd2\x1c\xf2\x3c\x80\x0a\xae\xe1\x63\x1d\xe3\x83\xcd\xd1\xf2\x9d\x20\xe2\xb5\xa1\x49\x3e\x8b\x38\xdd\x1c\x04\xa7", 127, { 0x75, 0x90, 0x2f, 0x4a, 0xfb, 0x9d, 0x91, 0xb1, 0x0e, 0x74, 0xd7, 0x1c, 0xb6, 0xb0, 0x98, 0x06 } },
{ "\xce\x26\x26\x4d\xca\xd2\x5a\x49\x30\xcf\xf6\x38\xaf\x9a\x68\x1c\x7d\x2f\xfb\x58\x31\xdd\x49\xd7\x3e\x32\x3e\x4d\x0d\x16\xc4\x96\xb6\xf4\x10\x3a\x5a\x13\x89\x12\x1f\x03\x50\x04\xc9\x32\x70\xe9\xf2\x9e\xa4\x90\xe6\xa5\xbf\xdc\x1d\xf8\xbc\x08\x55\xae\x62\x0b\x4c\x75\x93\x16\x17\xe3\x32\x3b\x22\xea\xaf\x27\xc5\x6a\x31\x10\x7f\xe1\x5f\xaa\xd1\x3d\xca\x52\xb9\xd2\xfa\x4e\xc9\x67\x13\x2c\xe4\x6b\x23\x46\x95\x45\x0b\x67\x0c\xc9\x08\x88\xb6\xc6\xde\xb3\x78\xbc\xa0\x09\x87\xab\x1e\xdf\xe7\x06\xeb\x02\x7d\xc7\x09\x1b", 128, { 0xdd, 0x13, 0xdd, 0xd7, 0xfd, 0x85, 0xb9, 0xfe, 0xcf, 0x5f, 0x63, 0xd8, 0x30, 0x7f, 0x81, 0x37 } },
{ "\xd3\x9c\x34\x2e\x69\x3f\xc8\x3c\xb2\xe3\x4f\x09\xb2\xca\xab\xf8\x31\xf3\xdc\x12\x9c\xf1\x6f\x25\x79\xd7\x84\x09\x85\x50\x7a\xfe\x6d\xcb\x39\x31\x25\xd3\x1b\x5d\xe7\x7c\x78\x8e\xcb\xf9\xcc\x02\xff\x4b\x87\x28\xa4\x14\x72\xca\x46\x8a\xb9\x46\xf5\x87\x99\xf7\x04\xbc\xa6\xb4\x5e\x06\xb9\x6e\x80\xd9\x76\xfd\x16\xd8\x76\xf4\x36\x87\x15\xb0\x33\x18\xd9\x70\x1f\x61\x7d\x9e\xe1\xef\x9a\x2c\xee\x34\xf1\x1a\xa7\xdb\x57\x14\x4f\x3c\x3d\x37\xa8\xeb\xdb\xf4\x29\x6b\xf9\x0d\xdd\x00\x5a\xbd\xda\xa2\xc5\xf4\x5d\x0e\xb1\xc0\x7f", 129, { 0x85, 0x9f, 0x82, 0x66, 0xc3, 0xf3, 0xd5, 0xcf, 0xc6, 0x98, 0x4e, 0xcb, 0x74, 0xa6, 0x30, 0xe5 } },
{ "\xf6\x8c\xc7\x96\x58\xa8\xf1\x2b\xec\xc3\x22\x93\xb6\x31\x25\x2c\xbc\xa8\xa4\x36\xd2\xa8\x53\x4b\x91\x85\x2d\x7c\x66\x12\xd7\x0a\xc6\xec\x20\xbe\x7f\x60\xaa\xe5\x2a\xfa\xa2\xec\xbd\xab\xaa\x93\x3d\x95\xd9\xd1\x90\x77\xd8\x45\x70\xb0\x2d\x54\x7c\xf1\x94\xe3\x68\x84\x89\xb2\x55\x33\xe3\x53\x3c\xd6\x9a\xc7\x83\x7d\xa9\xb4\xb2\x36\x0f\x44\x3f\x7b\xef\x9c\x85\x3b\xd7\xf7\xd3\x83\x1d\x5f\xa1\xc9\x65\x08\xde\xd5\x40\x49\x65\x4c\xef\x37\x8d\xdb\x45\xe0\xdf\xfc\xaa\x21\xe3\x68\x3b\x25\x13\x19\x0f\x7a\xf1\xfb\x95\xd1\x34\x2f", 130, { 0x09, 0x1a, 0xd5, 0xec, 0xd3, 0xed, 0x5a, 0x2d, 0xf5, 0x61, 0xe8, 0x95, 0x69, 0xe8, 0xda, 0x04 } },
{ "\x19\x52\x1e\xfa\x65\x9a\xe9\x50\x84\x52\x5f\xf9\xa2\x6d\x89\x5e\x0f\xfd\x7f\xf3\x62\xb3\x5e\x40\xba\xf1\x58\x8d\x20\x8e\xe6\x29\x08\x25\x18\x57\xf7\x1a\x0c\xd6\x3e\x2b\x7d\x0c\xe4\xae\x73\xce\xa2\x6d\x18\xce\x07\x1a\xab\xa2\xbc\x70\x8d\x6d\xe2\xe9\x79\x2c\x97\x16\xd1\x9f\x98\x9e\x13\xd1\x00\xd5\x6a\x46\x2f\xf8\x61\xc1\xc6\x03\xb2\xaf\xce\x2f\x3d\x33\xf8\x0b\x14\xcf\xff\x36\xb3\xab\x2a\xb7\x4d\x86\xed\xf9\x41\x36\xaf\x66\xac\xdd\xa7\x9e\x18\xaa\xdf\x54\x51\x49\x5b\xc5\x58\xe9\x53\xd6\x71\xe7\x9b\xca\x57\x1c\x23\x9d\x90", 131, { 0x76, 0x56, 0x7f, 0xe3, 0x54, 0xbe, 0xa3, 0x1f, 0xff, 0x3a, 0xb0, 0xfb, 0x98, 0xfd, 0x35, 0x13 } },
{ "\xc0\x9b\xff\xdb\xf9\x2f\xf0\xc5\x04\x70\xf4\x5a\xfe\x52\xf4\xf9\x50\x52\xb1\x41\xb5\xb0\xe5\x27\xea\xda\xf8\x2a\xf1\xe9\x5c\x9d\x01\x44\x85\x23\x0d\x62\x88\x3a\xef\xae\x4f\xed\x31\x83\x77\xad\x78\x56\xc6\x3b\x8e\xf3\x4c\xbc\x0a\xe0\x15\xee\x9e\xde\x87\x7a\xfd\x8d\x5f\x5f\x67\x2f\x42\x8e\xd2\x85\x03\x95\xb7\xd5\x70\x73\x76\x0d\xd9\x8a\x66\x02\x1e\xb2\x7d\xd1\x74\x69\x99\x66\xb4\x29\x69\x1b\x5f\xd2\xfa\x78\x32\x47\xe2\x19\x62\x15\x03\xad\x75\x4c\xfc\x1a\xbd\x72\x32\xbe\x71\x8d\x76\xd1\x69\x5f\x53\xe6\x76\xaf\xf7\x90\x5b\xc1", 132, { 0x0f, 0x4a, 0xeb, 0xeb, 0xe2, 0xf4, 0xfd, 0x1a, 0xa8, 0x5e, 0xe3, 0x62, 0x7d, 0xe0, 0xdf, 0x1e } },
{ "\xbc\x21\x6c\xe7\x51\x8e\xc2\x30\x89\x6e\x19\x3f\xc0\x21\x46\x38\xe6\x0e\x57\xd3\x29\x04\x49\x92\x43\xc2\x60\x0f\x5d\x92\x27\x6f\x9e\x0f\x04\xf3\x55\x08\x77\xad\xbf\x7d\xef\x4f\x75\xc6\x49\x1e\x75\xd3\xe6\x06\xcb\x8e\x67\xc8\xdb\x5d\x08\x4f\x4e\xc3\x96\x20\x97\x26\x0e\xee\x21\xab\xd4\x4d\x17\x3d\x8c\x7f\xcf\xad\x99\x6b\xd4\xf4\x30\xab\x8e\x93\x18\xc4\x90\x1b\x00\x71\x7e\xc9\x7c\x18\x99\x49\x9e\x5e\xe9\x9b\x2d\xd6\x06\x13\x85\xd4\x82\x7a\x0a\x60\xf6\x53\x4a\x46\xa8\x38\xaf\x4b\xd6\x62\xdd\x7a\xa1\x46\xae\x9c\x99\x5d\xc7\xc5\xe1", 133, { 0xee, 0x0d, 0xd8, 0x9d, 0x25, 0x60, 0xda, 0x7a, 0x2c, 0x60, 0x14, 0xc7, 0x3f, 0x1d, 0x3b, 0x3d } },
{ "\xfd\xd7\x0b\xff\x63\x6c\x52\x42\xd2\x71\x43\xd0\xd4\x48\x5b\x4b\x9f\x80\x1f\x20\x93\x33\x6e\x6c\xe0\xff\xee\x8a\x45\x9f\xa8\x3d\xf3\x25\xb0\x77\x90\xd6\xfd\xc4\x57\xa2\x57\x56\x5c\x3e\x6e\xad\xed\xe0\x06\xe3\x14\x96\x50\x91\x3a\x44\x55\x62\xe6\x38\x8b\x32\xa2\x6c\x8a\xe2\xfe\x57\xd8\xbb\xae\x70\xe0\x7c\xce\x40\x02\x01\x46\x22\xc4\x92\x49\x9a\x25\xc6\xf7\x50\x12\x12\x23\xa8\xf2\xf3\x2e\xfe\x5c\xb3\x12\x83\xe8\xda\x7b\xaf\x23\x35\x0f\x62\x9c\x7c\xcf\x9b\x1b\xa2\x95\xd3\xf1\xbe\xbd\xf7\x6b\x91\xe1\x01\x60\xb3\xbc\x32\xea\x5f\x30\xee", 134, { 0x8b, 0x7c, 0xff, 0x2f, 0x5f, 0xb8, 0xe4, 0xec, 0x5f, 0x22, 0xc5, 0x45, 0x5e, 0xb0, 0x62, 0x87 } },
{ "\x31\xd7\x62\x33\x63\x75\x03\xb7\xc0\x50\xaa\x9e\xd1\x87\x5d\xd5\xb8\x2d\x2f\x0e\xa3\xd1\x03\x58\x5a\xa8\x6e\x5a\xf8\x5a\xbb\x2b\xb7\x66\x08\xd1\xe4\x32\x8d\x55\xf1\xb3\xfd\x7f\xa9\xb5\x04\x34\x7e\xc7\xf1\x68\xfe\xc7\x6e\xc1\x64\x05\x6a\xca\x4b\x17\x17\xd0\x7e\x39\x0f\x5d\xea\x5e\x92\x4e\xb5\xd7\xea\x93\x67\x9f\xef\x83\x46\x41\xa7\xda\xc1\x66\x05\x50\x02\xff\xd2\xd6\xa6\x0b\xa9\x70\x89\x05\x1c\xaa\xba\xee\xf5\xb8\x8e\xf2\x96\x2e\xd0\xba\x82\x58\x16\x4d\xf4\x37\x2f\xa3\xad\x19\xb8\xc8\xcc\xd3\xce\xa9\xd5\x9e\xdd\x7f\xd4\x8c\x97\xd5\x9a", 135, { 0x8a, 0xa7, 0x2b, 0xac, 0xd5, 0x8c, 0x7a, 0xae, 0xe0, 0x26, 0xa8, 0xd7, 0xc0, 0xa2, 0x20, 0xb4 } },
{ "\xa5\x01\x37\x26\xa2\xa7\x79\x20\x45\xf0\xa1\x7e\x53\x8c\x72\x49\x2f\x09\x96\x7a\x15\x85\x67\xfe\xef\x7e\x5a\xd9\xd7\xc5\x08\x66\x2a\x91\xda\xbd\x45\xb0\x51\x2d\xdf\xd9\xf0\xe8\x03\x1c\xc6\xbe\xa8\x7a\x9c\x02\xef\x91\xb7\x89\xf8\x70\x4a\xd0\x60\x89\x7b\x3d\x5b\xc4\x10\x7e\x6b\xb0\xb6\x0e\xbb\xd4\xee\xd6\x1a\x24\x94\xf0\x97\x8f\x0d\x86\xb5\xb5\x0d\xd9\x4b\xb6\x03\x5e\xfb\x26\x21\x02\x4c\x1c\x0b\x8f\x67\x6a\x1b\x27\x6b\xe6\x4f\xec\x6d\xe7\xd0\xc2\x0f\xcc\x1f\x2c\xbb\xb6\xde\x53\x7d\x55\x39\x25\x7b\xe0\xef\x9a\x11\x1e\x01\x12\x8d\xa2\xf5\xdb", 136, { 0xcb, 0x2c, 0x7e, 0xc1, 0x58, 0xd4, 0xea, 0xf8, 0xb3, 0xc1, 0x82, 0x69, 0xcc, 0x3c, 0xaf, 0xa0 } },
{ "\x84\x14\xc7\xce\xcf\xa9\x6d\x18\x26\xb4\x06\x16\x56\x56\x9e\x5a\x22\x51\xa0\xcb\xb4\xfb\xd9\xe9\xbe\x4e\x25\x2d\x32\x1c\xb8\x8e\x9a\x60\x0b\x20\x14\xaf\x60\xd7\xee\xcd\xf4\x6a\xda\x5b\xc1\x53\xce\xae\xed\xf2\x7b\xbc\xd2\xd1\x67\x30\xab\x03\xa9\x9d\xd7\xa5\x41\xce\xcd\x86\x11\x3b\x9d\xe3\x7c\x99\x1f\x4b\x9a\x89\xba\xa1\x15\x70\xd2\x40\xa3\x66\xcf\x39\x20\x47\xc7\xb7\x46\xe8\xc7\x84\x0c\x64\xc3\xa4\x99\x94\x17\x1f\xe4\x9c\xb9\xdd\xea\xa2\xfe\xa9\x8a\x9a\x05\x58\x00\x3d\xc4\x03\xfc\x18\xad\x6f\x5e\xc1\xfc\x8e\x91\x24\xa0\x1e\x81\xfb\xc3\x70\x3a", 137, { 0xa3, 0xac, 0x47, 0xc4, 0x7f, 0xf0, 0xfa, 0xcf, 0x5a, 0x75, 0x87, 0xd1, 0x31, 0x8d, 0x68, 0xe1 } },
{ "\x5c\xf8\x43\x1f\x6c\x00\xcf\xc3\x31\x39\xdd\x67\x86\xa4\x13\x11\x27\x91\x4e\x45\xec\xe9\x28\x62\x13\x18\x99\x9c\xb6\x95\xb9\x92\x5b\x0f\xa3\x8c\xaa\x36\x76\x52\x39\x23\x75\xab\x83\x64\x4e\x71\xf8\xa8\x78\x4d\x2e\x03\xb5\x15\x35\xdf\xb7\xbf\x08\x80\xdf\x00\x1e\x32\x20\x85\x20\x11\x02\xcd\xb6\x75\xc3\xa1\x7b\xf8\x98\x31\x0f\x25\x11\xcd\x4a\xbe\x9a\x3c\x8d\xaa\x1d\xbd\x35\x79\xc5\x97\x29\x96\xde\x5f\x93\x08\xd8\xc6\xac\xe4\x6a\x1c\xaf\x53\xd4\x65\xef\x3c\x3c\x16\x04\x8d\x3a\x6d\x21\x2b\x6f\x6a\x81\x74\x50\x6d\x00\x6d\x01\x6d\xc6\x8d\x5c\xd2\x1e\x25", 138, { 0x4a, 0xa7, 0x74, 0xe4, 0x2d, 0x30, 0x28, 0x86, 0xda, 0x3f, 0x18, 0x0c, 0xef, 0x15, 0xa0, 0xb2 } },
{ "\x10\xf0\x65\x6a\xe6\x21\x1d\x21\x1f\x7e\x21\xe5\xfa\x3a\xf3\x18\x52\x9b\x31\x64\x63\x95\x27\xed\xad\x04\x7d\x15\xf1\x85\x11\xeb\x58\xf2\xe0\x31\xb3\x79\x1d\x08\xdd\x59\x64\x3a\x3d\x38\x08\x24\x68\x23\x88\x3e\xe3\x22\x21\x48\x06\x77\x7d\x13\xfb\x73\x89\xea\xe6\xf6\x64\x9b\x1f\x81\x73\x25\x9a\xf9\x91\xff\x68\xfb\x64\x03\x56\xd6\xcb\xf6\xb3\x29\x73\xb4\x30\x1a\x89\xfc\xdf\x30\x89\xd6\x5e\xce\x35\x9d\x0d\x4d\xa2\xad\x7a\xb5\x6c\xa9\xde\x17\x0a\x69\xc1\x89\x3c\x7f\xb8\xbf\xa1\x65\x4f\x42\x65\x44\x01\x50\x17\x63\x64\x51\x98\x2a\x62\xf1\x2f\xd2\xa1\xde\xba", 139, { 0xa0, 0x86, 0x6f, 0x0d, 0x37, 0x2f, 0x7e, 0xf8, 0x9a, 0x82, 0x03, 0x51, 0x49, 0x80, 0x26, 0x4c } },
{ "\xac\x04\x20\xff\x0a\x4b\x0f\x21\xce\xd6\xf6\x2e\x8d\x87\x43\xd5\x5f\xc4\x67\x35\x45\x9c\x50\xd0\x38\x01\x80\x9e\xca\x33\xca\x3e\x7b\x3e\xa9\x48\x9b\x99\x3d\xbd\xd6\xf0\xe3\xa0\x61\xfc\x6f\x9e\xc0\x8d\x09\xe8\x31\xa9\xa1\x21\xb8\xcf\x10\x73\xc8\x54\xcd\xbc\x8b\xef\x48\xe6\xce\x50\xe6\x55\x8c\xea\x9a\x79\x16\xd2\x1c\x83\xdc\xbf\xc9\x34\xda\x31\x17\xd0\xa1\xaf\xb3\x32\x00\x29\x39\xf9\x50\x7b\x8f\xe0\x59\x12\xdf\x2c\xe4\xa9\x2f\x3e\xde\x2d\x9e\x48\x26\xbf\x3d\x1c\xf4\xd7\x87\x20\xe5\x86\x7f\x5e\xa7\xd4\x65\xb8\x3d\x47\x14\x38\xef\x85\xfb\x86\xd2\x11\xb5\x73", 140, { 0x0f, 0x04, 0x8f, 0x85, 0xdf, 0x16, 0x04, 0x94, 0x33, 0x86, 0x13, 0xb6, 0x66, 0xea, 0xc7, 0x9c } },
{ "\xef\x31\x48\xb1\x13\xf0\xf7\xa5\x33\x40\xc4\x55\x79\x37\x86\x7c\xef\x7a\xa2\xbd\xc7\xb7\x69\xf4\x44\x4c\x0e\xa7\x49\x05\x42\x9b\x7c\x02\x6e\x83\x17\xb7\x8c\xd4\x4b\x0e\xa3\xb4\x50\xa7\xdd\x10\x0e\x3f\x46\xcc\x61\x2e\x23\x16\x0d\x0a\xed\xd4\x3e\x6a\xe9\x3b\xba\xc5\x65\x81\xa5\x78\x91\xaa\xf0\xe6\xf7\x7f\x60\xb9\x98\x9e\x36\x47\xa5\xaa\x80\x11\xd6\xa2\xfc\x65\x6b\x4f\xa4\x23\xbd\xd7\xdb\x9c\x80\x70\x32\x96\x98\x2e\xd7\x6c\x94\xfc\x9a\x52\xcb\xa9\x9d\xb7\x12\x1a\x98\xc3\x17\x9e\xc7\xff\x5d\x5f\x70\x14\xd4\xf3\x14\xac\x14\x12\x32\x75\x36\x62\xb2\x44\x4f\x6f\xf5", 141, { 0x4c, 0xac, 0xbf, 0x73, 0xc7, 0x06, 0x64, 0x46, 0xda, 0x86, 0x95, 0xdf, 0xb9, 0xb8, 0xb5, 0xd6 } },
{ "\x56\x8a\xb6\x76\xb1\xe1\xe0\x1d\xa9\x78\x0c\x20\x7e\x96\x45\x96\x23\x40\x13\x9a\x19\x74\x2d\x18\x7a\xff\x4c\x37\x12\xfb\x1a\x63\xa8\xe9\x49\xf6\x5a\x66\xc1\x82\x26\x8d\xf1\xbd\x85\xea\x47\x0a\x31\x16\xba\xa0\x08\xf4\x84\x59\x09\x86\xee\x19\x7b\x6d\x43\xd9\x2c\xfb\xe6\x4f\xd7\xf6\x80\x3c\x9f\xec\x51\x51\xb8\x2e\xa8\xbf\x25\x6a\x6e\x5a\x9b\xe9\xdc\x69\x85\x61\x4c\x0c\x21\x78\x2d\x4a\xc7\xd6\x11\xb7\x4a\xe5\xe1\xbe\x77\x28\x30\x91\xba\x35\xac\xaa\xe1\x50\xb1\xfc\xf8\xa6\xf7\xbb\x52\x23\x6c\xc5\xa9\xf0\x1d\xab\x5d\x8c\x4d\x60\xd8\x86\xa8\x7d\x13\xd4\x91\x2f\x31\xd4", 142, { 0x63, 0x7e, 0x3a, 0xe0, 0x18, 0xd6, 0xf3, 0xb6, 0x8a, 0x93, 0xae, 0x2c, 0x75, 0x4d, 0x53, 0x73 } },
{ "\x98\xbe\x49\xdc\x07\xba\x41\x7f\x9b\xc4\xd5\x5f\x50\xf6\xb7\xaa\x56\xf0\xd1\x33\x1f\x80\xa6\x2d\x9a\xed\xd6\x86\x7a\xe0\xc0\xde\xaf\xf0\x42\x22\xf6\xc9\x9c\xc9\x8c\xf8\x72\xab\xfe\x55\xf0\x79\x1c\x0c\x08\xdd\x0b\x4a\xd6\x2f\x7a\x82\x25\xd0\xed\x59\x0b\x27\x35\x34\xaf\x36\x00\x5b\x2b\xd8\x8c\xca\x8c\x99\x77\x94\xf6\x32\xb3\xf5\xe5\x9f\x95\xf2\x8e\xdf\x0c\xaf\x64\x48\xee\x4d\xb6\x84\x6e\x7d\x75\x2c\xaa\xa6\x14\xff\x61\xaf\x88\xbe\xc2\x69\x6f\xa8\x5f\x9c\x4d\x8d\x41\xad\xf1\x91\x15\xe8\xf6\x80\x83\x70\x65\xc8\x9f\xc0\x78\x17\x78\xdd\x79\x92\xce\x1a\xc9\xae\xe2\x87\xfd", 143, { 0xa5, 0xdb, 0x01, 0x1b, 0x71, 0xec, 0xf0, 0x5c, 0x2f, 0xaf, 0x35, 0x2c, 0xf6, 0x74, 0xfd, 0x3e } },
{ "\xaf\x65\xf8\x23\xbb\x92\xad\x22\x9a\x57\xa3\x3f\x0d\xae\x76\xbc\xc8\x0b\xf1\x9b\x0d\xee\x34\x80\xe5\x98\x81\xb4\xfe\xda\xa3\x46\x1c\x08\xfb\x4c\x3d\x0d\x28\x47\x4e\x98\x52\xa4\x83\x74\x13\x5f\x57\xf6\x03\xc2\x20\x8f\xdf\x4b\x4d\x82\x55\xac\x40\xaf\x6f\xec\xc2\x8d\x99\xab\x27\x36\x51\x82\xff\x9c\x6a\x89\x76\xd9\xfc\xf2\x49\xa5\xeb\xd2\x65\xe1\x13\x00\x1e\x50\x0d\x16\x08\x65\xa1\x95\x76\x36\xc8\x25\x8d\x90\x5c\xf9\x03\x25\x5e\x51\x7a\xe1\xe3\x19\x73\x5a\x9d\xaf\xf0\x66\x02\xc2\xab\xc6\x1b\x55\xec\xff\xeb\xe3\x0b\x49\x7a\x9d\x82\xa1\x7d\xcf\xae\xf9\xa3\x60\x22\x90\x7e\x78", 144, { 0x70, 0xcb, 0x37, 0x7b, 0x48, 0x29, 0xcd, 0x1c, 0x9e, 0xe6, 0x27, 0x97, 0xf6, 0x5f, 0xb5, 0xdd } },
{ "\xa8\x5c\x0c\x3c\xd5\xa2\xe2\xb5\x48\xa8\xf4\xce\x7f\x83\x03\x7c\x55\x0a\xa3\xf8\x1c\xeb\xe8\x28\x53\xdb\xad\x1c\x04\xe9\x80\xb3\xbd\xec\x2d\x5e\x28\x1b\xe6\xfc\x4a\xbb\x0c\xe5\x54\xf3\x9c\x02\x29\xbb\x39\x19\x6d\xf3\xd1\x27\x46\x90\xef\xe6\xb3\xf1\x9d\x6e\x85\x50\xf4\xf8\xfd\x53\x42\xbd\x04\xc2\xd6\xfd\x01\x54\x6a\x1b\x5b\xa2\x2e\xe5\x8b\x3d\x6d\xf2\xdc\xdb\xde\x24\xc2\xac\x89\x4e\xd4\xcb\x54\xef\x68\xd0\x2a\x1b\xca\x82\xc7\x7a\x18\xa9\x22\x6a\xbd\x02\x76\x40\x88\x4c\xef\xac\x68\x80\xee\x3a\xc6\x1d\xf9\xf5\x7f\xa1\x42\x26\x7d\x13\xd2\x3a\xf1\xbd\x52\xc1\x2f\xf8\x76\x93\x25", 145, { 0xaa, 0x81, 0x41, 0xd2, 0xf2, 0x8b, 0x40, 0x43, 0x3f, 0xcb, 0x21, 0x01, 0x79, 0xcd, 0xe5, 0xf0 } },
{ "\x1f\x06\x0d\x79\xa6\x8b\x79\x3f\x43\x92\x8c\x54\x4a\x9f\x08\x5a\x16\xb2\x82\x50\xa3\x6f\x3e\xcc\x39\xec\x36\xd8\x43\x1c\x39\x67\x3e\xd2\x30\x72\xcd\x75\x7d\xb4\xe9\x3f\x7c\xfe\x35\x31\x2b\x37\x6f\x97\xe6\xf4\x03\x33\x4b\xb0\xba\x09\x3c\xa8\x8f\xc6\x02\x56\xa2\xce\x8f\x87\x46\xe1\xdc\x1b\x35\x69\x71\x59\xe3\x62\x03\xec\xef\xe6\x37\x7e\xb6\x54\x85\xf0\x02\x1c\x37\x33\xe0\x2a\x91\xc6\x8f\xed\x0b\xcc\x94\x03\xba\xc9\xeb\x83\xcd\xf9\x58\xe6\x32\x4d\xdb\x92\x58\x03\x41\x05\x10\xe0\xd7\x9b\x8d\x0d\x3a\xfb\x8a\x8c\x4a\x24\x8a\x55\x3d\x10\x3b\x11\xcf\x02\xf4\x72\x97\x71\x5d\x2d\x75\x91", 146, { 0x08, 0xbc, 0x75, 0x2b, 0x6e, 0x0a, 0xe8, 0xd2, 0x25, 0x7e, 0x6c, 0x2b, 0x47, 0xf3, 0x79, 0x7f } },
{ "\x2e\x95\x28\x7a\x10\xd5\xfc\xf7\x9f\xca\xa0\xee\x91\x7b\x16\x67\xf0\x36\xc3\x3a\x83\x48\xa6\x59\x4b\x58\xa5\xb8\x29\x60\x03\xd5\x9d\x49\xee\xe7\xa9\x23\x35\xa7\xd3\xfe\x17\xb5\x4a\x67\x37\xa9\x20\x82\xab\xb7\xb6\xc1\x33\xfe\x35\x3e\x86\x6d\x38\xbb\xc8\x52\x87\x33\x19\x81\xff\x1c\x47\x1d\x8d\x3c\xa3\x06\x59\x25\xf1\xdf\xff\x4f\x79\xba\xf8\xd0\x3a\x63\x17\xba\x3e\x46\x30\x11\x09\xfd\xd3\x67\x2b\x7a\x36\x16\xf5\xce\x30\x1a\x48\x93\x62\x89\x89\xfc\x70\xaf\xb0\x77\x6d\xca\x80\xfc\x55\x5f\xd1\xf6\xb3\x37\x09\xca\x63\xf9\xa9\x08\x72\x65\x03\x2d\x21\x2a\x0a\x12\x09\x65\x41\xf5\x58\xb8\xd6", 147, { 0x43, 0x95, 0x30, 0xa4, 0x0d, 0xbc, 0x2d, 0x03, 0x2c, 0x61, 0xc3, 0x95, 0x63, 0xd2, 0x7d, 0x95 } },
{ "\x23\x39\x02\xc8\x3e\x52\xc0\x42\x30\x67\x00\x0c\xad\x1c\xfd\x17\x5e\xd7\x5c\x36\xaf\xc4\x02\xec\x36\xf2\x90\x60\xbe\x9a\x7c\x6d\xf0\x80\xcd\xd6\x9d\x73\x72\x97\xab\xee\x40\x56\x99\xe1\x87\xf8\xb0\x89\x4f\x50\xc8\x7b\x34\xf3\xb4\xc1\xdb\x27\x4b\x1b\x10\xfa\x14\x67\x7e\x6e\x8d\x1b\x0a\xd2\x18\xee\xcc\x2c\x83\x96\xaa\xd2\x32\xad\x93\x17\xeb\xad\x55\x23\x3e\x1a\x1c\xdc\x8f\xbf\x88\x00\xc1\x10\x69\x55\x81\xae\x1a\xf7\x2c\x0a\x77\xd0\x5e\x21\x7c\x27\x18\x65\x7b\x4f\x8d\xbc\xf9\x7f\x89\xa1\x26\xc2\x7f\x69\xf8\x05\x2d\xa0\xe3\x4e\xee\x92\x37\x0c\x9c\xe5\x15\x89\x1f\x63\x0f\x7b\x97\xd6\x5c\xba", 148, { 0x7a, 0x61, 0x56, 0xe4, 0xc4, 0xdf, 0xf9, 0x6d, 0xc2, 0x11, 0x35, 0x16, 0xf1, 0x9a, 0x6e, 0x89 } },
{ "\x22\x0c\xb4\x0d\x4a\xfa\xce\x1d\x0e\xfd\x74\x8b\x8b\x3b\x3f\x1d\x47\x28\xf5\x13\x1b\x25\x7b\x98\xba\x42\x78\x54\xe2\x24\x89\x1e\x1d\x02\x1a\xcf\x34\xc9\xe7\x32\x31\x60\x10\x17\x10\x06\xd2\x87\x02\xd7\xe8\x11\x5d\x6d\x7d\x43\x23\xa2\xcc\x35\x2c\x74\x56\x3f\xf3\x02\xbf\xca\xfb\xb3\x46\x44\xdc\x76\xdf\x2d\xee\x23\xef\x4e\x90\x00\xa3\x0a\x16\x60\xee\xcd\x4d\x67\x1d\xa1\xab\xe8\x18\xdf\x18\x6f\x37\x02\x53\x5a\xbe\x97\x03\x22\xf7\x51\x5b\xb7\xea\x39\x68\x0a\xbc\x02\xfa\xa4\xa2\x7a\x2c\x73\x80\x1d\x92\xa6\x22\xc4\xff\xad\x15\x7a\xf0\x63\x23\x6f\x99\x48\x6a\x06\x89\xe7\x18\x09\xfc\x56\xc6\xfc\xbd", 149, { 0x69, 0x0d, 0x82, 0x75, 0x0b, 0x92, 0x08, 0xa6, 0xae, 0xc8, 0x57, 0xe3, 0x5e, 0x0f, 0x9f, 0x3e } },
{ "\xdc\x6d\x8f\xb6\xad\x09\x2d\x16\xd0\xc8\xb1\x1d\x21\xef\x38\x87\x73\x4a\x11\x92\xcd\x4e\xd1\xae\xd5\xcd\x84\xc1\x4b\x54\xfd\x14\xac\x24\x4f\xdd\xf7\xcc\x54\x69\x8b\x5f\x6a\xe6\x2f\x57\x3e\xca\x2c\x06\xc0\xe4\x95\xb5\x36\xfd\xa7\x5b\x6d\x2a\x4b\xfb\x09\xb1\xb8\x9b\xfe\x96\x35\xdc\x17\xc1\xfc\x3b\xb4\xcd\x3a\xe3\x91\x6f\x33\x2c\xc0\x81\x83\xb4\xb9\xaa\x7f\x18\x88\xac\xba\x50\x24\x4a\xa4\xa5\xe0\xd4\x4c\x4f\xfb\x50\x46\xaf\x52\x47\xa7\x25\x34\x29\x2d\x8f\x56\x5e\x7c\x5f\xdd\xea\x83\x58\x99\xbb\xfd\xe5\x52\x92\x14\x16\x3a\x8c\x1f\x37\x81\x4c\x8c\x0f\x08\xc7\xd9\xb2\x2d\xac\xbc\x03\xc5\x6e\x63\xca", 150, { 0x7f, 0x92, 0xfb, 0x9d, 0x68, 0x56, 0xeb, 0x53, 0xe8, 0xab, 0x26, 0x4d, 0xd0, 0x3f, 0xf8, 0xe6 } },
{ "\x28\xef\xd6\x6e\x65\xca\x78\x4f\x96\x3d\xac\xc2\x4f\xb2\x93\xaa\x30\xe8\xf4\xaf\x9a\xc3\x35\x1e\x7e\xac\x86\x5d\x51\xa6\x1d\x09\x1c\xef\x9b\xae\xaf\x4f\x8e\x22\xf5\x00\x10\x7e\x63\x39\x8c\xba\x8b\x59\xa0\xe4\xcd\xad\x1e\xfd\x2c\xde\x2d\x70\x3e\xfa\x8d\x30\x3d\x1d\xf8\x6d\x3c\xbb\xa3\xf2\x73\x8d\xe4\x1e\xbb\x16\xed\x7d\x15\xd1\xb6\x02\x64\xf9\xf9\xe3\x3b\xf4\x57\x11\xd1\x5d\x98\x53\x11\xad\x10\xfe\xce\x85\x1c\x53\x14\x9a\xcc\x75\x99\x3d\x9b\x05\x53\x86\x59\x5c\x23\x1c\x29\x64\xaf\xa4\xa6\x13\x4d\xc4\x21\x85\x1a\xd3\x06\xb6\x2b\x1f\x5d\xd9\xdb\xd9\x6c\x57\x44\xa1\x79\x67\xc9\xaa\xac\x46\xcf\x8a\x13", 151, { 0xf3, 0x88, 0x0d, 0xa8, 0x7a, 0x3e, 0xd7, 0x9e, 0x5d, 0x42, 0x7d, 0x7a, 0xaa, 0xbc, 0x6e, 0xd4 } },
{ "\x14\x8b\xa1\xc0\x4b\xf6\x23\x0d\xdc\xde\xc3\x00\xe7\x16\xfd\xc9\x17\xce\x00\x68\x99\xfd\x37\x6b\xb7\xfa\x73\xd5\x15\x2a\xb7\x1b\x86\xd4\x9f\x48\x8c\x11\x6d\x40\x6a\xdf\xb1\x21\xe8\x54\x95\xc5\xa3\xef\xc2\x64\x0e\x0a\xf3\x57\x09\x6c\x14\xf7\x1c\xfb\x16\xa4\x50\x8e\x52\xe1\xaa\xe0\x97\x9d\x45\xa1\xd2\x8d\x0b\xa7\x59\xb4\x0f\x43\xd4\x04\x8a\xae\xc8\x1e\x71\xa1\xc1\x36\xaf\x03\x1c\x12\x04\xbd\x6e\x31\x79\xaa\x95\x08\x7f\xaa\x59\x67\xa4\xd6\xbd\xfb\xf1\xcd\xe8\xec\xe2\x2d\xab\xa7\x02\x1e\xaf\xb6\x23\x08\x3c\xca\x37\x64\xa8\xdb\xcf\xb0\x5a\x66\x2d\x7c\x7a\xd5\x07\xa2\x37\xfd\xdb\x93\xb4\xc1\xe9\xcb\x90\xd3", 152, { 0x60, 0x2b, 0xfd, 0x08, 0x46, 0xc2, 0x5c, 0x0d, 0x1d, 0x63, 0x05, 0xd8, 0x59, 0x14, 0x44, 0xd1 } },
{ "\xd2\x66\x10\x9b\xcb\xcd\xeb\x30\x7e\x89\xb2\x83\x7d\x38\xdb\x9b\x63\x9c\x69\xfa\x89\xd0\x39\x1f\x62\x97\xea\x25\x74\xcd\x6a\x89\xf3\xff\x1a\x09\xfc\x16\x9d\xa7\x6b\x2e\x42\xcc\x59\x85\x0b\x8a\x35\x8e\x5a\xfa\x7e\x25\x37\xc4\x1a\xde\x40\xbd\x56\x76\x2e\xab\x7b\x6b\xff\x23\x09\xa7\xc6\x93\x93\x57\x0b\x5c\x36\xdb\xe0\x17\xb7\xd6\x81\xf9\x38\x64\xa7\x51\x97\x6b\x69\x2e\x64\x0b\xcf\x1d\x7c\x2f\xf5\x0f\x46\x45\xd9\x5a\x8a\x0a\xc1\xd6\xe9\x7e\x4b\x28\xfd\xf7\x13\x1b\x0e\x52\xfa\x2a\x6d\x44\x19\x1a\x71\xce\x43\xc4\x0b\xcf\x2f\xf0\x08\xb3\x4a\x5d\xe4\x49\x18\xde\x45\xb3\x43\x9e\x1b\x77\x42\x84\x51\xb2\xa7\xb1\x30", 153, { 0xa7, 0xb7, 0x03, 0xe9, 0xa9, 0xbe, 0x15, 0x1f, 0xd7, 0x1d, 0x12, 0xad, 0xa6, 0x66, 0x1e, 0x57 } },
{ "\x6b\x05\x26\x51\x05\x7b\x83\x3d\xcf\xe2\xeb\xa3\xb6\x8f\x03\x34\x1a\xc5\x18\x1f\xbd\xba\x60\x24\xd8\x44\x58\x57\x48\x20\x4d\x74\xe5\xdf\xf7\xd9\xf3\x6e\x3f\x24\xb2\x40\x22\x69\x10\x1a\xad\x10\x7f\x7a\x28\x4a\xe0\xa5\x4f\x2e\x9e\x4c\xbb\x74\xd8\xda\x60\xcc\xb6\x5d\x2f\xdc\xdd\x0e\xdc\xd5\xfd\x7f\xba\xb0\x87\x60\xc2\x0b\x7c\xed\xb2\x9a\x61\xf8\x52\x4b\x4f\x8e\xd1\xfa\x27\x49\x4e\xce\xe2\x32\x74\x2e\x06\x50\x3d\x64\x34\xd1\xd7\xcc\xde\xd4\xa3\xb8\x17\xd1\x5a\xe4\x83\xa6\x4a\x90\x6d\x3f\xbf\x40\xf7\xe0\x7d\x0c\x6c\x12\x68\xa4\xb2\x28\x46\xe4\xdb\x6c\x9d\x10\xda\xeb\xb7\xac\x52\xda\xc4\xfb\x8a\xa4\x1e\x12\x7d\x91", 154, { 0x83, 0xe9, 0x8a, 0x97, 0xe1, 0xf0, 0xd6, 0x1c, 0x85, 0xf4, 0xed, 0x7e, 0x3a, 0x05, 0x3d, 0x25 } },
{ "\xa7\x97\x0f\x14\x4e\x1b\x59\xb1\xb1\x12\x25\x89\xdd\x6b\x75\x83\x30\xd0\x3e\x19\x5f\x7c\x32\xbc\x94\xb3\xbb\xe5\xb0\xe3\x03\xba\xae\x55\x30\x58\x27\x90\xad\xc3\xf2\x4a\xa4\x68\xe3\x0c\x88\x4a\xb4\x61\xce\xd1\x02\xba\xbb\x2c\x6e\xe1\x58\x5e\xe4\x18\x83\xec\xf8\xce\x20\x22\x6c\xfd\x6c\xfd\xce\x23\x72\xb8\x3e\xda\x96\xf6\x4e\x16\x4e\x88\x02\xfb\xf1\xdc\xf6\x59\xa7\x03\x9f\xc5\x80\x5d\xa9\x55\xa2\xe3\x80\xf7\x9a\x11\xeb\x6e\xd3\x6e\xd2\xea\x24\xb9\x20\x44\x83\xb2\xc3\xd3\xd7\x82\xd0\xed\xec\xc8\xc4\xfe\x80\x40\xe6\x3e\x7a\x12\xc8\x12\x3a\xb5\xec\x01\x0b\x7e\x82\x51\xb5\xc9\x4f\x3e\x30\xc2\xaa\xd6\x72\xd1\xa1\x74\x69", 155, { 0xad, 0x2e, 0x8c, 0x9f, 0xe8, 0x7f, 0x9d, 0xba, 0x71, 0xc8, 0x41, 0x50, 0x33, 0x22, 0x2e, 0xaa } },
{ "\xa0\x38\xd6\x05\xca\xd1\xdd\x6c\x21\xa7\xe2\x51\x9c\x74\xb0\x5f\xdb\x33\x21\xcf\x59\x00\x58\x19\x2d\x1b\x98\xc6\x7d\x0b\xbf\x64\x7f\xdf\x63\x94\x2d\x90\x88\x3d\x85\x82\xfa\xe3\x7a\x29\x4d\x12\x7a\xc8\x6f\xf4\x9d\x55\xe7\x02\x67\x79\xac\xd7\x3a\xb3\xa4\x20\x5b\x9c\xb8\xb0\x9f\x45\x90\xb0\xb1\xbc\xf0\xf4\x03\xae\xae\x68\x4f\x26\x4f\xa9\xc9\x74\x3e\xb0\xe3\x28\xa8\xa9\xbc\x3d\xf7\xe2\x26\x54\xe8\xdf\x52\x15\x4b\x8a\x1b\xba\x57\x87\xeb\xa7\xa7\xa6\x4e\x31\xd5\x72\x11\x7f\xb1\xe6\x16\x8e\x1f\x3f\xb7\x4e\x8a\xed\xd5\xea\x09\xa3\x7c\x25\x0c\x8d\x34\xdf\xc2\xa1\xe7\xb8\x0b\x0f\x6a\xcb\x15\xd2\xaa\x9b\x95\x68\x74\x0c\xa4\x9e", 156, { 0x0d, 0x7b, 0x57, 0x4b, 0x26, 0x26, 0xcd, 0x87, 0x41, 0x4e, 0x6a, 0xff, 0x3b, 0x56, 0x58, 0x20 } },
{ "\xe0\x88\x34\x85\x5b\x42\x2d\x81\x50\x97\x28\x7f\x73\x90\xc7\x46\xaa\x84\xaf\xe7\x97\xdb\x23\x4f\xc6\xed\x3e\xfb\x70\x08\xcc\xca\xea\x91\xc6\xee\xad\x41\x69\xfc\x02\x91\xf2\x24\x4a\x31\xf8\x7a\xe7\xb1\x65\x72\xcb\x43\x12\x6b\x9b\x97\xff\x62\x7f\xe6\x2c\xc7\x89\x0b\x16\x6c\xbf\xcb\xd1\x9a\xc7\x35\xbe\x3e\x2e\x25\xea\x41\x54\xe2\x04\xf5\xf8\xe7\xf8\xab\x5c\xbf\x2c\x61\x15\x09\x56\x98\x71\x9b\xf8\x44\x84\xc3\x79\xdd\xd1\xa9\xe1\x93\x92\xd0\x31\x9e\xa5\xbb\x5d\xb3\x13\xac\xe7\x92\x3d\x88\x19\xbc\xa5\xd6\xdf\x43\x56\xe6\x3f\xb9\xf1\x0e\x75\x4a\x56\x10\xaf\xe6\xeb\x97\x61\x96\x8c\xe0\x46\xf0\x0f\x76\xf5\xa6\x72\x15\x1c\x38\xa4", 157, { 0x73, 0xb6, 0x03, 0x35, 0xf6, 0xda, 0xb2, 0x8b, 0x69, 0xf1, 0x80, 0xf7, 0x40, 0x0b, 0x06, 0xc0 } },
{ "\xb0\x5b\xca\xec\x8e\xbf\x10\xfa\x8f\xd9\x65\x89\x8d\x7a\xfb\xad\xde\x0e\x2d\xbe\xf5\x93\xf1\xe1\x28\x34\x69\x30\x8c\x86\x98\x85\xfc\x5e\x31\xe8\x39\x4c\x8b\x92\x2b\xb9\xb2\x9e\x46\x99\x97\x4b\x08\xcc\x67\xf0\x9e\x17\xf9\x7d\xa6\xb9\x60\xa9\x10\xad\xa0\xbd\x1e\x7c\x7e\xfd\x8a\xbb\x70\xae\xc6\x28\xb4\xc9\x5e\x5d\x7d\x3a\x7a\x2f\x47\xd5\x7f\xa6\x4c\xd6\xd6\x98\x0f\x13\xc4\xe4\x15\xc0\x78\x48\xb3\xdf\x24\xe0\x03\x42\x43\x3c\xf0\x3e\xf8\x1c\x71\xee\x97\xca\xd3\x21\x3d\x14\x2e\xe1\x99\xe5\xf9\xa1\xce\x80\xba\x02\x71\x58\xd6\x42\xad\x8e\x86\x41\x86\x07\xea\x31\x3a\x29\x37\xda\xc3\x33\x0d\x88\x7a\x37\xe4\x92\xd5\xb4\xa4\x87\x51\xd4", 158, { 0x26, 0x34, 0xea, 0x0f, 0xa7, 0x52, 0x4b, 0x9a, 0xb2, 0xdf, 0xcc, 0x1f, 0xf1, 0xd3, 0xd0, 0x78 } },
{ "\x53\xf6\x6b\x3e\xe8\xda\xc8\x99\x3f\xa0\x74\x9f\x26\xd7\x47\xd9\x94\x73\x64\xfc\xf9\xbe\xdb\xe0\xb1\xdc\x6e\x19\x92\xf9\x71\x34\xa4\x11\xca\x90\xf0\x5b\x18\xd6\x71\x53\xf0\x16\x28\x63\x43\x7f\x4b\x2d\xdb\xbb\x9d\x77\x04\xe5\xd9\xb4\x47\x28\x48\x2b\x52\xf5\x72\xc6\xf3\xe0\xf1\x79\x43\x18\x60\x4d\xe0\x81\x73\x08\x52\x70\x21\x7b\x8f\x02\xfb\x68\x99\x19\xa0\x0d\x45\xf4\x49\x52\x18\x6a\x80\x8a\x4a\xc3\xee\xe9\xec\x33\x51\x83\x25\xf4\x8c\xfd\x98\xff\xd8\xd2\xe1\x66\x19\x44\x3f\x51\x4b\xdd\x4c\x93\x1b\xa0\xe6\xe8\x92\xd1\x32\xcd\xec\x5e\xb7\xfc\x87\xe9\xa5\x83\xf7\x73\x39\x80\x36\xa6\x38\x7b\xf9\xbe\x98\x60\x1d\x16\x3e\x17\x40\x4b\xe2", 159, { 0xe6, 0xaf, 0x93, 0x2f, 0xba, 0x00, 0x41, 0x8d, 0x2c, 0x9f, 0xc7, 0x71, 0x81, 0x48, 0x2c, 0xfe } },
{ "\xc2\x6d\x0f\x10\x92\xef\x8c\x47\x47\x46\x72\x44\x42\x38\x9f\x59\x48\xfe\x6a\xf6\xd5\x9f\x8c\x49\x1a\x5b\xac\x02\x96\x3d\x86\x2f\x4c\xd3\xb7\x47\xa6\xfa\x27\x42\xe6\xd3\x13\xe5\x45\xd2\xb6\x1c\xaa\xf5\x93\x7f\x08\x11\x62\xf7\x54\x47\x94\x7a\x22\x96\x85\xf1\xdb\x8b\x3e\x3b\x9d\x13\xe3\x4b\xaf\x71\xbf\x6d\x9f\x4a\xea\xa6\xfb\xdd\x95\x38\xa8\x51\xf2\x44\xe2\x27\xc2\x8a\xd0\xcf\x7c\x4c\xc3\x56\x17\x52\x0b\x3c\x75\x06\x76\x64\x6c\xbf\x66\x24\x71\x1b\x8e\x7c\xe3\x85\x49\x64\xf2\xd6\x96\x3e\x2a\x17\xb4\x6b\xa0\x65\x56\xfa\xb7\xed\x84\x7a\x8f\x17\x0e\xf0\x0b\xc0\xad\xe4\x7e\x9f\xb7\x96\xf2\xc9\x7e\x4e\x14\x4f\x47\xd1\xbf\x05\xe2\xef\x23\x5e", 160, { 0xc1, 0xf7, 0x9a, 0xa7, 0xb1, 0x56, 0xdd, 0x85, 0x09, 0x0d, 0x4f, 0xc0, 0xd1, 0x25, 0xb0, 0x3c } },
{ "\xeb\x39\x92\x73\x9b\xd1\xe7\x22\x4b\x5a\x93\x53\x87\x0e\x45\x56\xcb\xed\x35\x68\xdd\x13\x0e\x55\xc6\x23\x76\x37\x6e\xdf\x3b\x5c\xd3\xda\x1b\x45\xe6\x44\x30\xcb\x02\xe4\xf6\x5d\x09\x25\xfb\x64\x1d\x47\x22\xc7\xf8\xb6\x93\x8a\xe9\x78\x42\x91\x6c\xbf\x1b\x83\x64\x9e\xc7\xca\xf7\xd9\x1e\xb6\x06\xd5\x29\xc1\x48\xae\xd2\xed\x02\x67\x2b\x4a\x65\x3c\x57\x94\x83\x14\x19\xf8\xef\x12\xf7\xf7\xf1\x4b\x0a\x64\x63\x92\x65\x87\x2c\x6e\x20\x64\x56\x2d\x00\x15\xcd\x12\xc4\x54\xb6\xa9\x0e\x15\x69\x3c\xec\x50\x0d\x5e\x03\xdc\xcf\xa4\x57\x77\xbf\x74\xb9\xe2\x47\xf5\xce\x29\x48\x26\xb7\x01\xd2\x0a\x62\x49\x80\x01\x6d\xb3\x6e\x33\xb2\x7c\xd9\x25\x73\x57\x51", 161, { 0x25, 0x9e, 0x60, 0x07, 0xc6, 0x99, 0x48, 0xe3, 0x91, 0x92, 0x4e, 0x39, 0xd7, 0x44, 0x7f, 0x63 } },
{ "\x6d\x23\x68\x9d\x82\xcf\x6b\x2b\xad\x27\xf5\x32\x1c\x2d\xd3\x66\x15\x79\x8f\x57\x48\x26\x11\x67\x3d\x5d\x61\x66\xec\x7c\x8a\xcc\x6b\xe2\x9a\x92\xc2\x5a\xc7\xad\xda\x21\xac\x28\x94\x95\xb0\xdc\xf7\x7d\x87\xcf\x81\xef\xd2\x27\x9e\xe2\xe2\xc9\x36\x50\x9a\x93\x61\x07\x72\x32\x36\x0d\x98\xa0\xc1\xae\x31\x3d\x12\x24\xbd\x89\x72\xe1\x98\x7c\xb1\x7b\x9c\x82\x9b\x34\xe4\x16\x89\x25\xac\xfa\x13\x07\x54\x10\xe3\x9e\x83\xd9\xa5\xc3\x68\x87\x1a\x0c\x1c\xc0\x4c\x1a\x23\xf2\xdc\x7e\x12\x4d\x77\x48\x4a\x62\x67\x2e\xe2\x56\x45\x56\xa3\xc2\xcd\xc0\x2c\x2b\xca\x53\x36\x19\x30\x83\xf2\xd6\x48\x9b\x8f\xc4\x06\xac\xd8\x5b\x76\x12\xf9\xbf\x55\x9f\x61\xfa\xbc\x67", 162, { 0x02, 0xa5, 0xc5, 0x80, 0xb9, 0x42, 0x65, 0x02, 0xa8, 0x7b, 0x20, 0x32, 0x10, 0x65, 0xad, 0x6c } },
{ "\xc3\xdb\xf7\x28\x38\x6a\x74\x53\x7f\x06\xd7\xbb\x64\x1d\x2a\xd9\x3e\x88\x32\xba\x91\x81\xdd\x86\xd4\x42\xd7\xad\x4e\x3b\x3b\xaf\x1a\xee\x67\x88\x49\x6b\xe8\xb7\x26\x63\x94\xea\x94\xec\xb7\x48\x94\xd0\x65\x5a\xe3\x92\xef\x97\xc0\xfe\x70\x8a\xcb\x6c\x87\xa2\x09\x91\x1f\x96\x04\x01\x69\x73\x10\x45\xeb\x43\xa8\x94\xb2\x5b\xf6\x32\xa3\x42\x71\x62\xf0\x39\xa1\x09\xa6\x6c\xfe\xe1\x62\xb3\xf6\x56\x24\x05\x0e\x01\x3b\x7a\x20\xbe\x60\xfa\xc2\x6c\xdf\x87\xc7\x40\xf0\x25\xdf\xc6\x24\x62\x5a\x76\xfb\x85\x09\xef\x92\x57\x45\xd2\x79\x88\x09\x3e\xa3\xc0\x3a\xe3\x77\x44\x08\xc5\x03\x40\x6c\x8f\x50\xb7\xd1\x91\xd0\x04\xcf\x58\xf4\x0b\x12\xe9\xda\x02\x59\x99\x24", 163, { 0xbf, 0x90, 0x55, 0xe1, 0xda, 0x81, 0x3d, 0x69, 0x5c, 0x5d, 0x2c, 0x5d, 0x96, 0xe9, 0x51, 0x54 } },
{ "\xd6\x9b\x8c\xe4\x3b\x44\xc8\xb3\x53\x9c\xf5\xe1\xfa\x49\xb3\xc6\xac\xeb\x98\x37\x13\xe5\xc5\x14\x31\x3c\x24\xff\x27\x97\x40\x27\xa0\x66\xc0\x42\xdd\x20\x81\x99\x5d\xa4\x4f\x3d\x28\xc1\x40\x57\xb8\xd1\xae\x9d\x85\x80\xcf\xe1\x2c\xaa\xf3\xc3\x3b\x62\x59\x87\x9b\x10\xad\x01\x9a\xb2\x22\xad\x95\x43\x28\xdb\x3e\x38\xca\x07\x90\x27\xa7\x47\x4c\x83\x8b\xdd\x2e\x82\xaa\xec\xb1\x1f\x78\x48\x7c\x3b\x28\x66\x68\xdf\xbf\x72\x2e\x9c\xd3\x80\xb2\x13\xe5\xda\xe6\x91\x58\x78\x5c\xd2\x0e\x8d\x6d\xe7\x9e\x3e\xff\x32\x33\x6d\xf5\x8d\x04\xb4\x39\x8a\x6b\x6e\x5f\xd5\xa5\xef\xf6\xb6\x25\xd3\x70\xb3\x95\x74\xab\x52\x6a\x75\x1b\xfc\x22\x7b\x96\xfd\x1d\xfc\xbc\xa8\x15\x2f", 164, { 0x9c, 0x17, 0x1d, 0x5e, 0x84, 0xcb, 0x1e, 0x60, 0x48, 0xd1, 0x73, 0x61, 0xee, 0xa5, 0xbb, 0x78 } },
{ "\x01\x98\x3c\x23\x59\x11\xf1\xec\x7f\x84\x1e\xf7\xe1\x31\x30\x7f\x2b\xe7\xb4\x18\x6e\xe7\x8b\x69\xed\xe7\xc9\xf7\x84\x32\x30\x1c\xb6\xec\x44\x16\x51\x74\x0b\x3b\xc0\xe2\x63\x4b\xed\xaf\xff\xde\xd0\x74\x00\xfc\x99\xcb\x2c\xcb\x76\x52\xcd\x63\x01\xce\x28\xbe\x4a\x6b\x99\xcb\x7c\x21\x48\xa1\x2e\x33\x8a\xd0\x48\xd2\xd6\xd4\x90\xde\x61\xa3\xd0\xcc\x59\x65\xa6\xa2\xbb\x44\xe8\x1f\xd2\x59\xb1\xf9\x4d\xc5\x0d\x3e\xfb\xe1\x3d\xdc\x25\x8d\xa7\x3c\x88\xa5\x5d\x08\xed\x92\x48\x0e\x67\xfc\xf0\x3c\x29\x9d\xeb\xcb\x01\x31\xe1\x79\x75\x8a\x37\xee\x78\xbf\x60\x40\xc9\x84\xbc\x92\xe9\x95\x2b\xd7\xb7\x4d\x33\xb4\xa9\x53\xca\x84\xa9\x73\xc0\x75\x8a\x8b\xcb\xcf\x25\x9c\x31", 165, { 0x17, 0x89, 0x0e, 0x3e, 0xab, 0xcb, 0x90, 0x4d, 0xe0, 0xf3, 0x5b, 0x5b, 0xfe, 0x31, 0x6d, 0x40 } },
{ "\x55\x14\x00\x0c\xc4\x0a\xbb\x3d\x78\xce\xe9\xf0\x2e\xd2\x57\xc7\xe4\x74\x2e\xa5\xdd\xd0\xca\x1a\xc1\x40\xaa\x66\xe0\x71\x7f\x2c\x97\x23\x67\xb4\xcb\x7c\x33\xdd\x93\x0a\xe4\x9d\xf2\x54\x35\x36\xc1\x1b\x52\xf8\xac\x32\xa6\xad\x53\xf7\xd2\xa4\x90\x6d\xb9\x5d\xd8\xf7\xb8\xce\xba\xb3\xf3\x50\x85\x71\xcb\x29\x07\x4f\x6b\xb6\x6f\xf3\x82\x35\x54\x63\x0b\x2d\xce\x84\x47\x7a\xc2\x2d\xcd\xf9\x3c\xe7\xb7\xcc\xf5\x43\xfe\x4a\xf3\xd8\xe0\x86\x50\xd8\x7d\x7a\x12\x4e\x82\xd1\x39\xf7\xfc\x4e\xd8\xba\x4e\xdc\x5b\xc4\x3e\x32\xe7\x44\x29\x22\xdf\xc0\x57\x7f\x82\x13\x69\xa9\xb1\x03\xef\xb7\xce\x83\x16\x3f\xc1\x82\x7e\xc4\x14\x6d\x2a\xbd\x3e\x48\x91\x3e\xfd\x64\xd1\x46\xdc\xbe", 166, { 0xf1, 0x4c, 0xc8, 0x4a, 0x3c, 0x5d, 0xf7, 0xd0, 0x16, 0x5d, 0x8d, 0xf6, 0xf1, 0x0e, 0x52, 0xec } },
{ "\x03\xa4\xd4\xf4\xa9\x86\x0f\xe5\x44\x9f\xc7\xe3\x03\xf4\x4d\x97\x95\x44\x26\x72\x1f\x12\x50\xcc\x4a\x50\xa2\x9b\x73\xa9\x51\xd0\x06\x6b\x8f\x51\xe5\x10\x4d\x8f\x01\x68\x22\xc5\x0c\x64\x44\xcc\x45\x81\xb2\x9c\x72\xce\x74\x63\xec\x9c\xfa\x3b\xd4\xc2\xa2\x8c\x64\x8a\x55\xfe\x60\x3c\x51\x18\xaa\x44\x01\x7a\xf5\x02\x07\xb3\x92\x2f\x5c\xc0\x66\xe7\x8f\x22\xfd\x57\x29\x9b\xb7\x03\x32\x84\x20\xb4\xcc\xce\x5e\xfd\xfc\x93\xc3\x69\x89\x58\x82\x43\xfd\xe2\x7f\x02\xc8\xb1\x3f\x4e\x84\x1d\xff\xb3\x54\x0c\xe0\xe1\x65\x4e\x3f\x9d\x96\x95\x23\x48\x34\x14\xd0\x0a\xde\xb2\x78\x9b\x88\xeb\x11\xae\x9a\x44\x42\xfa\x38\x69\x77\xe6\x9d\xe4\x13\xd0\xa0\x7c\xc5\xfa\x59\x28\xf4\x11\xdd", 167, { 0xa0, 0x91, 0xf8, 0x02, 0x18, 0x02, 0x02, 0x74, 0x19, 0xaf, 0xbf, 0x2f, 0xe5, 0xd5, 0x0a, 0x96 } },
{ "\xae\x54\x5b\x24\xdd\x9a\x7d\x0c\x63\x4c\xe7\x77\x4c\xb1\xdd\x8f\x18\xe8\x22\x29\x77\x15\x43\x47\xa3\xb6\x7d\xb8\x5a\x14\x4c\xda\x77\xd4\x91\x80\x2c\xad\x5e\xee\xde\x34\x62\x01\x9d\xd2\xec\x6c\x3f\xd8\x9d\x1c\x18\xa9\xaf\xbd\x57\x15\xdc\x56\x00\xc7\xec\x10\x81\xd4\xde\x14\xf4\x73\xb2\x91\xf0\xcc\xd1\xdd\x0c\xe9\x1a\xb3\xf1\xc9\x8a\x9b\x1b\x93\x87\x67\x2c\xe8\xc9\xd9\xed\x51\xe6\x62\xe2\xd8\x78\x05\x88\xb2\xec\x5a\x2d\x19\xea\xaf\x6c\x38\x5c\x49\x44\x40\x1e\xc8\xd5\x98\x40\xa8\xb6\x31\xfa\xe4\xf5\xf7\x2d\xb5\xac\x63\x92\x78\x3c\x2d\x81\xad\x29\x1f\x60\x1b\x92\x05\xa6\x12\x4b\xc1\x8b\xc8\x99\x7b\x4e\xe5\x89\xf5\x22\x1a\xed\xfc\xb6\xec\xf4\xfa\x60\x8f\x65\xa9\xe5\xed", 168, { 0xac, 0x8b, 0xae, 0x88, 0xd7, 0x6a, 0x35, 0x60, 0x7d, 0x02, 0x43, 0x98, 0x20, 0x2a, 0xd6, 0xec } },
{ "\x78\xe2\xe7\xc6\x51\x93\xec\xf1\x19\xcf\x07\xc0\xbb\x00\x25\x81\x38\x37\xf5\x21\xa8\xa4\x75\xec\xce\x21\x16\x6f\x56\xe8\x8b\x7f\xad\x66\x33\x52\x7d\x27\x21\xca\xc4\xf0\xc4\xd2\x90\xeb\x38\xe1\x59\xfd\x28\x9c\xfb\x34\x5d\x98\x4e\x5c\xe8\x3d\x64\xb1\xe8\xc6\x5e\xae\xf9\x64\xeb\x04\x39\x82\x5e\xa6\xf8\x24\x6b\x01\xfc\x69\x7f\x49\x6d\x2f\xb9\xef\x63\xd5\x88\x2e\x0b\x1b\xe2\xc5\x70\x26\x1d\xbf\xec\xa1\x6e\x6e\x2a\xfd\xfd\x76\xd6\xd8\xa1\x05\xe4\x7b\x3d\x20\x7a\x7e\xb6\x19\x7b\x86\x90\x1d\xb1\x2f\x24\xe9\x96\x04\x80\x9d\xbf\xab\xdb\xa9\xe6\x1e\xb3\xe4\x92\x14\x85\xbb\x65\x7e\x28\x86\x02\x2d\xc7\xf6\x99\x90\x79\xab\x10\x9b\x7f\xe2\xcf\xc4\x19\x4c\x28\x27\x05\xf9\x62\xca\x95", 169, { 0xc4, 0xf0, 0x9b, 0x16, 0xea, 0x1a, 0x1a, 0x5a, 0x54, 0x70, 0x0c, 0xa8, 0xb4, 0xe3, 0x92, 0xd2 } },
{ "\x73\x61\xfa\x6c\xe8\xf3\xd4\xd4\x7f\xb9\xe0\xbf\xcb\xb0\xd7\x59\x5d\x5b\x85\x46\x73\x8f\xc9\x7d\xcf\xda\xba\xc0\xa3\x91\xe4\xb7\xa8\x75\xb0\xa8\x4e\x01\xe1\xd6\x0e\x53\x3b\x73\xdb\xb4\x3e\x42\xb6\xc6\x10\xce\x61\x49\x78\x40\x2b\x8a\x06\xe1\xea\x68\x51\x2d\x07\x04\x59\x90\xb3\x04\x0a\xc0\x38\x84\xe2\xb6\x6a\x9b\xa9\x4a\x3c\x85\x5f\x6a\x6f\x72\x34\xf6\x64\xea\xb1\x3b\x13\xdb\xf4\x0b\x14\x41\x18\x75\xdb\x70\xb3\x27\x01\x01\x79\x5c\xbd\x57\xfd\x83\x71\xcc\x98\x6e\x61\x7d\x62\x33\x7e\xaf\x5d\xa3\x60\xdd\xb2\x64\x53\x5f\x13\xae\x88\xb8\x3f\x9e\x6d\x7c\xa4\x3a\x17\xdc\x1e\x02\xda\xd0\xde\x2f\xfb\xe0\x66\x8b\x7d\x8e\xb0\xec\x17\x63\x6e\x4e\x47\x95\x6d\x8c\x80\x51\x13\xbb\x7f\xab", 170, { 0x69, 0x9d, 0x5b, 0x85, 0x72, 0xec, 0xe6, 0xbf, 0x17, 0x47, 0xff, 0x53, 0xb3, 0xf0, 0xd5, 0x34 } },
{ "\x07\x23\xbc\x20\xef\xdb\xfb\xc4\x54\x00\x01\x0c\xa3\x92\x64\x3a\x4d\xeb\x7c\x61\x0d\xdc\x76\x14\x49\x65\x87\xaf\x92\x11\x3c\x41\x46\xec\xf0\x15\x55\x22\x58\xdc\x20\x36\x38\x78\x7d\xdb\x38\x67\xd7\x72\xd1\xca\x73\x21\x62\x11\xcd\x8c\x5f\x45\x21\x33\xa8\xf2\x05\x68\xf8\xaf\x33\xeb\x74\x4c\x65\x24\x63\x96\x59\xfc\xfd\xc9\xf4\x58\x5c\x93\x83\x32\x8f\xc1\x1c\x56\xce\x88\x23\xb7\xc7\x72\xe8\x6c\x17\xe4\x6e\x4a\xd4\x48\x47\x1e\x47\xdb\x9a\x87\xb7\x14\x47\x6e\x60\xb0\x21\x24\x83\x57\x5a\x16\x97\xec\xfd\x2f\x9d\x76\x94\xca\x91\xa6\xe9\x53\xfc\x04\xea\x79\xa6\xba\xa5\x11\x69\xfd\x73\x8a\x21\x14\x32\x09\xc0\x06\xab\x21\x7e\xe4\x12\x30\x2d\xb0\xab\x59\xaa\xe9\x89\x19\x70\xb4\x71\x88\x46", 171, { 0x24, 0x02, 0xdb, 0x04, 0xc8, 0xa1, 0xc2, 0x6a, 0xaa, 0x95, 0x63, 0x35, 0x92, 0x4e, 0x35, 0x1b } },
{ "\xa9\xde\x2f\x19\x37\x1e\x80\xe4\xb4\x9a\xf6\xcb\x04\x33\xca\x48\xe5\xc7\x4f\x7c\xd6\xd2\xea\xa7\xa2\x31\xb2\xb3\x8d\x02\xa0\xeb\x19\xa8\x73\xc9\x75\xeb\x23\xec\x83\x3c\xff\x28\x85\x15\x65\xb8\x63\x7f\x1e\x8e\x9b\xad\x54\xcb\xc5\xc6\x30\x4a\xc2\xc0\x14\x57\x81\x68\x72\x7e\x6d\x7e\x47\x7d\x77\xfc\x38\x5b\xbb\x77\x47\x92\xd1\x9f\x32\x67\xb3\xe1\x68\x5b\x46\x2b\xa8\xba\x87\xcf\x39\x50\x53\x81\xc0\x3b\xd2\x7b\xc1\xdc\x82\xc0\xb5\xe7\xdc\x7c\xc3\x9a\xa4\x8a\x1f\x0b\xd2\x10\xfc\x99\x18\x45\x2f\x84\x10\x53\x61\x99\x04\x58\xf1\x06\x59\x86\x64\x4c\x98\x69\x89\x51\x1a\x48\x2e\x95\x50\xa5\x78\x7d\xac\xe0\xe3\xcb\x30\xf8\xd7\x2f\x91\x70\xe3\xf6\x07\x50\x98\xe1\xb4\x42\x04\x11\xae\xdd\xca\x1d", 172, { 0x95, 0x82, 0xd6, 0xd6, 0x0d, 0x72, 0x51, 0x58, 0xba, 0xbc, 0x56, 0xa9, 0xf6, 0x07, 0x54, 0xa6 } },
{ "\xab\x00\xdb\x46\x48\x54\xd3\xc2\xf6\xf3\xf2\x34\x82\x27\xb5\x3d\x3f\x4a\x10\x2c\xd1\xcd\x4b\xd1\x99\x55\x76\x6f\xb8\x00\x8a\xcf\xc2\xc6\x1e\x71\x01\xfa\xc3\xde\x63\xee\xfc\x19\x01\xb6\xdd\x34\x4c\x06\x3f\xfe\xd6\x35\x9d\xda\xba\x62\x8e\xab\xaa\xb5\xdf\xeb\x93\xbf\x4c\xdb\xef\xfb\xdb\xd4\xa6\x76\xd6\xbd\xa2\x8a\x63\x96\xee\xc6\xc1\x30\x89\xea\x21\xff\xcd\x0d\x1f\x08\x77\xe1\xdb\xf4\x52\x0f\xb8\x47\x85\xbc\xb1\xaa\x75\x2d\x53\x64\x58\x87\x5b\xe7\x58\xaf\xec\x87\x5f\x50\x6c\x45\x85\xfb\xfd\xca\x14\x68\x93\x6f\x34\xda\xb7\x79\x38\xa1\xd7\x62\x83\xb9\x47\x52\x18\x90\xd8\xad\xbe\xe5\x13\xc5\xcc\xcc\x13\xb0\x96\xce\xfc\x35\x74\x2d\x1c\xe0\x6c\x44\x9c\x35\x7b\x0d\xe2\x01\x85\xbd\x87\xcc\xd6", 173, { 0xd7, 0x83, 0x37, 0xf7, 0xb8, 0xcb, 0x87, 0x54, 0xc4, 0x01, 0x9d, 0xcf, 0x3b, 0x57, 0xf5, 0x8b } },
{ "\x8a\xf0\x6a\x54\x8c\x8b\xb1\x44\xc1\xa8\x44\xb5\x2b\xf1\x8e\x8c\x14\x88\xcb\x2d\x72\xbb\x40\xc3\x65\x66\x8b\x2d\xdc\xe6\x15\x86\x58\xb5\xa3\x4e\xc9\xa7\x0c\x3a\x94\xc0\x05\x94\xb6\xb0\x18\x50\x02\xec\xb3\xad\x86\x69\x5d\x84\x0c\xf7\x03\x31\xbc\x39\x71\x1b\xdf\x3d\xdc\xe1\xbe\xbc\x9b\x22\xa8\xef\xf6\xe9\x13\x0b\x17\xb4\xda\x5b\x1e\x1f\xa5\xf9\x50\x32\x67\x29\x6f\x44\x00\x52\x24\x89\x02\x9a\x99\x3f\x90\x1d\x23\x72\x62\xc9\x1d\x67\xe6\xd9\xd0\xab\x81\xeb\x8e\xb8\xf3\xc0\xde\x40\xd9\x90\xd1\x19\x4b\x08\x73\xc6\xa5\xe1\x5d\x9e\x64\x1e\x68\x9c\x26\xe2\x7c\xc2\xd3\xeb\x86\x2a\xdb\xaf\x87\xaa\x95\x11\xc9\x23\xc7\xc0\x2e\x66\x43\x2d\xa1\xc4\xae\x26\xad\x31\x5c\x14\x2c\x14\x57\xcd\x17\xae\x7f\x17", 174, { 0x8b, 0xaa, 0x35, 0x9d, 0xe0, 0x56, 0x8f, 0x69, 0xc8, 0x17, 0x5c, 0x10, 0x2d, 0x43, 0x12, 0xe3 } },
{ "\x7d\x24\xcb\xba\x8f\x2a\xad\x41\xd8\x4e\x94\x4d\x89\xdf\x8b\x95\xf2\x78\xff\x7d\x0d\x2c\x9d\x52\x35\x4f\x5a\x20\xf4\xdf\x8c\x30\xf9\x8e\x35\x22\x28\x6d\x61\xa3\xcc\x36\xa5\xca\xe8\x36\xc7\x14\xab\xd5\x7c\xfa\x01\xc4\x4c\x2d\x46\xc1\x92\x6e\x15\x0a\x9f\x0b\x3f\x5c\xff\xf9\xd8\xa6\xd3\x8b\x6b\x4f\x5d\xcd\x4d\x21\x9b\x7f\x0f\xd0\x0a\xb1\x0d\x2b\x8b\xf8\x23\xde\x63\x4a\x7f\xe1\x5d\x7b\x92\x81\x0a\x55\x21\x09\x29\x4d\x78\x0d\x21\xe8\xbd\x52\xaa\xaa\x62\x5d\x8c\xb6\xb4\x97\x80\x07\x91\x19\x33\x49\x36\x1a\x68\x55\x36\xf2\x3c\x48\x87\xca\x85\x3f\xb7\xe3\x54\xb0\x3c\x7f\x9a\x68\xe8\x6f\xe7\x1d\x7b\x3a\x4d\xaf\x53\xe7\x63\x00\x3e\x68\x66\x6c\x70\xa3\x79\xe7\x90\x1e\x0d\xb2\xec\x45\x5b\xba\xcd\x5b\x0e", 175, { 0x1e, 0x96, 0xa5, 0x23, 0xc0, 0x9c, 0x30, 0x9c, 0x2a, 0x99, 0x58, 0x95, 0xf2, 0x54, 0x8e, 0xed } },
{ "\x8d\xe6\xfb\xff\xf9\xe6\x4a\x18\x43\x2d\xbc\x59\x01\x9a\x7f\xf9\x95\x83\x87\xa4\x46\xbe\x37\xe3\xfc\xdc\xa9\x9a\x98\x76\x9a\xaa\xee\x9f\xf5\xb7\x60\x79\xfa\x04\x18\xe3\x0b\x79\xe1\x50\x13\xc7\xa2\xb3\x93\xa2\x79\x96\x4b\x2d\x70\x4a\x81\x74\xdf\x39\xf1\x12\x5e\x57\x51\xf3\x64\xb7\x7f\x34\x81\x3a\x49\x27\x34\x62\xd9\x72\x6a\x44\xbb\x56\x94\x9c\x8c\x0e\x63\xfb\x42\x4e\x23\x1b\x12\xea\xb1\x53\x02\x98\x1a\x25\x0b\xce\xd2\xf5\xea\xc5\x8e\xd5\x83\xa0\xbe\x7b\xf2\xa5\xaa\x54\x30\xa9\x28\xb1\x5f\x8b\xf8\x1f\xb3\xd6\xbc\xd6\xfc\x8a\x96\x47\xd3\xf5\x60\xd8\x61\xba\x83\xac\xc7\xf3\x74\x9b\x2d\x73\xd1\x41\x6b\x24\x14\x32\x4d\x8f\xf1\x4b\x86\x7b\x52\x24\x35\xac\xed\xa1\x0c\xcc\x7e\xa8\x23\x68\xa6\xed\x58\x11", 176, { 0xbe, 0xf1, 0xe8, 0x50, 0x33, 0xbf, 0xf7, 0xbe, 0x13, 0x51, 0x2e, 0x59, 0x3c, 0x64, 0x15, 0xd8 } },
{ "\x60\xe8\x60\xa3\x1c\xe8\x6f\xa2\x7a\x41\x80\xbf\xa4\xa1\x41\x7b\x48\x81\x9e\xe3\x31\xb7\xe9\x79\x81\x27\xa3\x33\x8d\x9a\xea\x9c\x55\xa7\x7e\x19\x9e\xcd\x71\x47\xae\xa3\x6b\x7f\xa3\x26\x97\x7b\x71\x32\x2e\x12\x76\x0c\x9e\x1f\xbc\x1c\x56\x80\xca\xb8\x31\x3a\x27\x1b\x7c\xba\x6c\x74\xa6\x36\x05\x1b\x83\x32\x84\xde\x3d\x1f\xa1\x7a\xd7\x1e\xd2\x25\x5f\xe2\x83\x48\xb0\xb3\xf3\x4a\xe1\x8e\xab\x88\x4e\x69\x9b\x60\x41\x95\xd2\x6d\x3c\x3d\xd9\xcd\xe5\x0b\xad\x9d\x8e\xea\x58\x86\x60\xe6\x2b\x71\x25\x2f\x9a\x56\xaf\x3c\xb4\x32\xe7\x0b\x3d\x17\x75\x87\x69\x5d\x09\x03\x38\xf6\x45\xe3\x69\xdf\x47\x5b\x1c\xb3\xd6\x4e\x07\x5a\x28\x58\x54\xde\x4d\xe7\x16\x5e\x3c\x84\x67\x1b\x78\x30\x1f\xaf\x5f\xe9\x33\x5e\x0f\x4c\xa7", 177, { 0x21, 0xa8, 0xb9, 0x73, 0x01, 0x98, 0x8c, 0x9f, 0x67, 0xfb, 0xb8, 0xdb, 0xc4, 0xde, 0x97, 0x6a } },
{ "\x85\xf5\x54\x31\x2f\xf4\x40\x6c\xc7\x2e\x93\xb5\xe7\x71\x35\xa6\x4f\x41\xb7\x2d\xf1\x7e\xb4\x48\x28\xb2\x53\x5f\x09\xf9\xe8\x3d\xd2\x8d\xba\xad\x80\xed\xdf\xad\x7c\xaa\x44\x51\x75\xce\xc9\x37\x49\xe9\x89\xa3\x1c\xb9\x37\x37\x8c\x75\xa3\x50\xfb\xb6\x5c\xff\x0b\x03\x70\xa2\x28\xf7\x4f\x1f\x3e\x11\xce\xbc\x8c\x18\x47\x9e\x90\x29\xdd\xdd\x22\x5f\xdd\x40\x9d\x1d\x40\x9a\x37\xfa\x4d\xc0\x72\x4a\x5b\x25\xab\x45\xb4\x15\xd0\xd7\x96\x8a\x2f\x03\x53\xae\x69\xf4\x98\xee\x85\xa2\xca\xb7\x21\x1d\x8c\xd0\xc3\x7a\x5d\x54\x4d\x57\x5f\x4a\x8f\x0c\x32\xe3\xf7\x6f\xff\x4f\x63\x44\x91\x3c\x17\x40\x9d\xec\xca\xb8\x22\xf1\xdb\xeb\xeb\x88\xa1\xe8\x32\x90\xdf\x1d\x5f\x25\x57\x7e\xed\xde\x75\x4e\x6e\x9f\x2c\x8d\xa5\x1b\xde\xff", 178, { 0x51, 0x6b, 0x97, 0x49, 0x9d, 0x54, 0x92, 0x2b, 0x34, 0xc3, 0x79, 0x77, 0x7f, 0x03, 0x8d, 0x1a } },
{ "\xd1\xe9\x75\xd4\x09\x42\x8f\xf9\xc2\x55\xd6\xfa\x6e\x47\x6d\x92\x3f\xca\x86\xd1\x09\x59\x10\xe8\x46\x0d\x8e\x94\x33\x1f\x03\x25\x17\xfb\x09\x2a\xa3\xfd\x02\xbb\x75\xca\x65\x63\xb7\x4e\x3a\xa7\xe4\x4d\xa1\x37\xab\xa8\x8b\xb1\xec\x2f\x8c\x0c\xdf\x1d\xc9\xa7\x54\x34\x0c\xee\x14\x76\xf6\xa6\x67\x7d\x54\xd7\xaf\x77\x8a\x53\x32\xc2\x2b\xe6\xf5\x20\xab\x1c\xc3\x97\x2c\xa9\x4d\xe8\x74\x79\x6b\xa9\x00\x55\x81\x01\x32\x2e\xfc\x00\xa5\x0a\xfc\x99\xa1\x88\x0c\x3d\xaa\x11\x0c\x14\x33\x9d\x30\xda\x70\x1f\x21\x55\x49\x8f\x41\x6e\x6a\x92\x0c\xf3\x77\xc7\x9a\xe8\x5d\xb0\x40\x86\xc4\x3b\x56\xd1\xa0\xec\x14\xd9\xe7\xaa\x96\x8d\x5d\x23\xff\x36\x8a\xdc\xb9\x98\xce\xd8\xda\xa0\x8b\xe4\xa2\xc9\x80\x7d\x21\x12\x36\x5f\xf6\x94\x92", 179, { 0x6b, 0xc1, 0x3c, 0x44, 0x56, 0x28, 0x48, 0xa5, 0xc9, 0x18, 0x9c, 0x37, 0xa8, 0x8e, 0xce, 0xf2 } },
{ "\xac\x31\xc2\x8b\xb5\x5a\x42\xf6\x67\x8b\x62\x7d\x55\xb8\x38\xaf\x5d\x0f\x5b\x31\xfa\x7a\x38\x11\x26\x42\x11\x3b\xea\xcd\x98\x04\x83\x88\x24\xe4\x32\xe0\x8e\x41\xa1\x69\xab\x66\xed\x22\x65\x92\x54\xd0\x78\x2d\x7c\x86\xc6\x16\x5e\x46\x58\x17\xcd\xc2\xf2\x7a\xa7\x3b\x52\xb5\x97\x8b\x05\x40\x84\x3d\xe5\x87\x99\xda\x32\xfb\xf2\x3f\x4c\x43\xe0\x29\x0a\x91\xd9\xd3\xbb\x0f\xff\xb6\xb7\x77\x4b\x6f\xa0\xc2\x56\xbf\x3a\xf8\xc4\xac\xe4\x26\x4d\xc4\xb3\x6e\x69\x81\x2a\x38\x97\xc8\x97\x87\x4b\x8c\x0c\x66\x72\x90\xf9\x80\xa3\x49\x63\xcf\xe3\xe1\xc3\x6d\x15\x58\x7d\x86\xfc\xc5\xfb\x6f\xee\xbb\x66\xcf\x18\xc6\x01\xfb\x68\x15\x22\x60\x1b\x31\xcd\x19\xe3\xeb\xee\xa1\xb4\x55\x33\xa2\x2b\xe6\x84\xec\x9b\xc1\x20\x81\xb6\x0f\x55\xcd", 180, { 0x38, 0xf4, 0xa7, 0xc3, 0x5e, 0x5a, 0xf4, 0x32, 0xf2, 0x12, 0x4c, 0xf5, 0x99, 0xb6, 0x90, 0xe8 } },
{ "\xd5\x88\xdb\xf3\xe4\x11\xce\x42\xed\x80\x47\xc6\x3f\x7b\x96\xfb\x3b\x7e\x1d\x9d\xba\xfb\xcc\x9b\x1e\x9b\x34\x29\xf4\xa3\x4a\xf4\x43\x72\xbb\x71\x74\x26\xe6\x4f\xdd\x5f\x7b\x0b\xec\x1b\xae\xa1\xec\xc0\x17\x60\x77\x39\x29\xd7\x79\x38\x4c\xeb\xdc\x99\x9a\x0a\xd5\xe7\x33\x7a\xca\xf7\x3e\xcb\xdf\xb2\x7c\x6b\x1c\xf1\x8a\x58\x3c\x81\xb3\xf0\x15\x45\x6f\xe4\x9f\x7b\x7d\x4b\xa2\x17\x98\xd3\x00\x4b\xd9\x12\x9c\x28\xa8\xfa\xe6\x5e\x60\x6b\x05\x1f\x7f\xe3\x9f\x22\x86\x50\x48\xc4\x73\x06\x84\x48\xd9\xcc\x7b\x3f\x99\x11\x38\x03\x3f\x3c\x9d\x5d\xfb\x21\xe7\x47\x3a\x8f\xda\xcb\xe0\x06\x89\x0a\x24\x86\xc4\x58\x09\x82\x1e\x85\x75\xf4\x99\x37\xf0\x8a\xf0\x72\xfa\xfe\x81\x3a\x08\x83\xd6\x50\x1d\x5b\xcf\x17\x02\x85\x6d\x9a\x22\x94\x3d", 181, { 0x0a, 0x5b, 0x96, 0x06, 0x98, 0x96, 0x99, 0xa6, 0xdc, 0x6a, 0xa5, 0xbf, 0xc4, 0xfa, 0x83, 0x48 } },
{ "\xa7\x04\x9b\x5f\x13\x65\x45\x32\x21\xf1\x01\x9e\xfe\x9e\x5a\xfd\x63\xa5\x64\xa6\x5d\x1e\x52\x18\x29\x38\x25\xc0\x39\x12\x7f\x67\x53\x38\x96\x3b\xd9\xbd\x44\x78\x23\xf1\x3a\xb3\x08\xbf\x55\xc3\x7c\xa6\x09\x4c\x53\x52\xf9\xe9\x24\xd6\xa9\xf6\x48\x88\x4b\xf7\x02\x7a\xb5\xa8\xb9\x90\x74\xa1\x60\x43\xfa\xa6\xf6\xf1\xf2\x89\x29\xde\xb5\xbc\x16\xcb\xd4\x77\x2b\x31\xd5\x82\x2e\x1a\xfc\xa0\x56\x9d\x3e\x98\x97\xf4\x5b\xe1\xfb\x3b\x04\xe9\x2c\xc7\x37\x02\x0e\x21\xac\xe9\x89\x9e\x67\xf5\x64\x9c\x6e\xd9\x4d\x5b\x95\x15\xf5\x75\x75\xff\x58\xfb\x7b\x6a\x1a\x2e\x1c\xf0\x0d\xd7\x26\xe2\xcf\x44\x32\xc8\x91\xdf\x36\x96\xf2\x6c\x37\x6e\x09\xde\x1b\x0c\x82\xd7\x9c\xd3\xdf\xbb\x59\x71\xe0\x70\x07\x31\xfe\xb4\xc4\xdd\x10\x1a\x8c\x4d\x11\xa2", 182, { 0x34, 0xf3, 0xea, 0x31, 0xf3, 0x44, 0x99, 0x87, 0x3c, 0x5f, 0x60, 0xbc, 0xe5, 0xb0, 0x5a, 0xdd } },
{ "\xc4\x90\xf9\xf4\xf4\xb6\xc7\x39\x92\x39\xa0\x96\xf9\x06\x11\x46\x79\x2b\x71\x53\x87\x1e\x62\xf1\x15\xa7\x76\x77\xfd\x6b\xed\xfb\x96\x17\x9d\x88\x69\x25\xeb\xfe\xfe\x4e\xf5\x87\xcf\x8c\xd2\xb2\x5e\xcb\xf1\x2d\x62\x2b\x9e\x23\x1d\xf4\xa3\x30\xc9\x17\x0b\xfd\x30\x5d\x01\x7d\x5b\x2f\xd8\xc3\x62\x00\x24\x7d\x62\x5b\x05\x11\x8d\x84\x84\x52\x5c\xce\x15\xdf\xdf\x79\x3c\x18\x34\x45\x4d\xbe\x16\x97\x4b\x26\x8d\x47\xf2\x1a\xc3\x04\xd1\x4d\xf7\x65\x8e\x78\x8b\x8c\x4d\x15\x37\x79\x2c\xb7\x60\xe9\xe5\x04\x83\xe8\x97\x51\xcb\xfa\x9e\xd8\xc3\xe2\x9e\x98\x26\x0d\x9f\xc9\xd1\x9e\x52\xfb\xd9\x17\x72\xca\xb9\xa0\x46\x40\xa4\xf7\x43\xc7\xf9\xf5\xce\xc9\xd5\xb9\x1e\xe6\xc2\x73\x40\xd1\x8c\xcf\x34\xc8\x34\xfb\x35\xae\xfe\x57\x16\xc6\xa5\xb9\xc8", 183, { 0xe0, 0x50, 0xf4, 0x13, 0x5b, 0x9f, 0x3d, 0x6c, 0xf8, 0xc9, 0x95, 0x5e, 0x68, 0x81, 0xb2, 0x0a } },
{ "\x0e\xaf\x59\xf8\x36\xdb\x60\xd5\x3b\xb6\x08\xef\xa5\x4c\x6f\x3b\x59\xfc\xfe\x33\x1b\x65\x70\x1a\xa4\x7c\x82\x5d\x5c\xc0\x36\x15\xb5\x84\xc3\xaa\x24\x6b\x1c\x91\xbc\xf3\x1b\x35\x68\x28\x4a\xf4\xc4\x78\x4e\x40\x99\xa7\xf1\xe6\xf3\xd9\xca\x6b\xe1\xcc\x8b\x92\xc9\x29\xe7\xfb\x65\xef\x1e\xe5\x5e\x0f\x26\x14\x83\x17\x77\xce\xa6\x06\x74\xff\x13\x3e\x71\x7c\xae\x97\x65\x64\xa8\x8f\x2d\xa3\xdd\xa0\x7a\x90\xce\xaa\x59\xb6\x36\x90\x5d\xb0\x4b\xdf\x6b\x2e\x92\xa8\x22\x14\x5f\x6e\xe5\xc1\xe8\x3b\x86\x6d\x05\xcd\x90\xab\x87\xee\x31\x0e\x32\xe1\xc2\xe8\x58\xf8\xf5\x2d\x13\x43\x9a\x77\x1e\x62\x0e\x23\x50\x7c\x40\x98\xb7\x48\x61\xa8\x4d\x48\x28\x0e\x59\x16\xbf\x17\x65\xd7\x4d\x5e\xd8\xcc\x21\xb0\x2f\x07\x78\x0e\xdc\x3a\xeb\xd0\xbb\xab\x78\xe6", 184, { 0xf1, 0x8e, 0x78, 0x9a, 0x46, 0xe0, 0x68, 0x70, 0x90, 0x49, 0x85, 0x8e, 0x6b, 0x49, 0x0b, 0x61 } },
{ "\x2d\x43\x2a\x3a\xd3\xc6\x68\x31\xe9\x1e\xd8\x51\x3c\xd0\xee\xfd\xc9\x90\x15\x5b\xf4\xef\x78\x56\x62\x32\x6d\xbf\x73\x3f\x7e\x8e\x5f\xb1\x16\x47\xec\x0a\xc5\x78\xc9\x08\x30\x5e\x8b\x10\x98\xa8\x41\xc7\x05\x53\xb5\xc0\x0e\xb4\x42\x4f\x48\x94\x4d\x7c\x49\x75\x61\x13\x58\xf3\xeb\xd9\xb2\x46\x8e\xd9\x7b\xe4\x24\xdc\x40\x43\x53\xf6\x25\xbc\xc7\x3d\xb0\x8b\x11\x95\x30\xf3\x1c\xa7\xcb\x7f\x47\xf0\x23\x62\x24\xf5\xa5\x00\xcd\x95\x6e\x86\xde\x77\xd9\xb3\x12\xa1\xa9\xba\x7d\x2a\xd0\x40\x63\x08\xda\x80\xb4\x03\xf9\x8e\x25\xcb\xad\xb9\xec\x24\x10\x09\x18\x3f\xbb\xe7\x8a\x25\x58\xdc\x94\x4c\xc6\x72\x2c\x4c\xe2\x37\xcc\xab\xf8\xdf\xea\xd4\xc6\x89\x0f\x27\x29\x1a\x97\x2a\x67\xc6\x04\xdc\x18\xad\xfe\x2a\xb1\xa1\xeb\x7b\xae\x06\x27\x66\x5c\xd3\xe0", 185, { 0x75, 0x72, 0xbf, 0x88, 0x25, 0xbe, 0x77, 0x6b, 0x6f, 0x97, 0x84, 0xf0, 0x11, 0xa5, 0xc8, 0x22 } },
{ "\x83\x40\xb5\x91\x5f\x27\x52\x19\x89\xcc\xaa\x77\xc9\x1f\x9a\x25\x71\x38\x7b\x0d\xcb\xd8\xe7\x2c\xb1\x97\x9b\xc2\x3c\xb0\x78\x33\x94\x65\xa4\x7e\xe7\x10\xcd\x57\x75\xc8\x8e\xe2\xc7\xac\xea\x0e\xff\xcf\xf5\x8c\x4b\xc0\x91\x6b\x74\xb4\x56\x52\x93\x52\x8b\x59\xe2\x1b\x51\x84\xb0\x75\xe0\xdc\x6e\x52\xe8\x2c\xe7\x81\x19\x55\x8d\x91\xbe\x4e\xee\x5e\x84\xbc\x46\x39\x59\x2a\x2d\xb8\x70\xe5\x71\x17\x60\x77\xfe\x49\x6c\xbe\xfa\x9f\xde\xa8\xed\x14\x8c\x8d\x1e\x32\x7c\x28\xf9\xa5\xa4\x33\xab\x5b\xca\x9f\xd0\x54\x8a\x6d\x44\x0b\x76\x2c\x16\x81\x57\x0f\x9b\xe2\x92\xaa\x9e\xd6\xa6\x49\xb5\x67\xd2\xed\xaf\x8a\xd3\xe4\x29\x4c\xcb\x04\x40\x9a\x3e\x83\x60\xad\x35\x76\x3b\x10\x85\xe4\xd6\x4f\x2a\x87\xbd\x3e\xcc\x1e\x57\xe3\x64\x71\x54\x01\xf8\xe3\x42\x42", 186, { 0xbb, 0x9a, 0xed, 0xe2, 0x33, 0x52, 0xb1, 0x0f, 0xad, 0xa6, 0x08, 0x76, 0x99, 0x43, 0xe4, 0x6e } },
{ "\x82\xad\x3f\x00\xef\xbd\xc9\xaf\x43\x2a\xb1\xeb\xd9\x52\x2f\x1a\xc8\x92\x0e\xbe\x62\xa1\x08\x5a\xd6\x89\x2c\xfd\xf5\x43\x7b\x6f\x11\xef\x93\xf7\x01\xad\x83\xc7\x42\x1b\xbd\x06\x38\x6b\x29\x78\x92\x1f\x56\xcb\xcb\x64\xcb\x80\xcc\x09\x7c\x73\xae\x1a\x58\x09\x9e\x1d\xff\xf6\xda\xe8\x91\xa8\xd7\x1d\xa2\x54\x85\x44\x49\x9b\xaa\x83\x58\xd8\x6b\xfc\xa6\x09\xea\xc3\x87\x57\x08\x7a\x5d\x43\x4b\x8c\x48\x6f\xdb\x02\xf8\x07\xa7\x05\xa4\xca\xa5\xf1\x13\xb9\x36\x11\xd8\x5a\xa7\xfd\x9b\xa9\xd4\x8d\x91\x9c\xe7\x79\x0d\x52\x3e\x8f\x30\xd7\x6b\x8f\xbd\x96\x54\xa6\x07\x5c\x7b\x85\x0b\x04\x59\x1e\x9a\xc5\xe3\xfb\xaf\x14\x2e\x3b\xdf\x37\x2e\x29\xee\x68\x9a\x7a\x7d\xa2\xec\x23\xe1\x0b\x84\xd5\x10\xfd\xec\x16\xb5\xb2\x36\xfd\x63\x8c\x82\x8a\xe5\xff\x9c\x9f\xb4", 187, { 0xf7, 0xcb, 0x61, 0x06, 0xde, 0xbd, 0x8d, 0xcb, 0xa7, 0x96, 0x12, 0x6e, 0x8e, 0x63, 0x04, 0x6a } },
{ "\x1c\xc2\x9f\x82\xba\x82\xa2\xf3\x43\xc2\x52\x6b\x18\xda\x65\x02\xa2\xdb\xb7\x94\xf6\xf3\x03\xf6\x24\xe8\x3e\x43\x1d\xc2\x90\x54\x3e\x86\xef\x4d\x7d\x1c\x5a\x54\x2f\x52\xb0\x5d\x73\xf9\x2a\x21\x3f\x2d\x7b\x2d\x05\xb3\x83\x17\x01\x7b\xaa\xcb\x22\x44\xdf\x03\x92\x5e\x2d\xd5\xe4\x8b\xf7\x66\xd9\xff\xd0\xbc\xcf\x87\xf9\xb2\xc7\xc6\x78\x04\x11\x99\xd1\x8a\x96\x9c\x64\x82\xc2\xc6\x62\x06\x0c\x30\x6d\x9c\x23\xf3\xcc\xec\xb6\x2e\x48\x8c\xe0\x27\xa6\xf8\x2f\x89\xc0\x9d\x50\xcb\x42\x14\x58\xb1\xfe\x6c\xb1\xec\xed\xa0\x9f\xdd\xc3\x27\x6e\x78\x2d\xab\x3a\x43\xaa\xd1\x5b\x7c\x03\x37\x6b\x5a\x68\x7c\xf4\x6c\x29\x78\xbb\x1e\x0e\x8c\xa3\x5b\x53\x15\x40\x4c\x43\xfa\xf9\x3d\x6f\x65\xd9\xa1\x8b\x8f\xf2\x2c\xe6\xb3\x6e\x1e\x85\x24\x33\xa0\xdb\x7a\x32\x46\x26\xe8", 188, { 0x32, 0x70, 0x9f, 0xcf, 0xca, 0x08, 0x40, 0x64, 0x9c, 0x92, 0xf1, 0x9d, 0xf0, 0x45, 0xa3, 0xf3 } },
{ "\x4d\xb5\x68\xa9\xe3\x5e\x00\x99\xf9\x68\xe5\xe0\x06\xac\x80\xc9\x5b\xda\x23\xb1\x2d\xb9\x29\x1a\x40\x8b\xaa\xdf\x32\x8e\xdb\x82\x62\x9f\x01\xa1\x7b\xfa\x10\x88\xa1\x1c\xe4\xdf\x36\xc9\x1b\x84\x49\x2a\x8d\x74\x9b\x0e\x69\xe2\x50\x51\xed\x6a\x2f\x5a\xc1\x5f\x96\xbd\xf1\x40\xfd\x02\x85\x4a\x24\x7d\xbf\x27\xdc\x5c\xf1\xc4\x6f\xbf\xf8\x40\xae\x01\x59\x00\x95\x16\xf6\xdf\x9c\x0d\x6c\x83\x9d\xb4\x0c\xcb\x5c\x5a\x75\x64\xb8\xaf\xcb\x7b\x6d\xc8\x96\x8c\x84\x3f\x39\x59\x58\x78\x0c\xf7\x45\xfe\x19\x02\xee\x0b\x70\xf1\x2b\x26\x27\x84\x8e\xe7\xf2\x5a\x90\xd6\x5b\x9f\x5d\x72\x9e\xe2\x39\x4d\x23\x04\x8d\x54\x98\x4c\x92\x2d\xaa\x98\x99\xb9\x61\xaf\xf4\xb9\x16\xb1\x85\x8c\x11\xaf\xe1\x16\xc3\x57\x1f\xbd\x9c\xc8\x43\x8d\x8a\x84\x42\x7b\x10\x83\xb2\xc7\xfe\x64\x10", 189, { 0xcd, 0x2f, 0x08, 0x67, 0xde, 0x29, 0xd6, 0xbf, 0xf2, 0x95, 0x3e, 0xe6, 0x88, 0x73, 0x70, 0x43 } },
{ "\x7a\xd3\xbf\xad\x0f\xab\x95\x35\x2e\xe6\xe9\xdd\x93\x58\x68\x29\x0e\x26\x43\x57\xa3\x43\x1e\x6b\xd1\x87\x20\xd8\xf0\x69\x2b\xc8\xb3\x59\x25\x08\xce\xbd\x75\x93\xb1\x85\x8a\xba\x71\x6d\x95\xec\xc8\xcf\x57\x28\x33\x22\x14\xfc\x39\x13\xa7\x38\xcc\x3e\xaf\x34\xc8\x89\xe7\xbb\x98\x1d\x94\x93\xf8\x02\x17\xaa\xd5\x56\xa2\xdf\x50\x2a\x07\x69\xe1\xf9\xac\xe8\x98\xc6\x9b\x06\x7f\xd1\xb6\xca\x1c\xf5\x08\x79\x13\xa1\x36\x29\xe7\x71\x14\xe3\xe1\x68\x53\x74\x09\xcc\x59\xd0\x51\x9f\x29\x24\xd2\xb5\x81\x77\x2d\x77\x03\xfc\x14\x32\x8b\xf6\xe1\x1c\x9f\x48\x63\xe5\x04\x98\xba\xf6\x6d\x1f\x58\x4a\x4f\x11\x27\xe5\xd0\xa3\xc9\xf4\xdd\xbd\xfb\x83\x5a\x3e\x13\xd5\x75\xe6\xa8\x63\x14\xe5\x50\x74\x6c\xfd\x84\x55\xb3\x56\xa0\x36\xed\xde\x07\xa1\xe3\x5a\x64\x97\x33\x0e\x0b\x49", 190, { 0x41, 0x35, 0xb4, 0x71, 0x1c, 0xe2, 0x04, 0x6c, 0x09, 0x30, 0x9e, 0x23, 0xaa, 0x6f, 0x9c, 0xed } },
{ "\x80\x05\x7d\x70\xc9\xfe\x0a\x49\xd8\xf3\x91\x31\xd1\x47\x63\xd8\xea\x8b\x46\x25\x39\xed\x95\xa6\xa8\x3d\x85\xb2\x06\x9d\x82\x1e\x38\xc5\xb8\x98\x8d\xf0\xad\xa3\x2f\x67\x0b\x2f\xb4\xa8\x7c\xd4\xd4\x33\xae\xab\x36\xf0\x76\xc9\x63\x40\x10\x59\xdc\x9e\x84\x55\x46\xf2\x25\xe1\xc3\x72\x34\x44\x5d\x35\xa1\x83\x9c\x56\xf1\x9c\x37\xc1\x3b\x5c\xe7\x9d\x4c\xdd\x56\x56\xea\xa0\x37\x45\x36\x43\x6f\xff\x2a\xdf\x70\x37\x42\x00\xa7\x51\x38\x42\x0b\x84\xe7\x84\xd6\xaa\x51\xcc\x51\xa7\xbc\xb7\xc7\x74\xf7\x9b\x9b\xc7\xba\x3b\x76\x61\x2b\x25\x98\xf3\x7d\xac\x50\xae\xb3\x7e\x87\x66\x83\xe1\x76\x61\x55\x8e\x78\xc2\x6a\x2a\xf9\x8d\x1a\x5d\x8e\x56\x36\x91\x1f\x8b\x8c\x49\x3f\x5a\x88\x1b\x9c\x40\xf7\x4c\x92\x30\xe7\x06\xda\x5c\xab\x38\x5f\xa2\xfb\x0a\x31\x96\x37\xfc\x92\x28\xcc", 191, { 0xb0, 0x0c, 0x4f, 0x1c, 0x01, 0xbc, 0x03, 0x95, 0xe7, 0x9b, 0x96, 0x28, 0xa3, 0x74, 0xc6, 0x15 } },
{ "\x9a\x2e\xf2\xaf\xde\x68\x21\x07\x22\xd0\xfd\xe7\xc0\xb0\x16\x39\x48\xc6\x0d\xb6\x5d\x0b\xfc\x11\x2c\xb2\x83\x34\x8a\x2c\x70\xa1\xa9\x68\x0a\xfb\x77\x19\xfa\x9e\x94\x2f\xd7\x68\xed\x67\x4c\x9b\xfa\xfe\xe8\xdb\x90\x81\x82\x51\x63\xc0\x47\xf2\x1c\xe0\x62\xda\x13\x90\x46\xd2\xaa\x54\x9b\xf1\x45\xfd\xc3\x5e\xe9\x39\x07\x33\x70\x46\x63\x7d\x66\xc2\xea\x60\x84\x9f\xd7\x57\xc7\x32\x32\x9a\x2b\x6a\x1c\x98\x09\x1d\xb7\xa3\x0a\x7e\xb4\xac\xc7\xab\x7a\x23\xff\x63\xf0\x00\x74\xd5\xe7\xe0\x62\x93\x27\x47\x9d\xa6\x14\x04\x46\xbb\xfd\xb2\x29\x7a\x02\x5a\xb6\xf7\x9f\x36\x9a\x41\xd9\x91\xfa\x18\x03\xca\x4c\xbd\x2d\x77\xd1\xe1\x2a\xa5\xb3\xbf\x3d\x3e\xb7\x45\x60\x84\x9e\x6d\x30\xb9\x1e\xab\x78\xe1\x78\x7f\x58\x9f\xb6\x2a\x11\x3c\x83\x7d\x70\xc7\xa6\xcc\xd5\x56\x1d\x02\xe0\x6a", 192, { 0xfd, 0xf1, 0x42, 0x20, 0xf2, 0xc8, 0x59, 0xfc, 0x92, 0xf4, 0x19, 0x99, 0x56, 0x5e, 0x8c, 0xc3 } },
{ "\xd2\xce\xb7\x71\xfc\xfc\xf5\x64\x15\xde\x32\x91\x73\xe8\x2b\x73\x86\x5c\x8e\xbd\xad\x1a\x65\x76\xb5\x99\x1c\x3d\xee\xf3\x30\xae\xec\x1a\x76\xdd\xd7\x28\x06\xff\x6f\xd5\xc2\x29\xa1\x02\x86\x30\xbe\x72\xee\xaf\x5e\x98\xbe\x26\xd0\x8a\xf2\x3f\x3c\x15\x63\x2d\x58\xeb\x13\x2d\xc3\xf8\x15\x2e\x01\xd3\x1c\x8d\x14\x4f\x9e\xf9\xb3\x30\xa4\x76\x2a\xfa\x31\x7d\xcd\xe5\x4c\x40\x1a\xee\xac\x0a\x6a\x07\x00\xc5\x54\x6f\xd8\x96\x9f\x1c\x33\xa3\xe1\x54\xa6\xf4\xb8\x5a\x25\x77\xa4\x46\x71\x1d\x80\xee\x1e\x23\x9b\x00\x40\x6b\x77\x32\xb2\x08\x1d\x90\x02\xd9\x1b\xf4\xfc\x4c\x1c\x94\xd1\x44\x22\xb5\xe4\x15\x62\x7e\x32\xac\xbc\xe7\x5b\xcc\xd5\xd0\x51\x73\xd3\x2e\x9c\x5b\xb4\x60\x47\x9c\x4b\xa0\x6b\x6e\xd9\x94\x55\x0d\x44\x04\x57\xb5\xf6\xa7\x28\xcd\x55\x16\xf8\x20\xda\xe2\x15\x46\xd0", 193, { 0x88, 0x74, 0xaf, 0x07, 0x77, 0x96, 0x60, 0x6a, 0xa4, 0xf8, 0xd3, 0xfe, 0x51, 0x45, 0xb1, 0x39 } },
{ "\xc1\x33\x21\xab\xe3\x5b\x83\x03\x63\x73\xed\x2b\xd9\x66\x72\x72\x0c\xef\xbb\xf6\xc4\x07\x23\x20\xbe\xe9\x02\xbf\xb9\xbe\x08\xc4\xae\xeb\xbf\x98\x1c\xf3\x32\x16\x81\x09\xac\x28\xe5\x36\x99\x01\x8f\x23\x8a\xf4\xdb\x84\x54\xf0\x24\x21\x57\x99\xdb\x82\xdc\x03\xb9\x37\x98\x0d\x11\xc1\x4e\x58\x3e\x21\x90\xe3\x3c\xfa\xa5\x44\x53\x65\x86\x85\xee\x51\x03\x99\x8a\x95\x03\x7a\xca\x94\xe8\xe0\xa7\x8c\x11\x9f\xc4\x7b\x79\x9e\xdf\xae\x7c\x6f\x64\xe7\xba\x90\x1c\x2a\x14\x3d\x02\x24\xd4\xf0\x36\x39\xe9\x77\x0b\x29\x4f\xaf\x3b\x5d\x8d\xe2\x3a\xd4\x58\xbb\xa7\xa5\x55\xe2\xdf\x30\xfc\x38\xac\xe5\x98\xb0\x1b\xb0\x6b\xc8\x6b\x00\xee\xa3\x21\x73\x35\xc5\x39\x20\x71\x6a\x77\x80\x90\x83\xdb\xf9\xff\x4e\x32\x09\x8f\x91\xc9\x0b\x75\x72\x3d\xa9\x6b\xf6\x6e\xdf\xf5\x14\x92\xa7\xbe\x75\x65\x47", 194, { 0x4a, 0x49, 0xa4, 0xc0, 0x34, 0x27, 0x8d, 0x1e, 0x19, 0x41, 0x7c, 0x92, 0xdc, 0x85, 0x81, 0xc3 } },
{ "\x21\xcb\x7e\x33\xc3\xcb\xbd\xa0\x5d\xc8\xe1\xa6\x97\xee\x36\x10\x10\x17\x6b\xc4\x7a\x4d\x82\xc9\xe3\xdd\xe0\xfa\x0e\x14\x84\x46\xff\x99\x54\xa1\x96\x66\x93\x8b\x53\x65\x70\x3b\x38\xa3\xb7\x68\xcc\x33\xaa\xb3\x3b\xa2\xeb\xb4\x9b\x12\x90\x9f\x49\xf5\x59\x93\x72\x68\xfd\x7f\xae\x29\xa0\xb1\xc6\x37\x62\xfc\x96\x05\x11\x86\x0e\x5a\xfe\x2c\x52\xc8\xed\x92\x01\xc6\x26\xca\x93\x6c\xa8\x9f\xdc\xcb\x7d\x80\xad\xe7\x29\x04\x9a\x53\x3c\x1e\xd5\x67\x07\xde\x39\x1f\x6b\xe1\x63\x93\xcd\x57\xfb\x0f\x25\xaf\x11\xce\x36\xe1\xa1\x58\xd8\x57\x39\x75\x71\x79\xb2\xcc\x82\xd4\x19\x1d\x5d\xe6\xb2\x18\xf5\x88\x12\xd8\xce\xf8\x6b\xff\x13\x82\xe5\x6e\xc6\xcb\x27\xa1\x11\xba\xf4\xa6\xbc\x04\xf2\xb8\xb8\x52\x87\x7c\xd8\x10\xdc\xd7\x9f\xd4\x03\x6a\x34\x69\x35\xab\x72\x78\x34\xa1\x1c\xd2\xcf\x3c\x2e", 195, { 0x9c, 0x25, 0x37, 0x70, 0xb4, 0xce, 0x2c, 0x7d, 0xff, 0xd2, 0xda, 0x7d, 0x85, 0x7e, 0x25, 0xe8 } },
{ "\x5c\x84\x4e\x4e\x98\x3f\x2a\x61\xcc\x41\xd8\x3a\xd1\x1c\xf1\x6e\x79\xda\x1d\x43\x9e\x3e\x27\xc7\xc3\x22\xba\xfc\x6a\xff\xbb\x31\xf2\x8b\x42\x6c\x29\x7d\x35\x03\x76\x6c\x83\x4a\x9c\xd5\xfb\x66\x2c\x3c\xc6\x40\x8a\x69\x87\x95\x99\xd3\x0e\x2b\x06\x1b\xb3\x1e\x2e\xaf\x55\x59\xad\x8f\xef\x20\x84\x2c\xd3\xc9\xe6\x6c\x87\x8b\x9f\xcb\x39\x6e\x22\x9b\xdf\x62\x2d\x6c\xef\x6c\x1b\x86\xb8\xfb\xc6\x93\x5c\x59\x16\x5a\x6a\x3d\x2b\xa6\x1c\x7d\x23\x45\x2a\xe0\x88\x2c\x48\x11\x59\xb8\x43\xb0\xb3\x0f\xb4\x83\x1c\xa5\x5e\xca\x6c\xda\x2a\xb0\x59\xc1\xbc\xdd\x9d\xfc\xb1\x28\xc6\xc3\x78\x6a\x9a\x03\xca\x6e\x24\xa3\xc7\x04\x5f\xe1\xae\x35\x7e\xdb\x39\x90\xd6\x2a\x93\xa3\x69\xa9\xf7\x86\x10\x53\xe6\x91\x44\x4a\x04\x2d\x89\xf4\x90\xc8\x77\x40\x7e\xe2\x66\x07\x33\x45\xb5\x8d\xdd\x51\xb7\x26\x6c\x75", 196, { 0xf4, 0x1a, 0xd3, 0x01, 0xe5, 0x69, 0x44, 0x94, 0x9b, 0x60, 0x1d, 0x3d, 0xbc, 0xc8, 0x4a, 0x94 } },
{ "\x3b\xf5\x30\xba\xb7\xd0\x10\x79\x11\x3f\x64\x2d\x09\xa4\x70\x63\x45\x74\x7e\xce\xfa\xa3\x97\x77\x35\x1e\xdd\x11\xc4\x72\x88\x6a\xc3\x8a\x7b\xfe\xc6\x95\x82\xa6\xa0\x06\x2b\x6d\xce\xb5\x3e\x83\x83\x23\xda\x4b\x51\xda\x2d\x8f\x71\xf3\xcf\xd3\xaf\xb2\xbc\xc7\xf5\x4b\xec\xd6\x72\xc8\x91\xdb\x66\x02\xec\xf3\x8d\xcc\xcc\x6d\x25\x30\xa5\xde\x9e\xd1\x49\x52\xde\x6f\x45\x9d\x2d\x89\xdb\xcf\xc4\x1d\x97\xc5\xed\x8b\x90\xdc\xd6\x98\x3d\xc1\xf8\x8e\xf1\x64\x1f\x80\xf4\x0b\x15\xaa\x40\x83\xef\xf7\xd5\x71\xf3\x9d\xb9\xc6\x24\xe4\x90\x50\x6d\x04\xd3\x6e\x66\x2b\xb0\xdc\xc5\x9d\x7e\xac\x64\xf6\xdb\x56\xea\x8b\x65\xe6\x19\xef\x11\x53\xb4\x91\x2b\xf0\x0b\x82\xea\xfc\x24\x55\xaf\x54\x88\x20\xda\x48\xa7\x9e\x49\x8a\xe8\x76\x6f\x42\x51\x97\x0c\x3f\xd0\xba\x8e\x49\x24\x09\x04\x92\x6b\xde\xbb\x4a\x48", 197, { 0xe0, 0x17, 0x3a, 0x13, 0x7d, 0xcc, 0xf3, 0x56, 0x2a, 0xf8, 0x56, 0x04, 0xdb, 0xcf, 0x6b, 0x4a } },
{ "\xa4\x77\x52\xb0\xda\x4f\x08\x52\x36\x26\x41\xa1\xe6\xc2\x55\x7f\xf1\x8a\x53\x87\xbc\xe0\x55\xf7\xa9\x19\xef\x39\xda\x15\xc1\x0c\x13\x80\x2c\x53\xbe\xa4\x21\x7a\x07\xe8\x15\x81\x27\xe8\x11\xa7\xbf\x32\xe5\xb3\x5a\x9b\x7c\xe1\x15\x3d\x4b\x68\x5b\x0e\xe4\xa4\xc8\x1d\xa7\xe5\x2f\x6b\x97\xd4\xb7\x63\x4a\x7c\x20\xf7\xfa\xfc\x23\x59\xba\xc8\xf8\x53\xc2\x97\xf1\x44\xeb\xed\x44\xb8\x36\x45\xe6\xa2\x86\xda\x92\x38\x6e\x12\xe8\x6b\x25\x88\xb3\x02\x96\xb4\x43\x52\x94\x39\xf9\x9c\x2b\xcc\xe1\x03\x12\xbc\x79\x28\x3c\x21\x90\x64\x8d\xa5\x4a\xa1\xaa\xea\x40\xd6\xe9\x97\xc4\x1d\x68\x02\x42\x72\x39\x7b\xc2\x0a\xbb\x33\x89\x4d\x04\xc8\xdf\x72\x7a\x6e\xec\xb6\x81\xbb\xbc\x39\x4e\x0f\x62\x75\xda\x9d\x38\x5b\xf3\x1b\x44\x0c\x6c\x02\xb6\x31\x75\x82\x8d\xf7\x05\x06\x5a\xaa\x73\x5f\x1d\xed\x25\x8f\x4b\x93", 198, { 0x1b, 0xae, 0x3b, 0x86, 0xbc, 0x4d, 0x77, 0x02, 0x5a, 0x90, 0x72, 0xf2, 0x23, 0x18, 0x3b, 0x53 } },
{ "\x5f\x35\xf1\x1e\x3d\x90\xf2\xd2\xbc\x31\x6c\x74\xf2\x42\x39\xa4\x5e\x6c\x92\xd4\x5a\x6a\xcd\xe4\xad\x28\x47\x5c\x3d\x97\x5c\x45\xe1\x10\x93\xa4\x55\x62\xd7\x94\x46\x7a\xe0\xff\x8e\xae\xb1\xf9\x7a\xa6\x3a\xb9\x46\xe7\x1d\x34\xaa\xfd\x8d\x57\x8d\x45\x53\xe1\xd8\x54\xeb\xdc\x66\x07\xcb\xb6\x17\x28\xc3\x00\x04\xba\x7f\xc2\xcc\xe2\x2a\x78\x0d\x72\x2d\xae\xef\x12\x15\x33\xda\x0d\x93\xfd\x47\xb6\x9c\x99\xb4\x75\xb1\x4c\xb1\x71\x39\xcc\x18\xdb\x0a\x94\x5a\xd5\x06\xe8\xf3\xfe\xe2\x65\xff\x9c\x02\x44\xe7\x64\x80\x2b\x34\xe8\x4c\xaf\x84\x9e\x6d\x6b\x99\x88\x66\xb6\x8f\x85\xb3\x03\x26\x34\x73\xda\x3d\x81\x1f\x6f\x60\xcd\x78\xdc\x78\xbe\x7f\x00\xa1\xa0\x9e\xf3\x19\x76\xe4\x25\x53\xa2\x6e\x12\x2b\x2c\xe1\xa3\x35\xb2\x13\x25\x2e\xed\xc9\xde\x94\xdb\x9b\x51\x51\x8e\xf4\x10\x93\x91\x36\x39\xb7\xf2\xfa", 199, { 0xa3, 0xff, 0x9e, 0xb2, 0xc4, 0x17, 0xce, 0xca, 0xb4, 0xda, 0x08, 0x92, 0xb0, 0x6b, 0xf7, 0x47 } },
{ "\x8e\x63\xf1\x75\x35\xb3\x43\xf6\x08\x8a\x03\x8b\x9a\x0b\x36\xc4\xc8\x20\xf9\x8f\xf7\x37\x4a\x42\xef\x0d\x6f\xb8\x53\xa2\x06\x92\xbc\x4c\xaa\x9f\x72\xe8\x3f\x56\xc6\x2b\xdb\x80\x0f\x16\x51\xf2\x3f\x88\x5b\x78\x21\xed\x63\xce\x31\x15\xee\xc8\x17\x1f\xa6\x91\xc2\x94\x02\x10\x1e\x90\x94\x66\x71\x1a\xef\x94\x57\x95\x32\x3a\x58\x50\x36\x7a\x23\x38\x50\xfb\x6a\xad\xc3\x09\x59\x71\xc5\xda\xb3\xbd\x2c\xec\x8c\x6e\xb5\x89\x9d\x5c\xf1\x6c\x47\xcb\xcd\x7e\x27\xfb\xbc\xb9\x52\xda\x83\x2e\x2d\xda\xa0\x21\xec\xdd\x58\x52\xa5\x4b\x5c\x57\x10\x46\x17\x24\xdd\xf5\x97\xad\xb2\xfd\x15\xa2\xc0\x0e\x22\x59\x01\x99\x71\xca\x10\x9f\x3b\xb3\xa4\xa5\x52\xdc\xaa\xc4\xc6\x75\xff\xdd\x2e\x9b\xc7\xe9\x94\xf9\x6d\x6e\xff\x8b\x37\x0d\x9d\x7e\x84\x38\x8d\x34\xa5\x02\x47\x63\x56\x0f\xa9\x5d\xd8\xaa\x9e\x6a\xac\xf5\x6d\x51", 200, { 0x6c, 0x74, 0x72, 0x4a, 0xa1, 0xeb, 0x2f, 0xe2, 0x8e, 0x27, 0xa4, 0xd0, 0xba, 0xf2, 0xf5, 0x30 } },
{ "\xed\x3b\x9c\xf6\x4b\x62\x7e\x1d\xa0\x7c\x60\x4d\x30\x7c\x4c\xcf\x82\x05\x78\xd6\xd5\x5e\x4e\xb8\x41\x82\x19\x5a\x6c\x55\x49\xab\xe5\xf0\x63\x47\x20\x1d\x88\x3b\x0e\xde\x9f\xe8\x59\x28\x22\x00\x39\xad\x82\xae\xf6\xd7\x38\xd2\xfa\xd0\x69\x6a\x92\xbe\x35\x0c\x41\x0c\x9d\x8f\xc1\xe4\x0e\xca\x97\xb9\x8e\x74\x51\x00\x82\x2a\x5f\xfe\x19\x90\x8c\xbc\x59\x8f\x17\x18\xc4\xbc\x72\xf6\xa6\xd8\x96\x93\xfe\x74\x01\xfa\x07\xad\x4d\x8f\x62\x15\x6e\xc8\xe1\xb2\x88\xfc\xf2\x20\x6b\x53\xa6\xd1\xac\xde\x5d\x75\x61\xc0\x10\x75\x78\x89\x3b\x98\xb4\xa3\x65\xc9\x46\xe5\x4d\xf0\x04\x45\xb3\xfc\x48\xaa\xc0\x02\x68\xe0\x12\x7f\xcd\xa5\x68\xb9\xb2\xe0\xe7\x44\x7b\xf1\x07\xa1\xaf\x23\x1d\x01\x94\x3e\x85\x27\x66\x3a\x6b\x6b\x33\x0e\x36\xda\x56\xa5\x93\x7b\x8e\xf2\x19\xad\xba\x1a\x9e\xac\x33\xd0\x16\x32\xc6\xbf\x22\x3a\x4c", 201, { 0xfc, 0xfb, 0x80, 0xef, 0x32, 0xd8, 0x1e, 0x33, 0xb0, 0x18, 0xbb, 0xfe, 0x11, 0x1f, 0x3a, 0x1f } },
{ "\x20\x3d\xdf\xe8\x6b\x7e\x63\xdd\x2a\x0a\x4c\x0a\xe8\x1a\xa9\x02\x49\xa5\x73\xcc\x33\xaa\x0e\x34\x2a\x1c\xef\xcc\xba\x69\x57\x82\x0d\xa9\x3d\xdf\x9c\x60\x49\xda\x02\xf0\xfd\x57\xec\x9e\xee\x3f\x2d\x3e\x30\x3c\xee\x7e\xd1\x11\x03\xcd\x7b\x95\x58\xe6\x3d\x4a\x8a\xfd\x63\x9e\x92\x84\x81\xbd\x9c\x9a\x8f\x11\xf6\x11\x2e\x57\x24\x1a\x09\x5f\x10\x8f\x57\x60\x5e\xdd\x7c\xf5\xde\x8c\xcf\xb8\x1b\x6d\x77\x7a\x10\x5f\x6e\x1c\xfa\xbd\xa7\x0d\x49\x68\x4c\x60\xb0\x6c\x20\x88\x5b\x51\x04\xb4\x40\x01\x95\xc1\x8f\x51\xf2\xe0\x43\x2d\x9b\xc6\xda\x65\x75\x89\x21\x0e\xea\x1e\x29\x96\x2d\x6c\x56\x68\x9b\x0c\x95\x38\x3d\xa0\xad\xeb\x6a\xcd\xaf\x26\x89\xd6\x88\x72\xf5\xd6\xb5\x09\xf9\x9a\x15\x40\xfa\x19\xda\x90\xb4\x90\x99\x42\x9c\x3e\x82\xb7\x65\xb9\xa9\x51\x9f\xec\x82\x02\x79\xae\x6c\x6f\xa7\xff\x64\xc0\x5f\xe1\xda\x07", 202, { 0xc3, 0xe1, 0xb0, 0x79, 0x72, 0x94, 0x8c, 0x0f, 0xa0, 0xfa, 0xa3, 0x43, 0x20, 0xc6, 0x93, 0x87 } },
{ "\x7d\xd1\xa0\x4a\xc6\xe0\xff\x2e\x49\x73\xe4\x42\xe1\x93\x38\xe6\xd8\xf2\x4d\xd7\xa6\x7b\x74\x58\xd7\x94\xab\xfd\x0a\xf3\x73\x17\x15\x1a\xc0\xb7\x46\x70\x0a\x61\xc5\xfe\x81\x4f\x15\xc3\x5d\x5e\xb9\xb4\x53\x99\xf3\x53\x23\x61\xa7\xea\x4e\x36\x5f\x64\xe6\x24\x68\xc9\x7d\xcc\x19\x54\x3f\x0e\x33\x33\x1c\x50\x64\xdb\x1d\x6e\xe6\x05\xd8\x3e\x44\x48\xff\xbe\x3d\x54\x12\xdc\xc1\xc8\x35\xe2\x18\xb1\x1c\x7d\x22\xa0\x22\x68\xb9\x67\x79\xba\x32\x6f\x7c\xc8\x03\xb9\x21\xb8\x7e\x6f\x8a\xa3\xbc\x26\xba\x66\x95\xb3\x64\x06\xcb\xa7\xdf\xbd\x46\x68\x37\xa5\x7a\xed\x5d\x00\xe4\x15\x7e\x22\xb4\xa5\x71\xfb\x85\xdd\x49\x45\x47\xb5\x0a\x46\x3e\xb9\x79\x42\x23\x7e\x0c\x81\x68\xc0\x01\xf8\x99\x19\x8b\x97\xde\x60\x26\x2f\x9d\x9c\x0c\xfd\x3c\xa4\xc0\xd7\x04\x54\xc7\xf1\x21\x6e\x76\x4c\xc6\x0a\xe7\xbd\x6d\xbb\x05\x96\x3c\x40\xc7", 203, { 0x8d, 0x91, 0xba, 0x03, 0xb1, 0xc8, 0xaa, 0x70, 0xf9, 0x6c, 0x1c, 0x25, 0xa2, 0x93, 0xa9, 0x8d } },
{ "\xf3\x83\xe4\x7a\xa2\x62\x73\x36\xb0\x88\xd9\x72\x8c\x16\x58\xb4\xdb\xa1\x65\x61\xd7\x56\x20\xb2\x64\x39\x6f\xc7\xb1\x86\xb6\xd6\x87\x38\x34\x7c\x32\xa7\xfd\x34\x08\x4c\x90\xe5\x9a\xa1\x14\x95\x77\x23\x34\x3c\x97\x79\x93\xb3\x6b\xaf\xee\xcb\x7f\x9b\xcd\x7a\xc8\x60\xe6\x31\x90\x10\x0e\x49\xfb\x6d\xdc\x9b\x35\xc8\xdc\x2e\x3a\x0b\x6d\x0b\x41\xd2\x38\x2d\xc6\xb3\x4d\x95\x32\x9e\xdc\x79\x2a\x60\x8c\x9c\x71\x42\x7b\xb9\x7b\xce\xd3\x19\x8f\xb1\x05\x44\x97\xbc\xa5\xd4\x87\x05\xe2\x65\x68\x2a\xa0\xa8\x00\xb5\x34\x97\x20\x9b\xbb\xc3\x8a\xdf\x17\xc8\x7c\x54\x88\xe3\xdd\x7f\xe3\x9a\x03\x9a\x71\x99\x1f\xb5\x66\x9d\x46\xf8\xfb\x89\x1c\x03\x2b\x96\x1f\x76\x08\xa8\x8d\x8c\xb7\xbb\xf3\xe2\x0e\x7c\x54\x56\xc8\xf4\xf2\x0b\x63\x5f\xbc\x88\x97\x1b\x53\x00\x72\xbc\xbb\xac\x14\x3c\x9b\x54\x05\x50\x30\xee\x2e\xd5\xd4\x5d\x7b\x69", 204, { 0xf3, 0x73, 0x23, 0x57, 0xe5, 0x6b, 0xc8, 0xaa, 0x91, 0xf1, 0x46, 0x79, 0x25, 0xaa, 0x4f, 0x9f } },
{ "\x27\x5a\x0a\x17\xd7\x70\x10\x2a\x12\x21\x49\x22\x85\x26\x19\xc5\x0f\xd4\x44\x4c\x07\x9a\x47\xbe\x26\xa7\x51\x5b\x13\xa8\xe1\x2e\x8a\xaf\xfd\xc6\x28\x2f\x0c\xfe\xd5\x24\x51\xf7\xce\x50\x04\x27\x4d\x9f\x0e\x8b\xd8\xac\x62\xf8\x23\x5c\xf3\x8f\xa3\xa8\x55\x4f\xb1\x79\xf4\xc5\x56\xac\xeb\xde\xb9\x35\x82\xdd\x22\x5f\x47\x67\xaa\x31\xc7\xbb\x82\xed\xe9\x00\xdc\xb2\xe8\xb7\x79\x41\xeb\x50\xd0\xdc\x43\xd8\xd8\x4a\x40\xcf\x72\xf8\xb0\x18\x76\x39\xf5\x09\x59\xae\xc2\xa2\x78\xc1\x72\xdb\x03\x4b\x05\x16\x89\x56\xb7\xb4\x1b\xfc\x3f\xc4\x20\x6e\xa1\xd5\xb5\x11\xb0\xec\xbe\xc2\x24\x91\x8e\x3a\x53\x04\x2f\x8d\x90\x8d\x4e\xcd\x1d\xf1\xc6\xcb\xcd\x00\xc7\xfd\x3b\x4c\xa3\x7b\xa1\xf4\x35\x24\x56\x9e\xee\xdd\xe6\x83\x7d\xf9\xcf\xa3\x1a\xb5\xd6\x1a\x70\xda\x04\x8b\x25\x85\x41\xb8\x07\x03\x8b\x34\xd4\xd6\xd3\x2f\xa6\xd5\x74\x71\xf9", 205, { 0x12, 0xe3, 0xc8, 0xd7, 0x9f, 0xe5, 0x62, 0x5e, 0xc1, 0x04, 0xed, 0xd2, 0x54, 0xf5, 0x06, 0xd8 } },
{ "\x6f\x51\x62\x5a\x10\x89\x45\xae\x9c\xda\x85\x1d\x18\x8f\x28\x99\x68\x27\x60\x0f\x33\x84\x40\x28\xe2\xcc\x8c\xbc\xc8\xe0\xa9\xd4\x5c\x77\xb3\x5a\xa1\xd6\xad\x5f\xa6\x2e\xd3\x09\x29\x92\x0c\x17\x57\x93\x7c\x13\xaf\x7a\x35\x13\x04\x21\x0d\x2b\xa1\x6e\x8a\x72\x86\x6d\xfa\xaf\xd1\x09\xa0\xa1\x38\x67\x08\xe8\xb3\xe0\x7c\x93\x37\x98\x8f\x47\x9c\xbf\xd6\x08\xa0\x64\xb7\xa7\x04\xf1\x59\xc8\xd4\x47\xbb\x8f\xc4\x77\xe0\xe7\xb6\x19\x28\x6f\x58\x4d\xbb\x01\xeb\x4c\x1e\xdf\x1e\xa9\xe7\x7c\x18\x2a\x8d\xe5\x95\x3d\x59\xca\x28\x19\x79\x2d\x9e\x72\x33\xa6\x83\xd8\x37\x50\xbe\xad\x0d\x54\x57\xc1\xad\x10\x5a\x8c\x2d\xe3\xd3\x07\x95\x97\xf8\x27\xce\x6c\x66\xf7\xb9\xbd\x84\x51\x5d\x51\x04\x38\x38\x41\x88\xd5\xb6\x81\x61\x0d\xbf\x0c\x72\xbb\x6b\xb0\x33\x8f\xd1\x73\xd1\x82\xfd\xa1\x73\xf5\xff\x73\x98\x65\x20\x5e\x9c\xcd\x30\xf5\x5a\x99", 206, { 0xa3, 0xaf, 0xd7, 0xe6, 0xa2, 0xce, 0x1a, 0x98, 0x7a, 0x71, 0xe1, 0xab, 0xf9, 0xce, 0x37, 0xb6 } },
{ "\xfb\x7b\xea\x42\xda\x09\x8b\x8a\x65\x58\x9c\x56\x46\x2c\xc5\x23\x29\x5e\x33\x26\xcf\x84\x00\x04\x42\x3e\xb0\x2b\x23\x20\xd7\xcb\x1f\x37\xd9\x75\x80\x3a\xca\x4e\xe0\x4f\x73\xef\xf8\x76\x76\xdd\x96\x96\x89\xa0\xad\x22\xde\x82\x86\x68\xa3\xe6\x15\x76\xa5\x42\x66\xa9\x10\xba\x36\xd3\x51\x5a\x9e\x08\x1c\xf0\xea\x06\x89\x84\x88\x3e\x59\x75\x1c\x83\x57\x32\xb1\x4e\xda\x91\x09\xab\x67\xcf\x15\xc4\x73\x25\x80\x08\x45\x03\x65\xf8\xfa\xec\x22\x8e\xa3\xed\x44\x4a\x89\xbb\xa1\xda\x90\x68\x85\x65\xb9\xc2\x04\x74\xc1\x48\x6f\x7d\xe7\xca\xe1\x0e\xcb\x9c\xf9\x93\x72\x76\xa2\xc4\x66\xeb\x0d\xad\xfa\x84\xc0\x5b\xab\x79\xc8\x20\xa2\x0b\x0a\x84\x57\x81\xb8\xc8\x4f\xbc\xdf\x17\x05\x79\x1c\x4c\xe7\x23\x6f\x5a\x77\x53\x27\x5c\x92\xe5\xfd\x3a\xce\xb8\x3d\xf4\xfc\x01\x1f\x8e\xcd\x4c\x34\x99\x90\x11\xfc\x59\x19\xef\x94\x98\xbe\x88\x8c\x06\x7b", 207, { 0x35, 0xc8, 0x5d, 0xea, 0xdd, 0x9b, 0x4e, 0xe1, 0x1d, 0x35, 0x19, 0x7e, 0x3c, 0xe6, 0xb8, 0x29 } },
{ "\xf8\xd7\x4b\x35\xf2\xdc\xab\x1b\x79\xa9\x55\x29\x39\x47\x48\xc6\x80\x28\xe3\x8e\xdc\xbd\x07\x2a\x51\x24\xea\x5a\x37\xff\x7b\x14\xae\x60\x6d\xc6\xbf\xe0\xe3\xb8\x11\xcf\xb6\x8d\x45\x85\x66\xe8\xee\xd7\x9a\x2c\x30\xa5\x55\x5b\xf4\x91\xb8\x20\xc5\xca\x6e\xe8\x4a\x06\xb7\x2a\x60\x8e\x15\xc8\xd4\x73\x8d\x8d\xba\xde\x9a\xd6\x6c\x85\xb4\x4e\x22\x3a\x77\xd2\x2b\x9d\x74\x73\xc6\xf2\x91\x99\x9f\x0d\x1d\x44\xe9\x6a\x74\x6e\x14\x59\x4b\x8d\x2c\x56\x99\x35\xce\x77\x23\xd9\xc7\xfe\xa2\xb1\x4a\x0e\x92\xb8\xce\x7b\x9a\xcd\x82\xba\x93\xf9\x6e\xf7\x36\xd0\x27\x46\x67\xf0\x2e\xf1\x18\xa7\xe7\xf0\xdb\xb1\x31\x76\x08\x1e\xa6\xa8\x87\x52\x70\x68\x3b\x26\xc6\x50\x0c\x2d\x02\xbb\x8e\x11\x61\xfd\x53\x1b\x56\xb2\xca\xd1\x8b\x34\xd2\xb9\x75\x26\xdf\x3c\x92\x2d\xc7\xa6\x42\xbf\x2a\x4a\x40\x13\x7c\xc2\xbb\x38\xb1\x54\x15\x42\x83\x71\x37\x9f\x63\x57", 208, { 0xdf, 0xbc, 0x18, 0xbe, 0xc2, 0x94, 0xd5, 0x70, 0x1c, 0xe7, 0x30, 0xe9, 0x86, 0xc7, 0x7f, 0x08 } },
{ "\x8f\x87\xfa\xd6\xa7\x92\x5a\x2f\x63\x63\xc6\x17\xd7\x82\x1a\xdc\xc2\x48\xd8\x9f\xab\xf3\xd1\xbf\x97\xd9\x6d\x57\x64\xba\x97\xdd\xc6\x2e\x47\xeb\xdb\x3d\xad\x1a\x6c\x0d\xf7\x0a\xc2\xb6\xbf\x7f\x23\x32\x14\xa6\xe8\x70\x24\x75\x3c\x87\x83\x30\x73\x07\x1a\x07\x04\x6c\xaf\xdd\x25\xac\x0c\x23\x01\xf0\xcf\xe3\x99\x5f\xce\xd9\x34\x15\x24\xbb\x84\x32\xdc\x9a\x57\x0f\x39\x60\xf6\x8c\xa0\x79\x1e\x85\x23\x8f\x98\x63\xab\x6d\x77\xce\xc1\x05\xee\x80\xf9\x8d\xcb\x35\xfb\xc3\x94\xbf\x2f\x52\x3d\x35\x05\x4d\x83\x4b\xde\xd8\xe7\xbd\x9a\xe6\x4a\xe6\xbf\x1c\x22\x6d\x42\xd4\x56\x1e\xf6\x3f\xbc\xd7\x8e\xa2\x2c\x99\x50\xc1\x41\xbe\x59\x59\xac\x4a\x87\xc6\x34\x59\x06\xc5\x4e\xb8\x7a\x54\x54\x90\xc6\xb6\x65\x3d\x77\x92\xda\x3e\xd1\x3b\x60\x45\x74\x0b\xb7\x6d\xa9\xe8\x06\x8b\x4f\xe8\xd8\x9c\x5c\x11\xb7\x5e\x12\x39\x63\xfc\xc1\x0c\xaf\xe1\x32\x2c\xf9", 209, { 0xb0, 0xbf, 0x87, 0x74, 0x91, 0x48, 0xb8, 0x57, 0xf4, 0xbb, 0xfa, 0x00, 0xec, 0x90, 0x0a, 0x39 } },
{ "\xae\xb7\x73\x44\x46\xcd\x54\xf4\x06\x6b\x5f\x25\x12\xea\xe3\xa8\xcb\x90\xad\x9a\x5c\xba\xef\xa3\x74\xa6\x3c\xc8\x0e\xda\xb0\xee\x6b\x35\x2d\xec\x22\x90\xc0\x4b\x4e\x11\x21\x9d\xe5\x0c\x59\x77\x28\x95\x7e\x0c\x36\xa6\x9a\x67\xbc\xe9\xaa\x44\xc7\x24\xc2\x8c\xba\x3f\x4e\x6c\x5b\xf2\x73\x11\x07\x0f\x93\x01\x06\x69\xef\x19\xf9\x60\x68\x1f\x70\x0a\x5e\x03\x98\x00\xb9\xb7\x11\xc2\x06\xa8\xde\xc8\xb9\xd7\x76\x26\x91\x99\xf7\xda\x19\xe7\x7a\xb6\x4a\x63\x81\xe4\x4e\xe8\x8d\x1b\x5f\xcc\xcd\x5d\xce\xb5\xf0\x6a\x20\x14\x1c\xc5\x52\x43\xf7\x60\x3e\x37\xe2\xe6\x16\xe2\x45\xa5\x0c\x28\x05\x17\x14\x7b\x12\x0b\xc1\x15\x1e\x75\x4c\xd1\x68\xce\xb4\xa7\xb6\x29\xff\xc2\x61\xd4\x9e\x40\x8a\xa7\xee\x85\x6b\xec\xdb\x3c\xc8\xeb\x9f\xec\x83\x10\xa8\x32\x4f\xbb\x98\xa1\x7d\xa4\x66\x33\xf2\xe9\xa2\x6a\x3a\xb6\xd5\x07\xb5\x90\x06\x66\xef\x3e\x59\x74\x0e\x54", 210, { 0x3f, 0x07, 0x92, 0x47, 0x77, 0x40, 0x22, 0x89, 0xaf, 0x63, 0xa4, 0x03, 0x20, 0xa2, 0x2a, 0x9e } },
{ "\xd3\x4e\x64\x5d\x38\x78\x24\xa4\x15\xb7\x32\x9f\xda\x8c\x13\xf9\x1f\x4d\x04\x3b\x07\xe1\x26\x16\xc2\x17\xd6\x4f\x2c\xa4\x1b\x47\x93\xb7\x39\x26\x96\x3b\x62\xa4\x28\xa8\x8c\x74\xb4\x86\x5e\x4d\x5b\x80\x44\x95\x58\x21\xa2\xac\x85\x2d\x24\xd3\xf7\x9e\x34\xb8\xc3\x3a\x85\x9f\xe4\xcd\xcc\x2e\x35\x09\x8f\xd5\x98\xf6\x15\xd8\x39\x17\x7d\xfa\xed\x1b\xeb\xc8\xb4\x3c\xdd\x7f\x5d\x38\x9c\x9b\x49\xea\xcd\xfd\x47\x85\x27\xb7\x9f\x6c\x3f\x77\x2a\xb0\x7f\x8f\x9e\x26\x35\x9d\xe8\x24\xc7\xdc\xb2\xd9\x05\x05\x04\x46\x97\xe3\x06\xdd\x95\x72\xe3\xb4\x35\xb3\xca\x66\x9f\x0f\x0e\x86\xdd\x65\x6e\xc1\xee\xb2\x6e\x8f\xf9\x5c\xe4\x95\x9f\x1b\x8f\x9b\x69\x84\xe0\x25\x61\x34\xdd\xb0\xbc\x95\x1b\x8b\xb4\x50\xe9\x4f\x74\x4f\x8c\xfc\x2d\x6d\xa4\xb1\x46\xbd\xad\x07\xb0\x74\xa1\x0b\x57\x74\xbb\xd7\xb4\x76\x57\xcc\x7f\xd2\xf5\x6e\x82\xee\x5d\xdc\x89\x41\xdc\xea\x76", 211, { 0xb4, 0xe4, 0x99, 0x88, 0x69, 0xa4, 0xd3, 0x42, 0x48, 0xfd, 0x35, 0x33, 0xd4, 0xa3, 0x66, 0x48 } },
{ "\x03\xac\xdd\x71\x73\x72\xc2\x3c\xc4\xe0\x6b\xa4\xa5\x7b\x07\x13\x6c\x77\x5d\x43\xde\xbe\xa5\x0d\x9d\x0f\x03\x5f\x2e\x43\xa1\x4c\xa6\xab\x67\x3e\xf1\x96\x5a\x47\xbf\xef\x8e\x94\x0c\x02\xc9\x02\x4a\x5f\xb6\xcb\x2c\xd7\xc3\x59\x11\xa3\x98\x3b\x0c\xa5\x33\x35\x8c\xc6\xa4\x71\xad\x7e\x62\xd4\x1a\x72\x49\x6c\x9c\x37\x31\x05\x44\x34\x2c\xdb\x31\xa6\x5b\x69\xff\xbe\x6d\x60\xcc\xe5\xb6\xb3\xe8\x1f\xe8\xcf\x18\x8d\x70\xe8\x87\x0c\x6f\x6f\x4a\xfc\x53\xa0\x8e\x1b\x12\x37\x61\x8f\x03\x42\x19\x02\x59\x12\x65\xe7\x3c\x4b\xee\xdc\x85\x1b\xa9\x28\x16\x87\xbc\x63\xd4\xe1\x0a\x35\x43\x56\x5d\x36\xbc\xa3\x2f\xf5\x4d\xe8\x15\x15\x2d\x95\xc9\x91\xda\x81\xd2\x1f\xd1\x19\xd2\xb2\xea\xb5\x6c\x1a\x4d\x06\xcf\xc7\x14\xac\xe4\xab\x7f\xe4\x10\x3d\xce\x5f\xa6\x99\xbf\x2d\xec\xc4\xa4\xd8\xc8\x0e\x20\x8e\x08\x90\x7e\x76\x4b\xd5\xad\x23\x8e\x95\xdc\x26\x57\x9d\xae\xbd", 212, { 0xba, 0xe8, 0x47, 0x79, 0xba, 0x43, 0xe2, 0x04, 0xf1, 0x5a, 0x99, 0xb4, 0x4d, 0xf6, 0x3b, 0x7c } },
{ "\x78\xca\x1a\xaa\x80\x33\xe3\x1f\xc4\x67\xae\xd5\x05\xcb\xc5\x3c\xbe\x26\x67\xcd\x0d\x38\xc9\x7b\x3b\x84\xae\x48\xea\x2f\x9e\xf3\xda\x01\xc6\xce\x57\x88\x6b\xae\x43\x5b\x0c\xfd\xbc\x7c\x14\xe9\x69\xd7\x39\xbf\x66\xe7\x74\x52\xc9\x78\x9d\x95\xd1\x87\xa2\x49\xab\x45\x63\xcf\xe0\x3a\x2e\x1f\x5e\x87\xd2\xd1\x20\x44\x62\x59\x7d\x08\x88\x50\x0b\x86\x83\xed\x2d\x54\xbe\x92\x40\xc7\x0e\x83\x5e\xfb\x88\xb1\xcd\xef\xfd\xb5\x08\xcd\x14\xd8\x67\x46\x02\x4e\x6d\x1c\xe8\x84\xae\xb9\xc8\x8f\x5b\xfd\x25\xc3\x6c\x15\x37\x65\x68\x86\xc8\x78\xd4\x4a\xae\xb3\x36\x11\xe5\x94\xc4\xa8\x48\x8e\xae\x77\xec\x5e\x05\x24\x1a\x7c\x46\x56\xe6\xac\x87\x94\x70\xb3\x3d\x4b\xb7\x27\xa8\xee\x15\x60\xdc\x38\x5b\x8b\x6c\x8d\x89\xdd\xb4\x7e\x2a\xe3\xc3\x6c\x4a\xf8\xe3\x43\x15\xd1\xc7\x68\x0e\x32\x51\xae\xe8\xc3\xfd\x81\x05\xfd\xed\x25\x88\x3f\x91\x19\x19\xfd\xf2\x95\x35\x17\x9c", 213, { 0x36, 0x5a, 0x4c, 0xec, 0xc3, 0x99, 0x8e, 0x50, 0xdb, 0xee, 0x2e, 0xc0, 0x07, 0x66, 0x78, 0x82 } },
{ "\x33\x2e\x48\x26\xaa\xc6\xb5\x5f\xb4\x64\xa5\x17\x12\xf1\x50\x87\xa6\x52\xd0\x5e\xbd\x82\x34\x69\x6f\x4a\xb4\xdd\x1f\xa0\xfe\x44\x1e\x9a\xaf\x02\x6c\x0d\xdd\x83\x9e\xc7\xd1\x04\xdb\x64\xd2\xfa\x00\xd1\x1c\x22\xe4\x5b\xf0\x26\xb5\x73\xc5\xe9\x81\x00\x9c\x70\xd1\x61\x71\x6c\x70\xfe\xcd\x68\x49\x89\x78\x8b\x6e\xd7\x4c\x0b\x35\x55\x26\x2f\x7d\x28\xf5\xfe\x0e\xe5\xf4\x89\x1e\xea\xf4\xd2\xf0\x57\xd2\x58\x97\xac\x09\x40\xd9\x01\x60\x21\x2f\xdc\xc4\x6c\xe8\xb2\x30\x77\x6c\xfd\xc8\x24\x9e\xf6\x06\x32\x5b\xf0\x0b\x20\x20\x51\x17\xb9\xc8\x2d\x14\x41\x72\x80\x4d\x3a\x81\x08\x3c\xd3\xbd\xbd\x80\xee\x96\xee\xed\xcd\x89\xfa\xbe\x58\x9c\xa7\xe5\x0d\x03\x22\x83\x84\xe5\x93\x74\x9e\x38\x5c\x01\x4a\xc8\xef\xb9\xf3\x57\x49\x59\x89\xdf\x0f\xe8\x2b\xf3\x43\xe0\x6e\x43\xa3\x86\x4d\x3e\x9e\x5b\xad\xd2\xf4\xab\x8b\x4f\xe9\x42\xc4\x69\x25\x3e\x80\x5c\x2b\x00\x3c\x2a\x74", 214, { 0x94, 0xb3, 0x23, 0x0b, 0x78, 0xb0, 0xe5, 0x34, 0x86, 0x5b, 0x43, 0xce, 0xc8, 0x6b, 0xc9, 0x2e } },
{ "\x3c\x73\x00\x28\x30\xe9\xb7\x78\xf2\x94\x76\x9a\xe1\x27\x74\x62\x3d\x3c\xdb\x7a\xd3\x1d\xc8\x3e\xd1\x2e\x6f\x36\xb8\xfb\x5c\x31\x6f\xda\xd4\xfa\x73\x3d\x5a\xba\x2c\x96\x4d\xea\x5b\xe7\xae\xf2\xb0\xe5\x00\x63\x78\xc8\x48\xce\x74\xb2\x34\x3f\xc5\xb9\x58\x59\x03\x93\x93\x0f\x6c\xdd\x90\xfc\x39\x08\x69\x60\x0c\xe0\x65\xb8\x86\xba\xe2\xf9\xf6\x3a\x4e\x68\x2c\xbe\x4f\x19\x6b\x6b\x03\x02\x7c\xd2\x61\xbb\xdf\x3e\xbe\xd4\x1d\x9c\x6c\xd2\x39\x08\x7d\xc8\x45\xf0\xa5\x8f\x10\xe7\x3d\xa2\xfd\x08\x64\x98\xef\x05\x40\x3e\x60\xcb\x62\x50\x91\xd3\x48\xd4\xdc\x08\xfc\x14\x25\x50\xd9\x36\x6f\xba\x6d\x79\x8a\x42\x7a\x0e\xea\x43\x17\x91\x94\x7a\xf4\x22\x31\xb2\xba\x25\x45\x12\x91\x92\x79\xff\xbb\xf9\x14\xaf\x5d\x16\x88\x4c\x5e\x0c\x29\xc0\x68\x42\xf8\x23\x0c\xd7\x9e\xbf\x02\xc3\x74\xbc\x8e\x8b\xbf\x6d\xfd\xa0\xf9\x35\x4f\x55\x4a\x17\xef\x7c\x16\xda\x9d\x9c\xb0\xd5\x6c", 215, { 0xa5, 0xbf, 0x5d, 0x7b, 0xd6, 0x14, 0xfe, 0x55, 0x13, 0xa2, 0x41, 0x76, 0x5d, 0xa5, 0x45, 0x8d } },
{ "\xd0\x2c\x5c\xe7\x3f\x51\xc7\x4f\x33\xad\x1b\x00\xd8\xc3\x58\xf2\x93\x60\x5f\x5a\xfd\x0c\x7d\x70\x7b\x9b\x98\x4c\x7b\xe3\xf4\xea\x4d\x87\xa3\x4c\x9b\xf2\x87\xbe\x87\x60\x05\x35\x60\x16\x48\xd1\x00\xb2\x2f\x82\xc4\x9d\xd4\xc6\x29\x9c\xba\x90\x00\x86\x92\x45\x4d\x10\xaa\xdf\xc2\xc4\xf3\x34\xd3\x10\xad\x51\x76\x7b\xb1\x00\x79\xf2\x5b\x6a\x7f\x41\x12\xd5\x98\x9d\x75\x8d\x7c\x5e\x23\xe1\x64\xc9\x27\x45\x86\x0b\xfe\x95\x2b\x43\x47\x79\x6e\xb6\x07\xe3\x93\x26\x95\xb5\x01\x3c\x28\x80\xdd\x22\xfb\x68\x45\x2d\x1a\x23\x26\xb8\xcd\x20\xbb\x0c\x9e\xc4\xa2\x7b\x07\xcf\x9c\x8c\xbd\xdd\xe1\x50\x93\x09\x1e\xd3\x0d\xac\x0d\xae\x82\x43\xae\xba\xec\x6f\x27\xdf\x50\x15\x35\x0a\xc4\xa5\x4e\x3d\x22\x78\x55\xc0\x48\x53\xf9\x75\x08\x09\xab\x49\xb3\x3c\x3a\xc6\x3d\x43\x0c\xc6\x0d\x28\xfb\x42\x64\xc8\xc9\x49\x67\x1d\x42\x0c\xca\x99\xed\x16\x1b\xa8\x6e\x98\xa8\x58\x7b\xe2\x0f\x15", 216, { 0x59, 0xfe, 0x50, 0x4c, 0xbb, 0x58, 0x09, 0xb1, 0x1b, 0xce, 0xbc, 0x3c, 0x1d, 0x12, 0xfc, 0x29 } },
{ "\x79\xee\x60\x8d\xb2\x17\xf0\xb2\x35\xe7\xbd\xde\x4b\x0d\x79\x10\x91\x6d\x35\x54\xb7\x52\xab\x41\xd4\xbf\x28\x9c\x63\xe3\x14\x1b\xde\xaa\x1f\x43\xf5\x70\x0a\x72\x6b\xd0\x0f\xf9\x8e\x9b\xef\x61\x51\xe5\x96\xcf\x07\x39\x6c\x82\xbe\xec\x3a\x78\x36\x8f\xb7\x30\x7d\x7e\xaf\x8b\x28\x95\xcf\xcc\x2f\x02\x0f\xbe\x66\x36\xbc\xf5\x94\xf6\x21\x2c\x32\x8e\xd1\xce\xc1\x24\x94\x90\xc8\x2a\xec\x3b\x69\xed\x42\x87\x9f\x4b\xb2\x23\x17\x81\x70\xd2\xa7\x22\xd5\xaf\x6f\x24\x0a\x91\x8b\x15\x50\x89\x72\x6b\xe9\x88\xee\xf8\xa6\x1e\xb8\x7c\x0e\x5d\xaf\x55\x28\xdc\xb5\x51\xe6\xd7\x26\xa4\x2e\x74\xf6\xf5\x85\x20\x66\x92\x5a\x18\x63\xc4\xed\x04\x7b\x3e\xda\xbd\x7c\xaf\x46\x2f\xab\x77\xa5\x5a\x9e\x76\x25\x73\xb7\xff\xba\x27\x3d\xeb\xc7\xff\x18\xca\x66\xde\x29\x35\x54\xa8\x44\x3f\x7c\xfd\xcb\x0a\x1e\x23\xe8\x75\x9c\x02\x66\xf3\xe1\x48\x2d\x77\x6e\xad\x58\x8b\x02\x51\xf5\x80\xe6\x41\x58", 217, { 0x46, 0x60, 0x93, 0x1b, 0x8d, 0xd0, 0x21, 0x38, 0x27, 0xd0, 0x23, 0xfe, 0x9a, 0x06, 0xe0, 0x3f } },
{ "\xc0\xd2\x18\x1c\x3f\x7f\xc2\xb7\x10\xca\x5c\xb9\x14\x82\x37\xa1\xcd\xce\xfc\x5c\x50\x3e\xe9\x5b\x59\x6c\x39\x4f\x79\x4e\xa2\x84\x6a\x1c\xf8\xd7\x83\xd8\xef\x56\x46\x7f\xfd\x72\x17\xca\x5e\xe3\x08\xa1\x31\x19\xcf\x1d\xc0\x5a\xd9\x38\xdd\x7c\x77\x14\x67\x41\x37\xe4\xec\x4a\x4e\x7b\x68\xba\x30\x6f\x2c\x68\x42\xea\xda\xc0\xa7\x0d\x8d\x37\x37\x00\x15\x68\x8e\x9a\x60\xd0\x40\xaa\xf4\x23\x2a\xe0\x98\x48\xe4\x5b\x13\xf8\x52\xd0\x0c\xee\x5a\x53\x10\xcc\xb0\xc6\xb2\x8a\xcc\xdb\xcd\x9b\xfd\xc7\xe5\x9e\x97\x85\xac\xbd\x48\xc3\xa0\xa6\x89\x6d\xd1\x24\x30\xd5\xc1\x59\x22\x7c\x64\x9a\x25\x80\x46\xe7\xd3\xa1\xcc\x05\xaa\xf1\x92\x3a\x41\x18\xf4\xa5\x95\x41\xa8\x37\x82\xa5\x88\x52\x4d\xb2\x6e\xfc\x5e\xd7\x39\xc0\x0b\xef\xec\x21\x8f\x47\xf5\xbc\x19\x4e\x8a\xf9\x01\xd8\x2f\x6d\x15\x0e\x55\xd0\x12\xf6\x1c\x96\x2e\x9b\x39\x2c\xd2\xd9\xf3\xd0\x65\xc7\x47\x0d\x44\xc1\x2c\xd2\xce\x10", 218, { 0xf3, 0xa4, 0xbf, 0xef, 0x5b, 0x96, 0x17, 0xce, 0x45, 0x37, 0xa1, 0x87, 0x6e, 0x17, 0x10, 0x65 } },
{ "\xf1\x2e\x9b\x39\xc2\xab\xcc\xdf\x49\x44\x7d\x55\x70\x03\x57\xc4\x45\x3b\x43\xf5\xf7\x5f\x39\xbc\xfd\x7e\x36\x02\x56\x5c\x4c\x6d\xeb\x2e\xd3\xae\x10\xf1\xbd\x66\x29\x67\x07\xb6\x69\xaf\x4c\x62\xb6\x80\x74\xa1\xa9\x02\x89\x83\xb1\x49\xaa\xff\x57\x6e\x18\x98\x9c\x4c\x6e\xac\x7e\xd4\xed\x59\x99\x0e\xf5\x0c\xeb\xac\x90\xa7\xb0\x4c\x29\x4e\x6c\x4b\x5a\xcf\x07\x40\xe3\xac\x12\x1c\xb6\xc5\x27\xdc\x2e\xa6\x6f\xe8\x60\x6b\x3e\x0c\x8a\x4c\xd8\x10\xa9\xc9\x57\xf5\x26\x73\xa4\xcf\xb0\x54\xa3\x3a\x25\xfc\x6b\x7a\xa9\x29\xb2\x33\xda\x12\x48\xc1\x1a\x50\xba\x35\x0b\x6a\x17\xed\x19\xfb\xcf\xcf\x30\x82\xb6\x22\x8f\xc6\x28\x08\xb4\xe4\x47\x9c\x77\xf6\x60\xa4\x33\x62\x6c\xf3\x1f\xbc\xb2\x5e\xba\x59\x8a\x58\x65\x71\x3c\x2d\x0a\xf0\x06\xd8\xfa\xd9\x3c\x81\xd5\x57\x8a\x96\xa2\x62\x55\x84\xff\x0a\x40\x12\x72\x64\xcb\x5a\xdd\x0e\x15\x66\x10\x5f\xc2\x63\x79\x30\xf7\x84\xf6\x42\x82\x76\xcb", 219, { 0xcd, 0x28, 0x77, 0xaa, 0xac, 0x2a, 0xed, 0x44, 0x22, 0x6b, 0x47, 0x34, 0xb0, 0x26, 0xb4, 0xb3 } },
{ "\x4a\x17\x7b\xaf\xa0\xb6\x26\x01\x80\x28\xb2\x18\xfa\xa7\xfe\xb7\x7c\x9d\x29\xa0\xfa\x85\xc6\x3f\xb7\xee\xbc\x87\xb3\x9e\xbd\xdb\xb1\xd3\x61\x22\xe4\xa5\xde\xfd\x35\x87\x66\x15\x34\xec\x1d\x8d\x98\x06\xb6\x97\x74\xa9\xa9\x22\xf9\x4e\x13\xb1\xe2\x34\x29\x47\x80\xa1\x5a\x7e\x62\x47\x01\xb8\xa0\xc1\xc4\x6c\xc4\x3c\x8c\xa2\x56\x33\x21\x7a\x03\x52\x60\xe6\x69\x7c\x1c\x77\x82\xe8\x8f\x55\xaa\x66\x7b\x49\x4e\xc0\xe2\xf4\x33\x3b\x5e\x23\x60\x3d\x1e\x3a\xaa\x08\x7d\xcc\xda\xa4\x40\x4d\x7c\xdb\x6c\xdf\x47\x5e\xc2\x48\x24\x06\xd9\x05\xdd\x34\xd6\x78\x63\x59\xc9\x3c\xbc\x91\xa7\x99\x87\x6a\xe0\x13\x2d\xc4\x9d\x1d\xa6\xd0\x98\xeb\xe5\x3a\x97\x65\xe1\x63\x86\x37\x41\xc7\x30\x05\xa4\x7e\x9a\xbc\x8a\xde\xfc\x04\xe2\x3d\x5d\xd1\x30\xd4\xc9\x19\x5a\xbf\x32\xa0\x10\x20\xe1\xee\xd7\x64\x97\x63\xb0\x9e\x44\x61\x69\x0f\x63\xa7\x7f\xc7\xf0\xbe\xae\x64\x07\x8b\x18\xf9\x1b\x05\x0d\x0b\xcf\x01", 220, { 0x95, 0x6d, 0xe2, 0x53, 0x57, 0x08, 0xe7, 0x97, 0x5e, 0xe4, 0x9d, 0xac, 0x5c, 0x77, 0xf2, 0xd4 } },
{ "\x7d\xb5\x74\x2a\xf7\xeb\x20\x0c\x5d\xdb\x67\x4f\x27\x6f\xb9\x00\x50\xef\x58\x0b\xe0\xc4\xf3\xc5\x17\x57\xe4\xa6\x33\x18\x03\x14\xdf\x9b\x25\x5a\x38\xdc\x35\xb7\x7c\xa3\xec\x93\x51\x03\x30\xcd\xb0\xfc\x07\x2f\x6c\x40\x36\x03\x63\x13\x7a\xbf\x3f\x4b\x62\xdb\xe9\x77\xc7\xc3\x8c\x1c\xe9\xe8\x41\x1a\x28\xf1\x5c\x2e\x5c\x10\x52\x91\xd1\x6f\x46\xbd\xe2\xc8\x2f\x4f\x30\x14\xa6\x08\x80\x5b\x51\xed\x2f\xaa\xe8\x8f\x52\x23\xe1\x65\x6e\x2e\xbc\x55\xd0\xce\xed\x51\x8b\x0e\x61\x1e\x0c\x99\xae\x30\x5b\x69\xdc\xb2\xf0\x98\xca\xa2\x30\x52\x3f\x18\x83\xec\x8e\xa6\x12\x0d\xe5\xdb\x22\x0a\x32\xe2\x08\x50\xb1\x48\xc8\xfa\x17\xdb\x4e\x83\x54\xad\x48\x95\x09\x38\x9e\x00\x86\x9b\x8f\xc6\x7e\x03\x69\x53\x4a\x25\xe5\xca\xd6\xe7\x1d\x7a\x2c\x1e\x2a\x6f\x9b\x72\x5e\x25\x83\x5e\x1b\x58\xc0\x54\x4e\xf7\xb1\xfd\x8c\x36\xe4\x9e\xc7\xc2\x60\x96\xac\x10\x8e\x04\x9d\xcf\xc4\x85\x46\x7c\x6f\x1b\xde\x13\xdd", 221, { 0x6d, 0xbe, 0xd8, 0x8e, 0x62, 0x40, 0x88, 0xb8, 0xf7, 0x63, 0xda, 0xce, 0x3a, 0x5f, 0x47, 0x0f } },
{ "\x29\xfc\xe3\xa5\x9b\x13\x7b\xb3\x22\x8c\xc0\xf4\x9d\x7e\x20\x9a\x93\x0d\xe1\x0c\xdd\x5c\x93\x6e\x61\x30\xa4\xc8\xf8\x22\x12\xd5\x71\x80\x3b\x6b\xaf\xc2\xd0\x81\xed\xa4\xe8\xe2\x22\xe4\x31\x6d\x80\x9a\x41\x06\x16\x2f\x0a\x1d\x95\xfb\x6c\xd9\x2f\x7f\xe5\xe0\x9b\x3c\x63\xc5\x35\x2e\x3a\x0f\x43\xe8\x31\xe2\x53\x4d\xb7\x4d\xc0\xe8\x33\x2f\x53\xe9\x79\xc6\x05\xbd\x58\xd6\xe2\x48\x50\x72\x0a\xc2\x61\x6a\xd2\x2b\xfe\x51\x07\x6c\x21\x29\x52\x0c\x78\xad\xb7\x8b\xbd\x3b\x38\x5f\x23\x5d\x96\x88\xec\xf3\x37\xd2\x17\xfe\x23\xa7\x37\x0b\x28\x8e\x5f\x3a\x1e\x44\x3a\xe9\x45\x75\x37\x02\xca\x17\xe1\xdf\x69\x64\x4c\xd5\x2c\x72\xab\x64\x51\x70\xff\xf9\x49\x7f\xea\x5d\xdd\x2a\xa2\xbe\x6b\x84\x01\x58\x4f\xdd\xb8\xc0\x5d\x20\xa4\xcd\x8a\xcc\x39\x23\xaa\x8d\x1b\x5d\x53\x76\xcb\xd6\xe1\xde\x7e\x15\x93\xd4\x0a\xd3\xd3\x40\xf9\xa9\x93\x97\xc0\x5a\xd3\x3a\x09\xbb\x3f\x6c\x2d\x3b\xf4\xee\xbe\x13\x63\x39", 222, { 0x19, 0x9e, 0x3f, 0xa3, 0xf4, 0x23, 0x12, 0xaa, 0x16, 0x04, 0xf8, 0xc9, 0x22, 0xd4, 0x39, 0x69 } },
{ "\x3f\x0b\x4e\x57\xf7\xe5\xf0\x5f\xda\x6b\x3a\x8b\xa8\x10\x37\xf8\x12\x45\x90\xf9\x16\x5b\x2c\x17\x6c\x10\xef\xa9\x18\xb8\xa4\x0c\x20\x09\xf5\x80\x58\x83\x97\x9f\x59\x7a\x82\x7b\x90\xc2\x5e\x52\x72\xf5\xfa\xf0\xcd\x63\xf5\xa2\x3a\x97\x7f\xd2\xaf\x82\x3a\x44\x31\x47\x3a\xec\xa6\xa2\x2b\x69\xc8\xf6\x92\x22\x36\xf1\x2c\xfc\xa5\xde\x72\xb5\x33\x86\x3e\xe0\xdc\xc4\x87\x7d\x05\xa4\x48\x34\x36\x37\x80\x2e\xe5\xf6\x52\x91\x6d\xd0\x4c\x96\x1e\xd3\xc4\x48\x6d\x73\x5e\xda\x4c\x79\xab\xa5\x95\x8a\xec\xbe\x9f\x52\xf4\x31\xf3\x4e\x2b\xdc\x19\x34\xaf\xe9\x8b\xff\x94\xe9\xcb\x9a\x4c\x8a\x90\x28\x22\xf5\x1c\xee\xe0\xc6\xa9\xde\x91\xa9\x01\xc1\x61\x8d\x2d\x00\xa0\x88\x45\x0e\xe5\x47\x67\x75\x4f\x30\xc2\x7c\xe7\x16\xd2\x72\x21\x98\xa6\x9b\x82\x17\x5c\xb4\x5b\x51\xaf\x23\xb3\x9f\xa7\xc0\x43\xc7\x6f\x3a\x9a\x7f\x43\x27\x61\x7f\x88\xac\xb9\x41\xf5\xc5\xc0\x58\xef\x32\x33\x6a\xf3\xff\x2d\xcc\x2d\xae\x0e", 223, { 0xbc, 0xe6, 0x12, 0x5a, 0xda, 0xbc, 0x5a, 0x1b, 0x21, 0xa2, 0x7a, 0x18, 0x4b, 0x6a, 0x4b, 0xd2 } },
{ "\xb8\x25\x02\x77\x2d\x99\x6b\x71\x64\x65\x09\xad\xea\x9e\x5e\xe6\x5f\xbf\x95\x63\xc9\xfb\x69\x98\x95\xb1\xb4\xec\x2c\xfe\x95\x96\x2e\x6c\x3c\xa0\x4e\xc7\x55\x0e\xca\x10\x0e\x18\x85\x8b\xc7\x92\xc2\xf3\x55\x6d\x7f\xce\xf3\x56\x6c\xdb\xc6\x7c\x87\xa7\x0c\x6f\x55\x3f\xe0\x24\xbe\xff\xac\x15\x86\x89\x2b\x85\x64\xd2\x19\xe1\xc1\x56\x7a\x42\x0e\x6b\x84\x0f\xc2\xb3\x2b\xc0\xef\xac\x47\xbd\x80\x62\x63\x64\x11\x43\x24\x2a\x6b\x13\x54\xd8\x9a\xf6\x8a\xf7\xad\xab\x78\x47\x0a\xd8\xd1\x68\x6b\x7f\xd5\x9c\x79\x90\x7a\xa4\x03\x99\x38\xc6\xdb\x99\x71\xf0\x4d\xf2\x7c\xf7\x3d\x7c\x4d\xf8\xdc\xb6\xc2\xdf\x07\x17\x8c\x06\x3c\x82\xb8\xf5\x39\xd9\xd9\x48\x33\xa9\xd4\xae\xb0\x03\x48\x0d\x01\x6a\x98\xc3\x60\xe8\x96\x08\xa2\x3b\xcd\x6f\x98\x2d\x6b\x8b\x08\x01\xc9\xae\x43\x09\x99\xcd\x63\x55\xab\x7f\x23\x85\x5c\x24\x5b\xc0\x9b\x23\x16\xbd\x99\x4b\x51\xfe\x0d\x5b\x53\x1d\x21\x9d\xa4\x3f\xfc\xad\xab\xa5\xed\x57", 224, { 0xcf, 0x7a, 0x97, 0xcb, 0x19, 0xd8, 0x74, 0x61, 0x6c, 0x4b, 0xe9, 0xa4, 0xa7, 0x52, 0x11, 0x02 } },
{ "\xe7\x61\x30\x9e\xca\x25\x61\xd9\x2d\x37\x7a\xf9\x74\x80\x8a\xe5\x40\xc4\xf5\x06\xca\xed\x62\xde\xe3\x80\x45\xd3\x85\x31\x08\x0a\x14\x91\xd8\x6f\x37\xe5\x0b\xd0\x4e\x13\x29\x29\x8f\xb6\x9e\xfb\xc0\x50\xfa\xd5\x93\x9f\xe2\xf4\x99\x08\x97\x06\xe4\x30\x20\x0b\x3e\xaa\x17\x54\xff\xca\x0e\xae\x8f\x98\x9c\x98\x79\xe1\x07\xaf\x27\x67\x8f\x2b\x40\x38\x55\x82\x9b\x1c\xe7\x2a\xde\x27\x69\x7c\xb7\x6f\x61\xb4\xb7\xd8\x1e\x15\xc6\x0b\xc6\x29\x47\x85\xfb\x26\xc9\xcb\x29\x6c\x66\xbc\x6f\x2b\xbc\xff\x28\x22\x85\x9a\x15\x82\xd6\xa4\x29\xf1\xf1\x13\xf0\xaa\xc6\xaf\x9c\xc1\x0d\x28\xad\x84\xf9\x42\x3c\xfe\x99\x2e\xe5\x95\xd0\xf5\x4f\xd3\xef\x0d\x5a\x41\xec\x0e\xe3\x6f\x42\x7e\x36\x11\x64\xc8\x11\x44\xdf\x4c\x51\xee\x0b\x90\x3c\x42\x6d\x93\x6a\x63\xac\x42\x50\x20\x2e\x04\x6a\xde\x4f\x70\x05\x93\x3e\x1e\xa6\xee\x73\x9b\x1a\x95\xd0\x76\x1b\x86\x54\x77\x9c\x9e\x76\xf0\xb2\x38\x24\x2b\x1e\xee\x57\x02\x7b\x7b\x52", 225, { 0x73, 0x28, 0x7e, 0x73, 0x4a, 0x07, 0x8f, 0xf9, 0x54, 0x98, 0x66, 0x8b, 0xe6, 0x28, 0x69, 0x5a } },
{ "\x8e\x01\xc1\x78\xf7\xf3\xe5\xc8\x6b\xab\x98\xf6\x2a\x40\x71\x27\xbd\xd1\x76\x48\x46\x71\xba\x0b\xf3\xaf\x20\x7f\x8d\xc0\x3d\x4a\x2d\x4b\x5c\x86\x0d\xd1\x9b\x36\x7c\xb7\x32\x64\xeb\xf2\xd4\xd8\x25\x4e\x2e\x76\x9c\x5b\x8c\x35\xde\xf4\x9b\xb8\x27\x6d\x49\x8a\x0f\x58\xc8\xfb\x64\xf4\xb2\x91\x12\x34\x47\x2d\x3f\x67\xd1\xbc\x74\x88\x28\x96\xf5\x24\x52\x76\x31\xe4\x42\x16\x54\xb6\xc1\x67\xfa\x9c\x6a\x6a\xff\x11\xcf\xae\x72\x13\xba\x66\xa8\xd2\x8e\x26\x6c\xf3\xcb\x3a\x54\x81\xb0\xa3\x2f\x71\xfa\xaf\x9a\xd0\xcb\x34\xb2\x8b\xa6\x69\xe3\xdb\x97\x60\xdf\x4b\x6f\x24\xab\x67\x2d\x6b\xd3\x03\x79\xf8\xbe\x25\x49\x90\x1c\x90\xa6\x96\x7b\xed\x89\x45\xf9\x98\xdf\x8a\x14\x05\xac\x7c\x9d\xce\x4c\x79\xcc\x5a\xd4\xde\x6c\x2b\x96\x62\x37\xc3\xc3\x10\x3c\x34\x2b\xdf\x7c\x43\x21\xef\x95\x38\x7a\x62\x96\x45\xb4\xd5\xf1\x90\x32\x7a\x8e\xdf\xa5\xd3\xfc\xdf\x87\x0d\xc2\x11\xc1\xf7\xd4\x52\x6d\x9d\x21\x05\xb5\x85\x49\xdf", 226, { 0xd2, 0x88, 0x52, 0x3d, 0x56, 0x8c, 0x3e, 0x43, 0xc1, 0x91, 0x26, 0x52, 0x1f, 0x00, 0x34, 0x9b } },
{ "\xda\x85\x6c\x4e\x51\x3f\x1e\x87\x08\x3b\x76\x2b\xc6\x9f\x46\x95\xb0\x8d\x65\xc2\xe7\x19\x27\xde\x4f\xe6\xee\x67\x6b\x5c\xda\x00\x64\x88\x9c\xd4\x29\x8f\x68\xa5\xaf\xd0\x08\x70\x59\x96\x0e\xbf\x74\x76\x22\x8b\xd9\x4b\x79\xcb\xb5\xcc\x66\x9d\x66\xc7\x0a\x10\x7b\xfc\x28\x8c\xde\x99\x13\xdf\x0e\x4d\xc8\xe4\x84\x0a\x18\xd2\x37\x5c\xfe\x1e\x3b\x61\x0f\x0e\x85\x64\x0f\xdc\xc9\x1a\x9f\x83\x84\x78\x14\x28\x16\x2f\x64\x28\x07\xc6\x3a\xba\x6a\x52\x29\x41\x5e\xd3\xfc\x31\xb1\x2e\x2c\x9b\xa0\xc3\x6b\x56\xba\xaa\x5d\xcc\xe2\x8f\x66\x5e\x73\x27\x9e\x75\x7e\x4e\xe1\x26\x2e\xbf\x04\x81\x4d\x6f\x22\x77\xdf\x86\x14\x65\xec\xfb\xfe\x41\x5c\xca\x06\x60\xfa\xd5\x20\xe1\x9b\x55\x05\xba\x58\xfb\x43\x5d\xf6\xa7\x6d\x06\x67\xc4\x72\xa4\x6a\x3e\x0b\x61\x4d\x21\x4e\xd0\xd2\xcf\x60\xb2\x30\xdd\x47\x65\x30\x8c\xde\xea\x78\xdf\xe1\x91\xe5\x5d\x28\x43\x11\x65\xf1\x6c\xf4\x29\x56\x83\xa6\x49\xdd\x37\x42\x0c\x2e\x37\xe7\x3d\xdf", 227, { 0xcd, 0x4e, 0x87, 0xb7, 0x10, 0x56, 0x56, 0xa0, 0x13, 0xe5, 0xbd, 0x06, 0xe2, 0x8d, 0x82, 0xf9 } },
{ "\x9e\xd8\xa0\x6c\xfa\xc3\x10\x42\xf8\x1f\x36\xfa\xad\xae\xe8\xa3\x83\x22\x1b\x38\xbe\xdc\x86\x31\xaa\xcf\xd6\x35\x63\x83\x87\xec\x40\x36\x66\xf3\xdd\x1d\xa0\xe9\xc4\xc8\x85\xa6\xb8\xa3\xdf\x8c\x9d\x98\xe9\xf5\x07\xd2\xee\xcb\x5d\x9d\x80\x37\xaa\x69\x51\x72\x00\xee\xb1\xb7\x89\x69\xa4\x59\x2b\x04\x9f\xf0\xd7\x53\xdf\xaf\x6f\xfa\x66\xd1\x51\x6d\x94\x32\x85\xd0\x5e\xfa\x7f\xfd\x2d\x74\x91\xa3\x55\xfd\xf0\x8b\x30\x17\x60\xf6\xd7\x9d\x7f\x23\x7c\x11\xac\xed\x67\x11\x92\x90\x8b\xe9\x54\x8c\xc4\x41\x57\xa2\xb0\xf7\xa6\xfb\x27\x08\x06\x68\x58\xa7\x97\xc4\x81\x70\x24\x76\x4a\xb6\x07\x13\xbf\xf8\xbc\x20\xe9\x73\x00\xf5\x08\xd1\xa5\xbc\x6e\xff\xf5\x99\xfa\x1c\xc1\xe4\x30\x8d\x05\x91\xf2\x2d\x39\xb1\xdd\xa2\x36\x11\x43\xcb\xc3\x00\xc0\x55\xbc\x2f\x7b\x6d\xde\xb1\xfb\x8f\x31\x21\xa3\x7d\x12\xe2\xcc\x4c\xdc\x81\x7d\x99\x33\x38\xc9\xd8\xed\x8c\xc6\xcc\x9c\x15\x25\x13\xbc\x5e\x73\xd9\x76\xe2\xeb\xad\x0f\x37\xf5\x70", 228, { 0x19, 0xfd, 0x74, 0x3f, 0xff, 0x64, 0x2f, 0xff, 0xfa, 0x16, 0xbb, 0xd2, 0x16, 0x92, 0x6c, 0x7b } },
{ "\xf0\x47\x3f\x69\x52\xb8\x77\x76\x86\x6d\xa2\x5e\xde\x26\x1c\x41\x3b\x43\x2c\x30\x34\xa6\x43\xd9\xe0\xb5\xd2\x5b\x41\x7f\xc5\x38\x4d\xf6\x9f\x68\x9a\x5a\x33\x8a\xa5\xb7\xfc\x36\xbe\x85\x1a\x6d\x94\x6d\xe9\x32\x59\x2b\x40\x32\x91\x8e\x43\x9e\x7d\x9d\xe6\x1a\x1e\xd4\xfe\xcd\xe8\x8c\x05\x99\x05\x78\x6a\x65\x42\x4b\x93\x95\x9a\x77\x70\x9b\x5d\x11\xbd\xa7\xfd\xd0\xd4\x7a\x75\x35\x2c\x58\x57\xc4\x72\x1c\x70\x70\x42\x65\x19\x14\x82\xee\x1d\x1e\x5b\xfc\x42\x46\xd5\xf0\x76\x7c\x0f\xfc\x98\xe0\x17\xf2\xdf\xeb\x6c\x38\xeb\xab\x0d\xd8\x66\x2b\x3d\xb4\x56\xf1\xd6\xd7\x03\xa0\x47\xe6\x7f\xbe\x2c\xb5\xd7\x90\x88\xf6\xd5\x22\xa2\x0c\x6e\x63\x79\xfd\xcf\x6c\xe4\x86\xff\xe1\x4b\x49\x30\x70\xfc\x22\xa8\x77\x2b\x97\x47\xc2\x89\x6d\xb4\x71\x7f\x57\x28\xf8\x07\xca\xd6\x41\x27\xec\xf6\xec\x0c\xaa\x6d\xb6\xbf\xe9\x1b\xf7\x86\xd4\xc0\x45\xd9\x54\x8b\x37\xff\x9e\x41\x44\x46\xe5\x07\xc9\xdb\x31\xcc\x29\x4b\x48\x55\x0e\x97\xc8\xe6", 229, { 0x8d, 0xe4, 0xe9, 0x8c, 0x60, 0x20, 0x2e, 0x6e, 0x67, 0x30, 0xe5, 0x90, 0xee, 0x49, 0x57, 0x5d } },
{ "\xce\xed\x56\x8a\xea\x09\xf1\x00\xb0\x28\x60\x42\x00\xa4\xca\xb2\x2b\x31\x64\xe4\xf7\xb2\x44\x02\xbf\xb9\x55\xe0\xb7\x9a\x63\x9d\x4e\x2a\x56\xf2\xac\x19\x7c\x1a\x5b\xe9\xff\x5e\x02\x8a\x75\x3c\x4b\x94\x96\x33\xe6\x20\x09\x2e\xa7\x44\x57\x8b\xcb\x28\x01\xc6\x58\xc9\xac\xc6\xa2\x4a\x7a\xc4\xe9\xf2\x75\xd4\x20\xc7\x25\x12\xc9\xc4\x41\x6a\xfb\xb9\x82\x03\x85\xc0\x92\x49\x2d\x57\x24\x95\x11\x1f\x1d\x7c\x2f\x30\xef\xb1\x00\x17\x07\x97\x3f\x6d\x5d\x67\xc7\x15\x28\xfb\x9a\x11\xb4\x35\xfe\x01\x4b\x0e\xc1\x3a\x50\x3f\x9e\x7f\x6c\x85\xb8\x5f\xdf\x95\x43\x2a\xf8\x9c\x42\x91\xa0\x71\x6a\xd9\x10\x5a\x26\xbc\xa3\x78\xb4\xdc\xc5\xd0\x6c\xe4\x0b\xf6\x21\x3e\x5d\x58\xa0\x6a\x4e\x24\x56\x02\x8c\xd7\x0e\x47\x73\x64\xd7\x66\xbc\xfd\x03\x4d\x52\x80\x4c\x25\x45\x81\x12\x6b\xd7\xf0\xc0\xae\xb4\x67\x4c\xe7\x3f\x13\xdc\x70\x08\x3c\x86\xa3\x5a\x72\x30\x10\xf0\x56\x97\x5a\xc7\x02\x6b\xae\x0e\x16\x96\xe1\x3a\x60\x9d\x26\x36\x27\x1f\x69", 230, { 0xe1, 0x2d, 0x4d, 0x4e, 0x25, 0xde, 0x0e, 0xf5, 0xcb, 0x99, 0x33, 0xe3, 0x45, 0x90, 0xf0, 0x1f } },
{ "\xb8\x14\x7b\x4a\xaf\xbe\x8f\xb9\xd8\xd0\x9b\x2a\x70\xd9\x43\x75\x04\xb0\xb3\x4e\x64\x82\xdd\x5c\xb6\x7a\xde\xf7\xa6\x0b\x7e\x83\xbb\xdd\xd6\x64\xc5\x51\x0c\x9c\x72\xb3\x3a\x08\x01\xfb\x6d\x34\x0e\x40\xc9\xd6\x8b\xba\xca\xee\xcf\x72\xa5\x78\xc8\x88\xcd\xca\x4d\x21\x91\x90\x8e\xeb\xe2\x62\xad\x4e\x33\xff\x65\x30\x79\x29\xe8\x65\x52\x1c\xc7\xb2\x42\xac\x0b\x7c\x18\xfa\x61\x12\x6f\xd2\x71\x94\x50\xa5\x4f\x7e\xf5\x1e\x0a\xd5\xa8\x62\x63\xec\xca\xe9\x98\xd3\xf0\xf4\x5d\x5d\x28\xaa\xec\xcd\x59\xd3\x33\x1c\xc8\x30\x2c\xea\xb7\x74\x4b\xff\x28\xe3\x10\x7c\xbf\x86\xbc\xa2\xc5\x08\x20\x3e\x49\x31\x90\xb0\x61\xfc\xf7\x97\x8e\x56\x05\x1c\x3d\x76\x83\xd7\x6a\xd3\xc6\x61\x5f\xc7\x42\x77\x9b\xbe\x36\xc7\xcd\x1a\x85\x5b\xff\xa7\xa5\x9f\xd0\xb6\xb0\x10\xda\xd8\x98\x83\x32\x38\x78\x0f\x0d\x96\x05\x99\x7f\xdc\x23\xfa\x5f\x5e\x94\xcf\x47\xb3\xcb\xdc\xb8\x2a\xdf\x1d\x5a\xeb\x9b\x6b\x60\xac\xb2\xc8\x0a\x0c\x0f\x47\x44\x90\x4b\x9b\x6c", 231, { 0xfc, 0xbf, 0x92, 0xda, 0xc4, 0x1d, 0xb9, 0x19, 0x4f, 0x37, 0xa0, 0xbf, 0x95, 0xd0, 0x48, 0x08 } },
{ "\x37\x38\x3a\x31\xbb\x9a\x2d\x97\x67\xf5\x77\x16\x90\x82\x1f\xe2\xb1\x3b\xdc\x46\x27\x15\x5d\x2a\x85\x4e\x32\xb3\x95\xdc\x5a\x09\xec\x05\x69\x34\x29\x0c\x56\x1f\xca\x09\xdf\xbf\xaa\xd2\x99\x4d\x1b\x15\x98\xaf\x9c\x88\xb6\xf5\x37\x37\x84\xfe\x7a\xfc\xcb\x3b\x0f\x0d\xbd\x8b\xfa\xaf\xbd\x46\x6f\x09\xc8\x56\x4e\x13\x7e\x7f\x3c\xad\xd1\xbe\x5f\xcc\x49\xcf\xb5\xcf\xf1\xc9\x12\xf0\xe1\xe5\xb4\xd6\x69\x5c\x84\x46\xd7\x6e\xec\xfa\xe6\x7e\x4f\x8a\xc3\x5a\x29\x87\xbd\x99\xc5\xa7\x88\x1e\x95\x1a\x2d\xb9\x31\xfb\x92\xec\xef\xe2\xa1\xca\x1b\x96\x18\xfb\xd3\xe0\xed\xdd\x82\x7a\x5c\xc5\xf7\x26\x8e\x63\x21\xdc\xe7\x43\x69\x1b\xed\x70\xac\x61\xd0\x33\xd4\xb6\x9a\xf9\x12\x62\xf4\x52\xb9\xbe\x92\x16\xba\x28\x3c\xa2\xb8\x10\x7a\x40\xc7\x2f\xdc\xa5\xc6\xd8\xe3\x93\x56\x66\x8f\x9f\x76\xd5\x86\x0d\xbd\x6d\xde\xd7\x33\x99\x87\xcd\xcb\xd6\x58\xd6\x81\xc7\xb4\x54\x0c\x65\xd9\xa5\x41\x53\xc5\xc6\x04\x4f\xc5\x13\xeb\xc5\x9b\x2a\x70\x7e\x4b\xed", 232, { 0xfb, 0xcb, 0x4a, 0x19, 0x4e, 0xcd, 0x8d, 0x1e, 0x33, 0xad, 0xf0, 0x88, 0x02, 0xf2, 0x49, 0xb6 } },
{ "\xd3\x75\x82\xa5\x8a\xca\xa4\x44\x66\xd0\x70\xc3\x44\x41\x52\xaa\x6c\x91\x46\xae\x89\x5f\x64\x74\x45\x08\x0c\x74\x81\x56\xae\xf9\x2e\x56\x36\x44\xcb\x47\x13\xd0\x7b\xae\xe3\xb1\xc2\x87\xbd\x16\xdc\x96\x1a\xed\xba\xdb\x60\xa5\x99\x23\x0d\x0f\x41\xbb\x7c\x5e\xd8\x40\x57\x4d\x60\x92\x9a\x5f\xd4\xe7\x3f\x42\xda\xfb\x8c\x4d\x24\x65\xf5\x28\x69\x05\xae\x8b\xfc\x9a\xd2\x1f\x06\x70\x29\x80\x65\x36\x99\x64\x1f\xee\x2c\xd5\x84\xfd\xba\x9a\xe0\x62\x33\xb4\xda\x03\x8b\x04\x6d\x23\x42\x0a\x80\xf1\x8f\xc8\x23\x3a\x28\xc5\x68\x3d\xb1\x2d\xdc\x9f\xbf\x15\xa1\x75\x87\xdd\x29\x7f\x27\xae\x91\x75\x91\x23\x98\x78\x10\x05\x3a\xab\x78\x2e\xed\xdb\xee\x8e\x77\x59\x51\x4c\x6a\xe9\x44\x04\x3d\xd3\xda\x2f\x09\x16\xdc\xa0\xdd\xbc\xb9\x2b\xbe\x49\x0b\x60\x3e\x4d\xc2\x75\xb7\x19\xef\x42\x25\x8e\x2f\x65\x9d\x11\xb2\x85\x6e\x9a\xe7\xb4\xd3\xec\xc6\xee\x51\xdf\xb9\xbe\xb3\xd9\x28\x00\xa0\x5b\xa0\xc1\xd6\xb7\x9f\x42\x05\xe0\xfe\x1c\x4a\x5a\xfb\x7d\x46", 233, { 0x6f, 0xa3, 0x9b, 0x06, 0x9a, 0x45, 0xa7, 0x4c, 0x93, 0x2a, 0xae, 0x07, 0x2f, 0x79, 0x54, 0x7f } },
{ "\x50\xe2\x90\xdb\xc4\x58\xfb\x83\xe0\x44\x82\x4c\x6c\x5a\xc9\x74\x59\x45\x36\x9c\x7a\x71\xf5\xac\x52\x72\x15\x44\x08\x14\xcf\xda\x77\x00\xe7\x75\x62\x07\x2c\x05\xc5\x0e\x19\x5c\x46\x96\x9e\xcd\xca\xe7\xf8\x60\x25\xd9\xbd\xaf\xe9\x3f\xf4\x60\x5f\xf0\x60\x3f\x94\x73\xde\xf6\x8a\x46\xe4\x6c\x90\xcb\x29\xb8\xac\xd0\x63\xc1\x34\xba\x2c\x74\x7c\x4d\xfe\xa0\xa9\x1a\x5d\x71\xa4\x85\x14\x87\x2a\x71\x97\xb2\x01\x8b\x87\x4c\x45\x30\x55\x33\xe1\xfc\xfe\x62\x19\xf0\xf4\x2c\x43\x3f\x1d\x14\x96\xb5\xf4\x4b\x1a\xc4\xce\xc7\xbf\x2d\x37\xfc\x8a\x48\x7b\x39\xea\xef\x40\xa2\x29\x0d\x50\xc6\xfe\xbe\x75\xdc\x3f\x23\x7d\x9f\xb3\xc6\x5d\xc3\x05\xa4\x72\x12\xd5\xdb\xe2\x28\xe9\xf1\x21\xc7\x81\xbe\x90\xd8\xc8\xf8\x40\xff\x66\x59\xd4\xd9\x32\x6f\x83\xd5\x05\x00\x3d\xac\xaa\x17\xe5\x7e\xf1\xaf\xbd\x8b\xe3\xfb\xe0\x8a\x0f\x50\xe8\xa9\x03\xb0\xaf\x22\xd7\xf4\x33\x77\xe3\x95\x93\x4a\x90\x17\x36\xdb\x4c\x12\x0b\x1e\x97\xde\xea\x78\x3b\xda\x19\x16\x98\x59", 234, { 0x10, 0x62, 0x10, 0x20, 0x9a, 0x5a, 0x01, 0xcb, 0x7e, 0x58, 0x93, 0xef, 0x8a, 0x77, 0xe5, 0xb3 } },
{ "\xeb\xea\x1d\x2a\x84\x43\xd3\xc3\xb7\x08\x13\x09\x10\x91\x58\xbf\xed\x23\x2f\x88\xc7\x05\x4b\x9a\x8f\x43\xb5\x01\x33\xff\x20\x8d\x3f\x6e\x5a\x5a\xa0\x76\xdf\xfa\x85\xe2\x88\x41\x5e\x40\x61\xac\x06\x58\x97\x6e\xfd\x49\x90\x19\xce\x41\x15\xe6\x90\xd8\xaa\xa1\x87\x0a\xff\x33\xea\xcf\x7f\xcd\xbf\x59\x05\xaf\xe3\xea\xae\x92\x26\x4f\xc9\xb8\x92\xfc\xeb\x8e\xcc\xc5\x20\xfa\x94\x37\x3c\x47\x67\x91\x4f\xab\x44\x62\x36\x71\x8b\xc0\x4e\xc7\x00\x22\x44\x26\xef\xdb\x08\x59\x6a\x34\xe0\x2d\xae\x24\x99\xb4\xa4\xae\xd8\x35\x83\xd7\x8e\xb3\x92\x43\x8a\x18\x0b\x6b\x28\xff\x1b\x7d\x27\x1b\x07\xd1\x98\x46\x68\x03\xf3\x2a\x97\xb1\x44\x86\x23\xd2\x82\x1e\x7f\xb1\x00\x42\xb9\x86\xfd\xf8\x65\xaf\x56\xc8\x98\x90\x5b\x25\x10\x04\xbe\x73\x71\x7e\xa7\xb9\xaa\xc1\xe5\xe5\x76\x38\x40\xb6\xff\xf2\xea\x4a\x9d\x3e\x14\x44\xbb\xdf\x9c\x99\xda\xed\xe3\xf8\xaf\x48\xbd\xd4\x68\xb9\x82\x0f\x0d\xa6\x41\x44\x01\x72\x14\xb1\xa7\x6f\x8f\xbf\x21\x81\x52\x30\x33\x50\xbe", 235, { 0x58, 0xde, 0x89, 0x88, 0x29, 0xd3, 0x62, 0xe9, 0xc9, 0x41, 0x78, 0xc1, 0x0e, 0x4c, 0x32, 0xc6 } },
{ "\xe4\x46\x29\xc4\x48\x89\x72\xd9\x5c\x32\xc8\x06\x5e\xe2\xb7\x1b\x18\x27\x15\x03\xc3\x1b\xfb\x33\x97\x29\x61\x3d\xf0\xef\x55\x81\x1e\x3f\xd8\x02\xc9\x40\x55\x56\xff\xb2\xbf\xb8\xdc\x4f\x45\x38\xd5\x4c\xb5\x11\xa1\xff\x6b\x1b\xc4\x9a\xf3\x57\xb9\x15\x43\xa8\xbb\x2a\x8e\xa1\x30\x7a\xc6\x79\xb3\xcd\xb1\x1b\xbc\x77\xa7\x5a\xee\xd5\xe5\x42\xfd\xf7\x91\x8a\x3a\x58\x4b\x25\xbd\xc8\x6c\xf7\x2e\x6b\xde\xa5\x30\xda\x98\x85\x6a\x67\xb4\xb5\x32\x7d\x2e\x47\xd8\x26\x3b\x9a\x8d\xa7\x44\xc4\xef\x46\x7e\x2b\x32\x2c\x27\x33\xec\x64\x5e\x11\x7c\x03\x9f\xbe\x18\x62\xdb\x08\x73\x87\x30\xc2\x07\xa2\x4a\x1c\x04\xb3\x55\x0d\xd5\x49\x9e\xec\x4b\x64\xc5\x1f\x68\x7c\xa1\xea\xca\x9b\xb0\x69\xb3\xa5\x39\x99\xb1\xb8\x00\x90\xee\xae\xa5\xcd\x16\xbf\x9a\xaa\xe0\x44\xbf\xee\x3c\x96\x9c\x7f\x17\x15\x3a\x34\xce\x44\x9e\xc9\x2d\x12\xe3\xec\x22\x34\xf3\x74\x02\xad\xe2\xb1\x77\x6f\xe7\xde\x06\x1b\xf7\xb3\x39\xe5\x00\x17\x6d\x93\xbf\xf3\x3a\xa4\x3e\x22\x7a\x8d\xee\x84", 236, { 0x74, 0xb2, 0x77, 0xc9, 0x12, 0x1c, 0x72, 0xf0, 0xb8, 0x7a, 0x7e, 0xee, 0xce, 0x93, 0x97, 0xe0 } },
{ "\xf8\x1f\x5c\x32\xb7\x04\x93\xcb\xdc\x68\x0f\xed\x39\xb4\x59\xc0\x76\x75\x44\xac\xde\x5b\xc2\x2a\xc3\x5e\x63\xb8\x8f\xfb\x6c\xe6\x69\x9c\x90\x8e\x80\x16\x4e\x21\xf7\x4c\xe6\x1b\x6d\xf1\xf9\x98\x28\x6a\xbc\x01\xdd\xd1\xd8\xdf\x1e\x16\xe2\xd0\x60\x40\x72\x96\xa8\xd1\xe2\x4d\xd2\x48\xa5\xb5\x7e\xc0\x41\xad\x97\xb6\xea\xc1\x81\xe8\xbd\xda\xdf\x58\x95\x14\xfe\x70\x8e\xad\xe1\x3f\x14\x18\xe9\xe1\xa6\x31\x21\xfd\x2d\x8c\x24\x68\xf3\xe6\xab\xe8\x42\xbd\xb7\x13\x9a\xfc\x57\x55\x8d\xce\x17\x0f\x3b\x93\x05\xdd\x66\xf0\x61\xf0\x31\x01\xe0\x9a\x7a\xaa\x9d\xe9\xd0\x0b\x9d\x6a\x13\x11\xfd\x0f\xa7\x29\xba\x2b\x54\x10\x1d\x99\xbe\xc6\xc1\xfd\x7b\xa1\x42\x22\xd6\x7e\x84\x83\x20\x12\x9d\xe5\xad\x5e\x60\x21\x72\x41\x87\x00\x39\x27\x7c\x3e\x7e\xe0\xc4\xb1\xea\x8b\x09\x83\x69\xb1\xc2\x9b\xea\x6e\x81\x1b\xb2\xc9\xd8\x02\x5e\x25\xe9\xf0\x73\xd1\x89\x0a\xa3\xba\x11\xf4\x9f\x40\xc1\xfb\x93\x25\xd0\x55\x43\xa2\x14\x7f\xc0\x94\x4a\xc6\xc6\xd3\x03\xe2\xb5\xa4\x2c", 237, { 0x3d, 0xd8, 0x41, 0x6f, 0xd4, 0x1f, 0x87, 0xc7, 0x02, 0x20, 0x21, 0xd6, 0xeb, 0x2d, 0xd3, 0xc7 } },
{ "\xbc\x2a\x44\x38\x51\x3a\xa4\xec\xae\x4b\x35\xc6\x1b\x8e\xd9\x0b\x54\x1c\xf8\x6c\xf2\xac\xb4\x54\xe9\xef\x34\xd1\x2a\x88\x1d\x1c\x69\xab\x1f\xc6\xf9\x51\xab\x81\xd3\x15\xc3\x89\xb5\xaf\xe9\xad\x67\x0a\x39\xfe\x19\x03\x93\x12\xe8\xc0\xf0\xf5\x7f\xab\xd6\xae\xda\x0a\xe6\x69\x26\x3d\x93\x46\xc4\x93\xed\xbd\x6d\xc8\x9b\xab\x6f\x1f\xe7\xfe\x16\x18\xf4\xfa\x26\xcf\x0a\x25\x84\xf1\x12\xd5\xf4\x5b\x1d\x54\xfc\x51\x1e\xad\xe8\x57\xb8\x16\xe7\xaf\x32\xf9\x53\x70\x88\xa1\x0e\x40\x9b\x7e\x07\xae\x88\x14\xdc\x2e\x15\x86\x9a\xb2\x47\xbb\x9f\xe1\x12\x2a\xa1\xf6\x28\x48\xc7\x38\xf3\x8b\xf5\x11\x9d\x19\x25\xce\x4c\x12\xf0\xf2\x6c\x77\x2d\x37\x24\xb5\xb0\x22\xea\xd2\x34\x42\x32\x33\x53\x05\x41\x07\xb1\x36\x21\x54\x39\x16\xad\x9c\x7f\x16\xcc\x2b\x45\x2f\xba\x00\x19\xdf\x82\x56\x1b\xc1\x88\xcd\xdd\xc7\x42\x3a\x63\x16\x07\x08\x49\x7d\x00\x49\x04\x93\x3b\x5d\x41\x6d\xde\x3d\x69\xf9\x78\x65\x46\xfb\x73\x5c\xbf\xa1\xa6\xe1\xbf\xc4\x07\xb4\x34\xbe\x7d\xfd\x34\xe2", 238, { 0xe4, 0xf0, 0xa4, 0xab, 0x6f, 0x2a, 0xdd, 0x87, 0x83, 0xbb, 0xc7, 0x5c, 0x71, 0x24, 0x8f, 0x1b } },
{ "\xd4\x5d\x39\x9c\xca\x90\x8d\x26\x46\xbc\xc1\xe4\xa8\x58\x57\x5e\x3b\xbc\x7f\xd7\xc7\x41\xfe\x8e\x44\x14\x2b\x91\xa9\x9c\x14\x38\xe1\x85\xbd\x45\xdf\x69\x88\x96\xc9\x1b\xb0\x84\x4f\x8f\xef\xdc\x1f\x69\x40\x78\x71\x20\xbf\x79\xbd\xcf\xac\x22\x8d\x98\x8e\x54\x6c\xb5\x74\xa2\xfe\x1d\x57\x10\x29\xcf\x9b\x6d\x71\xbd\xb4\x4a\x62\x58\xe5\x96\x26\xb4\x24\xd7\x36\x58\x1a\x07\x2d\xa5\x46\x09\xb8\xe1\x41\xc6\xaa\xde\x1c\xe9\x2c\x4b\xe5\x33\x12\x97\x49\x7b\x48\x7d\x53\x46\x6b\x31\x53\xff\x74\x25\xda\xd3\x8f\x78\xe1\x2b\x0a\xfc\x09\xc7\x69\xe2\xfc\x74\x96\x04\xf3\x69\x35\xcf\x52\x44\x16\xcb\x6e\x9c\xc4\xc9\x6e\x42\x3a\xa8\x4f\x19\xb5\xc3\x01\x8f\xa5\xfa\xaf\xb7\xbd\x5c\x1c\x05\x29\x6e\x29\xa5\xdf\x1b\x73\x78\x0f\x37\x19\xac\xb4\xb1\x9b\xf6\x4c\x55\xdd\x6f\xa4\x3c\x4b\x08\xcd\xd1\x17\xab\x2b\x80\x9e\xf0\xab\xfc\xe9\x79\x14\x2d\x50\xeb\x77\xb5\x38\x89\xc1\x1e\xfc\x6e\x6f\xa2\xe9\x67\x60\x95\x64\x6b\xc6\x73\x27\xb8\x36\x82\xa8\x8b\xe0\x24\x9a\x7b\xd8\xbb\x8c", 239, { 0x70, 0xdd, 0xb0, 0x46, 0x25, 0x38, 0xe4, 0xdd, 0x2a, 0x78, 0x64, 0xf0, 0x7b, 0xdc, 0x71, 0xa8 } },
{ "\x72\x92\x38\xb0\x49\x6d\x43\xb7\xff\x66\x01\xd7\x96\xed\x84\xee\x8b\xd4\xd5\xc0\xf0\x64\x96\x5d\x27\x8a\x57\x9e\x3d\x2f\x78\xcd\xe0\xa5\xb6\x64\xff\x3d\x53\xee\xfc\xf5\xe6\x0a\x90\x4e\xbc\x8f\x3c\x3c\xea\xc9\x68\x37\xf1\xe0\x1a\x6f\x0c\x59\x54\x1c\x18\xb6\x0a\xf3\x20\x39\xbe\xb4\x85\xc7\xba\xe0\xc6\xe7\xea\x89\xf2\xe9\x53\x41\xa7\x23\x34\x34\xc5\x57\xb7\x52\xb5\x30\x54\xa4\x4f\xeb\xc3\xc0\x6d\x13\x9b\x58\x0a\x64\x8c\xec\x15\xd1\x35\xa0\xd8\xa2\xa3\x28\x00\xb5\x68\xdf\x48\xe4\x53\xf7\xc6\x87\xd1\xcb\xd2\x10\xdf\x51\x8f\xd5\xab\xab\x17\xeb\xc7\xdc\x47\x2d\x08\x98\x24\x5c\x01\x34\xe8\x60\x17\xbc\xad\xad\x41\x23\xb5\xc1\x5f\x95\x54\xc9\x33\xe9\x7a\x64\x00\x32\xe1\x7f\xbd\x74\xcf\x5f\xf6\x74\x88\xbd\x40\xa9\x54\x0b\x57\x4e\x28\xd5\xd6\x99\xf4\x43\x91\x05\x88\xbb\x92\xcc\x24\xa3\xaf\x71\x9a\x44\xc5\x79\x22\xca\x93\x39\xba\x67\x35\xcb\x38\x98\x3a\x1a\xee\x80\x65\x1d\xf8\x70\xfd\x21\x24\x88\xd1\x3e\x7f\x76\xcc\xeb\x78\x5d\x30\xae\xb3\xd2\x72\xec\x6d\x00", 240, { 0x77, 0xeb, 0x85, 0x90, 0xee, 0xde, 0x0a, 0x48, 0x59, 0x1f, 0xe2, 0xa5, 0xa9, 0x24, 0xea, 0x47 } },
{ "\xb2\xde\x87\xeb\xd6\xa4\x31\xd1\x42\x74\x34\xad\x36\xeb\xdb\xd5\xc3\x84\x7f\xc3\x6b\x26\xae\xf0\x54\xd7\xf8\xdc\x29\x8f\x55\x2b\x8e\x27\x36\xe9\x3d\x70\x26\xee\xc2\x60\x1d\x5d\xcf\x68\x62\x46\x3d\xe1\x19\x6b\xa0\xa4\x70\x20\x85\xb8\x62\x4b\x4a\x26\x27\x8b\x9a\xe9\x39\x76\x85\x02\x02\xfa\x38\xa7\x27\xe4\x5d\x9b\x6b\x7f\x12\x99\x41\x55\x7e\xea\xf3\x11\x16\x16\x68\x84\x6b\xb7\x95\xc6\xac\x69\x83\x75\xc0\xdd\xf8\x19\xf8\x0d\xc5\xa8\x75\x8a\xac\x25\x16\xf1\xeb\x62\x1b\x7c\x69\xe7\x5b\xb4\x7c\xeb\x1e\x44\x55\x7f\x98\xe9\x09\xca\x03\x86\x3c\x6f\x57\x54\x6c\x0b\xa9\x37\xd7\xda\x1e\x2b\x0a\x79\x8a\xdd\x08\xc6\xa9\x56\x13\xe3\xf8\xd2\x1a\x5a\x31\xaf\xbe\x5a\x62\x81\x02\x20\xa9\x42\x8f\x71\x8e\xa7\xa2\x43\xfd\x8d\x93\x7c\xde\x92\x03\xd2\x53\xc1\xa0\xd1\x8d\x65\x97\xfb\x4b\xfe\x98\x09\xf1\x52\x7f\x50\x41\x9a\xa4\x3f\xb8\xdd\xc0\x04\x87\x5b\x7a\x4f\x2c\x1f\x8d\x2f\xad\xb8\x98\x18\x71\x01\x83\x05\xbb\x1b\x88\xba\xc3\x7c\xe5\x23\x73\x21\x1d\xd8\xbb\xdf\xe5\xc2\x91", 241, { 0x54, 0x10, 0x15, 0x52, 0x9c, 0x0e, 0x89, 0x84, 0xd4, 0x90, 0xfc, 0x2a, 0x5b, 0x5e, 0xf2, 0xed } },
{ "\xd7\xb2\xd7\x89\x08\xdd\x01\x0c\xff\x6f\x1c\x38\xa9\x8f\x1e\x54\x49\x85\x52\xee\x84\x6a\xbd\x93\x9a\x6e\xa1\x2b\xaf\xc6\x1f\xee\x47\x30\xf7\x07\xd1\x24\x6c\xc3\x5a\x99\x43\x76\x62\x70\xe9\xeb\xcc\x81\xb4\x85\xee\x41\x42\xf6\xc9\x0d\xfe\x9b\x52\x15\xc1\x73\xef\xe7\x94\xbb\xfd\x97\x94\x27\x8e\x89\xee\xbe\x30\xdb\x0a\x52\xe8\x71\xc5\x9b\x3e\x9e\xd6\xf0\x72\x6b\x52\xa1\xcc\x88\x4a\xf3\x11\xcd\x92\xb9\x11\x6b\x9d\x8b\x5e\xb3\x84\xa6\x17\x83\x25\x60\xe2\x49\x68\x46\xf8\xb5\x9d\xd4\x59\xff\x01\xcf\x21\xd2\x60\x43\xf3\xd4\xd4\x15\x91\xd2\xab\x44\x8e\x8d\x67\xc0\x1a\x1b\xde\xe7\xfd\xfc\x82\x98\x9f\xba\xbb\xf6\x43\x3b\x70\xbb\x54\xa7\xa5\x36\xd8\xf0\x3e\xe2\x01\x02\xe2\xa5\xe2\x89\xfb\xa2\x3f\x59\xd9\xbb\x7d\x7f\xf6\xa6\xb8\xe2\x54\xaa\xf3\x94\x03\xf7\x6a\xbb\xbf\xa0\x04\x16\xb5\x36\xe5\x2e\x66\x02\x1f\x1c\xa5\xde\x88\xf1\xba\xb0\xa6\xc5\xa9\x84\xc7\x5f\x8d\x45\x2e\x7e\x1d\x18\x67\xc2\x50\x56\xbc\x3a\x1d\x24\xc0\x8b\x5c\xb0\x08\xb9\xc8\x09\xfa\x95\x25\x9b\xbd\xc3", 242, { 0x58, 0x95, 0xff, 0xfc, 0x90, 0xfc, 0xfe, 0x6c, 0x68, 0xf7, 0x0c, 0x90, 0x74, 0x06, 0x60, 0xaa } },
{ "\x7e\x46\x50\x67\x88\x1b\xb7\x6c\x23\xb3\x4f\x70\xfe\x2b\x43\x4f\x59\xbf\x17\x4b\xe6\x02\x61\xd5\xc9\xb7\x98\xfb\xbf\x50\x05\x6d\x5a\x00\xd6\x2d\x6a\x7f\x51\xd3\x78\x5a\x26\x7a\x6c\xf4\xdd\x4b\x4e\x1d\x6e\xa3\x29\x4c\xef\xe4\x0b\x7c\x68\xd5\x2a\xa1\xc2\xb7\x21\xc6\xde\xe5\x57\xc5\xc3\x26\x81\xa2\xef\x93\x3d\x84\xce\x1f\xdf\x50\x49\xc8\x49\xe3\x75\x59\xf3\xec\x6c\xd9\x0b\x65\x39\x94\xb6\xac\xed\xc3\x74\x42\xce\xda\xa1\x1e\xaf\x6f\x17\xaf\x5b\xc2\xf1\x6d\x2b\xed\x6b\x1b\xb7\xa9\xe5\x9b\xa9\xba\x06\x6d\xad\xf8\xfd\xc6\x84\xfc\xe3\x49\x38\x63\x3d\x64\x6a\xc2\x9d\x4a\xc7\x26\x67\x88\x99\x46\xb1\x46\x7a\x48\x44\x1d\x23\x2c\xc0\x8f\x62\xd9\xdb\x27\x2a\xc2\xc9\x2e\xc4\x35\xb8\x07\x24\x40\x73\x28\x56\x40\x26\xb5\x17\x07\x41\xbb\x80\xa9\x75\x05\xdd\xe3\xdb\x9f\x9c\x29\x34\xe5\x61\x4b\x4b\x46\x37\xc3\x77\x9b\xe0\x9d\x3c\x1e\x4d\x03\x11\x08\x29\x64\x3d\xcb\x8f\x41\xdb\xe9\xdd\x94\xfc\x6f\xa0\xdd\xeb\x12\xae\xca\x8b\xe4\x53\x82\xdd\xb3\xa3\x8e\x9e\xff\xef\x64\x0d\x95\x52", 243, { 0x55, 0x8a, 0xc2, 0x51, 0x47, 0xe1, 0xcc, 0x16, 0x28, 0xb4, 0x10, 0x71, 0x27, 0x29, 0xa8, 0xba } },
{ "\x92\xcb\xcc\x6b\x83\xda\x5b\x25\xf1\xc8\xd6\xb1\xe8\xe5\xc3\x95\x73\xaf\x5d\xde\xe5\x4f\xe4\x71\xc5\x3c\x9f\x80\x57\xfe\x70\x18\xc3\x0d\x12\xd6\xe5\xd8\xf1\xba\xb0\xe1\xa5\x13\x3f\x05\x0d\x9a\x7a\xd9\x04\x9b\x61\x30\xc3\x4e\xf8\xba\xd3\x44\xcf\xc7\xac\xfd\x2d\x29\xef\x96\xd9\x36\x3d\x9f\x84\xec\xb2\x0b\xd6\x30\x02\x41\x13\x2f\x2e\x4f\x6a\xe5\xe2\x3e\xda\xbc\x6e\x80\xc1\x4c\x5f\x86\x03\x41\xba\x6e\xd3\x5a\xd4\xda\x21\x8e\xd1\xdc\xa0\x49\xb7\x0d\x73\xd4\x2e\xbd\x73\xd2\xd6\x44\x1f\xe6\x45\x77\x21\x72\x9b\x36\x79\x7b\xc4\x23\x48\xa8\x4a\x6d\x3b\x69\xd4\xca\x92\x35\x40\x83\xcc\xeb\x58\xa9\xf1\x5a\x33\x65\x7c\xdc\x2b\x6d\xe2\x1d\x76\x93\xc3\xf9\x63\x77\xac\x84\x33\x5d\x87\x23\x92\x19\xa0\xd7\xb0\x27\x54\x9a\x01\xd7\x58\xe2\x8d\xa5\xa3\x42\xf4\xa7\xf9\x30\x02\x1f\x16\xe1\xeb\x30\x73\x50\x23\xae\xb7\x5e\xdc\x0e\xbd\x14\x1d\x7c\x3e\x04\x7c\x0c\x1b\xcd\x78\x08\x4a\xbc\x75\x68\x5a\x8f\x54\x5f\xa4\x56\xae\x12\x10\x73\xae\x64\x81\xc0\x88\xec\xde\xcf\x9a\x08\xbe\x4c\x1d\x0b", 244, { 0x25, 0x5e, 0xd3, 0x6f, 0x90, 0x16, 0x46, 0xa9, 0xa0, 0x75, 0x89, 0xa0, 0xd2, 0x74, 0x1b, 0x84 } },
{ "\xaf\x7f\x88\x91\x24\xee\x81\xf4\xf8\x20\x80\xd7\xa3\x7b\x03\xdf\xf8\x4f\x68\x82\x98\xec\x6a\xf7\xf7\xed\x3a\x4d\x08\x98\x39\x98\x88\x5d\x50\x46\xe4\x7c\xed\x8f\xc8\xc4\x9a\x0b\x46\x76\x3b\x5d\x9f\x48\xe4\x0d\xb0\x85\x55\x74\xfb\x51\x13\xd0\x51\x0b\x24\x77\x1a\xcb\x66\x29\x41\x0b\x8c\x7e\xbe\x61\xb6\x7e\xc1\x6a\xac\x4f\x78\xc3\xb8\x09\x7d\x31\x1d\xa6\xdf\xe0\x37\x15\xcb\xc9\x30\x6d\xd8\x2c\x5c\x3e\xec\x3d\x32\x04\xcd\xdb\xe8\xb5\x48\x7b\xaa\x7a\xf8\x23\x76\x7a\xb3\x93\x97\xd1\x97\x7e\xbb\x9f\xac\xf5\xb3\x3d\x36\xe5\xc8\x8b\x9a\xb7\xb4\x65\xea\x15\x44\x34\x0f\xcd\x88\xa0\x92\xce\xb3\x63\x07\x4e\x96\x39\x16\x0e\xb4\xf4\x27\xb5\x01\xab\xa9\x59\x3c\x12\x00\x1d\xe6\xe6\x09\xf4\xdd\x7f\x4b\x84\x9a\x87\xbb\x25\x04\xc9\x2b\x08\xee\x23\x51\x75\x34\x96\x70\x2c\x6d\x7c\xa5\xed\x4d\xd9\xd0\x13\x9a\xc9\x1d\x5c\xc9\x19\x2e\xc4\x35\xf2\xe7\x8e\xfb\xb1\xd5\x64\x74\xd2\x3c\x96\x50\x0a\xbb\x7e\x4b\x73\x9e\x04\x8f\xe2\xc0\x3e\xa6\x54\x1b\x2f\x1a\x87\xee\xb0\xac\xa6\x89\x6d\x2d\x1c\xb8", 245, { 0x5e, 0xd5, 0xdf, 0xa5, 0xd4, 0xe1, 0xeb, 0x5b, 0x7e, 0x24, 0x12, 0xa1, 0xd3, 0x55, 0xba, 0x11 } },
{ "\x06\xfb\x8a\xca\x55\x1c\xd3\x3d\xcf\xf0\x54\x07\x03\x96\x31\x83\x40\xde\xcb\xf7\x54\xe6\x4e\xbe\x6e\x53\x66\x17\x25\x2e\x11\x88\x92\x58\x8f\xf0\x97\xab\x77\x28\x43\xaf\xe4\x55\x4e\xf6\xcc\xce\xbf\x15\x70\xa4\xad\x3f\xef\xd2\x21\x7f\xf6\x02\x1b\x92\x92\xfa\xac\x5e\x26\xa1\x40\x13\x78\xb2\xfe\xdd\xe5\xfc\x48\x43\xb5\x53\x5d\x1f\x89\x17\x1e\x3a\xf1\x5e\xee\x83\x1a\xc1\xb2\xec\xa5\xc0\xf7\xe2\x92\xd3\x33\x67\x5b\x0e\x24\xcd\x1d\x6f\x55\x10\xf1\xc7\xbf\xd1\x5a\x43\x8c\xeb\xd6\x97\xf7\xb4\x97\xc6\x4f\xd2\x4c\x90\x19\xb7\x18\x77\x55\xba\xa4\x70\xd9\xd3\x50\x23\xda\xf3\x84\xdf\x8a\xfe\x25\x1e\xdb\x66\x24\xaf\x61\x65\x30\x86\x55\xd7\x8b\x1c\xb5\xb1\xfa\x84\x89\x22\xd6\x0c\x41\x44\x40\x8c\x3b\x7f\x72\x4e\x60\x7b\x30\x99\xee\xbf\x5c\xdc\x50\xeb\xa9\x74\x29\x8e\x68\x1a\x6f\xa5\x7e\xec\xb4\xb1\x77\x16\x81\x73\xb3\x1d\xdb\x47\xbe\xc8\xe7\x1a\xbe\xab\xa9\x0a\x05\x51\xe8\x99\xc7\x05\x2e\x8c\xe5\x3d\xeb\x66\xe7\xa4\xb9\x7c\x09\xc3\xbb\xb5\x6c\x4b\x1e\xe0\x6d\x01\xc1\xb2\x13\x46\xf1\x5a", 246, { 0xf1, 0x29, 0xb7, 0xfb, 0x81, 0x56, 0xef, 0x46, 0xb7, 0x0b, 0xc5, 0xc2, 0xbe, 0xbe, 0xb5, 0x5b } },
{ "\xa2\x42\x4d\xc3\x4c\xad\xc9\x66\x07\x39\xce\xc9\x7a\x9f\x7d\x97\x11\x45\x14\x5d\x30\x89\x6a\xdf\x83\xad\x94\x15\x74\x5f\xaa\xc5\xb6\xe3\xa3\xbe\xfe\xdf\x5d\xae\xd2\xc3\xba\xa1\x7a\xd3\xe4\x16\x12\xd2\xb0\xbf\xc1\x4c\x20\xd6\x04\x81\x03\x17\x24\xe9\xb7\x5e\xc6\x68\x0f\xdd\xa1\x10\x4f\xf9\x4a\x8d\x54\xc2\x2b\x31\xd1\x0d\x92\x9d\xb3\x30\xe5\x08\xa6\x5a\xf4\x2f\xb1\x8c\x67\xd9\xfd\x38\x56\x06\xb3\x74\xf7\xb4\x03\xdb\x72\x4d\x40\x01\xd1\xb0\x28\x90\x13\xda\x42\x04\x60\x31\x60\xff\x56\x6d\x44\x49\x81\x23\x5f\x68\xea\xf0\xb4\xd8\xc6\x3e\xdc\xe8\x4f\xb6\x22\x31\xb0\x42\xce\xb3\x1a\xbd\x7f\x8d\xf4\x3a\xb1\x59\x2f\xee\x5f\x22\xb7\xbb\xc2\x02\x05\x59\x37\x5d\xd1\x23\x3e\xb4\xe5\x7c\x9e\x26\x0d\xdc\xa7\x8a\x2b\x7b\x90\x21\x67\x98\xfe\xfb\x83\x66\xa6\xe9\x4c\x94\x09\x1b\x2c\x77\x5e\x55\xdd\xd7\x8e\xd2\x38\x53\x59\xb5\x2c\x71\x96\x28\xca\x46\x97\x14\x7c\xbe\xaa\x7b\x56\x89\xbc\x75\x84\xa3\x19\xc5\xe3\x7d\x4f\x17\xad\xcd\x30\xd8\x4c\xef\xf5\xb2\x4f\xf6\x7f\xa3\x7a\x54\xb9\xb9\xf7\x21\x1a", 247, { 0xca, 0x55, 0x81, 0x7c, 0x31, 0x59, 0x07, 0x16, 0xf7, 0xce, 0x7f, 0x9d, 0xf9, 0xbc, 0x6d, 0xb1 } },
{ "\x6c\xab\x0b\x47\x97\xa4\xdb\xd5\x15\xae\xa0\x2c\xf4\x05\x7a\x87\x59\x24\x05\x17\xf0\xbc\x5f\x47\x0d\xc0\xd8\x1b\x64\x9d\x35\xb2\x61\x87\xa1\xea\x25\xef\x31\x22\x1e\x11\x12\x1a\x42\xa9\x52\xf7\xe8\xc5\x47\x64\x43\xb6\x9f\xd2\x7b\x20\x06\xdf\x6a\x31\x6e\xd5\xf0\xee\xfe\x49\xf3\xa9\x99\xe4\xf6\x8f\x09\x3e\x55\x5e\xc8\xe6\x1a\x33\x6b\x7e\x7f\x81\xac\x03\x01\x1e\x12\x2b\x1e\x77\x3f\xe7\xab\xe4\xd5\x08\xd4\x16\x06\xfe\xb0\xad\xb8\xbb\x7f\xe6\x51\xb5\x84\x72\x24\x0b\x79\x62\x77\xbf\xb4\x3d\x30\x21\xd4\x34\x1c\x4d\x27\x6d\xdc\xcb\x9c\x7b\x6d\x54\x5f\xef\x52\xb4\x17\x08\x60\xcb\xb8\x85\x26\xad\x05\x9b\xf7\xe9\xa6\x03\x95\xe7\xe1\x2a\x7b\x6a\xf8\x8c\xc7\x36\x1f\x1b\xc2\xcb\x19\xd9\x0d\x4f\x6e\x85\x6b\x89\x4b\x71\x25\x09\xf6\x72\x1e\x66\xec\xf2\x73\xa0\x98\x20\xce\xa4\xb2\x46\x48\xed\x32\x3a\xf8\x47\xf0\xee\x1d\xae\xda\x23\xe3\x56\xd1\x3a\xd6\xc4\x20\x2b\xe0\x19\x99\x8e\x00\x6f\x4e\xd7\x8a\x5c\xe9\x9f\x14\x94\xa9\x1d\x04\xab\xf9\xb3\xb4\xf7\xaf\xa5\x3f\x93\xde\xe4\xeb\x81\x58\x09\x33\xe7", 248, { 0x1c, 0x9e, 0x80, 0x30, 0x66, 0x3c, 0xf5, 0x9d, 0x8e, 0x03, 0x83, 0xcf, 0x5c, 0x89, 0x97, 0x74 } },
{ "\xc3\x5d\x6f\x7a\x56\x15\x22\xf8\x31\x9b\xe0\xcf\x57\x07\xda\xdb\x49\xac\x08\x4d\x3f\xcf\xf1\xa7\x05\x73\x1a\xe3\x71\x50\x09\xb3\x7d\xe1\xf4\xe4\x05\x9c\x0b\xdc\x1e\x3d\x5f\x42\x10\x3c\x6d\xbc\xf2\x5d\x4b\xd3\xe1\x66\x6e\xf4\xdc\xea\x16\x90\x3f\x44\x56\x62\xda\xa0\xc3\xd0\xae\x33\xb9\x6b\x43\x8a\x45\x91\xa9\x00\xb2\x32\x09\x4a\xb3\xaf\xe6\x2c\x2a\xde\xf6\x4e\x93\x2a\x97\x29\x10\xd8\xf0\x1c\x11\x64\xa5\x9b\x9f\x0a\x36\x87\x46\x60\xf5\x98\x9d\x20\xa2\xf6\x73\x04\xa4\xe7\x98\xca\xe6\xa3\x45\x57\x4c\x44\x29\xf8\xd1\xd9\x10\xc3\x3f\x9a\x32\x1c\x89\x35\x16\x53\xc8\x47\x21\x6b\xd0\xe6\xbf\xf6\x6f\x5b\x2d\x1c\x42\xed\xe0\xba\x33\xd8\x95\xa6\x92\x5d\xf6\x11\xcf\xf3\xe6\x06\xd2\x9b\x69\x0f\xf7\x51\x33\xf6\xa9\x9e\xcc\xa9\x9a\x4b\x5c\x37\x9c\x30\x19\xf7\x1f\x2a\x49\xc7\x48\x2a\xf6\x72\xaa\x6a\x2e\x2b\xa3\xbb\xf4\x36\x55\xfb\xc7\xa6\x40\xa2\xcc\x41\x79\x7b\x9a\x7f\x89\x6f\xa2\xb1\xe5\x7c\x39\x3f\x05\xc5\x44\x0a\x22\xc4\x7f\xf0\x91\x9b\x6a\x6d\xb7\x87\xd0\x5e\xa8\x75\xf5\xe1\x61\xaf\x5b\x59\x9d", 249, { 0x41, 0x95, 0xad, 0xac, 0x33, 0xb1, 0xf3, 0x9e, 0xa2, 0x05, 0x82, 0x08, 0xb3, 0xc7, 0x1d, 0xbc } },
{ "\xaf\x31\x8e\x57\x14\x59\xf1\xde\xb2\x14\xfd\x8e\xc4\x4d\xb8\x30\x3c\x7f\x59\xf0\x3b\x43\x03\xf7\x9d\x79\xaf\xa5\xab\x13\x29\x6c\xf4\x79\x31\x4c\x35\x9c\xc2\xe6\x75\x9b\x6f\x40\x2e\x0b\xe8\x14\xa5\xe7\x9c\xd5\x5b\x14\x79\x3f\x9c\x8e\xce\x99\x34\x35\x52\x8a\x41\x2e\x3e\x95\x24\xf7\x95\x33\x91\x0b\x84\x8c\xc6\x2e\xe3\xd1\xd9\x56\xdb\x39\x29\x36\xa2\x95\xf6\x68\x62\x92\x0d\x35\x39\x8b\x9c\x04\x59\x09\x24\x5e\x4e\xd8\x8c\x9a\x60\xc6\x51\x2a\x0e\xfb\xdb\x80\xbb\xf0\xeb\x9e\x65\x0e\x31\x39\x8f\xe3\xfb\x89\x41\x03\x07\xb0\x26\x79\x79\xc4\xd3\xe9\xe8\x7b\x27\x43\x92\x72\xcd\x26\xb0\x1a\xde\xcf\xe5\x3f\xa4\xbc\xcf\x36\x7a\xe1\xc0\xa3\xcf\x86\x87\xe4\x49\xbb\x67\x1e\x05\x79\x29\xe2\xfd\x57\x4d\x7b\x83\xe5\x5c\xd6\xea\xa9\x59\x0e\x43\xb4\x56\x94\x45\xdf\x22\xf8\x46\xa7\x20\x56\x66\xa2\x33\x5f\xcb\x9d\xd5\x03\x06\x55\x47\xb8\x94\xce\xe3\x6a\x81\x52\x8d\xff\x27\x09\x48\x85\x15\x32\xe4\xfb\x0b\xfc\xd5\xb9\x21\x03\x20\x7d\x06\x6a\x6e\x12\x66\x91\x43\x9e\x65\x73\x48\x89\x49\x9f\xc4\x06\x34\xd1\x29\x3f", 250, { 0xc2, 0x69, 0xc7, 0x4f, 0x4c, 0x81, 0x48, 0x5e, 0x87, 0x0f, 0xfb, 0xfe, 0xda, 0xfa, 0xa7, 0x72 } },
{ "\x0e\x2d\xcb\x21\x81\x17\xab\xc1\x1e\xb1\x72\x69\x9d\xf2\x79\x44\x41\x60\x05\xa1\x5a\x6a\x90\xe7\xe4\x64\x42\x16\x4d\x1f\x7f\xf5\x54\x24\x9a\xde\x0d\x8d\xa7\x22\x01\x81\x6d\x1a\x72\x4a\x7a\xcb\xbb\x15\x51\x35\xd6\x45\xbf\x38\xf8\x73\x4c\x24\x57\x06\xcc\xdc\x0b\x6c\x15\xa5\x12\xf2\xca\x90\x6e\x46\x56\x82\x69\x86\xf5\xdd\xf9\x04\xec\xcd\x3e\x99\xd9\x31\x27\xa3\x25\x23\x35\x9c\x95\x26\x58\x58\x00\xeb\xf5\xdb\x1b\xc0\x09\xd4\x70\x96\x67\xba\x6d\xad\x1d\x82\x99\xde\xf5\xfa\xe1\x84\x17\xc5\x11\x08\xcc\xf3\x5e\x08\x5d\x3c\x20\x24\x1a\xda\x9d\x65\x76\x00\xff\x49\x4f\xfa\x68\x6f\x4c\xe2\x1c\xdb\x60\xfc\xdd\xe7\x6b\xaf\x54\xc7\xff\x21\xab\xb7\x3f\x6d\x37\xc3\xe4\x84\x53\x32\x59\x9d\x48\x90\x06\x5a\x68\x57\xab\x79\x3a\x3a\xe2\x33\xcf\x0d\xc6\x34\x33\x54\xb3\x38\xff\x66\x23\x3f\x0c\x3d\xb7\x6c\x42\xdd\x57\x80\x8e\x5f\x70\xed\xf2\x9a\x5c\x9a\xb6\x6c\xe0\x33\xbc\xaa\xce\x29\xf1\xa2\xcb\x4d\xdf\x49\x2b\x04\x60\x06\xf8\x28\x6e\x1a\x12\x7c\x15\xaa\x70\xc9\x89\x6a\x84\x99\x54\xe8\xbd\x8f\xa7\x72\x26\x61\xd2", 251, { 0xdd, 0x00, 0x50, 0x61, 0xae, 0x25, 0x19, 0xe0, 0x00, 0x24, 0x84, 0x43, 0x02, 0x6f, 0x1c, 0xd4 } },
{ "\xa5\x2c\x74\xcf\x94\x7c\x13\xf9\x91\x0b\x4b\xda\x9b\x2f\x65\x21\x64\xeb\x01\xb3\xfd\x48\xcb\xd8\x20\xde\xdd\x96\x1a\x72\xb1\x1b\x53\xb9\xc1\x53\x7b\x3b\xbd\x9a\x53\x53\x68\x8b\x15\x53\x10\xf7\x81\xc4\xa8\xf2\x86\xca\x83\x07\x89\xa6\xaf\x8b\x54\x56\xec\x0f\x9e\x57\x48\xef\x33\x8a\x58\x07\xc0\x34\x15\x86\x3d\x20\x50\xda\xf7\xdf\xd3\xcb\x39\x30\x16\xa4\x96\x7a\x9b\x8b\xd6\x76\xe7\xf2\x7b\xe9\x1d\x26\xee\x8f\x38\x05\x4b\x14\xe4\xcc\xc6\x3b\xfa\x0e\xb8\x22\x96\xc1\x4a\x9c\xd7\x73\xbc\xbe\x33\x9a\x53\x76\x74\x08\xdd\x54\x53\x7d\xe2\x6c\xaf\x57\x69\x54\x6a\x64\x64\x49\xe1\xd8\xb9\x6e\x06\x5a\xed\x34\x1b\x38\x6f\xd5\x0c\xbc\x7f\xf9\x6a\x96\xb9\x7c\x00\x78\x42\x47\x14\xc1\x8d\x5b\x3b\x51\xcb\xec\xd9\x7b\xed\xaa\x35\x18\x57\x1a\x35\xb8\x22\x23\xba\xf4\x0e\xa5\x9a\xdf\x03\x44\x36\x9b\x42\x43\xb8\x07\x2d\x8a\xeb\x96\xaf\xca\xb7\x3b\x49\xb7\x37\x80\xba\x74\x79\xb6\x4b\x0d\xd1\x47\xb4\x1d\xda\x27\xae\x90\x0b\x69\x16\x83\xf1\xee\xbb\x48\x0e\x38\xc4\x85\x4e\x5c\xc1\x7c\x22\x16\x4c\x65\x3c\xf7\x5b\xf7\xe5\xb9", 252, { 0x93, 0x1d, 0xc4, 0x3f, 0x9e, 0x01, 0xe4, 0xa0, 0xba, 0x31, 0x17, 0x62, 0xb1, 0x71, 0x07, 0x29 } },
{ "\x26\x05\xfe\xb3\xaf\x45\x91\x67\xf3\x2d\x13\x39\xab\xf7\x38\x3b\xbf\xc3\x73\x23\x48\xda\x09\x5e\x40\x10\xd1\x3d\xc9\x44\x8a\x4e\x16\x02\xd9\xc6\xfa\x47\xdd\x19\x0b\x64\x70\xac\x72\xfb\xfd\xa2\x52\x26\xf9\xd3\xd3\xb8\x00\xdb\xca\x9b\x8c\x4e\x07\x58\x54\x09\x3a\xb6\x3f\xa4\x82\x79\x03\x03\x94\x4b\x5f\x0c\x84\xb9\xf1\x73\x33\x54\xb4\xb0\x56\xf8\x1a\x12\x1e\x29\xc2\xed\x89\x99\xd7\xef\x45\xc6\x04\x91\x3c\xc0\x17\xa9\xc1\x08\x31\x1c\x55\x94\xa7\xb0\x15\xf0\x79\xff\xc4\x7e\x6d\x87\x71\xde\xc7\xdf\xc5\x68\xa6\x04\xeb\xd6\xfe\xd2\x1c\xff\x2d\x8e\xc6\xbe\x3c\xa0\x58\xf1\xb5\x5e\xac\x9d\x1c\x03\x12\x2f\x0b\xbe\xf5\xdd\xed\xe7\x2d\x2b\x57\x4c\xe0\x8a\xfe\xaa\x15\x1b\x59\x37\xd7\x91\xd4\x5c\x02\x34\xad\x80\xad\x01\x6f\x00\x34\xef\x09\x3b\xe0\x4c\x8b\xc9\x35\x46\x7c\xcb\xd9\x86\xda\x5d\x1c\xf7\xaf\x28\x28\xa5\x4c\x15\xc6\xc0\x25\x1c\xca\xbf\x48\x3a\x1d\xa1\x7b\x81\x65\x4e\xf2\x49\x53\x1c\xaa\x84\x88\x6f\x65\x30\x25\x78\x42\xe5\xee\x1e\xf8\x8e\x19\xf9\xaf\x34\xb9\x7a\x7b\xf6\xc2\x29\x75\x15\xb8\x07\x78\x2c\xf9", 253, { 0x52, 0x78, 0x7c, 0x98, 0x49, 0x6b, 0xa5, 0xff, 0x30, 0xbc, 0xb9, 0x58, 0x43, 0x67, 0x74, 0xff } },
{ "\x12\x75\x7c\xa3\xe7\x74\x65\x23\x81\x28\xf1\x5b\x1f\x2d\x8b\x6f\x44\xe0\xcd\x2d\xd8\xdb\x77\x16\x6c\x0b\x7b\x0d\x9b\x70\x34\x9b\x8c\x71\xb7\xdd\x93\xda\x42\x0b\xf7\x73\xd2\xa5\xce\x3e\xd1\x3c\x05\x4c\xeb\xda\x7c\x3c\x01\x0f\x4e\x51\x37\x99\x2e\x2f\x28\xaf\xed\x32\x39\xea\x18\x6b\x0b\xd0\xbd\x39\x0a\xff\x4e\x7f\x22\xf3\x9f\x87\x92\x74\x0a\x73\xd8\x9f\xb2\x5b\xcc\x8e\xe4\x08\xc9\xa7\x99\x4c\x06\x7e\x18\xfc\x02\x68\xb8\x8c\x1e\x9d\xc3\x45\x44\x08\x77\x25\xc5\xaf\x26\x53\x41\xba\x7d\x3d\xbf\x22\xe1\x50\xdd\xf7\xf5\x53\x21\x4d\x38\x61\x6d\xc4\xcc\x81\x91\xb3\x51\xe3\xfb\xf1\xf0\xba\x89\x3f\x74\xb0\x7f\x55\x92\x0a\x94\x88\x49\x5a\x27\x14\x64\xdb\x8f\x0c\x1d\x6c\x90\xdb\xdc\x2c\xe9\x76\x1d\xae\x09\x20\x6f\xd9\xe2\xd9\x98\x5f\xd7\x64\xd6\xd8\xcf\xf4\x40\x7a\x6b\x72\x4b\x77\x54\x6d\x69\xf4\xad\x9f\xcc\xa1\xa8\x18\x49\xf9\x34\x0a\x57\x18\xd4\x30\x36\x34\x8b\xdb\x2c\xb9\xf4\x9a\xea\x05\x6e\x85\x0e\xbd\x73\x26\xc2\xca\x0a\x05\x81\xf4\x53\xcf\xfa\x19\x40\x22\x0d\x09\x63\xf8\xf2\x01\xe1\xad\x79\xc3\x86\xae\x6b\x4e", 254, { 0xce, 0x3c, 0x1c, 0xb0, 0xc1, 0xd5, 0x9a, 0x1f, 0xad, 0x95, 0x57, 0x98, 0xc6, 0x74, 0x61, 0x10 } },
{ "\xf3\xc0\x10\x3f\xf2\xea\xca\xc4\xea\x01\xdf\x39\x6d\xce\x54\x61\xef\xdd\xf4\x42\x92\xe5\x70\x9d\x1c\xcf\xa0\x08\x0a\x65\xe8\xaa\xbe\x98\xb6\x93\xd3\x6d\x27\xb5\x91\x86\xc9\x83\x7c\x49\x7b\x25\x07\xaf\x71\x55\x66\xab\x54\xd9\x81\x34\x86\x9d\x04\xf1\x83\x6c\x93\x80\x63\x4b\x1b\x64\x7b\x72\x44\x89\x24\xe8\x02\x74\x93\xba\x4b\x0b\xe7\xd7\xe3\xfe\x42\x8b\x53\xd1\x0e\x96\xf8\x88\x61\xe9\x37\xee\x7b\xfc\xce\x81\x6c\xce\x46\xfd\xd3\x7a\x84\x83\xc1\x73\x7f\x66\xbb\x5c\x0c\x93\xde\x86\xd6\x9a\x1d\x07\x69\x5d\xa6\x73\x6d\x54\x64\x3a\xef\x7a\x9d\x9e\xdb\xd7\xba\x4f\x86\xab\x27\xa4\x68\x34\x51\x78\xe7\x1c\xcc\x9f\x4e\x83\x97\x04\xdc\xa4\x77\x61\xf9\x26\x7f\x99\x84\x01\xb1\xb5\x47\x0b\xbf\x79\x8c\x1f\xea\xa2\xc9\xe8\x0c\xbf\x76\x4f\xb1\xa9\xff\x7f\x5f\xa1\xd5\x91\xf6\x04\xa0\xd9\x32\xad\x8f\xcc\x4e\xe7\xcf\x8c\xc3\x0d\x19\x12\x2f\xc1\x66\xf7\x50\xc5\xbe\xdf\x2f\x79\x2e\x83\x59\xf1\xd8\x59\x48\xb2\x24\xe1\xe1\x0a\x15\x8e\x17\x09\xb6\x50\xad\x1f\xb3\xba\x18\x54\x03\xd5\x82\x1e\xc3\x80\xeb\xe2\x1f\x82\x6a\x0a\x69\x2e\x92", 255, { 0xbb, 0x05, 0x2f, 0x98, 0xc3, 0x43, 0x0a, 0x44, 0x7d, 0xae, 0x93, 0xea, 0x0a, 0x81, 0x4e, 0x3a } },
{ "\xbf\x4b\xb1\xf0\x43\x19\xfc\xb0\xee\x40\x48\x5f\xc3\xdc\x4a\xca\xaf\x65\xf5\x06\x5d\x88\xe7\x89\xb8\x14\x71\x76\xfe\x0b\x46\xf6\x7e\xd9\xbf\xc1\xee\xa1\xc8\xbd\x6b\xb2\x6b\xbd\x0d\x18\xf7\x6a\x26\x4f\xcc\x3f\x18\x13\xc6\xae\xd0\x53\x44\x60\xe3\x43\xd4\x9a\x43\x91\x7c\xbb\x9d\xaf\xa7\xe1\x53\x4f\xab\xac\x11\xed\xf3\x1a\x0e\x85\xce\x92\xe1\x66\xd3\xfc\xfd\x1f\xee\x0d\xcb\x95\x0c\xa0\x63\x36\x5f\x40\xe6\x48\x4e\xc2\x7a\x5b\xfd\x0f\xe3\xdd\x74\x00\xbb\xcc\x6e\x62\x4e\x86\xc0\x18\x14\xbc\x64\x60\xcb\x85\x22\x2e\x31\x8f\xda\xb4\x5b\x70\x70\x03\xb5\x1a\x54\xcb\x97\x6d\xac\x3e\x7f\xe7\x21\x13\xf1\x77\x43\xa7\xe8\x6f\x9a\x3e\xf7\x97\x4a\x66\x01\x5d\x62\x7c\x91\x2a\xc1\x48\xd7\xd1\xa5\xc4\x40\x21\xd1\xfa\xb1\x9a\x5b\x0b\x5f\x3c\x0f\x4b\x4d\x7a\x83\x8a\x63\x4e\xb9\x6e\x28\x66\x6a\xfc\x1d\x7c\xf5\x35\xb5\xc3\xe4\xdd\xf4\x7d\x16\x57\xa2\xa9\x8f\xab\x2c\xda\xd9\xaa\x18\x23\x14\x29\x23\x2f\xa1\x69\xf9\x6d\x67\x97\x91\x68\xc0\x6e\x22\x34\x04\xfa\xc5\x04\xb0\x78\xa8\xd9\x32\x5a\xec\x55\x53\x66\x1d\xae\x41\xfe\x4b\xbe\x38\x7a", 256, { 0x64, 0xe1, 0xfd, 0x82, 0x3c, 0xe4, 0x10, 0x8b, 0x41, 0xca, 0x5d, 0xff, 0xed, 0x9f, 0x47, 0x22 } },
snapraid-12.1/cmdline/parity.c 0000664 0000000 0000000 00000062156 14166610522 0016342 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
/**
* Pseudo random limits for parity
*/
#define PARITY_LIMIT(size, split, level) \
size ? size + (123562341 + split * 634542351 + level * 983491341) % size : 0
/****************************************************************************/
/* parity */
block_off_t parity_allocated_size(struct snapraid_state* state)
{
block_off_t parity_block;
tommy_node* i;
/* compute the size of the parity file */
parity_block = 0;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
/* start from the declared size */
block_off_t block = fs_size(disk);
/* decrease the block until an allocated one, but part of a file */
/* we don't stop at deleted blocks, because we want to have them cleared */
/* if they are at the end of the parity */
while (block > parity_block && !block_has_file(fs_par2block_find(disk, block - 1)))
--block;
/* get the highest value */
if (block > parity_block)
parity_block = block;
}
return parity_block;
}
block_off_t parity_used_size(struct snapraid_state* state)
{
block_off_t parity_block;
tommy_node* i;
/* compute the size of the parity file */
parity_block = 0;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
/* start from the declared size */
block_off_t block = fs_size(disk);
/* decrease the block until an used one */
while (block > parity_block && !block_has_file_and_valid_parity(fs_par2block_find(disk, block - 1)))
--block;
/* get the highest value */
if (block > parity_block)
parity_block = block;
}
return parity_block;
}
int parity_is_invalid(struct snapraid_state* state)
{
block_off_t blockmax;
block_off_t i;
blockmax = parity_allocated_size(state);
for (i = 0; i < blockmax; ++i) {
tommy_node* node_disk;
int one_invalid;
int one_valid;
/* for each disk */
one_invalid = 0;
one_valid = 0;
for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) {
struct snapraid_disk* disk = node_disk->data;
struct snapraid_block* block = fs_par2block_find(disk, i);
if (block_has_file(block))
one_valid = 1;
if (block_has_invalid_parity(block))
one_invalid = 1;
}
/* if both valid and invalid, we need to update */
if (one_invalid && one_valid)
return 1;
}
return 0;
}
void parity_overflow(struct snapraid_state* state, data_off_t size)
{
tommy_node* i;
block_off_t blockalloc;
int found = 0;
char esc_buffer[ESC_MAX];
/* don't report if everything is outside or if the file is not accessible */
if (size == 0) {
return;
}
blockalloc = size / state->block_size;
/* for all disks */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
tommy_node* j;
/* for all files */
for (j = disk->filelist; j != 0; j = j->next) {
struct snapraid_file* file = j->data;
if (file->blockmax > 0) {
block_off_t parity_pos = fs_file2par_get(disk, file, file->blockmax - 1);
if (parity_pos >= blockalloc) {
found = 1;
log_tag("outofparity:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
log_fatal("outofparity %s%s\n", disk->dir, file->sub);
}
}
}
}
if (found) {
log_fatal("\nYour data requires more parity than the available space.\n");
log_fatal("Please move the files 'outofparity' to another data disk.\n");
}
}
void parity_size(struct snapraid_parity_handle* handle, data_off_t* out_size)
{
unsigned s;
data_off_t size;
/* now compute the size summing all the parity splits */
size = 0;
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
size += split->size;
}
*out_size = size;
}
int parity_create(struct snapraid_parity_handle* handle, const struct snapraid_parity* parity, unsigned level, int mode, uint32_t block_size, data_off_t limit_size)
{
unsigned s;
data_off_t block_mask;
/* mask of bits used by the block size */
block_mask = ((data_off_t)block_size) - 1;
handle->level = level;
handle->split_mac = 0;
for (s = 0; s < parity->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
int ret;
int flags;
advise_init(&split->advise, mode);
pathcpy(split->path, sizeof(split->path), parity->split_map[s].path);
split->size = parity->split_map[s].size;
split->limit_size = PARITY_LIMIT(limit_size, s, level);
/* opening in sequential mode in Windows */
flags = O_RDWR | O_CREAT | O_BINARY | advise_flags(&split->advise);
split->f = open(split->path, flags, 0600);
if (split->f == -1) {
/* LCOV_EXCL_START */
log_fatal("Error opening parity file '%s'. %s.\n", split->path, strerror(errno));
goto bail;
/* LCOV_EXCL_STOP */
}
/* we have a valid file handle */
++handle->split_mac;
/* get the stat info */
ret = fstat(split->f, &split->st);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
goto bail;
/* LCOV_EXCL_STOP */
}
/* the initial valid size is the size on disk */
split->valid_size = split->st.st_size;
/**
* If the parity size is not yet set, set it now.
* This happens when expanding the number of parities,
* or when upgrading from a content file that has not split->size data.
*/
if (split->size == PARITY_SIZE_INVALID) {
split->size = split->st.st_size;
/* ensure that the resulting size if block aligned */
if ((split->size & block_mask) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in preallocated size of parity file '%s' with size %" PRIu64 " and block %u .\n", split->path, split->size, block_size);
goto bail;
/* LCOV_EXCL_STOP */
}
}
ret = advise_open(&split->advise, split->f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
goto bail;
/* LCOV_EXCL_STOP */
}
}
return 0;
bail:
/* LCOV_EXCL_START */
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
close(split->f);
split->f = -1;
}
return -1;
/* LCOV_EXCL_STOP */
}
static int parity_handle_grow(struct snapraid_split_handle* split, data_off_t previous_size, data_off_t size, int skip_fallocate)
{
int ret;
(void)previous_size;
/* simulate a failure for testing limits */
if (split->limit_size != 0 && size > (data_off_t)split->limit_size)
return -1;
#if HAVE_FALLOCATE
if (!skip_fallocate) {
/*
* Allocate real space using the specific Linux fallocate() operation.
* If the underline file-system doesn't support it, this operation fails.
*
* Instead posix_fallocate() fallbacks to write the whole file,
* and we cannot use it as we may need to initialize a multi terabyte
* file.
*
* See: fallocate vs posix_fallocate
* http://stackoverflow.com/questions/14063046/fallocate-vs-posix-fallocate
*
* To work better with Btrfs, use as offset the previous allocated size.
* Otherwise Btrfs will count as space needed even the already allocated one.
*
* See: Massive loss of disk space
* https://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg66454.html
*/
ret = fallocate(split->f, 0, previous_size, size - previous_size);
/*
* In some legacy system fallocate() may return the error number
* as positive integer, and in this case it doesn't set errno.
*
* Detect and handle this case.
*
* See: Fix fallocate error return on i386
* https://sourceware.org/ml/libc-hacker/2010-04/msg00000.html
*
* See: [PATCH XFS] Fix error return for fallocate() on XFS
* http://oss.sgi.com/archives/xfs/2009-11/msg00201.html
*/
if (ret > 0) {
/* LCOV_EXCL_START */
errno = ret;
ret = -1;
/* LCOV_EXCL_STOP */
}
} else {
errno = EOPNOTSUPP;
ret = -1;
}
/*
* Fallback to ftruncate() if the operation is not supported.
*
* We get EOPNOTSUPP if the operation is not supported, like in ext3/ext2
* or ENOSYS with kernel before 2.6.23, because fallocate is not supported
* at all.
*
* See: man fallocate
* ENOSYS - This kernel does not implement fallocate().
* EOPNOTSUPP - The file system containing the file referred to by fd does not support this operation
*/
if (ret != 0 && (errno == EOPNOTSUPP || errno == ENOSYS)) {
/* fallback using ftruncate() */
ret = ftruncate(split->f, size);
}
#else
(void)skip_fallocate; /* avoid the warning */
/* allocate using a sparse file */
ret = ftruncate(split->f, size);
#endif
if (ret != 0)
log_tag("split:grow:%s:%" PRIu64 ": failed with error %s\n", split->path, size, strerror(errno));
else
log_tag("split:grow:%s:%" PRIu64 ": ok\n", split->path, size);
return ret;
}
static int parity_handle_shrink(struct snapraid_split_handle* split, data_off_t size)
{
int ret;
ret = ftruncate(split->f, size);
if (ret != 0)
log_tag("split:shrink:%s:%" PRIu64 ": failed with error %s\n", split->path, size, strerror(errno));
else
log_tag("split:shrink:%s:%" PRIu64 ": ok\n", split->path, size);
return ret;
}
/**
* Get the highest bit set.
*/
uint64_t hbit_u64(uint64_t v)
{
unsigned ilog;
ilog = 0;
while ((v /= 2) != 0)
++ilog;
return 1ULL << ilog;
}
static int parity_handle_fill(struct snapraid_split_handle* split, data_off_t size, uint32_t block_size, int skip_fallocate, int skip_space_holder)
{
data_off_t base;
data_off_t delta;
data_off_t block_mask;
#ifdef _WIN32
/*
* In Windows we want to avoid the annoying warning
* message of disk full.
*
* To ensure to leave some space available, we first create
* a spaceholder file >200 MB, to ensure to not fill completely
* the disk.
*/
char spaceholder_path[PATH_MAX];
pathprint(spaceholder_path, sizeof(spaceholder_path), "%s%s", split->path, ".spaceholder");
if (!skip_space_holder) {
data_off_t spaceholder_size = 256 * 1024 * 1024;
int spaceholder_f;
spaceholder_f = open(spaceholder_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);
if (spaceholder_f == -1) {
log_fatal("Failed to create space holder file '%s'.\n", spaceholder_path);
return -1;
}
/* note that in Windows ftruncate is really allocating space */
if (ftruncate(spaceholder_f, spaceholder_size) != 0) {
log_fatal("WARNING Failed to resize the space holder file '%s' to %" PRIu64 " bytes.\n", spaceholder_path, spaceholder_size);
log_fatal("Assuming that no more space is available.\n");
close(spaceholder_f);
remove(spaceholder_path);
return 0;
}
if (fsync(spaceholder_f) != 0) {
log_fatal("Failed to sync the space holder file '%s'.\n", spaceholder_path);
close(spaceholder_f);
remove(spaceholder_path);
return -1;
}
if (close(spaceholder_f) != 0) {
log_fatal("Failed to close the space holder file '%s'.\n", spaceholder_path);
remove(spaceholder_path);
return -1;
}
}
#else
(void)skip_space_holder;
#endif
/* mask of bits used by the block size */
block_mask = ((data_off_t)block_size) - 1;
/* present size */
base = split->st.st_size;
/* truncate it to block size multiplier */
/* in case of damage the size may get wrong */
base &= ~block_mask;
/* size we have to increase */
delta = size - base;
log_tag("split:fill:%s:%" PRIu64 ":%" PRIu64 ":\n", split->path, base, size);
/* grow the size one bit at time, like a kind of binary search */
while (delta != 0) {
int ret;
data_off_t run = hbit_u64(delta);
/* mask out the bit we process */
delta &= ~run;
log_tag("split:delta:%s:%" PRIu64 ":%" PRIu64 ":\n", split->path, base, run);
ret = parity_handle_grow(split, base, base + run, skip_fallocate);
if (ret != 0) {
/* we cannot grow, fallback enabling all the smaller bits */
delta = run - 1;
/* mask out the block size */
delta &= ~block_mask;
} else {
/* increase the effective size */
base += run;
}
}
/* ensure that the resulting size if block aligned */
if ((base & block_mask) != 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in requested parity size %" PRIu64 " with block %u\n", base, block_size);
os_abort();
/* LCOV_EXCL_STOP */
}
#ifdef _WIN32
/* now delete the spaceholder file */
if (remove(spaceholder_path) != 0) {
log_fatal("WARNING Failed to remove the space holder file '%s'.\n", spaceholder_path);
log_fatal("Continuing anyway.\n");
}
#endif
/* shrink to the expected size to ensure to throw away any extra */
/* data allocated when the grow operation fails */
return parity_handle_shrink(split, base);
}
static int parity_handle_chsize(struct snapraid_split_handle* split, data_off_t size, uint32_t block_size, int skip_fallocate, int skip_space_holder)
{
int ret;
int f_ret;
int f_errno;
int f_dir;
if (split->st.st_size < size) {
f_ret = parity_handle_fill(split, size, block_size, skip_fallocate, skip_space_holder);
f_errno = errno;
f_dir = 1;
} else if (split->st.st_size > size) {
f_ret = parity_handle_shrink(split, size);
f_errno = errno;
f_dir = -1;
} else {
f_ret = 0;
f_errno = 0;
f_dir = 0;
}
/* get the stat info */
ret = fstat(split->f, &split->st);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
/* now check the error */
if (f_ret != 0) {
/* LCOV_EXCL_START */
if (f_dir > 0) {
if (f_errno == ENOSPC) {
log_fatal("Failed to grow parity file '%s' to size %" PRIu64 " due lack of space.\n", split->path, size);
} else {
log_fatal("Error growing parity file '%s' to size %" PRIu64 ". Do you have enough space? %s.\n", split->path, size, strerror(f_errno));
}
} else {
log_fatal("Error truncating parity file '%s' to size %" PRIu64 ". %s.\n", split->path, size, strerror(f_errno));
}
return -1;
/* LCOV_EXCL_STOP */
}
/* if we shrink, update the valid size, but don't update when growing */
if (split->valid_size > split->st.st_size)
split->valid_size = split->st.st_size;
return 0;
}
static int parity_split_is_fixed(struct snapraid_parity_handle* handle, unsigned s)
{
/* next one */
++s;
/* the latest one is always growing */
if (s >= handle->split_mac)
return 0;
/* if the next it's 0, this one is growing */
if (handle->split_map[s].size == 0)
return 0;
return 1;
}
int parity_chsize(struct snapraid_parity_handle* handle, struct snapraid_parity* parity, int* is_modified, data_off_t size, uint32_t block_size, int skip_fallocate, int skip_space_holder)
{
int ret;
unsigned s;
data_off_t block_mask;
/* mask of bits used by the block size */
block_mask = ((data_off_t)block_size) - 1;
if (size < 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
int is_fixed = parity_split_is_fixed(handle, s);
data_off_t run;
if (is_fixed) {
/* if the required size is smaller, we have to reduce also the file */
/* ignoring the previous size */
if (size <= split->size) {
/* mark it as not fixed anymore for the later check */
is_fixed = 0;
run = size; /* allocate only the needed size */
} else {
/* if the size cannot be changed, use the fixed one */
run = split->size;
if ((run & block_mask) != 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in split '%s' size with extra '%" PRIu64 "' bytes.\n", split->path, run & block_mask);
return -1;
/* LCOV_EXCL_STOP */
}
}
} else {
/* otherwise tries to allocate all the needed remaining size */
run = size;
}
ret = parity_handle_chsize(split, run, block_size, skip_fallocate, skip_space_holder);
if (ret != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
if (split->st.st_size > run) {
/* LCOV_EXCL_START */
log_fatal("Unexpected over resizing parity file '%s' to size %" PRIu64 " resulting in size %" PRIu64 ".\n", split->path, run, (uint64_t)split->st.st_size);
return -1;
/* LCOV_EXCL_STOP */
} else if (is_fixed && split->st.st_size < run) {
/* LCOV_EXCL_START */
log_fatal("Failed restoring parity file '%s' to size %" PRIu64 " resulting in size %" PRIu64 ".\n", split->path, run, (uint64_t)split->st.st_size);
return -1;
/* LCOV_EXCL_STOP */
} else {
/* here it's possible to get less than the requested size */
run = split->st.st_size;
if ((run & block_mask) != 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in final parity size %" PRIu64 " with block size %u\n", run, block_size);
os_abort();
/* LCOV_EXCL_STOP */
}
/* store what we have allocated */
split->size = run;
/* decrease the remaining size */
size -= run;
}
}
/* if we cannot allocate all the space */
if (size != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to allocate all the required parity space. You miss %" PRIu64 " bytes.\n", size);
return -1;
/* LCOV_EXCL_STOP */
}
/* now copy the new size in the parity data */
if (is_modified)
*is_modified = 0;
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
if (parity->split_map[s].size != split->size) {
parity->split_map[s].size = split->size;
if (is_modified)
*is_modified = 1;
}
}
return 0;
}
int parity_open(struct snapraid_parity_handle* handle, const struct snapraid_parity* parity, unsigned level, int mode, uint32_t block_size, data_off_t limit_size)
{
unsigned s;
data_off_t block_mask;
handle->level = level;
handle->split_mac = 0;
/* mask of bits used by the block size */
block_mask = ((data_off_t)block_size) - 1;
for (s = 0; s < parity->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
int ret;
int flags;
advise_init(&split->advise, mode);
pathcpy(split->path, sizeof(split->path), parity->split_map[s].path);
split->size = parity->split_map[s].size;
split->limit_size = PARITY_LIMIT(limit_size, s, level);
/* open for read */
/* O_NOATIME: do not change access time */
flags = O_RDONLY | O_BINARY | advise_flags(&split->advise);
split->f = open_noatime(split->path, flags);
if (split->f == -1) {
/* LCOV_EXCL_START */
log_fatal("Error opening parity file '%s'. %s.\n", split->path, strerror(errno));
goto bail;
/* LCOV_EXCL_STOP */
}
/* we have a valid file handle */
++handle->split_mac;
/* get the stat info */
ret = fstat(split->f, &split->st);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing parity file '%s'. %s.\n", split->path, strerror(errno));
goto bail;
/* LCOV_EXCL_STOP */
}
/* the initial valid size is the size on disk */
split->valid_size = split->st.st_size;
/**
* If the parity size is not yet set, set it now.
* This happens when expanding the number of parities,
* or when upgrading from a content file that has not split->size data.
*/
if (split->size == PARITY_SIZE_INVALID) {
split->size = split->st.st_size;
/* ensure that the resulting size if block aligned */
if ((split->size & block_mask) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in preallocated size of parity file '%s' with size %" PRIu64 " and block %u .\n", split->path, split->size, block_size);
goto bail;
/* LCOV_EXCL_STOP */
}
}
ret = advise_open(&split->advise, split->f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
goto bail;
/* LCOV_EXCL_STOP */
}
}
return 0;
bail:
/* LCOV_EXCL_START */
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
close(split->f);
split->f = -1;
}
return -1;
/* LCOV_EXCL_STOP */
}
int parity_sync(struct snapraid_parity_handle* handle)
{
#if HAVE_FSYNC
unsigned s;
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
int ret;
/* Ensure that data changes are written to disk. */
/* This is required to ensure that parity is more updated than content */
/* in case of a system crash. */
ret = fsync(split->f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error syncing parity file '%s'. %s.\n", split->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
}
#endif
return 0;
}
int parity_truncate(struct snapraid_parity_handle* handle)
{
unsigned s;
int f_ret = 0;
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
int ret;
/* truncate any data that we know it's not valid */
ret = ftruncate(split->f, split->valid_size);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error truncating the parity file '%s' to size %" PRIu64 ". %s.\n", split->path, split->valid_size, strerror(errno));
f_ret = -1;
/* LCOV_EXCL_STOP */
/* continue to truncate the others */
}
}
return f_ret;
}
int parity_close(struct snapraid_parity_handle* handle)
{
unsigned s;
int f_ret = 0;
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
int ret;
ret = close(split->f);
if (ret != 0) {
/* LCOV_EXCL_START */
/* This is a serious error, as it may be the result of a failed write */
/* identified at later time. */
/* In a normal file-system (not NFS) it should never happen */
log_fatal("Error closing parity file '%s'. %s.\n", split->path, strerror(errno));
f_ret = -1;
/* LCOV_EXCL_STOP */
/* continue to close the others */
}
/* reset the descriptor */
split->f = -1;
}
return f_ret;
}
struct snapraid_split_handle* parity_split_find(struct snapraid_parity_handle* handle, data_off_t* offset)
{
unsigned s;
if (*offset < 0)
return 0;
for (s = 0; s < handle->split_mac; ++s) {
struct snapraid_split_handle* split = &handle->split_map[s];
if (*offset < split->size)
return split;
*offset -= split->size;
}
return 0;
}
int parity_write(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size)
{
ssize_t write_ret;
data_off_t offset;
struct snapraid_split_handle* split;
int ret;
offset = pos * (data_off_t)block_size;
split = parity_split_find(handle, &offset);
if (!split) {
/* LCOV_EXCL_START */
log_fatal("Writing parity data outside range at extra offset %" PRIu64 ".\n", offset);
return -1;
/* LCOV_EXCL_STOP */
}
/* update the valid range */
if (split->valid_size < offset + block_size)
split->valid_size = offset + block_size;
write_ret = pwrite(split->f, block_buffer, block_size, offset);
if (write_ret != (ssize_t)block_size) { /* conversion is safe because block_size is always small */
/* LCOV_EXCL_START */
if (errno == ENOSPC) {
log_fatal("Failed to grow parity file '%s' using write due lack of space.\n", split->path);
} else {
log_fatal("Error writing file '%s'. %s.\n", split->path, strerror(errno));
}
return -1;
/* LCOV_EXCL_STOP */
}
ret = advise_write(&split->advise, split->f, offset, block_size);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int parity_read(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size, fptr* out)
{
ssize_t read_ret;
data_off_t offset;
unsigned count;
struct snapraid_split_handle* split;
int ret;
offset = pos * (data_off_t)block_size;
split = parity_split_find(handle, &offset);
if (!split) {
/* LCOV_EXCL_START */
out("Reading parity data outside range at extra offset %" PRIu64 ".\n", offset);
return -1;
/* LCOV_EXCL_STOP */
}
/* if read is completely out of the valid range */
if (offset >= split->valid_size) {
/* LCOV_EXCL_START */
out("Missing data reading file '%s' at offset %" PRIu64 " for size %u.\n", split->path, offset, block_size);
return -1;
/* LCOV_EXCL_STOP */
}
count = 0;
do {
read_ret = pread(split->f, block_buffer + count, block_size - count, offset + count);
if (read_ret < 0) {
/* LCOV_EXCL_START */
out("Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", split->path, offset + count, block_size - count, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
if (read_ret == 0) {
/* LCOV_EXCL_START */
out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", split->path, offset, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
count += read_ret;
} while (count < block_size);
ret = advise_read(&split->advise, split->f, offset, block_size);
if (ret != 0) {
/* LCOV_EXCL_START */
out("Error advising parity file '%s'. %s.\n", split->path, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return block_size;
}
snapraid-12.1/cmdline/parity.h 0000664 0000000 0000000 00000011355 14166610522 0016342 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __PARITY_H
#define __PARITY_H
#include "support.h"
/****************************************************************************/
/* parity */
struct snapraid_split_handle {
char path[PATH_MAX]; /**< Path of the file. */
int f; /**< Handle of the files. */
struct stat st; /**< Stat info of the opened file. */
struct advise_struct advise; /**< Advise information. */
/**
* Size of the parity split.
* Only the latest not zero size is allowed to grow.
* Note that this value CANNOT be PARITY_SIZE_INVALID.
*/
data_off_t size;
/**
* Valid size of the parity split.
* This is the size effectively written, and not the result of a chsize operation.
* It's used to make read operations failing if read over that size.
*
* Parity is also truncated to that size when fixing it, in case of a Break (Ctrl+C)
* of the program.
*/
data_off_t valid_size;
/**
* Artificial size limit for testing.
* 0 means unlimited.
*/
data_off_t limit_size;
};
struct snapraid_parity_handle {
struct snapraid_split_handle split_map[SPLIT_MAX];
unsigned split_mac; /**< Number of parity splits. */
unsigned level; /**< Level of the parity. */
};
/**
* Compute the size of the allocated parity data in number of blocks.
*
* This includes parity blocks not yet written and still invalid.
*/
block_off_t parity_allocated_size(struct snapraid_state* state);
/**
* Compute the size of the used parity data in number of blocks.
*
* This includes only parity blocks used for files, not counting
* potential invalid parity at the end.
*
* If the array is fully synced there is no difference between
* parity_allocate_size() and parity_used_size().
* But if the sync is interrupted, the parity_used_size() returns
* the position of the latest BLK block, ignoring CHG, REL and DELETED ones,
* because their parity may be still not even written in the parity file.
*/
block_off_t parity_used_size(struct snapraid_state* state);
/**
* Check if the parity needs to be updated with a "sync".
*
* This is the same logic used in "status" to detect an incomplete "sync",
* that ignores invalid block, if they are not used by a file in any disk.
* This means that DELETED blocks won't necessarily imply an invalid parity.
*/
int parity_is_invalid(struct snapraid_state* state);
/**
* Report all the files outside the specified parity size.
*/
void parity_overflow(struct snapraid_state* state, data_off_t size);
/**
* Create the parity file.
* \param out_size Return the size of the parity file.
*/
int parity_create(struct snapraid_parity_handle* handle, const struct snapraid_parity* parity, unsigned level, int mode, uint32_t block_size, data_off_t limit_size);
/**
* Change the parity size.
* \param out_size Return the size of the parity file. The out_size is set also on error to reflect a partial resize.
*/
int parity_chsize(struct snapraid_parity_handle* handle, struct snapraid_parity* parity, int* is_modified, data_off_t size, uint32_t block_size, int skip_fallocate, int skip_space_holder);
/**
* Get the size of the parity.
*
* This returns the cached/expected version of the split sizes, and not the real file size.
*/
void parity_size(struct snapraid_parity_handle* handle, data_off_t* out_size);
/**
* Open an already existing parity file.
*/
int parity_open(struct snapraid_parity_handle* handle, const struct snapraid_parity* parity, unsigned level, int mode, uint32_t block_size, data_off_t limit_size);
/**
* Flush the parity file in the disk.
*/
int parity_sync(struct snapraid_parity_handle* handle);
/**
* Truncate the parity file to the valid size.
*/
int parity_truncate(struct snapraid_parity_handle* handle);
/**
* Close the parity file.
*/
int parity_close(struct snapraid_parity_handle* handle);
/**
* Read a block from the parity file.
*/
int parity_read(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size, fptr* out);
/**
* Write a block in the parity file.
*/
int parity_write(struct snapraid_parity_handle* handle, block_off_t pos, unsigned char* block_buffer, unsigned block_size);
#endif
snapraid-12.1/cmdline/pool.c 0000664 0000000 0000000 00000027235 14166610522 0016002 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "elem.h"
#include "state.h"
struct snapraid_pool {
char file[PATH_MAX];
char linkto[PATH_MAX];
int64_t mtime_sec;
int mtime_nsec;
/* nodes for data structures */
tommy_hashdyn_node node;
};
struct snapraid_pool* pool_alloc(const char* dir, const char* name, const char* linkto, const struct stat* st)
{
struct snapraid_pool* pool;
pool = malloc_nofail(sizeof(struct snapraid_pool));
pathprint(pool->file, sizeof(pool->file), "%s%s", dir, name);
pathcpy(pool->linkto, sizeof(pool->linkto), linkto);
pool->mtime_sec = st->st_mtime;
pool->mtime_nsec = STAT_NSEC(st);
return pool;
}
static inline tommy_uint32_t pool_hash(const char* file)
{
return tommy_hash_u32(0, file, strlen(file));
}
void pool_free(struct snapraid_pool* pool)
{
free(pool);
}
int pool_compare(const void* void_arg, const void* void_data)
{
const char* arg = void_arg;
const struct snapraid_pool* pool = void_data;
return strcmp(arg, pool->file);
}
/**
* Remove empty dir.
* Return == 0 if the directory is empty, and it can be removed
*/
static int clean_dir(const char* dir)
{
DIR* d;
int full = 0;
d = opendir(dir);
if (!d) {
/* LCOV_EXCL_START */
log_fatal("Error opening pool directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
while (1) {
char path_next[PATH_MAX];
struct stat st;
const char* name;
struct dirent* dd;
/* clear errno to detect erroneous conditions */
errno = 0;
dd = readdir(d);
if (dd == 0 && errno != 0) {
/* LCOV_EXCL_START */
log_fatal("Error reading pool directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (dd == 0 && errno == 0) {
break; /* finished */
}
/* skip "." and ".." files */
name = dd->d_name;
if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;
pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &st);
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows */
if (st.st_mode == 0) {
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#else
/* get lstat info about the file */
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
if (S_ISDIR(st.st_mode)) {
/* recurse */
pathslash(path_next, sizeof(path_next));
if (clean_dir(path_next) == 0) {
int ret;
/* directory is empty, try to remove it */
ret = rmdir(path_next);
if (ret < 0) {
#ifdef _WIN32
if (errno == EACCES) {
/* in Windows just ignore EACCES errors removing directories */
/* because it could happen that the directory is in use */
/* and it cannot be removed */
log_fatal("Directory '%s' not removed because it's in use.\n", path_next);
full = 1;
} else
#endif
{
/* LCOV_EXCL_START */
log_fatal("Error removing pool directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
} else {
/* something is present */
full = 1;
}
} else {
/* something is present */
full = 1;
}
}
if (closedir(d) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing pool directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return full;
}
/**
* Read all the links in a directory tree.
*/
static void read_dir(tommy_hashdyn* poolset, const char* base_dir, const char* sub_dir)
{
char dir[PATH_MAX];
DIR* d;
pathprint(dir, sizeof(dir), "%s%s", base_dir, sub_dir);
d = opendir(dir);
if (!d) {
/* LCOV_EXCL_START */
log_fatal("Error opening pool directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
while (1) {
char path_next[PATH_MAX];
struct stat st;
const char* name;
struct dirent* dd;
/* clear errno to detect erroneous conditions */
errno = 0;
dd = readdir(d);
if (dd == 0 && errno != 0) {
/* LCOV_EXCL_START */
log_fatal("Error reading pool directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (dd == 0 && errno == 0) {
break; /* finished */
}
/* skip "." and ".." files */
name = dd->d_name;
if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;
pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &st);
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows */
if (st.st_mode == 0) {
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#else
/* get lstat info about the file */
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
if (S_ISLNK(st.st_mode)) {
struct snapraid_pool* pool;
char linkto[PATH_MAX];
int ret;
ret = readlink(path_next, linkto, sizeof(linkto));
if (ret < 0 || ret >= PATH_MAX) {
/* LCOV_EXCL_START */
log_fatal("Error in readlink symlink '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
linkto[ret] = 0;
/* store the link info */
pool = pool_alloc(sub_dir, name, linkto, &st);
tommy_hashdyn_insert(poolset, &pool->node, pool, pool_hash(pool->file));
} else if (S_ISDIR(st.st_mode)) {
pathprint(path_next, sizeof(path_next), "%s%s/", sub_dir, name);
read_dir(poolset, base_dir, path_next);
} else {
msg_verbose("Ignoring pool file '%s'\n", path_next);
}
}
if (closedir(d) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing pool directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/**
* Remove the link
*/
static void remove_link(void* void_arg, void* void_pool)
{
char path[PATH_MAX];
const char* arg = void_arg;
struct snapraid_pool* pool = void_pool;
int ret;
pathprint(path, sizeof(path), "%s%s", arg, pool->file);
/* delete the link */
ret = remove(path);
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Error removing symlink '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/**
* Create a link to the specified disk link.
*/
static void make_link(tommy_hashdyn* poolset, const char* pool_dir, const char* share_dir, struct snapraid_disk* disk, const char* sub, int64_t mtime_sec, int mtime_nsec)
{
char path[PATH_MAX];
char linkto[PATH_MAX];
char linkto_exported[PATH_MAX];
struct snapraid_pool* found;
int ret;
/* make the source path */
pathprint(path, sizeof(path), "%s%s", pool_dir, sub);
/* make the linkto path */
if (share_dir[0] != 0) {
/* with a shared directory, use it */
pathprint(linkto, sizeof(linkto), "%s%s/%s", share_dir, disk->name, sub);
} else {
/* without a share directory, use the local disk paths */
pathprint(linkto, sizeof(linkto), "%s%s", disk->dir, sub);
}
/* search for the sub path */
found = tommy_hashdyn_search(poolset, pool_compare, sub, pool_hash(sub));
if (found) {
/* remove from the set */
tommy_hashdyn_remove_existing(poolset, &found->node);
/* check if the info match */
if (found->mtime_sec == mtime_sec
&& found->mtime_nsec == mtime_nsec
&& strcmp(found->linkto, linkto) == 0
) {
/* nothing to do */
pool_free(found);
return;
}
/* delete the link */
ret = remove(path);
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Error removing symlink '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
pool_free(found);
}
/* create the ancestor directories */
ret = mkancestor(path);
if (ret != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* convert back slashes */
pathexport(linkto_exported, sizeof(linkto_exported), linkto);
/* create the symlink */
ret = symlink(linkto_exported, path);
if (ret != 0) {
if (errno == EEXIST) {
log_fatal("WARNING! Duplicate pooling for '%s'\n", path);
#ifdef _WIN32
} else if (errno == EPERM) {
/* LCOV_EXCL_START */
log_fatal("You must run as Administrator to be able to create symlinks.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
#endif
} else {
/* LCOV_EXCL_START */
log_fatal("Error writing symlink '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
if (mtime_sec) {
ret = lmtime(path, mtime_sec, mtime_nsec);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error setting time to symlink '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
void state_pool(struct snapraid_state* state)
{
tommy_hashdyn poolset;
tommy_node* i;
char pool_dir[PATH_MAX];
char share_dir[PATH_MAX];
unsigned count;
tommy_hashdyn_init(&poolset);
if (state->pool[0] == 0) {
/* LCOV_EXCL_START */
log_fatal("To use the 'pool' command you must set the pool directory in the configuration file\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
msg_progress("Reading...\n");
/* pool directory with final slash */
pathprint(pool_dir, sizeof(pool_dir), "%s", state->pool);
pathslash(pool_dir, sizeof(pool_dir));
/* share directory with final slash */
pathprint(share_dir, sizeof(share_dir), "%s", state->share);
pathslash(share_dir, sizeof(share_dir));
/* first read the previous pool tree */
read_dir(&poolset, pool_dir, "");
msg_progress("Writing...\n");
/* for each disk */
count = 0;
for (i = state->disklist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_disk* disk = i->data;
/* for each file */
for (j = disk->filelist; j != 0; j = j->next) {
struct snapraid_file* file = j->data;
make_link(&poolset, pool_dir, share_dir, disk, file->sub, file->mtime_sec, file->mtime_nsec);
++count;
}
/* for each link */
for (j = disk->linklist; j != 0; j = j->next) {
struct snapraid_link* slink = j->data;
make_link(&poolset, pool_dir, share_dir, disk, slink->sub, 0, 0);
++count;
}
/* we ignore empty dirs in disk->dir */
}
msg_progress("Cleaning...\n");
/* delete all the remaining links */
tommy_hashdyn_foreach_arg(&poolset, (tommy_foreach_arg_func*)remove_link, pool_dir);
/* delete empty dirs */
clean_dir(pool_dir);
tommy_hashdyn_foreach(&poolset, (tommy_foreach_func*)pool_free);
tommy_hashdyn_done(&poolset);
if (count)
msg_status("%u links\n", count);
else
msg_status("No link\n");
log_tag("summary:link_count::%u\n", count);
log_tag("summary:exit:ok\n");
log_flush();
}
snapraid-12.1/cmdline/portable.h 0000664 0000000 0000000 00000026430 14166610522 0016642 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __PORTABLE_H
#define __PORTABLE_H
#if HAVE_CONFIG_H
#include "config.h" /* Use " to include first in the same directory of this file */
#endif
/***************************************************************************/
/* Config */
#ifdef __MINGW32__
/**
* Enable the GNU printf functions instead of using the MSVCRT ones.
*
* Note that this is the default if _POSIX is also defined.
* To disable it you have to set it to 0.
*/
#define __USE_MINGW_ANSI_STDIO 1
/**
* Define the MSVCRT version targeting Windows Vista.
*/
#define __MSVCRT_VERSION__ 0x0600
/**
* Include Windows Vista headers.
*
* Like for InitializeCriticalSection().
*/
#define _WIN32_WINNT 0x600
/**
* Enable the rand_s() function.l
*/
#define _CRT_RAND_S
#include
#endif
/**
* Specify the format attribute for printf.
*/
#ifdef __MINGW32__
#if defined(__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO == 1
#define attribute_printf gnu_printf /* GNU format */
#else
#define attribute_printf ms_printf /* MSVCRT format */
#endif
#else
#define attribute_printf printf /* GNU format is the default one */
#endif
/**
* Compiler extension
*/
#ifndef __always_inline
#define __always_inline inline __attribute__((always_inline))
#endif
#ifndef __noreturn
#define __noreturn __attribute__((noreturn))
#endif
/**
* Architecture for inline assembly.
*/
#if HAVE_ASSEMBLY
#if defined(__i386__)
#define CONFIG_X86 1
#define CONFIG_X86_32 1
#endif
#if defined(__x86_64__)
#define CONFIG_X86 1
#define CONFIG_X86_64 1
#endif
#endif
/**
* Includes some platform specific headers.
*/
#if HAVE_SYS_PARAM_H
#include
#endif
#if HAVE_SYS_MOUNT_H
#include
#endif
#if HAVE_SYS_VFS_H
#include
#endif
#if HAVE_SYS_STATFS_H
#include
#endif
#if HAVE_SYS_FILE_H
#include
#endif
#if HAVE_SYS_IOCTL_H
#include
#endif
#if HAVE_LINUX_FS_H
#include
#endif
#if HAVE_LINUX_FIEMAP_H
#include
#endif
#if HAVE_BLKID_BLKID_H
#include
#if HAVE_BLKID_DEVNO_TO_DEVNAME && HAVE_BLKID_GET_TAG_VALUE
#define HAVE_BLKID 1
#endif
#endif
/**
* Includes some standard headers.
*/
#include
#include /* On many systems (e.g., Darwin), `stdio.h' is a prerequisite. */
#include
#include
#include
#include
#include
#include
#include
#include
#if HAVE_STDINT_H
#include
#endif
#if HAVE_INTTYPES_H
#include
#endif
#if HAVE_UNISTD_H
#include
#endif
#if TIME_WITH_SYS_TIME
#include
#include
#else
#if HAVE_SYS_TIME_H
#include
#else
#include
#endif
#endif
#if HAVE_MACH_MACH_TIME_H
#include
#endif
#if HAVE_DIRENT_H
#include
#define NAMLEN(dirent) strlen((dirent)->d_name)
#else
#define dirent direct
#define NAMLEN(dirent) (dirent)->d_namlen
#if HAVE_SYS_NDIR_H
#include
#endif
#if HAVE_SYS_DIR_H
#include
#endif
#if HAVE_NDIR_H
#include
#endif
#endif
#if HAVE_SYS_TYPES_H
#include
#endif
#if HAVE_SYS_MKDEV
#include
#endif
#if HAVE_SYS_SYSMACROS_H
#include
#endif
#if HAVE_SYS_STAT_H
#include
#endif
#if HAVE_SYS_WAIT_H
#include
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif
#if HAVE_GETOPT_H
#include
#endif
#if HAVE_FNMATCH_H
#include
#else
#include "fnmatch.h"
#endif
#if HAVE_MATH_H
#include
#endif
#if HAVE_EXECINFO_H
#include
#endif
/**
* Enable thread use.
*/
#ifdef _WIN32
#define HAVE_THREAD 1
typedef void* windows_thread_t;
typedef CRITICAL_SECTION windows_mutex_t;
typedef CONDITION_VARIABLE windows_cond_t;
typedef void* windows_key_t;
/* remap to pthread */
#define thread_id_t windows_thread_t
#define thread_mutex_t windows_mutex_t
#define thread_cond_t windows_cond_t
#define pthread_mutex_init windows_mutex_init
#define pthread_mutex_destroy windows_mutex_destroy
#define pthread_mutex_lock windows_mutex_lock
#define pthread_mutex_unlock windows_mutex_unlock
#define pthread_cond_init windows_cond_init
#define pthread_cond_destroy windows_cond_destroy
#define pthread_cond_signal windows_cond_signal
#define pthread_cond_broadcast windows_cond_broadcast
#define pthread_cond_wait windows_cond_wait
#define pthread_create windows_create
#define pthread_join windows_join
#else
#if HAVE_PTHREAD_H
#include
#endif
#if HAVE_PTHREAD_CREATE
#define HAVE_THREAD 1
typedef pthread_t thread_id_t;
typedef pthread_mutex_t thread_mutex_t;
typedef pthread_cond_t thread_cond_t;
#endif
#endif
/**
* Disable case check in Windows.
*/
#ifdef _WIN32
#define FNM_CASEINSENSITIVE_FOR_WIN FNM_CASEFOLD
#else
#define FNM_CASEINSENSITIVE_FOR_WIN 0
#endif
#if HAVE_IO_H
#include
#endif
#if HAVE_GETOPT_LONG
#define SWITCH_GETOPT_LONG(a, b) a
#else
#define SWITCH_GETOPT_LONG(a, b) b
#endif
/**
* Enables lock file support.
*/
#if HAVE_FLOCK && HAVE_FTRUNCATE
#define HAVE_LOCKFILE 1
#endif
/**
* Basic block position type.
* With 32 bits and 128k blocks you can address 256 TB.
*/
typedef uint32_t block_off_t;
/**
* Basic data position type.
* It's signed as file size and offset are usually signed.
*/
typedef int64_t data_off_t;
/**
* Includes specific support for Windows or Linux.
*/
#ifdef __MINGW32__
#include "mingw.h"
#else
#include "unix.h"
#endif
/**
* Include list support to have tommy_node.
*/
#include "tommyds/tommylist.h"
/**
* Another name for link() to avoid confusion with local variables called "link".
*/
static inline int hardlink(const char* a, const char* b)
{
return link(a, b);
}
/**
* Get the device UUID.
* Return 0 on success.
*/
int devuuid(uint64_t device, char* uuid, size_t size);
/**
* Physical offset not yet read.
*/
#define FILEPHY_UNREAD_OFFSET 0
/**
* Special value returned when the file-system doesn't report any offset for unknown reason.
*/
#define FILEPHY_UNREPORTED_OFFSET 1
/**
* Special value returned when the file doesn't have a real offset.
* For example, because it's stored in the NTFS MFT.
*/
#define FILEPHY_WITHOUT_OFFSET 2
/**
* Value indicating real offsets. All offsets greater or equal at this one are real.
*/
#define FILEPHY_REAL_OFFSET 3
/**
* Get the physcal address of the specified file.
* This is expected to be just a hint and not necessarily correct or unique.
* Return 0 on success.
*/
int filephy(const char* path, uint64_t size, uint64_t* physical);
/**
* Check if the underline file-system support persistent inodes.
* Return -1 on error, 0 on success.
*/
int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_hardlinks, uint64_t* total_space, uint64_t* free_space);
/**
* Get the tick counter value.
*
* Note that the frequency is unspecified, because the time measure
* is meant to be used to compare the ratio between usage times.
*/
uint64_t tick(void);
/**
* Get the tick counter value in millisecond.
*/
uint64_t tick_ms(void);
/**
* Initializes the system.
*/
void os_init(int opt);
/**
* Deinitialize the system.
*/
void os_done(void);
/**
* Abort the process with a stacktrace.
*/
void os_abort(void) __noreturn;
/**
* Clear the screen.
*/
void os_clear(void);
/**
* Log file.
*
* This stream if fully buffered.
*
* If no log file is selected, it's 0.
*/
extern FILE* stdlog;
/**
* Exit codes for testing.
*/
extern int exit_success;
extern int exit_failure;
extern int exit_sync_needed;
#undef EXIT_SUCCESS
#undef EXIT_FAILURE
#define EXIT_SUCCESS exit_success
#define EXIT_FAILURE exit_failure
#define EXIT_SYNC_NEEDED exit_sync_needed
/**
* Fill memory with pseudo-random values.
*/
int randomize(void* ptr, size_t size);
/**
* Standard SMART attributes.
*/
#define SMART_START_STOP_COUNT 4
#define SMART_REALLOCATED_SECTOR_COUNT 5
#define SMART_POWER_ON_HOURS 9
#define SMART_AIRFLOW_TEMPERATURE_CELSIUS 190
#define SMART_LOAD_CYCLE_COUNT 193
#define SMART_TEMPERATURE_CELSIUS 194
/**
* Additional SMART attributes.
*/
#define SMART_ERROR 256 /**< ATA Error count. */
#define SMART_SIZE 257 /**< Size in bytes. */
#define SMART_ROTATION_RATE 258 /**< Rotation speed. 0 for SSD. */
#define SMART_FLAGS 259 /**< Flags returned by smartctl. */
/**
* SMART attributes count.
*/
#define SMART_COUNT 260
/**
* Flags returned by smartctl.
*/
#define SMARTCTL_FLAG_UNSUPPORTED (1 << 0) /**< Device not recognized, requiring the -d option. */
#define SMARTCTL_FLAG_OPEN (1 << 1) /**< Device open or identification failed. */
#define SMARTCTL_FLAG_COMMAND (1 << 2) /**< Some SMART or ATA commands failed. This is a common error, also happening with full info gathering. */
#define SMARTCTL_FLAG_FAIL (1 << 3) /**< SMART status check returned "DISK FAILING". */
#define SMARTCTL_FLAG_PREFAIL (1 << 4) /**< We found prefail Attributes <= threshold. */
#define SMARTCTL_FLAG_PREFAIL_LOGGED (1 << 5) /**< SMART status check returned "DISK OK" but we found that some (usage or prefail) Attributes have been <= threshold at some time in the past. */
#define SMARTCTL_FLAG_ERROR (1 << 6) /**< The device error log contains records of errors. */
#define SMARTCTL_FLAG_ERROR_LOGGED (1 << 7) /**< The device self-test log contains records of errors. */
/**
* SMART max attribute length.
*/
#define SMART_MAX 64
/**
* Value for unassigned SMART attribute.
*/
#define SMART_UNASSIGNED 0xFFFFFFFFFFFFFFFFULL
/**
* Device info entry.
*/
struct devinfo_struct {
uint64_t device; /**< Device ID. */
char name[PATH_MAX]; /**< Name of the disk. */
char mount[PATH_MAX]; /**< Mount point or other contained directory. */
char smartctl[PATH_MAX]; /**< Options for smartctl. */
char file[PATH_MAX]; /**< File device. */
#ifdef _WIN32
char wfile[PATH_MAX]; /**< File device in Windows format. Like \\.\PhysicalDriveX, or \\?\Volume{X}. */
#endif
struct devinfo_struct* parent; /**< Pointer at the parent if any. */
uint64_t smart[SMART_COUNT]; /**< SMART raw attributes. */
char smart_serial[SMART_MAX]; /**< SMART serial number. */
char smart_vendor[SMART_MAX]; /**< SMART vendor. */
char smart_model[SMART_MAX]; /**< SMART model. */
#if HAVE_THREAD
thread_id_t thread;
#endif
tommy_node node;
};
typedef struct devinfo_struct devinfo_t;
#define DEVICE_LIST 0
#define DEVICE_DOWN 1
#define DEVICE_UP 2
#define DEVICE_SMART 3
/**
* Query all the "high" level devices with the specified operation,
* and produces a list of "low" level devices to operate on.
*
* The passed "low" device list must be already initialized.
*/
int devquery(tommy_list* high, tommy_list* low, int operation, int others);
#endif
snapraid-12.1/cmdline/rehash.c 0000664 0000000 0000000 00000005122 14166610522 0016272 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "util.h"
#include "elem.h"
#include "import.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
#include "raid/raid.h"
/****************************************************************************/
/* rehash */
void state_rehash(struct snapraid_state* state)
{
block_off_t blockmax;
block_off_t i;
blockmax = parity_allocated_size(state);
/* check if a rehash is already in progress */
if (state->prevhash != HASH_UNDEFINED) {
/* LCOV_EXCL_START */
log_fatal("You already have a rehash in progress.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (state->hash == state->besthash) {
/* LCOV_EXCL_START */
log_fatal("You are already using the best hash for your platform.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* copy the present hash as previous one */
state->prevhash = state->hash;
memcpy(state->prevhashseed, state->hashseed, HASH_MAX);
/* set the new hash and seed */
state->hash = state->besthash;
if (randomize(state->hashseed, HASH_MAX) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to get random values.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* mark all the block for rehashing */
for (i = 0; i < blockmax; ++i) {
snapraid_info info;
/* if it's unused */
info = info_get(&state->infoarr, i);
if (info == 0) {
/* skip it */
continue;
}
if (info_get_rehash(info)) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for a rehash already in progress\n");
os_abort();
/* LCOV_EXCL_STOP */
}
/* enable the rehash */
info = info_set_rehash(info);
/* save it */
info_set(&state->infoarr, i, info);
}
/* save the new content file */
state->need_write = 1;
msg_status("A rehash is now scheduled. It will take place progressively in the next\n");
msg_status("'sync' and 'scrub' commands. You can check the rehash progress using the\n");
msg_status("'status' command.\n");
}
snapraid-12.1/cmdline/scan.c 0000664 0000000 0000000 00000171337 14166610522 0015760 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
struct snapraid_scan {
struct snapraid_state* state; /**< State used. */
struct snapraid_disk* disk; /**< Disk used. */
thread_id_t thread; /**< Thread used for scanning the disk */
int is_diff; /**< If it's a diff command or a scanning */
int need_write; /**< If a state write is required */
#if HAVE_THREAD
/**
* Mutex for protecting the disk stampset table
*/
thread_mutex_t mutex;
#endif
/**
* Counters of changes.
*/
unsigned count_equal; /**< Files equal. */
unsigned count_move; /**< Files with a different name, but equal inode, size and timestamp in the same disk. */
unsigned count_restore; /**< Files with equal name, size and timestamp, but different inode. */
unsigned count_change; /**< Files with same name, but different size and/or timestamp. */
unsigned count_copy; /**< Files new, with same name size and timestamp of a file in a different disk. */
unsigned count_insert; /**< Files new. */
unsigned count_remove; /**< Files removed. */
tommy_list file_insert_list; /**< Files to insert. */
tommy_list link_insert_list; /**< Links to insert. */
tommy_list dir_insert_list; /**< Dirs to insert. */
/* nodes for data structures */
tommy_node node;
};
static struct snapraid_scan* scan_alloc(struct snapraid_state* state, struct snapraid_disk* disk, int is_diff)
{
struct snapraid_scan* scan;
scan = malloc_nofail(sizeof(struct snapraid_scan));
scan->state = state;
scan->disk = disk;
scan->count_equal = 0;
scan->count_move = 0;
scan->count_copy = 0;
scan->count_restore = 0;
scan->count_change = 0;
scan->count_remove = 0;
scan->count_insert = 0;
tommy_list_init(&scan->file_insert_list);
tommy_list_init(&scan->link_insert_list);
tommy_list_init(&scan->dir_insert_list);
scan->is_diff = is_diff;
scan->need_write = 0;
#if HAVE_THREAD
thread_mutex_init(&disk->stamp_mutex);
#endif
return scan;
}
static void scan_free(struct snapraid_scan* scan)
{
#if HAVE_THREAD
thread_mutex_destroy(&scan->disk->stamp_mutex);
#endif
free(scan);
}
static void stamp_lock(struct snapraid_disk* disk)
{
#if HAVE_THREAD
thread_mutex_lock(&disk->stamp_mutex);
#else
(void)disk;
#endif
}
static void stamp_unlock(struct snapraid_disk* disk)
{
#if HAVE_THREAD
thread_mutex_unlock(&disk->stamp_mutex);
#else
(void)disk;
#endif
}
/**
* Remove the specified link from the data set.
*/
static void scan_link_remove(struct snapraid_scan* scan, struct snapraid_link* slink)
{
struct snapraid_disk* disk = scan->disk;
/* state changed */
scan->need_write = 1;
/* remove the file from the link containers */
tommy_hashdyn_remove_existing(&disk->linkset, &slink->nodeset);
tommy_list_remove_existing(&disk->linklist, &slink->nodelist);
/* deallocate */
link_free(slink);
}
/**
* Insert the specified link in the data set.
*/
static void scan_link_insert(struct snapraid_scan* scan, struct snapraid_link* slink)
{
struct snapraid_disk* disk = scan->disk;
/* state changed */
scan->need_write = 1;
/* insert the link in the link containers */
tommy_hashdyn_insert(&disk->linkset, &slink->nodeset, slink, link_name_hash(slink->sub));
tommy_list_insert_tail(&disk->linklist, &slink->nodelist, slink);
}
/**
* Process a symbolic link.
*/
static void scan_link(struct snapraid_scan* scan, int is_diff, const char* sub, const char* linkto, unsigned link_flag)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
struct snapraid_link* slink;
char esc_buffer[ESC_MAX];
/* check if the link already exists */
slink = tommy_hashdyn_search(&disk->linkset, link_name_compare_to_arg, sub, link_name_hash(sub));
if (slink) {
/* check if multiple files have the same name */
if (link_flag_has(slink, FILE_IS_PRESENT)) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for link '%s%s'\n", disk->dir, sub);
os_abort();
/* LCOV_EXCL_STOP */
}
/* mark as present */
link_flag_set(slink, FILE_IS_PRESENT);
/* check if the link is not changed and it's of the same kind */
if (strcmp(slink->linkto, linkto) == 0 && link_flag == link_flag_get(slink, FILE_IS_LINK_MASK)) {
/* it's equal */
++scan->count_equal;
if (state->opt.gui) {
log_tag("scan:equal:%s:%s\n", disk->name, esc_tag(slink->sub, esc_buffer));
}
} else {
/* it's an update */
/* we have to save the linkto/type */
scan->need_write = 1;
++scan->count_change;
log_tag("scan:update:%s:%s\n", disk->name, esc_tag(slink->sub, esc_buffer));
if (is_diff) {
msg_info("update %s\n", fmt_term(disk, slink->sub, esc_buffer));
}
/* update it */
free(slink->linkto);
slink->linkto = strdup_nofail(linkto);
link_flag_let(slink, link_flag, FILE_IS_LINK_MASK);
}
/* nothing more to do */
return;
} else {
/* create the new link */
++scan->count_insert;
log_tag("scan:add:%s:%s\n", disk->name, esc_tag(sub, esc_buffer));
if (is_diff) {
msg_info("add %s\n", fmt_term(disk, sub, esc_buffer));
}
/* and continue to insert it */
}
/* insert it */
slink = link_alloc(sub, linkto, link_flag);
/* mark it as present */
link_flag_set(slink, FILE_IS_PRESENT);
/* insert it in the delayed insert list */
tommy_list_insert_tail(&scan->link_insert_list, &slink->nodelist, slink);
}
/**
* Insert the specified file in the parity.
*/
static void scan_file_allocate(struct snapraid_scan* scan, struct snapraid_file* file)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
block_off_t i;
block_off_t parity_pos;
/* state changed */
scan->need_write = 1;
/* allocate the blocks of the file */
parity_pos = disk->first_free_block;
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_block* block;
struct snapraid_block* over_block;
snapraid_info info;
/* increment the position until the first really free block */
while (block_has_file(fs_par2block_find(disk, parity_pos)))
++parity_pos;
/* get block we are going to overwrite, if any */
over_block = fs_par2block_find(disk, parity_pos);
/* deallocate it */
if (over_block != BLOCK_NULL)
fs_deallocate(disk, parity_pos);
/* get block specific info */
info = info_get(&state->infoarr, parity_pos);
/* get the new block we are going to write */
block = fs_file2block_get(file, i);
/* if the file block already has an updated hash without rehash */
if (block_has_updated_hash(block) && !info_get_rehash(info)) {
/* the only possible case is for REP blocks */
assert(block_state_get(block) == BLOCK_STATE_REP);
/* convert to a REP block */
block_state_set(block, BLOCK_STATE_REP);
/* and keep the hash as it's */
} else {
unsigned over_state;
/* convert to a CHG block */
block_state_set(block, BLOCK_STATE_CHG);
/* state of the block we are going to overwrite */
over_state = block_state_get(over_block);
/* if the block is an empty one */
if (over_state == BLOCK_STATE_EMPTY) {
/* the block was empty and filled with zeros */
/* set the hash to the special ZERO value */
hash_zero_set(block->hash);
} else {
/* otherwise it's a DELETED one */
assert(over_state == BLOCK_STATE_DELETED);
/* copy the past hash of the block */
memcpy(block->hash, over_block->hash, BLOCK_HASH_SIZE);
/* if we have not already cleared the past hash */
if (!state->clear_past_hash) {
/* in this case we don't know if the old state is still the one */
/* stored inside the parity, because after an aborted sync, the parity */
/* may be or may be not have been updated with the new data */
/* Then we reset the hash to a bogus value */
/* For example: */
/* - One file is deleted */
/* - Sync aborted after, updating the parity to the new state, */
/* but without saving the content file representing this new state. */
/* - Another file is added again (exactly here) */
/* with the hash of DELETED block not representing the real parity state */
hash_invalid_set(block->hash);
}
}
}
/* store in the disk map, after invalidating all the other blocks */
fs_allocate(disk, parity_pos, file, i);
/* set the new free position */
disk->first_free_block = parity_pos + 1;
}
/* insert in the list of contained files */
tommy_list_insert_tail(&disk->filelist, &file->nodelist, file);
}
/**
* Delete the specified file from the parity.
*
* Note that the parity remains allocated, but the blocks and the file are marked as DELETED.
* The file is then inserted in the deleted set, and it should not be deallocated,
* as the parity still references it.
*/
static void scan_file_deallocate(struct snapraid_scan* scan, struct snapraid_file* file)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
block_off_t i;
/* remove from the list of contained files */
tommy_list_remove_existing(&disk->filelist, &file->nodelist);
/* state changed */
scan->need_write = 1;
/* here we are supposed to adjust the ::first_free_block position */
/* with the parity position we are deleting */
/* but we also know that we do only delayed insert, after all the deletion, */
/* so at this point ::first_free_block is always at 0, and we don't need to update it */
if (disk->first_free_block != 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for first free position at '%u' deallocating file '%s'\n", disk->first_free_block, file->sub);
os_abort();
/* LCOV_EXCL_STOP */
}
/* free all the blocks of the file */
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_block* block = fs_file2block_get(file, i);
unsigned block_state;
/* in case we scan after an aborted sync, */
/* we could get also intermediate states */
block_state = block_state_get(block);
switch (block_state) {
case BLOCK_STATE_BLK :
/* we keep the hash making it an "old" hash, because the parity is still containing data for it */
break;
case BLOCK_STATE_CHG :
/* if we have not already cleared the past hash */
if (!state->clear_past_hash) {
/* in these cases we don't know if the old state is still the one */
/* stored inside the parity, because after an aborted sync, the parity */
/* may be or may be not have been updated with the data that it's now */
/* deleted. Then we reset the hash to a bogus value. */
/* For example: */
/* - One file is added */
/* - Sync aborted after updating the parity to the new state, */
/* but without saving the content file representing this new state. */
/* - File is now deleted after the aborted sync */
/* - Sync again, deleting the blocks (exactly here) */
/* with the hash of CHG block not representing the real parity state */
hash_invalid_set(block->hash);
}
break;
case BLOCK_STATE_REP :
/* we just don't know the old hash, and then we set it to invalid */
hash_invalid_set(block->hash);
break;
default :
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file '%s' deallocating block '%u:%u' state %u\n", file->sub, i, file->blockmax, block_state);
os_abort();
/* LCOV_EXCL_STOP */
}
/* set the block as deleted */
block_state_set(block, BLOCK_STATE_DELETED);
}
/* mark the file as deleted */
file_flag_set(file, FILE_IS_DELETED);
/* insert it in the list of deleted blocks */
tommy_list_insert_tail(&disk->deletedlist, &file->nodelist, file);
}
static void scan_file_delayed_allocate(struct snapraid_scan* scan, struct snapraid_file* file)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
/* if we sort for physical offsets we have to read them for new files */
if (state->opt.force_order == SORT_PHYSICAL
&& file->physical == FILEPHY_UNREAD_OFFSET
) {
char path_next[PATH_MAX];
pathprint(path_next, sizeof(path_next), "%s%s", disk->dir, file->sub);
if (filephy(path_next, file->size, &file->physical) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in getting the physical offset of file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* insert in the delayed list */
tommy_list_insert_tail(&scan->file_insert_list, &file->nodelist, file);
}
/**
* Check if a file is completely formed of blocks with invalid parity,
* and no rehash is tagged, and if it has at least one block.
*/
static int file_is_full_invalid_parity_and_stable(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
{
block_off_t i;
/* with no block, it never has an invalid parity */
if (file->blockmax == 0)
return 0;
/* check all blocks */
for (i = 0; i < file->blockmax; ++i) {
snapraid_info info;
struct snapraid_block* block = fs_file2block_get(file, i);
block_off_t parity_pos;
/* exclude blocks with parity */
if (!block_has_invalid_parity(block))
return 0;
/*
* Get the parity position.
*
* Note that here we expect to always have mapped
* parity, because kept files always have it.
*
* Anyway, checking for POS_NULL doesn't hurt.
*/
parity_pos = fs_file2par_find(disk, file, i);
/* if it's not mapped, it cannot have rehash */
if (parity_pos != POS_NULL) {
/* get block specific info */
info = info_get(&state->infoarr, parity_pos);
/* if rehash fails */
if (info_get_rehash(info))
return 0;
}
}
return 1;
}
/**
* Check if a file is completely formed of blocks with an updated hash,
* and no rehash is tagged, and if it has at least one block.
*/
static int file_is_full_hashed_and_stable(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
{
block_off_t i;
/* with no block, it never has a hash */
if (file->blockmax == 0)
return 0;
/* check all blocks */
for (i = 0; i < file->blockmax; ++i) {
snapraid_info info;
struct snapraid_block* block = fs_file2block_get(file, i);
block_off_t parity_pos;
/* exclude blocks without hash */
if (!block_has_updated_hash(block))
return 0;
/*
* Get the parity position.
*
* Note that it's possible to have files
* not mapped into the parity, even if they
* have a valid hash.
*
* This happens for example, for 'copied' files
* that have REP blocks, but not yet mapped.
*
* If there are multiple copies, it's also possible
* that such files are used as 'source' to copy
* hashes, and then to get them inside this function.
*/
parity_pos = fs_file2par_find(disk, file, i);
/* if it's not mapped, it cannot have rehash */
if (parity_pos != POS_NULL) {
/* get block specific info */
info = info_get(&state->infoarr, parity_pos);
/* exclude blocks needing a rehash */
if (info_get_rehash(info))
return 0;
}
}
return 1;
}
/**
* Refresh the file info.
*
* This is needed by Windows as the normal way to list directories may report not
* updated info. Only the GetFileInformationByHandle() func, called file-by-file,
* really ensures to return synced info.
*
* If this happens, we read also the physical offset, to avoid to read it later.
*/
static void scan_file_refresh(struct snapraid_scan* scan, const char* sub, struct stat* st, uint64_t* physical)
{
#if HAVE_LSTAT_SYNC
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
/* if the st_sync is not set, ensure to get synced info */
if (st->st_sync == 0) {
char path_next[PATH_MAX];
struct stat synced_st;
pathprint(path_next, sizeof(path_next), "%s%s", disk->dir, sub);
/* if we sort for physical offsets we have to read them for new files */
if (state->opt.force_order == SORT_PHYSICAL
&& *physical == FILEPHY_UNREAD_OFFSET
) {
/* do nothing, leave the pointer to read the physical offset */
} else {
physical = 0; /* set the pointer to 0 to read nothing */
}
if (lstat_sync(path_next, &synced_st, physical) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (st->st_mtime != synced_st.st_mtime
|| st->st_mtimensec != synced_st.st_mtimensec
) {
#ifndef _WIN32
/*
* In Windows having different metadata is expected with open files
* because the metadata in the directory is updated only when the file
* is closed.
*
* The same happens for hardlinks that duplicate metadata.
* The link metadata is updated only when the link is opened.
* This extends also to st_size and st_nlink.
*
* See also:
* Why is the file size reported incorrectly for files that are still being written to?
* http://blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx
*/
log_fatal("WARNING! Detected uncached time change from %" PRIu64 ".%09u to %" PRIu64 ".%09u for file '%s'\n",
(uint64_t)st->st_mtime, (uint32_t)st->st_mtimensec, (uint64_t)synced_st.st_mtime, (uint32_t)synced_st.st_mtimensec, sub);
log_fatal("It's better if you run SnapRAID without other processes running.\n");
#endif
st->st_mtime = synced_st.st_mtime;
st->st_mtimensec = synced_st.st_mtimensec;
}
if (st->st_size != synced_st.st_size) {
#ifndef _WIN32
log_fatal("WARNING! Detected uncached size change from %" PRIu64 " to %" PRIu64 " for file '%s'\n",
(uint64_t)st->st_size, (uint64_t)synced_st.st_size, sub);
log_fatal("It's better if you run SnapRAID without other processes running.\n");
#endif
st->st_size = synced_st.st_size;
}
if (st->st_nlink != synced_st.st_nlink) {
#ifndef _WIN32
log_fatal("WARNING! Detected uncached nlink change from %u to %u for file '%s'\n",
(uint32_t)st->st_nlink, (uint32_t)synced_st.st_nlink, sub);
log_fatal("It's better if you run SnapRAID without other processes running.\n");
#endif
st->st_nlink = synced_st.st_nlink;
}
if (st->st_ino != synced_st.st_ino) {
log_fatal("DANGER! Detected uncached inode change from %" PRIu64 " to %" PRIu64 " for file '%s'\n",
(uint64_t)st->st_ino, (uint64_t)synced_st.st_ino, sub);
log_fatal("It's better if you run SnapRAID without other processes running.\n");
/* at this point, it's too late to change inode */
/* and having inconsistent inodes may result to internal failures */
/* so, it's better to abort */
exit(EXIT_FAILURE);
}
}
#else
(void)scan;
(void)sub;
(void)st;
(void)physical;
#endif
}
/**
* Insert the file in the data set.
*/
static void scan_file_insert(struct snapraid_scan* scan, struct snapraid_file* file)
{
struct snapraid_disk* disk = scan->disk;
/* insert the file in the containers */
if (!file_flag_has(file, FILE_IS_WITHOUT_INODE))
tommy_hashdyn_insert(&disk->inodeset, &file->nodeset, file, file_inode_hash(file->inode));
stamp_lock(disk);
tommy_hashdyn_insert(&disk->pathset, &file->pathset, file, file_path_hash(file->sub));
tommy_hashdyn_insert(&disk->stampset, &file->stampset, file, file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec));
stamp_unlock(disk);
/* delayed allocation of the parity */
scan_file_delayed_allocate(scan, file);
}
/**
* Remove the file from the data set.
*
* File is then deleted.
*/
static void scan_file_remove(struct snapraid_scan* scan, struct snapraid_file* file)
{
struct snapraid_disk* disk = scan->disk;
/* remove the file from the containers */
if (!file_flag_has(file, FILE_IS_WITHOUT_INODE))
tommy_hashdyn_remove_existing(&disk->inodeset, &file->nodeset);
tommy_hashdyn_remove_existing(&disk->pathset, &file->pathset);
stamp_lock(disk);
tommy_hashdyn_remove_existing(&disk->stampset, &file->stampset);
stamp_unlock(disk);
/* deallocate the file from the parity */
scan_file_deallocate(scan, file);
}
/**
* Keep the file as it's (or with only a name/inode modification).
*
* If the file is kept, nothing has to be done.
*
* But if a file contains only blocks with invalid parity, it's reallocated to ensure
* to always minimize the space used in the parity.
*
* This could happen after a failed sync, when some other files are deleted,
* and then new ones can be moved backward to fill the hole created.
*/
static void scan_file_keep(struct snapraid_scan* scan, struct snapraid_file* file)
{
struct snapraid_disk* disk = scan->disk;
/* if the file is full invalid, schedule a reinsert at later stage */
if (file_is_full_invalid_parity_and_stable(scan->state, disk, file)) {
struct snapraid_file* copy = file_dup(file);
/* remove the file */
scan_file_remove(scan, file);
/* reinsert the copy in the delayed list */
scan_file_insert(scan, copy);
}
}
/**
* Process a file.
*/
static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub, struct stat* st, uint64_t physical)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
struct snapraid_file* file;
tommy_node* i;
int is_original_file_size_different_than_zero;
int is_file_already_present;
data_off_t file_already_present_size;
int64_t file_already_present_mtime_sec;
int file_already_present_mtime_nsec;
int is_file_reported;
char esc_buffer[ESC_MAX];
char esc_buffer_alt[ESC_MAX];
/*
* If the disk has persistent inodes and UUID, try a search on the past inodes,
* to detect moved files.
*
* For persistent inodes we mean inodes that keep their values when the file-system
* is unmounted and remounted. This don't always happen.
*
* Cases found are:
* - Linux FUSE with exFAT driver from https://code.google.com/p/exfat/.
* Inodes are reassigned at every mount restarting from 1 and incrementing.
* As worse, the exFAT support in FUSE doesn't use sub-second precision in timestamps
* making inode collision more easy (exFAT by design supports 10ms precision).
* - Linux VFAT kernel (3.2) driver. Inodes are fully reassigned at every mount.
*
* In such cases, to avoid possible random collisions, it's better to disable the moved
* file recognition.
*
* For persistent UUID we mean that it has the same UUID as before.
* Otherwise, if the UUID is changed, likely it's a new recreated file-system,
* and then the inode have no meaning.
*
* Note that to disable the search by past inode, we do this implicitly
* removing all the past inode before searching for files.
* This ensures that no file is found with a past inode, but at the same time,
* it allows to find new files with the same inode, to identify them as hardlinks.
*/
int has_past_inodes = !disk->has_volatile_inodes && !disk->has_different_uuid && !disk->has_unsupported_uuid;
/* always search with the new inode, in the all new inodes found until now, */
/* with the eventual presence of also the past inodes */
uint64_t inode = st->st_ino;
file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode));
/* identify moved files with past inodes and hardlinks with the new inodes */
if (file) {
/* check if the file is not changed */
if (file->size == st->st_size
&& file->mtime_sec == st->st_mtime
&& (file->mtime_nsec == STAT_NSEC(st)
/* always accept the stored value if it's STAT_NSEC_INVALID */
/* it happens when upgrading from an old version of SnapRAID */
/* not yet supporting the nanosecond field */
|| file->mtime_nsec == STAT_NSEC_INVALID
)
) {
/* check if multiple files have the same inode */
if (file_flag_has(file, FILE_IS_PRESENT)) {
/* if has_volatile_hardlinks is true, the nlink value is not reliable */
if (!disk->has_volatile_hardlinks && st->st_nlink == 1) {
/* LCOV_EXCL_START */
log_fatal("Internal inode '%" PRIu64 "' inconsistency for file '%s%s' already present\n", (uint64_t)st->st_ino, disk->dir, sub);
os_abort();
/* LCOV_EXCL_STOP */
}
/* it's a hardlink */
scan_link(scan, is_diff, sub, file->sub, FILE_IS_HARDLINK);
return;
}
/* mark as present */
file_flag_set(file, FILE_IS_PRESENT);
/* update the nanoseconds mtime only if different */
/* to avoid unneeded updates */
if (file->mtime_nsec == STAT_NSEC_INVALID
&& STAT_NSEC(st) != file->mtime_nsec
) {
file->mtime_nsec = STAT_NSEC(st);
/* we have to save the new mtime */
scan->need_write = 1;
}
if (strcmp(file->sub, sub) != 0) {
/* if the path is different, it means a moved file with the same inode */
++scan->count_move;
log_tag("scan:move:%s:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer), esc_tag(sub, esc_buffer_alt));
if (is_diff) {
msg_info("move %s -> %s\n", fmt_term(disk, file->sub, esc_buffer), fmt_term(disk, sub, esc_buffer_alt));
}
/* remove from the name set */
tommy_hashdyn_remove_existing(&disk->pathset, &file->pathset);
/* save the new name */
file_rename(file, sub);
/* reinsert in the name set */
tommy_hashdyn_insert(&disk->pathset, &file->pathset, file, file_path_hash(file->sub));
/* we have to save the new name */
scan->need_write = 1;
} else {
/* otherwise it's equal */
++scan->count_equal;
if (state->opt.gui) {
log_tag("scan:equal:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
}
}
/* mark the file as kept */
scan_file_keep(scan, file);
/* nothing more to do */
return;
}
/*
* Here the file matches the inode, but not the other info
*
* It could be a modified file with the same name,
* or a restored/copied file that get assigned a previously used inode,
* or a file-system with not persistent inodes.
*
* In NTFS it could be also a hardlink, because in NTFS
* hardlink don't share the same directory information,
* like attribute and time.
*
* For example:
* C:> echo A > A
* C:> mklink /H B A
* ...wait one minute
* C:> echo AAAAAAAAAAAAAA > A
* C:> dir
* ...both time and size of A and B don't match!
*/
if (file_flag_has(file, FILE_IS_PRESENT)) {
/* if has_volatile_hardlinks is true, the nlink value is not reliable */
if (!disk->has_volatile_hardlinks && st->st_nlink == 1) {
/* LCOV_EXCL_START */
log_fatal("Internal inode '%" PRIu64 "' inconsistency for files '%s%s' and '%s%s' with same inode but different attributes: size %" PRIu64 "?%" PRIu64 ", sec %" PRIu64 "?%" PRIu64 ", nsec %d?%d\n",
file->inode, disk->dir, sub, disk->dir, file->sub,
file->size, (uint64_t)st->st_size,
file->mtime_sec, (uint64_t)st->st_mtime,
file->mtime_nsec, STAT_NSEC(st));
os_abort();
/* LCOV_EXCL_STOP */
}
/* LCOV_EXCL_START */
/* suppose it's hardlink with not synced metadata */
scan_link(scan, is_diff, sub, file->sub, FILE_IS_HARDLINK);
return;
/* LCOV_EXCL_STOP */
}
/* assume a previously used inode, it's the worst case */
/* and we handle it removing the duplicate stored inode. */
/* If the file is found by name later, it will have the inode restored, */
/* otherwise, it will get removed */
/* remove from the inode set */
tommy_hashdyn_remove_existing(&disk->inodeset, &file->nodeset);
/* clear the inode */
/* this is not really needed for correct functionality */
/* because we are going to set FILE_IS_WITHOUT_INODE */
/* but it's easier for debugging to have invalid inodes set to 0 */
file->inode = 0;
/* mark as missing inode */
file_flag_set(file, FILE_IS_WITHOUT_INODE);
/* go further to find it by name */
}
/* initialize for later overwrite */
is_file_reported = 0;
is_original_file_size_different_than_zero = 0;
/* then try finding it by name */
file = tommy_hashdyn_search(&disk->pathset, file_path_compare_to_arg, sub, file_path_hash(sub));
/* keep track if the file already exists */
is_file_already_present = file != 0;
if (is_file_already_present) {
/* if the file is without an inode */
if (file_flag_has(file, FILE_IS_WITHOUT_INODE)) {
/* set it now */
file->inode = st->st_ino;
/* insert in the set */
tommy_hashdyn_insert(&disk->inodeset, &file->nodeset, file, file_inode_hash(file->inode));
/* unmark as missing inode */
file_flag_clear(file, FILE_IS_WITHOUT_INODE);
} else {
/* here the inode has to be different, otherwise we would have found it before */
if (file->inode == st->st_ino) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in inode '%" PRIu64 "' for files '%s%s' as unexpected matching\n", file->inode, disk->dir, sub);
os_abort();
/* LCOV_EXCL_STOP */
}
}
/* for sure it cannot be already present */
if (file_flag_has(file, FILE_IS_PRESENT)) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in path for file '%s%s' matching and already present\n", disk->dir, sub);
os_abort();
/* LCOV_EXCL_STOP */
}
/* check if the file is not changed */
if (file->size == st->st_size
&& file->mtime_sec == st->st_mtime
&& (file->mtime_nsec == STAT_NSEC(st)
/* always accept the stored value if it's STAT_NSEC_INVALID */
/* it happens when upgrading from an old version of SnapRAID */
/* not yet supporting the nanosecond field */
|| file->mtime_nsec == STAT_NSEC_INVALID
)
) {
/* mark as present */
file_flag_set(file, FILE_IS_PRESENT);
/* update the nano seconds mtime only if different */
/* to avoid unneeded updates */
if (file->mtime_nsec == STAT_NSEC_INVALID
&& STAT_NSEC(st) != STAT_NSEC_INVALID
) {
file->mtime_nsec = STAT_NSEC(st);
/* we have to save the new mtime */
scan->need_write = 1;
}
/* if when processing the disk we used the past inodes values */
if (has_past_inodes) {
/* if persistent inodes are supported, we are sure that the inode number */
/* is now different, because otherwise the file would have been found */
/* when searching by inode. */
/* if the inode is different, it means a rewritten file with the same path */
/* like when restoring a backup that restores also the timestamp */
++scan->count_restore;
log_tag("scan:restore:%s:%s\n", disk->name, esc_tag(sub, esc_buffer));
if (is_diff) {
msg_info("restore %s\n", fmt_term(disk, sub, esc_buffer));
}
/* remove from the inode set */
tommy_hashdyn_remove_existing(&disk->inodeset, &file->nodeset);
/* save the new inode */
file->inode = st->st_ino;
/* reinsert in the inode set */
tommy_hashdyn_insert(&disk->inodeset, &file->nodeset, file, file_inode_hash(file->inode));
/* we have to save the new inode */
scan->need_write = 1;
} else {
/* otherwise it's the case of not persistent inode, where doesn't */
/* matter if the inode is different or equal, because they have no */
/* meaning, and then we don't even save them */
++scan->count_equal;
if (state->opt.gui) {
log_tag("scan:equal:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
}
}
/* mark the file as kept */
scan_file_keep(scan, file);
/* nothing more to do */
return;
}
/* here if the file is changed but with the correct name */
/* save the info for later printout */
file_already_present_size = file->size;
file_already_present_mtime_sec = file->mtime_sec;
file_already_present_mtime_nsec = file->mtime_nsec;
/* keep track if the original file was not of zero size */
is_original_file_size_different_than_zero = file->size != 0;
/* remove it, and continue to insert it again */
scan_file_remove(scan, file);
/* and continue to insert it again */
} else {
file_already_present_size = 0;
file_already_present_mtime_sec = 0;
file_already_present_mtime_nsec = 0;
}
/* refresh the info, to ensure that they are synced, */
/* note that we refresh only the info of the new or modified files */
/* because this is slow operation */
scan_file_refresh(scan, sub, st, &physical);
#ifndef _WIN32
/* do a safety check to ensure that the common ext4 case of zeroing */
/* the size of a file after a crash doesn't propagate to the backup */
/* this check is specific for Linux, so we disable it on Windows */
if (is_original_file_size_different_than_zero && st->st_size == 0) {
if (!state->opt.force_zero) {
/* LCOV_EXCL_START */
log_fatal("The file '%s%s' has unexpected zero size!\n", disk->dir, sub);
log_fatal("It's possible that after a kernel crash this file was lost,\n");
log_fatal("and you can use 'snapraid fix -f /%s' to recover it.\n", fmt_poll(disk, sub, esc_buffer));
if (!is_diff) {
log_fatal("If this an expected condition you can '%s' anyway using 'snapraid --force-zero %s'\n", state->command, state->command);
exit(EXIT_FAILURE);
}
/* LCOV_EXCL_STOP */
}
}
#else
/* avoid the unused warning in Windows */
(void)is_original_file_size_different_than_zero;
#endif
/* insert it */
file = file_alloc(state->block_size, sub, st->st_size, st->st_mtime, STAT_NSEC(st), st->st_ino, physical);
/* mark it as present */
file_flag_set(file, FILE_IS_PRESENT);
/* if copy detection is enabled */
/* note that the copy detection is tried also for updated files */
/* this makes sense because it may happen to have two different copies */
/* of the same file, and we move the right one over the wrong one */
/* in such case we have a "copy" over an "update" */
if (!state->opt.force_nocopy) {
tommy_uint32_t hash = file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec);
/* search for a file with the same name and stamp in all the disks */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* other_disk = i->data;
struct snapraid_file* other_file;
stamp_lock(other_disk);
/* if the nanosecond part of the time stamp is valid, search */
/* for name and stamp, otherwise for path and stamp */
if (file->mtime_nsec != 0 && file->mtime_nsec != STAT_NSEC_INVALID)
other_file = tommy_hashdyn_search(&other_disk->stampset, file_namestamp_compare, file, hash);
else
other_file = tommy_hashdyn_search(&other_disk->stampset, file_pathstamp_compare, file, hash);
stamp_unlock(other_disk);
/* if found, and it's a fully hashed file */
if (other_file && file_is_full_hashed_and_stable(scan->state, other_disk, other_file)) {
/* assume that the file is a copy, and reuse the hash */
file_copy(other_file, file);
/* revert old counter and use the copy one */
++scan->count_copy;
log_tag("scan:copy:%s:%s:%s:%s\n", other_disk->name, esc_tag(other_file->sub, esc_buffer), disk->name, esc_tag(file->sub, esc_buffer_alt));
if (is_diff) {
msg_info("copy %s -> %s\n", fmt_term(other_disk, other_file->sub, esc_buffer), fmt_term(disk, file->sub, esc_buffer_alt));
}
/* mark it as reported */
is_file_reported = 1;
/* no need to continue the search */
break;
}
}
}
/* if not yet reported, do it now */
/* we postpone this to avoid to print two times the copied files */
if (!is_file_reported) {
if (is_file_already_present) {
++scan->count_change;
log_tag("scan:update:%s:%s: %" PRIu64 " %" PRIu64 ".%d -> %" PRIu64 " %" PRIu64 ".%d\n", disk->name, esc_tag(sub, esc_buffer),
file_already_present_size, file_already_present_mtime_sec, file_already_present_mtime_nsec,
file->size, file->mtime_sec, file->mtime_nsec
);
if (is_diff) {
msg_info("update %s\n", fmt_term(disk, sub, esc_buffer));
}
} else {
++scan->count_insert;
log_tag("scan:add:%s:%s\n", disk->name, esc_tag(sub, esc_buffer));
if (is_diff) {
msg_info("add %s\n", fmt_term(disk, sub, esc_buffer));
}
}
}
/* insert the file in the delayed list */
scan_file_insert(scan, file);
}
/**
* Remove the specified dir from the data set.
*/
static void scan_emptydir_remove(struct snapraid_scan* scan, struct snapraid_dir* dir)
{
struct snapraid_disk* disk = scan->disk;
/* state changed */
scan->need_write = 1;
/* remove the file from the dir containers */
tommy_hashdyn_remove_existing(&disk->dirset, &dir->nodeset);
tommy_list_remove_existing(&disk->dirlist, &dir->nodelist);
/* deallocate */
dir_free(dir);
}
/**
* Insert the specified dir in the data set.
*/
static void scan_emptydir_insert(struct snapraid_scan* scan, struct snapraid_dir* dir)
{
struct snapraid_disk* disk = scan->disk;
/* state changed */
scan->need_write = 1;
/* insert the dir in the dir containers */
tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub));
tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir);
}
/**
* Process a dir.
*/
static void scan_emptydir(struct snapraid_scan* scan, const char* sub)
{
struct snapraid_disk* disk = scan->disk;
struct snapraid_dir* dir;
/* check if the dir already exists */
dir = tommy_hashdyn_search(&disk->dirset, dir_name_compare, sub, dir_name_hash(sub));
if (dir) {
/* check if multiple files have the same name */
if (dir_flag_has(dir, FILE_IS_PRESENT)) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for dir '%s%s'\n", disk->dir, sub);
os_abort();
/* LCOV_EXCL_STOP */
}
/* mark as present */
dir_flag_set(dir, FILE_IS_PRESENT);
/* nothing more to do */
return;
} else {
/* and continue to insert it */
}
/* insert it */
dir = dir_alloc(sub);
/* mark it as present */
dir_flag_set(dir, FILE_IS_PRESENT);
/* insert it in the delayed insert list */
tommy_list_insert_tail(&scan->dir_insert_list, &dir->nodelist, dir);
}
struct dirent_sorted {
/* node for data structures */
tommy_node node;
#if HAVE_STRUCT_DIRENT_D_INO
uint64_t d_ino; /**< Inode number. */
#endif
#if HAVE_STRUCT_DIRENT_D_TYPE
uint32_t d_type; /**< File type. */
#endif
#if HAVE_STRUCT_DIRENT_D_STAT
struct stat d_stat; /**< Stat result. */
#endif
char d_name[]; /**< Variable length name. It must be the last field. */
};
#if HAVE_STRUCT_DIRENT_D_INO
static int dd_ino_compare(const void* void_a, const void* void_b)
{
const struct dirent_sorted* a = void_a;
const struct dirent_sorted* b = void_b;
if (a->d_ino < b->d_ino)
return -1;
if (a->d_ino > b->d_ino)
return 1;
return 0;
}
#endif
static int dd_name_compare(const void* void_a, const void* void_b)
{
const struct dirent_sorted* a = void_a;
const struct dirent_sorted* b = void_b;
return strcmp(a->d_name, b->d_name);
}
/**
* Return the stat info of a dir entry.
*/
#if HAVE_STRUCT_DIRENT_D_STAT
#define DSTAT(file, dd, buf) dstat(dd)
struct stat* dstat(struct dirent_sorted* dd)
{
return &dd->d_stat;
}
#else
#define DSTAT(file, dd, buf) dstat(file, buf)
struct stat* dstat(const char* file, struct stat* st)
{
if (lstat(file, st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", file, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return st;
}
#endif
/**
* Process a directory.
* Return != 0 if at least one file or link is processed.
*/
static int scan_sub(struct snapraid_scan* scan, int level, int is_diff, char* path_next, char* sub_next, char* tmp)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
int processed = 0;
DIR* d;
tommy_list list;
tommy_node* node;
size_t path_len;
size_t sub_len;
path_len = strlen(path_next);
sub_len = strlen(sub_next);
tommy_list_init(&list);
d = opendir(path_next);
if (!d) {
/* LCOV_EXCL_START */
log_fatal("Error opening directory '%s'. %s.\n", path_next, strerror(errno));
if (level == 0)
log_fatal("If this is the disk mount point, remember to create it manually\n");
else
log_fatal("If it's a permission problem, you can exclude it in the config file with:\n\texclude /%s\n", sub_next);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* read the full directory */
while (1) {
struct dirent_sorted* entry;
const char* name;
struct dirent* dd;
size_t name_len;
/*
* Clear errno to differentiate the end of the stream and an error condition
*
* From the Linux readdir() manpage:
* "If the end of the directory stream is reached, NULL is returned and errno is not changed.
* If an error occurs, NULL is returned and errno is set appropriately."
*/
errno = 0;
dd = readdir(d);
if (dd == 0 && errno != 0) {
/* LCOV_EXCL_START */
/* restore removing additions */
path_next[path_len] = 0;
sub_next[sub_len] = 0;
log_fatal("Error reading directory '%s'. %s.\n", path_next, strerror(errno));
log_fatal("You can exclude it in the config file with:\n\texclude /%s\n", sub_next);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (dd == 0) {
break; /* finished */
}
/* skip "." and ".." files */
name = dd->d_name;
if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;
pathcatl(path_next, path_len, PATH_MAX, name);
/* check for not supported file names */
if (name[0] == 0) {
/* LCOV_EXCL_START */
log_fatal("Unsupported name '%s' in file '%s'.\n", name, path_next);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* exclude hidden files even before calling lstat() */
if (filter_hidden(state->filter_hidden, dd) != 0) {
msg_verbose("Excluding hidden '%s'\n", path_next);
continue;
}
/* exclude content files even before calling lstat() */
if (filter_content(&state->contentlist, path_next) != 0) {
msg_verbose("Excluding content '%s'\n", path_next);
continue;
}
name_len = strlen(dd->d_name);
entry = malloc_nofail(sizeof(struct dirent_sorted) + name_len + 1);
/* copy the dir entry */
#if HAVE_STRUCT_DIRENT_D_INO
entry->d_ino = dd->d_ino;
#endif
#if HAVE_STRUCT_DIRENT_D_TYPE
entry->d_type = dd->d_type;
#endif
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &entry->d_stat);
/* note that at this point the st_mode may be 0 */
#endif
memcpy(entry->d_name, dd->d_name, name_len + 1);
/* insert in the list */
tommy_list_insert_tail(&list, &entry->node, entry);
}
if (closedir(d) != 0) {
/* LCOV_EXCL_START */
/* restore removing additions */
path_next[path_len] = 0;
log_fatal("Error closing directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (state->opt.force_order == SORT_ALPHA) {
/* if requested sort alphabetically */
/* this is mainly done for testing to ensure to always */
/* process in the same way in different platforms */
tommy_list_sort(&list, dd_name_compare);
}
#if HAVE_STRUCT_DIRENT_D_INO
else if (!disk->has_volatile_inodes) {
/* if inodes are persistent */
/* sort the list of dir entries by inodes */
tommy_list_sort(&list, dd_ino_compare);
}
/* otherwise just keep the insertion order */
#endif
/* process the sorted dir entries */
node = list;
while (node != 0) {
struct snapraid_filter* reason = 0;
struct dirent_sorted* dd = node->data;
const char* name = dd->d_name;
struct stat* st;
int type;
#if !HAVE_STRUCT_DIRENT_D_STAT
struct stat st_buf;
#endif
pathcatl(path_next, path_len, PATH_MAX, name);
pathcatl(sub_next, sub_len, PATH_MAX, name);
/* start with an unknown type */
type = -1;
st = 0;
/* if dirent has the type, use it */
#if HAVE_STRUCT_DIRENT_D_TYPE
switch (dd->d_type) {
case DT_UNKNOWN : break;
case DT_REG : type = 0; break;
case DT_LNK : type = 1; break;
case DT_DIR : type = 2; break;
default : type = 3; break;
}
#endif
/* if type is still unknown */
if (type < 0) {
/* get the type from stat */
st = DSTAT(path_next, dd, &st_buf);
#if HAVE_STRUCT_DIRENT_D_STAT
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
/* because we use a directory reading method that doesn't read info about ReparsePoint. */
/* Note that here we cannot call here lstat_sync(), because we don't know what kind */
/* of file is it, and lstat_sync() doesn't always work */
if (st->st_mode == 0) {
if (lstat(path_next, st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#endif
if (S_ISREG(st->st_mode))
type = 0;
else if (S_ISLNK(st->st_mode))
type = 1;
else if (S_ISDIR(st->st_mode))
type = 2;
else
type = 3;
}
if (type == 0) { /* REG */
if (filter_path(&state->filterlist, &reason, disk->name, sub_next) == 0) {
/* late stat, if not yet called */
if (!st)
st = DSTAT(path_next, dd, &st_buf);
#if HAVE_LSTAT_SYNC
/* if the st_ino field is missing, takes care to fill it using the extended lstat() */
/* this can happen only in Windows */
if (st->st_ino == 0 || st->st_nlink == 0) {
if (lstat_sync(path_next, st, 0) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#endif
scan_file(scan, is_diff, sub_next, st, FILEPHY_UNREAD_OFFSET);
processed = 1;
} else {
msg_verbose("Excluding file '%s' for rule '%s'\n", path_next, filter_type(reason, tmp, PATH_MAX));
}
} else if (type == 1) { /* LNK */
if (filter_path(&state->filterlist, &reason, disk->name, sub_next) == 0) {
int ret;
ret = readlink(path_next, tmp, PATH_MAX);
if (ret >= PATH_MAX) {
/* LCOV_EXCL_START */
log_fatal("Error in readlink file '%s'. Symlink too long.\n", path_next);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Error in readlink file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (ret == 0)
log_fatal("WARNING! Empty symbolic link '%s'.\n", path_next);
/* readlink doesn't put the final 0 */
tmp[ret] = 0;
/* process as a symbolic link */
scan_link(scan, is_diff, sub_next, tmp, FILE_IS_SYMLINK);
processed = 1;
} else {
msg_verbose("Excluding link '%s' for rule '%s'\n", path_next, filter_type(reason, tmp, PATH_MAX));
}
} else if (type == 2) { /* DIR */
if (filter_subdir(&state->filterlist, &reason, disk->name, sub_next) == 0) {
#ifndef _WIN32
/* late stat, if not yet called */
if (!st)
st = DSTAT(path_next, dd, &st_buf);
/* in Unix don't follow mount points in different devices */
/* in Windows we are already skipping them reporting them as special files */
if ((uint64_t)st->st_dev != disk->device) {
log_fatal("WARNING! Ignoring mount point '%s' because it appears to be in a different device\n", path_next);
} else
#endif
{
/* recurse */
pathslash(path_next, PATH_MAX);
pathslash(sub_next, PATH_MAX);
if (scan_sub(scan, level + 1, is_diff, path_next, sub_next, tmp) == 0) {
/* restore removing additions */
pathcatl(sub_next, sub_len, PATH_MAX, name);
/* scan the directory as empty dir */
scan_emptydir(scan, sub_next);
}
/* or we processed something internally, or we have added the empty dir */
processed = 1;
}
} else {
msg_verbose("Excluding directory '%s' for rule '%s'\n", path_next, filter_type(reason, tmp, PATH_MAX));
}
} else {
if (filter_path(&state->filterlist, &reason, disk->name, sub_next) == 0) {
/* late stat, if not yet called */
if (!st)
st = DSTAT(path_next, dd, &st_buf);
log_fatal("WARNING! Ignoring special '%s' file '%s'\n", stat_desc(st), path_next);
} else {
msg_verbose("Excluding special file '%s' for rule '%s'\n", path_next, filter_type(reason, tmp, PATH_MAX));
}
}
/* next entry */
node = node->next;
/* free the present one */
free(dd);
}
return processed;
}
/**
* Process a directory.
* Return != 0 if at least one file or link is processed.
*/
static int scan_dir(struct snapraid_scan* scan, int level, int is_diff, const char* dir, const char* sub)
{
/* working buffers used by scan_sub() */
char path_next[PATH_MAX];
char sub_next[PATH_MAX];
char tmp[PATH_MAX];
pathcpy(path_next, sizeof(path_next), dir);
pathcpy(sub_next, sizeof(sub_next), sub);
return scan_sub(scan, level, is_diff, path_next, sub_next, tmp);
}
static void* scan_disk(void* arg)
{
struct snapraid_scan* scan = arg;
struct snapraid_disk* disk = scan->disk;
int ret;
int has_persistent_inodes;
int has_syncronized_hardlinks;
uint64_t start;
/* check if the disk supports persistent inodes */
ret = fsinfo(disk->dir, &has_persistent_inodes, &has_syncronized_hardlinks, 0, 0);
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing disk '%s' to get file-system info. %s.\n", disk->dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!has_persistent_inodes) {
disk->has_volatile_inodes = 1;
}
if (!has_syncronized_hardlinks) {
disk->has_volatile_hardlinks = 1;
}
/* if inodes or UUID are not persistent/changed/unsupported */
if (disk->has_volatile_inodes || disk->has_different_uuid || disk->has_unsupported_uuid) {
/* remove all the inodes from the inode collection */
/* if they are not persistent, all of them could be changed now */
/* and we don't want to find false matching ones */
/* see scan_file() for more details */
tommy_node* node = disk->filelist;
while (node) {
struct snapraid_file* file = node->data;
node = node->next;
/* remove from the inode set */
tommy_hashdyn_remove_existing(&disk->inodeset, &file->nodeset);
/* clear the inode */
file->inode = 0;
/* mark as missing inode */
file_flag_set(file, FILE_IS_WITHOUT_INODE);
}
}
start = tick_ms();
scan_dir(scan, 0, scan->is_diff, disk->dir, "");
if (!scan->is_diff)
msg_progress("Scanned %s in %" PRIu64 " seconds\n", disk->name, (tick_ms() - start) / 1000);
return 0;
}
static int state_diffscan(struct snapraid_state* state, int is_diff)
{
tommy_node* i;
tommy_node* j;
tommy_list scanlist;
int done;
fptr* msg;
struct snapraid_scan total;
int no_difference;
char esc_buffer[ESC_MAX];
tommy_list_init(&scanlist);
if (is_diff)
msg_progress("Comparing...\n");
else
msg_progress("Scanning...\n");
/* allocate all the scan data */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
struct snapraid_scan* scan;
scan = scan_alloc(state, disk, is_diff);
tommy_list_insert_tail(&scanlist, &scan->node, scan);
}
/* first scan all the directory and find new and deleted files */
for (i = scanlist; i != 0; i = i->next) {
struct snapraid_scan* scan = i->data;
#if HAVE_THREAD
if (state->opt.skip_multi_scan)
scan_disk(scan);
else
thread_create(&scan->thread, scan_disk, scan);
#else
scan_disk(scan);
#endif
}
#if HAVE_THREAD
/* wait for all threads to terminate */
for (i = scanlist; i != 0; i = i->next) {
struct snapraid_scan* scan = i->data;
void* retval;
/* wait for thread termination */
if (!state->opt.skip_multi_scan)
thread_join(scan->thread, &retval);
}
#endif
/* we split the search in two phases because to detect files */
/* moved from one disk to another we have to start deletion */
/* only when all disks have all the new files found */
/* now process all the new and deleted files */
for (i = scanlist; i != 0; i = i->next) {
struct snapraid_scan* scan = i->data;
struct snapraid_disk* disk = scan->disk;
tommy_node* node;
unsigned phy_count;
unsigned phy_dup;
uint64_t phy_last;
struct snapraid_file* phy_file_last;
/* check for removed files */
node = disk->filelist;
while (node) {
struct snapraid_file* file = node->data;
/* next node */
node = node->next;
/* remove if not present */
if (!file_flag_has(file, FILE_IS_PRESENT)) {
++scan->count_remove;
log_tag("scan:remove:%s:%s\n", disk->name, esc_tag(file->sub, esc_buffer));
if (is_diff) {
msg_info("remove %s\n", fmt_term(disk, file->sub, esc_buffer));
}
scan_file_remove(scan, file);
}
}
/* check for removed links */
node = disk->linklist;
while (node) {
struct snapraid_link* slink = node->data;
/* next node */
node = node->next;
/* remove if not present */
if (!link_flag_has(slink, FILE_IS_PRESENT)) {
++scan->count_remove;
log_tag("scan:remove:%s:%s\n", disk->name, esc_tag(slink->sub, esc_buffer));
if (is_diff) {
msg_info("remove %s\n", fmt_term(disk, slink->sub, esc_buffer));
}
scan_link_remove(scan, slink);
}
}
/* check for removed dirs */
node = disk->dirlist;
while (node) {
struct snapraid_dir* dir = node->data;
/* next node */
node = node->next;
/* remove if not present */
if (!dir_flag_has(dir, FILE_IS_PRESENT)) {
scan_emptydir_remove(scan, dir);
}
}
/* sort the files before inserting them */
/* we use a stable sort to ensure that if the reported physical offset/inode */
/* are always 0, we keep at least the directory order */
switch (state->opt.force_order) {
case SORT_PHYSICAL :
tommy_list_sort(&scan->file_insert_list, file_physical_compare);
break;
case SORT_INODE :
tommy_list_sort(&scan->file_insert_list, file_inode_compare);
break;
case SORT_ALPHA :
tommy_list_sort(&scan->file_insert_list, file_path_compare);
break;
case SORT_DIR :
/* already in order */
break;
}
/* insert all the new files, we insert them only after the deletion */
/* to reuse the just freed space */
/* also check if the physical offset reported are fakes or not */
node = scan->file_insert_list;
phy_count = 0;
phy_dup = 0;
phy_last = FILEPHY_UNREAD_OFFSET;
phy_file_last = 0;
while (node) {
struct snapraid_file* file = node->data;
/* if the file is not empty, count duplicate physical offsets */
if (state->opt.force_order == SORT_PHYSICAL && file->size != 0) {
if (phy_file_last != 0 && file->physical == phy_last
/* files without offset are expected to have duplicates */
&& phy_last != FILEPHY_WITHOUT_OFFSET
) {
/* if verbose, print the list of duplicates real offsets */
/* other cases are for offsets not supported, so we don't need to report them file by file */
if (phy_last >= FILEPHY_REAL_OFFSET) {
log_fatal("WARNING! Files '%s%s' and '%s%s' have the same physical offset %" PRId64 ".\n", disk->dir, phy_file_last->sub, disk->dir, file->sub, phy_last);
}
++phy_dup;
}
phy_file_last = file;
phy_last = file->physical;
++phy_count;
}
/* next node */
node = node->next;
/* insert in the parity */
scan_file_allocate(scan, file);
}
/* mark the disk without reliable physical offset if it has duplicates */
/* here it should never happen because we already sorted out hardlinks */
if (state->opt.force_order == SORT_PHYSICAL && phy_dup > 0) {
disk->has_unreliable_physical = 1;
}
/* insert all the new links */
node = scan->link_insert_list;
while (node) {
struct snapraid_link* slink = node->data;
/* next node */
node = node->next;
/* insert it */
scan_link_insert(scan, slink);
}
/* insert all the new dirs */
node = scan->dir_insert_list;
while (node) {
struct snapraid_dir* dir = node->data;
/* next node */
node = node->next;
/* insert it */
scan_emptydir_insert(scan, dir);
}
}
/* propagate the state change (after all the scan operations are called) */
for (i = scanlist; i != 0; i = i->next) {
struct snapraid_scan* scan = i->data;
if (scan->need_write) {
state->need_write = 1;
}
}
/* check for disks where all the previously existing files where removed */
if (!state->opt.force_empty) {
int all_missing = 0;
int all_rewritten = 0;
done = 0;
for (i = state->disklist, j = scanlist; i != 0; i = i->next, j = j->next) {
struct snapraid_disk* disk = i->data;
struct snapraid_scan* scan = j->data;
if (scan->count_equal == 0
&& scan->count_move == 0
&& scan->count_restore == 0
&& (scan->count_remove != 0 || scan->count_change != 0)
) {
if (!done) {
done = 1;
log_fatal("WARNING! All the files previously present in disk '%s' at dir '%s'", disk->name, disk->dir);
} else {
log_fatal(", disk '%s' at dir '%s'", disk->name, disk->dir);
}
/* detect the special condition of all files missing */
if (scan->count_change == 0)
all_missing = 1;
/* detect the special condition of all files rewritten */
if (scan->count_remove == 0)
all_rewritten = 1;
}
}
if (done) {
log_fatal("\nare now missing or rewritten!\n");
if (all_rewritten) {
log_fatal("This could happen when restoring a disk with a backup\n");
log_fatal("program that is not setting correctly the timestamps.\n");
}
if (all_missing) {
log_fatal("This could happen when some disks are not mounted\n");
log_fatal("in the expected directory.\n");
}
if (!is_diff) {
log_fatal("If you want to '%s' anyway, use 'snapraid --force-empty %s'.\n", state->command, state->command);
exit(EXIT_FAILURE);
}
}
}
/* check for disks without the physical offset support */
if (state->opt.force_order == SORT_PHYSICAL) {
done = 0;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
if (disk->has_unreliable_physical) {
if (!done) {
done = 1;
log_fatal("WARNING! Physical offsets not supported for disk '%s'", disk->name);
} else {
log_fatal(", '%s'", disk->name);
}
}
}
if (done) {
log_fatal(". Files order won't be optimal.\n");
}
}
/* check for disks without persistent inodes */
done = 0;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
if (disk->has_volatile_inodes) {
if (!done) {
done = 1;
log_fatal("WARNING! Inodes are not persistent for disks: '%s'", disk->name);
} else {
log_fatal(", '%s'", disk->name);
}
}
}
if (done) {
log_fatal(". Not using inodes to detect move operations.\n");
}
/* check for disks with changed UUID */
done = 0;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
/* don't print the message if the UUID changed because before */
/* it was no set. */
/* this is the normal condition for an empty disk because it */
/* isn't stored */
if (disk->has_different_uuid && !disk->had_empty_uuid) {
if (!done) {
done = 1;
log_fatal("WARNING! UUID is changed for disks: '%s'", disk->name);
} else {
log_fatal(", '%s'", disk->name);
}
}
}
if (done) {
log_fatal(". Not using inodes to detect move operations.\n");
}
/* check for disks with unsupported UUID */
done = 0;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
if (disk->has_unsupported_uuid) {
if (!done) {
done = 1;
log_fatal("WARNING! UUID is unsupported for disks: '%s'", disk->name);
} else {
log_fatal(", '%s'", disk->name);
}
}
}
if (done) {
log_fatal(". Not using inodes to detect move operations.\n");
#if defined(_linux) && !HAVE_BLKID
log_fatal("The 'blkid' library is not linked in SnapRAID!\n");
log_fatal("Try rebuilding it after installing the libblkid-dev or libblkid-devel package.\n");
#endif
}
total.count_equal = 0;
total.count_move = 0;
total.count_copy = 0;
total.count_restore = 0;
total.count_change = 0;
total.count_remove = 0;
total.count_insert = 0;
for (i = scanlist; i != 0; i = i->next) {
struct snapraid_scan* scan = i->data;
total.count_equal += scan->count_equal;
total.count_move += scan->count_move;
total.count_copy += scan->count_copy;
total.count_restore += scan->count_restore;
total.count_change += scan->count_change;
total.count_remove += scan->count_remove;
total.count_insert += scan->count_insert;
}
if (is_diff) {
msg_status("\n");
msg = msg_status;
} else {
msg = msg_verbose;
}
msg("%8u equal\n", total.count_equal);
msg("%8u added\n", total.count_insert);
msg("%8u removed\n", total.count_remove);
msg("%8u updated\n", total.count_change);
msg("%8u moved\n", total.count_move);
msg("%8u copied\n", total.count_copy);
msg("%8u restored\n", total.count_restore);
log_tag("summary:equal:%u\n", total.count_equal);
log_tag("summary:added:%u\n", total.count_insert);
log_tag("summary:removed:%u\n", total.count_remove);
log_tag("summary:updated:%u\n", total.count_change);
log_tag("summary:moved:%u\n", total.count_move);
log_tag("summary:copied:%u\n", total.count_copy);
log_tag("summary:restored:%u\n", total.count_restore);
no_difference = !total.count_move && !total.count_copy && !total.count_restore
&& !total.count_change && !total.count_remove && !total.count_insert;
if (is_diff) {
if (no_difference) {
msg_status("No differences\n");
} else {
msg_status("There are differences!\n");
}
}
if (no_difference) {
log_tag("summary:exit:equal\n");
} else {
log_tag("summary:exit:diff\n");
}
log_flush();
tommy_list_foreach(&scanlist, (tommy_foreach_func*)scan_free);
/* check the file-system on all disks */
state_fscheck(state, "after scan");
if (is_diff) {
/* check for file difference */
if (!no_difference)
return 1;
/* check also for incomplete "sync" */
if (parity_is_invalid(state))
return 1;
}
return 0;
}
int state_diff(struct snapraid_state* state)
{
return state_diffscan(state, 1);
}
void state_scan(struct snapraid_state* state)
{
(void)state_diffscan(state, 0); /* ignore return value */
}
snapraid-12.1/cmdline/scrub.c 0000664 0000000 0000000 00000063560 14166610522 0016150 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
#include "io.h"
#include "raid/raid.h"
/****************************************************************************/
/* scrub */
/**
* Buffer for storing the new hashes.
*/
struct snapraid_rehash {
unsigned char hash[HASH_MAX];
struct snapraid_block* block;
};
/**
* Scrub plan to use.
*/
struct snapraid_plan {
struct snapraid_state* state;
int plan; /**< One of the SCRUB_*. */
time_t timelimit; /**< Time limit. Valid only with SCRUB_AUTO. */
block_off_t lastlimit; /**< Number of blocks allowed with time exactly at ::timelimit. */
block_off_t countlast; /**< Counter of blocks with time exactly at ::timelimit. */
};
/**
* Check if we have to process the specified block index ::i.
*/
static int block_is_enabled(struct snapraid_plan* plan, block_off_t i)
{
time_t blocktime;
snapraid_info info;
/* don't scrub unused blocks in all plans */
info = info_get(&plan->state->infoarr, i);
if (info == 0)
return 0;
/* bad blocks are always scrubbed in all plans */
if (info_get_bad(info))
return 1;
switch (plan->plan) {
case SCRUB_FULL :
/* in 'full' plan everything is scrubbed */
return 1;
case SCRUB_EVEN :
/* in 'even' plan, scrub only even blocks */
return i % 2 == 0;
case SCRUB_NEW :
/* in 'sync' plan, only blocks never scrubbed */
return info_get_justsynced(info);
case SCRUB_BAD :
/* in 'bad' plan, only bad blocks (already reported) */
return 0;
}
/* if it's too new */
blocktime = info_get_time(info);
if (blocktime > plan->timelimit) {
/* skip it */
return 0;
}
/* if the time is less than the limit, always include */
/* otherwise, check if we reached the last limit count */
if (blocktime == plan->timelimit) {
/* if we reached the count limit */
if (plan->countlast >= plan->lastlimit) {
/* skip it */
return 0;
}
++plan->countlast;
}
return 1;
}
static void scrub_data_reader(struct snapraid_worker* worker, struct snapraid_task* task)
{
struct snapraid_io* io = worker->io;
struct snapraid_state* state = io->state;
struct snapraid_handle* handle = worker->handle;
struct snapraid_disk* disk = handle->disk;
block_off_t blockcur = task->position;
unsigned char* buffer = task->buffer;
int ret;
char esc_buffer[ESC_MAX];
/* if the disk position is not used */
if (!disk) {
/* use an empty block */
memset(buffer, 0, state->block_size);
task->state = TASK_STATE_DONE;
return;
}
/* get the block */
task->block = fs_par2block_find(disk, blockcur);
/* if the block is not used */
if (!block_has_file(task->block)) {
/* use an empty block */
memset(buffer, 0, state->block_size);
task->state = TASK_STATE_DONE;
return;
}
/* get the file of this block */
task->file = fs_par2file_get(disk, blockcur, &task->file_pos);
/* if the file is different than the current one, close it */
if (handle->file != 0 && handle->file != task->file) {
/* keep a pointer at the file we are going to close for error reporting */
struct snapraid_file* report = handle->file;
ret = handle_close(handle);
if (ret == -1) {
/* LCOV_EXCL_START */
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
if (errno == EIO) {
log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to scrub.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_IOERROR;
return;
}
log_tag("error:%u:%s:%s: Close error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to scrub.\n");
log_fatal("Ensure that file '%s' can be accessed.\n", handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_ERROR;
return;
/* LCOV_EXCL_STOP */
}
}
ret = handle_open(handle, task->file, state->file_mode, log_error, 0);
if (ret == -1) {
if (errno == EIO) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Open EIO error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output open error in a data disk, it isn't possible to scrub.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_IOERROR;
return;
/* LCOV_EXCL_STOP */
}
log_tag("error:%u:%s:%s: Open error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
/* check if the file is changed */
if (handle->st.st_size != task->file->size
|| handle->st.st_mtime != task->file->mtime_sec
|| STAT_NSEC(&handle->st) != task->file->mtime_nsec
/* don't check the inode to support filesystem without persistent inodes */
) {
/* report that the block and the file are not synced */
task->is_timestamp_different = 1;
/* follow */
}
/* note that we intentionally don't abort if the file has different attributes */
/* from the last sync, as we are expected to return errors if running */
/* in an unsynced array. This is just like the check command. */
task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
if (task->read_size == -1) {
if (errno == EIO) {
log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
log_error("Input/Output error in file '%s' at position '%u'\n", handle->path, task->file_pos);
task->state = TASK_STATE_IOERROR_CONTINUE;
return;
}
log_tag("error:%u:%s:%s: Read error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
/* store the path of the opened file */
pathcpy(task->path, sizeof(task->path), handle->path);
task->state = TASK_STATE_DONE;
}
static void scrub_parity_reader(struct snapraid_worker* worker, struct snapraid_task* task)
{
struct snapraid_io* io = worker->io;
struct snapraid_state* state = io->state;
struct snapraid_parity_handle* parity_handle = worker->parity_handle;
unsigned level = parity_handle->level;
block_off_t blockcur = task->position;
unsigned char* buffer = task->buffer;
int ret;
/* read the parity */
ret = parity_read(parity_handle, blockcur, buffer, state->block_size, log_error);
if (ret == -1) {
if (errno == EIO) {
log_tag("parity_error:%u:%s: Read EIO error. %s\n", blockcur, lev_config_name(level), strerror(errno));
log_error("Input/Output error in parity '%s' at position '%u'\n", lev_config_name(level), blockcur);
task->state = TASK_STATE_IOERROR_CONTINUE;
return;
}
log_tag("parity_error:%u:%s: Read error. %s\n", blockcur, lev_config_name(level), strerror(errno));
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
task->state = TASK_STATE_DONE;
}
static int state_scrub_process(struct snapraid_state* state, struct snapraid_parity_handle* parity_handle, block_off_t blockstart, block_off_t blockmax, struct snapraid_plan* plan, time_t now)
{
struct snapraid_io io;
struct snapraid_handle* handle;
void* rehandle_alloc;
struct snapraid_rehash* rehandle;
unsigned diskmax;
block_off_t blockcur;
unsigned j;
unsigned buffermax;
data_off_t countsize;
block_off_t countpos;
block_off_t countmax;
block_off_t autosavedone;
block_off_t autosavelimit;
block_off_t autosavemissing;
int ret;
unsigned error;
unsigned silent_error;
unsigned io_error;
unsigned l;
unsigned* waiting_map;
unsigned waiting_mac;
char esc_buffer[ESC_MAX];
bit_vect_t* block_enabled;
/* maps the disks to handles */
handle = handle_mapping(state, &diskmax);
/* rehash buffers */
rehandle = malloc_nofail_align(diskmax * sizeof(struct snapraid_rehash), &rehandle_alloc);
/* we need 1 * data + 2 * parity */
buffermax = diskmax + 2 * state->level;
/* initialize the io threads */
io_init(&io, state, state->opt.io_cache, buffermax, scrub_data_reader, handle, diskmax, scrub_parity_reader, 0, parity_handle, state->level);
/* possibly waiting disks */
waiting_mac = diskmax > RAID_PARITY_MAX ? diskmax : RAID_PARITY_MAX;
waiting_map = malloc_nofail(waiting_mac * sizeof(unsigned));
error = 0;
silent_error = 0;
io_error = 0;
msg_progress("Selecting...\n");
/* first count the number of blocks to process */
countmax = 0;
plan->countlast = 0;
block_enabled = calloc_nofail(1, bit_vect_size(blockmax)); /* preinitialize to 0 */
for (blockcur = blockstart; blockcur < blockmax; ++blockcur) {
if (!block_is_enabled(plan, blockcur))
continue;
bit_vect_set(block_enabled, blockcur);
++countmax;
}
/* compute the autosave size for all disk, even if not read */
/* this makes sense because the speed should be almost the same */
/* if the disks are read in parallel */
autosavelimit = state->autosave / (diskmax * state->block_size);
autosavemissing = countmax; /* blocks to do */
autosavedone = 0; /* blocks done */
/* drop until now */
state_usage_waste(state);
countsize = 0;
countpos = 0;
msg_progress("Scrubbing...\n");
/* start all the worker threads */
io_start(&io, blockstart, blockmax, block_enabled);
state_progress_begin(state, blockstart, blockmax, countmax);
while (1) {
unsigned char* buffer_recov[LEV_MAX];
snapraid_info info;
int error_on_this_block;
int silent_error_on_this_block;
int io_error_on_this_block;
int block_is_unsynced;
int rehash;
void** buffer;
/* go to the next block */
blockcur = io_read_next(&io, &buffer);
if (blockcur >= blockmax)
break;
/* until now is scheduling */
state_usage_sched(state);
/* one more block processed for autosave */
++autosavedone;
--autosavemissing;
/* by default process the block, and skip it if something goes wrong */
error_on_this_block = 0;
silent_error_on_this_block = 0;
io_error_on_this_block = 0;
/* if all the blocks at this address are synced */
/* if not, parity is not even checked */
block_is_unsynced = 0;
/* get block specific info */
info = info_get(&state->infoarr, blockcur);
/* if we have to use the old hash */
rehash = info_get_rehash(info);
/* for each disk, process the block */
for (j = 0; j < diskmax; ++j) {
struct snapraid_task* task;
int read_size;
unsigned char hash[HASH_MAX];
struct snapraid_block* block;
int file_is_unsynced;
struct snapraid_disk* disk;
struct snapraid_file* file;
block_off_t file_pos;
unsigned diskcur;
/* if the file on this disk is synced */
/* if not, silent errors are assumed as expected error */
file_is_unsynced = 0;
/* until now is misc */
state_usage_misc(state);
/* get the next task */
task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac);
/* until now is disk */
state_usage_disk(state, handle, waiting_map, waiting_mac);
/* get the task results */
disk = task->disk;
block = task->block;
file = task->file;
file_pos = task->file_pos;
read_size = task->read_size;
/* by default no rehash in case of "continue" */
rehandle[diskcur].block = 0;
/* if the disk position is not used */
if (!disk)
continue;
state_usage_file(state, disk, file);
/* if the block is unsynced, errors are expected */
if (block_has_invalid_parity(block)) {
/* report that the block and the file are not synced */
block_is_unsynced = 1;
file_is_unsynced = 1;
/* follow */
}
/* if the block is not used */
if (!block_has_file(block))
continue;
/* if the block is unsynced, errors are expected */
if (task->is_timestamp_different) {
/* report that the block and the file are not synced */
block_is_unsynced = 1;
file_is_unsynced = 1;
/* follow */
}
/* handle error conditions */
if (task->state == TASK_STATE_IOERROR) {
/* LCOV_EXCL_START */
++io_error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR) {
/* LCOV_EXCL_START */
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR_CONTINUE) {
++error;
error_on_this_block = 1;
continue;
}
if (task->state == TASK_STATE_IOERROR_CONTINUE) {
++io_error;
if (io_error >= state->opt.io_error_limit) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Too many input/output read error in a data disk, it isn't possible to scrub.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, task->path);
log_fatal("Stopping at block %u\n", blockcur);
goto bail;
/* LCOV_EXCL_STOP */
}
/* otherwise continue */
io_error_on_this_block = 1;
continue;
}
if (task->state != TASK_STATE_DONE) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in task state\n");
os_abort();
/* LCOV_EXCL_STOP */
}
countsize += read_size;
/* now compute the hash */
if (rehash) {
memhash(state->prevhash, state->prevhashseed, hash, buffer[diskcur], read_size);
/* compute the new hash, and store it */
rehandle[diskcur].block = block;
memhash(state->hash, state->hashseed, rehandle[diskcur].hash, buffer[diskcur], read_size);
} else {
memhash(state->hash, state->hashseed, hash, buffer[diskcur], read_size);
}
/* until now is hash */
state_usage_hash(state);
if (block_has_updated_hash(block)) {
/* compare the hash */
if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
unsigned diff = memdiff(hash, block->hash, BLOCK_HASH_SIZE);
log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u/%u\n", blockcur, disk->name, esc_tag(file->sub, esc_buffer), file_pos, diff, BLOCK_HASH_SIZE * 8);
/* it's a silent error only if we are dealing with synced files */
if (file_is_unsynced) {
++error;
error_on_this_block = 1;
} else {
log_error("Data error in file '%s' at position '%u', diff bits %u/%u\n", task->path, file_pos, diff, BLOCK_HASH_SIZE * 8);
++silent_error;
silent_error_on_this_block = 1;
}
continue;
}
}
}
/* buffers for parity read and not computed */
for (l = 0; l < state->level; ++l)
buffer_recov[l] = buffer[diskmax + state->level + l];
for (; l < LEV_MAX; ++l)
buffer_recov[l] = 0;
/* until now is misc */
state_usage_misc(state);
/* read the parity */
for (l = 0; l < state->level; ++l) {
struct snapraid_task* task;
unsigned levcur;
task = io_parity_read(&io, &levcur, waiting_map, &waiting_mac);
/* until now is parity */
state_usage_parity(state, waiting_map, waiting_mac);
/* handle error conditions */
if (task->state == TASK_STATE_IOERROR) {
/* LCOV_EXCL_START */
++io_error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR) {
/* LCOV_EXCL_START */
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR_CONTINUE) {
++error;
error_on_this_block = 1;
/* if continuing on error, clear the missing buffer */
buffer_recov[levcur] = 0;
continue;
}
if (task->state == TASK_STATE_IOERROR_CONTINUE) {
++io_error;
if (io_error >= state->opt.io_error_limit) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Too many input/output read error in the %s disk, it isn't possible to scrub.\n", lev_name(levcur));
log_fatal("Ensure that disk '%s' is sane and can be read.\n", lev_config_name(levcur));
log_fatal("Stopping at block %u\n", blockcur);
goto bail;
/* LCOV_EXCL_STOP */
}
/* otherwise continue */
io_error_on_this_block = 1;
/* if continuing on error, clear the missing buffer */
buffer_recov[levcur] = 0;
continue;
}
if (task->state != TASK_STATE_DONE) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in task state\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
/* if we have read all the data required and it's correct, proceed with the parity check */
if (!error_on_this_block && !silent_error_on_this_block && !io_error_on_this_block) {
/* compute the parity */
raid_gen(diskmax, state->level, state->block_size, buffer);
/* compare the parity */
for (l = 0; l < state->level; ++l) {
if (buffer_recov[l] && memcmp(buffer[diskmax + l], buffer_recov[l], state->block_size) != 0) {
unsigned diff = memdiff(buffer[diskmax + l], buffer_recov[l], state->block_size);
log_tag("parity_error:%u:%s: Data error, diff bits %u/%u\n", blockcur, lev_config_name(l), diff, state->block_size * 8);
/* it's a silent error only if we are dealing with synced blocks */
if (block_is_unsynced) {
++error;
error_on_this_block = 1;
} else {
log_fatal("Data error in parity '%s' at position '%u', diff bits %u/%u\n", lev_config_name(l), blockcur, diff, state->block_size * 8);
++silent_error;
silent_error_on_this_block = 1;
}
}
}
/* until now is raid */
state_usage_raid(state);
}
if (silent_error_on_this_block || io_error_on_this_block) {
/* set the error status keeping other info */
info_set(&state->infoarr, blockcur, info_set_bad(info));
} else if (error_on_this_block) {
/* do nothing, as this is a generic error */
/* likely caused by a not synced array */
} else {
/* if rehash is needed */
if (rehash) {
/* store all the new hash already computed */
for (j = 0; j < diskmax; ++j) {
if (rehandle[j].block)
memcpy(rehandle[j].block->hash, rehandle[j].hash, BLOCK_HASH_SIZE);
}
}
/* update the time info of the block */
/* and clear any other flag */
info_set(&state->infoarr, blockcur, info_make(now, 0, 0, 0));
}
/* mark the state as needing write */
state->need_write = 1;
/* count the number of processed block */
++countpos;
/* progress */
if (state_progress(state, &io, blockcur, countpos, countmax, countsize)) {
/* LCOV_EXCL_START */
break;
/* LCOV_EXCL_STOP */
}
/* autosave */
if (state->autosave != 0
&& autosavedone >= autosavelimit /* if we have reached the limit */
&& autosavemissing >= autosavelimit /* if we have at least a full step to do */
) {
autosavedone = 0; /* restart the counter */
/* until now is misc */
state_usage_misc(state);
state_progress_stop(state);
msg_progress("Autosaving...\n");
state_write(state);
state_progress_restart(state);
/* drop until now */
state_usage_waste(state);
}
}
state_progress_end(state, countpos, countmax, countsize);
state_usage_print(state);
if (error || silent_error || io_error) {
msg_status("\n");
msg_status("%8u file errors\n", error);
msg_status("%8u io errors\n", io_error);
msg_status("%8u data errors\n", silent_error);
} else {
/* print the result only if processed something */
if (countpos != 0)
msg_status("Everything OK\n");
}
if (error)
log_fatal("WARNING! Unexpected file errors!\n");
if (io_error)
log_fatal("DANGER! Unexpected input/output errors! The failing blocks are now marked as bad!\n");
if (silent_error)
log_fatal("DANGER! Unexpected data errors! The failing blocks are now marked as bad!\n");
if (io_error || silent_error) {
log_fatal("Use 'snapraid status' to list the bad blocks.\n");
log_fatal("Use 'snapraid -e fix' to recover them.\n");
log_fatal("Use 'snapraid -p bad scrub' to recheck after fixing.\n");
}
log_tag("summary:error_file:%u\n", error);
log_tag("summary:error_io:%u\n", io_error);
log_tag("summary:error_data:%u\n", silent_error);
if (error + silent_error + io_error == 0)
log_tag("summary:exit:ok\n");
else
log_tag("summary:exit:error\n");
log_flush();
bail:
/* stop all the worker threads */
io_stop(&io);
for (j = 0; j < diskmax; ++j) {
struct snapraid_file* file = handle[j].file;
struct snapraid_disk* disk = handle[j].disk;
ret = handle_close(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", blockcur, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
free(handle);
free(rehandle_alloc);
free(waiting_map);
io_done(&io);
free(block_enabled);
if (state->opt.expect_recoverable) {
if (error + silent_error + io_error == 0)
return -1;
} else {
if (error + silent_error + io_error != 0)
return -1;
}
return 0;
}
/**
* Return a * b / c approximated to the upper value.
*/
static uint32_t md(uint32_t a, uint32_t b, uint32_t c)
{
uint64_t v = a;
v *= b;
v += c - 1;
v /= c;
return v;
}
int state_scrub(struct snapraid_state* state, int plan, int olderthan)
{
block_off_t blockmax;
block_off_t countlimit;
block_off_t i;
block_off_t count;
time_t recentlimit;
int ret;
struct snapraid_parity_handle parity_handle[LEV_MAX];
struct snapraid_plan ps;
time_t* timemap;
unsigned error;
time_t now;
unsigned l;
/* get the present time */
now = time(0);
msg_progress("Initializing...\n");
if ((plan == SCRUB_BAD || plan == SCRUB_NEW || plan == SCRUB_FULL)
&& olderthan >= 0) {
/* LCOV_EXCL_START */
log_fatal("You can specify -o, --older-than only with a numeric percentage.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
blockmax = parity_allocated_size(state);
/* preinitialize to avoid warnings */
countlimit = 0;
recentlimit = 0;
ps.state = state;
if (state->opt.force_scrub_even) {
ps.plan = SCRUB_EVEN;
} else if (plan == SCRUB_FULL) {
ps.plan = SCRUB_FULL;
} else if (plan == SCRUB_NEW) {
ps.plan = SCRUB_NEW;
} else if (plan == SCRUB_BAD) {
ps.plan = SCRUB_BAD;
} else if (state->opt.force_scrub_at) {
/* scrub the specified amount of blocks */
ps.plan = SCRUB_AUTO;
countlimit = state->opt.force_scrub_at;
recentlimit = now;
} else {
ps.plan = SCRUB_AUTO;
if (plan >= 0) {
countlimit = md(blockmax, plan, 100);
} else {
/* by default scrub 8.33% of the array (100/12=8.(3)) */
countlimit = md(blockmax, 1, 12);
}
if (olderthan >= 0) {
recentlimit = now - olderthan * 24 * 3600;
} else {
/* by default use a 10 day time limit */
recentlimit = now - 10 * 24 * 3600;
}
}
/* identify the time limit */
/* we sort all the block times, and we identify the time limit for which we reach the quota */
/* this allow to process first the oldest blocks */
timemap = malloc_nofail(blockmax * sizeof(time_t));
/* copy the info in the temp vector */
count = 0;
log_tag("block_count:%u\n", blockmax);
for (i = 0; i < blockmax; ++i) {
snapraid_info info = info_get(&state->infoarr, i);
/* skip unused blocks */
if (info == 0)
continue;
timemap[count++] = info_get_time(info);
}
if (!count) {
/* LCOV_EXCL_START */
log_fatal("The array appears to be empty.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* sort it */
qsort(timemap, count, sizeof(time_t), time_compare);
/* output the info map */
i = 0;
log_tag("info_count:%u\n", count);
while (i < count) {
unsigned j = i + 1;
while (j < count && timemap[i] == timemap[j])
++j;
log_tag("info_time:%" PRIu64 ":%u\n", (uint64_t)timemap[i], j - i);
i = j;
}
/* compute the limits from count/recentlimit */
if (ps.plan == SCRUB_AUTO) {
/* no more than the full count */
if (countlimit > count)
countlimit = count;
/* decrease until we reach the specific recentlimit */
while (countlimit > 0 && timemap[countlimit - 1] > recentlimit)
--countlimit;
/* if there is something to scrub */
if (countlimit > 0) {
/* get the most recent time we want to scrub */
ps.timelimit = timemap[countlimit - 1];
/* count how many entries for this exact time we have to scrub */
/* if the blocks have all the same time, we end with countlimit == lastlimit */
ps.lastlimit = 1;
while (countlimit > ps.lastlimit && timemap[countlimit - ps.lastlimit - 1] == ps.timelimit)
++ps.lastlimit;
} else {
/* if nothing to scrub, disable also other limits */
ps.timelimit = 0;
ps.lastlimit = 0;
}
log_tag("count_limit:%u\n", countlimit);
log_tag("time_limit:%" PRIu64 "\n", (uint64_t)ps.timelimit);
log_tag("last_limit:%u\n", ps.lastlimit);
}
/* free the temp vector */
free(timemap);
/* open the file for reading */
for (l = 0; l < state->level; ++l) {
ret = parity_open(&parity_handle[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without an accessible %s file, it isn't possible to scrub.\n", lev_name(l));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
error = 0;
ret = state_scrub_process(state, parity_handle, 0, blockmax, &ps, now);
if (ret == -1) {
++error;
/* continue, as we are already exiting */
}
for (l = 0; l < state->level; ++l) {
ret = parity_close(&parity_handle[l]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Unexpected close error in %s disk.\n", lev_name(l));
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
/* abort if required */
if (error != 0)
return -1;
return 0;
}
snapraid-12.1/cmdline/search.c 0000664 0000000 0000000 00000020442 14166610522 0016267 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2014 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "search.h"
/****************************************************************************/
/* search */
static void search_file(struct snapraid_state* state, const char* path, data_off_t size, int64_t mtime_sec, int mtime_nsec)
{
struct snapraid_search_file* file;
tommy_uint32_t file_hash;
file = malloc_nofail(sizeof(struct snapraid_search_file));
file->path = strdup_nofail(path);
file->size = size;
file->mtime_sec = mtime_sec;
file->mtime_nsec = mtime_nsec;
file_hash = file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec);
tommy_hashdyn_insert(&state->searchset, &file->node, file, file_hash);
}
void search_file_free(struct snapraid_search_file* file)
{
free(file->path);
free(file);
}
struct search_file_compare_arg {
const struct snapraid_state* state;
const struct snapraid_block* block;
const struct snapraid_file* file;
unsigned char* buffer;
data_off_t offset;
unsigned read_size;
int prevhash;
};
int search_file_compare(const void* void_arg, const void* void_data)
{
const struct search_file_compare_arg* arg = void_arg;
const struct snapraid_search_file* file = void_data;
const struct snapraid_state* state = arg->state;
unsigned char buffer_hash[HASH_MAX];
const char* path = file->path;
int f;
ssize_t ret;
/* compare file info */
if (arg->file->size != file->size)
return -1;
if (arg->file->mtime_sec != file->mtime_sec)
return -1;
if (arg->file->mtime_nsec != file->mtime_nsec)
return -1;
/* read the block and compare the hash */
f = open(path, O_RDONLY | O_BINARY);
if (f == -1) {
/* LCOV_EXCL_START */
if (errno == ENOENT) {
log_fatal("DANGER! file '%s' disappeared.\n", path);
log_fatal("If you moved it, please rerun the same command.\n");
} else {
log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = pread(f, arg->buffer, arg->read_size, arg->offset);
if (ret < 0 || (unsigned)ret != arg->read_size) {
/* LCOV_EXCL_START */
log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = close(f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* compute the hash */
if (arg->prevhash)
memhash(state->prevhash, state->prevhashseed, buffer_hash, arg->buffer, arg->read_size);
else
memhash(state->hash, state->hashseed, buffer_hash, arg->buffer, arg->read_size);
/* check if the hash is matching */
if (memcmp(buffer_hash, arg->block->hash, BLOCK_HASH_SIZE) != 0)
return -1;
if (arg->read_size != state->block_size) {
/* fill the remaining with 0 */
memset(arg->buffer + arg->read_size, 0, state->block_size - arg->read_size);
}
return 0;
}
int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_file* missing_file, block_off_t missing_file_pos, struct snapraid_block* missing_block, unsigned char* buffer)
{
struct snapraid_search_file* file;
tommy_uint32_t file_hash;
struct search_file_compare_arg arg;
arg.state = state;
arg.block = missing_block;
arg.file = missing_file;
arg.buffer = buffer;
arg.offset = state->block_size * (data_off_t)missing_file_pos;
arg.read_size = file_block_size(missing_file, missing_file_pos, state->block_size);
arg.prevhash = prevhash;
file_hash = file_stamp_hash(arg.file->size, arg.file->mtime_sec, arg.file->mtime_nsec);
/* search in the hashtable, and also check if the data matches the hash */
file = tommy_hashdyn_search(&state->searchset, search_file_compare, &arg, file_hash);
if (!file)
return -1;
/* if found, buffer is already set with data */
return 0;
}
static void search_dir(struct snapraid_state* state, struct snapraid_disk* disk, const char* dir, const char* sub)
{
DIR* d;
d = opendir(dir);
if (!d) {
/* LCOV_EXCL_START */
log_fatal("Error opening directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
while (1) {
char path_next[PATH_MAX];
char sub_next[PATH_MAX];
char out[PATH_MAX];
struct snapraid_filter* reason = 0;
struct stat st;
const char* name;
struct dirent* dd;
/* clear errno to detect erroneous conditions */
errno = 0;
dd = readdir(d);
if (dd == 0 && errno != 0) {
/* LCOV_EXCL_START */
log_fatal("Error reading directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (dd == 0) {
break; /* finished */
}
/* skip "." and ".." files */
name = dd->d_name;
if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;
pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
pathprint(sub_next, sizeof(sub_next), "%s%s", sub, name);
/* exclude hidden files even before calling lstat() */
if (disk != 0 && filter_hidden(state->filter_hidden, dd) != 0) {
msg_verbose("Excluding hidden '%s'\n", path_next);
continue;
}
/* exclude content files even before calling lstat() */
if (disk != 0 && filter_content(&state->contentlist, path_next) != 0) {
msg_verbose("Excluding content '%s'\n", path_next);
continue;
}
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &st);
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
/* because we use a directory reading method that doesn't read info about ReparsePoint. */
/* Note that here we cannot call here lstat_sync(), because we don't know what kind */
/* of file is it, and lstat_sync() doesn't always work */
if (st.st_mode == 0) {
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#else
/* get lstat info about the file */
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
if (S_ISREG(st.st_mode)) {
if (disk == 0 || filter_path(&state->filterlist, &reason, disk->name, sub_next) == 0) {
search_file(state, path_next, st.st_size, st.st_mtime, STAT_NSEC(&st));
} else {
msg_verbose("Excluding link '%s' for rule '%s'\n", path_next, filter_type(reason, out, sizeof(out)));
}
} else if (S_ISDIR(st.st_mode)) {
if (disk == 0 || filter_subdir(&state->filterlist, &reason, disk->name, sub_next) == 0) {
pathslash(path_next, sizeof(path_next));
pathslash(sub_next, sizeof(sub_next));
search_dir(state, disk, path_next, sub_next);
} else {
msg_verbose("Excluding directory '%s' for rule '%s'\n", path_next, filter_type(reason, out, sizeof(out)));
}
}
}
if (closedir(d) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
void state_search(struct snapraid_state* state, const char* dir)
{
char path[PATH_MAX];
msg_progress("Importing...\n");
/* add the final slash */
pathimport(path, sizeof(path), dir);
pathslash(path, sizeof(path));
search_dir(state, 0, path, "");
}
void state_search_array(struct snapraid_state* state)
{
tommy_node* i;
/* import from all the disks */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
/* skip data disks that are not accessible */
if (disk->skip_access)
continue;
msg_progress("Searching disk %s...\n", disk->name);
search_dir(state, disk, disk->dir, "");
}
}
snapraid-12.1/cmdline/search.h 0000664 0000000 0000000 00000003432 14166610522 0016274 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2014 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __SEARCH_H
#define __SEARCH_H
#include "elem.h"
#include "state.h"
/****************************************************************************/
/* search */
/**
* Search file.
* File used to search for moved data.
*/
struct snapraid_search_file {
char* path; /**< Full path of the file. */
char* name; /**< Pointer of the name inside the path. */
data_off_t size;
int64_t mtime_sec;
int mtime_nsec;
/* nodes for data structures */
tommy_node node;
};
/**
* Deallocate a search file.
*/
void search_file_free(struct snapraid_search_file* file);
/**
* Fetch a file from the size, timestamp and name.
* Return ==0 if the block is found, and copied into buffer.
*/
int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_file* missing_file, block_off_t missing_file_pos, struct snapraid_block* missing_block, unsigned char* buffer);
/**
* Import files from the specified directory.
*/
void state_search(struct snapraid_state* state, const char* dir);
/**
* Import files from all the data disks.
*/
void state_search_array(struct snapraid_state* state);
#endif
snapraid-12.1/cmdline/selftest.c 0000664 0000000 0000000 00000045705 14166610522 0016664 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "snapraid.h"
#include "util.h"
#include "raid/raid.h"
#include "raid/cpu.h"
#include "raid/combo.h"
#include "raid/internal.h"
#include "raid/test.h"
#include "elem.h"
#include "state.h"
#include "support.h"
#include "tommyds/tommyhash.h"
#include "tommyds/tommyarray.h"
#include "tommyds/tommyarrayblkof.h"
#include "tommyds/tommyhashdyn.h"
struct hash32_test_vector {
const char* data;
int len;
uint32_t digest;
};
struct strhash32_test_vector {
char* data;
uint32_t digest;
};
struct hash64_test_vector {
const char* data;
int len;
uint64_t digest;
};
struct hash_test_vector {
const char* data;
int len;
unsigned char digest[HASH_MAX];
};
/**
* Test vectors for tommy_hash32
*/
static struct hash32_test_vector TEST_HASH32[] = {
{ "", 0, 0x8614384c },
{ "a", 1, 0x12c16c36 },
{ "abc", 3, 0xc58e8af5 },
{ "message digest", 14, 0x006b32f1 },
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x7e6fcfe0 },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0x8604adf8 },
{ "The quick brown fox jumps over the lazy dog", 43, 0xdeba3d3a },
{ "\x00", 1, 0x4a7d1c33 },
{ "\x16\x27", 2, 0x8b50899b },
{ "\xe2\x56\xb4", 3, 0x60406493 },
{ "\xc9\x4d\x9c\xda", 4, 0xa049144a },
{ "\x79\xf1\x29\x69\x5d", 5, 0x4da2c2f1 },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0x59de30cf },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0x219e149c },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0x25067520 },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0xa1f368d8 },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0x805fc63d },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0x7f75dd0f },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0xb9154382 },
{ 0, 0, 0 }
};
/**
* Test vectors for tommy_strhash32
*/
struct strhash32_test_vector TEST_STRHASH32[] = {
{ "", 0x0af1416d },
{ "a", 0x68fa0f3f },
{ "abc", 0xfc68ffc5 },
{ "message digest", 0x08477b63 },
{ "abcdefghijklmnopqrstuvwxyz", 0x5b9c25e5 },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x1e530ce7 },
{ "The quick brown fox jumps over the lazy dog", 0xaf93eefe },
{ "\xff", 0xfc88801b },
{ "\x16\x27", 0xcd7216db },
{ "\xe2\x56\xb4", 0x05f98d02 },
{ "\xc9\x4d\x9c\xda", 0xf65206f8 },
{ "\x79\xf1\x29\x69\x5d", 0x72bd6bda },
{ "\xff\x7e\xdf\x1e\x31\x1c", 0x57dfb9b4 },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 0x499ff634 },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 0xe896b7ce },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 0xfe3939f0 },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 0x4351d482 },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\xff\xb7\xae", 0x88e92135 },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 0x01109c16 },
{ "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 0xbcb050dc },
{ "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 0xbe5e1fd5 },
{ "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 0x70d8c97f },
{ "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 0x957440a9 },
{ 0, 0 }
};
/**
* Test vectors for tommy_hash64
*/
static struct hash64_test_vector TEST_HASH64[] = {
{ "", 0, 0x8614384cb5165fbfULL },
{ "a", 1, 0x1a2e0298a8e94a3dULL },
{ "abc", 3, 0x7555796b7a7d21ebULL },
{ "message digest", 14, 0x9411a57d04b92fb4ULL },
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x3ca3f8d2b4e69832ULL },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0x6dae542ba0015a4dULL },
{ "The quick brown fox jumps over the lazy dog", 43, 0xe06d8cbb3d2ea1a6ULL },
{ "\x00", 1, 0x201e664fb5f2c021ULL },
{ "\x16\x27", 2, 0xef42fa8032c4b775ULL },
{ "\xe2\x56\xb4", 3, 0x6e6c498a6688466cULL },
{ "\xc9\x4d\x9c\xda", 4, 0x5195005419905423ULL },
{ "\x79\xf1\x29\x69\x5d", 5, 0x221235b48afee7c1ULL },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0x1b1f18b9266f095bULL },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0x2cbafa8e741d49caULL },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0x4677f04c06e0758dULL },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0x5afe09e8214e2163ULL },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0x115b6276d209fab6ULL },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0xd0636d2f01cf3a3eULL },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0x6d259f5fef74f93eULL },
{ 0, 0, 0 }
};
/**
* Test vectors for MurmorHash3_x86_128
*/
static struct hash_test_vector TEST_MURMUR3[] = {
#include "murmur3test.c"
{ 0, 0, { 0 } }
};
/**
* Test vectors for SpookyHash_128
*/
static struct hash_test_vector TEST_SPOOKY2[] = {
#include "spooky2test.c"
{ 0, 0, { 0 } }
};
#define HASH_TEST_MAX 512 /* tests are never longer than 512 bytes */
static void test_hash(void)
{
unsigned i;
unsigned char* seed_aligned;
void* seed_alloc;
unsigned char* buffer_aligned;
void* buffer_alloc;
uint32_t seed32;
uint64_t seed64;
seed_aligned = malloc_nofail_align(HASH_MAX, &seed_alloc);
buffer_aligned = malloc_nofail_align(HASH_TEST_MAX, &buffer_alloc);
seed32 = 0xa766795d;
seed64 = 0x2f022773a766795dULL;
seed_aligned[0] = 0x5d;
seed_aligned[1] = 0x79;
seed_aligned[2] = 0x66;
seed_aligned[3] = 0xa7;
seed_aligned[4] = 0x73;
seed_aligned[5] = 0x27;
seed_aligned[6] = 0x02;
seed_aligned[7] = 0x2f;
seed_aligned[8] = 0x6a;
seed_aligned[9] = 0xa1;
seed_aligned[10] = 0x9e;
seed_aligned[11] = 0xc1;
seed_aligned[12] = 0x14;
seed_aligned[13] = 0x8c;
seed_aligned[14] = 0x9e;
seed_aligned[15] = 0x43;
for (i = 0; TEST_HASH32[i].data; ++i) {
uint32_t digest;
memcpy(buffer_aligned, TEST_HASH32[i].data, TEST_HASH32[i].len);
digest = tommy_hash_u32(seed32, buffer_aligned, TEST_HASH32[i].len);
if (digest != TEST_HASH32[i].digest) {
/* LCOV_EXCL_START */
log_fatal("Failed hash32 test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (i = 0; TEST_STRHASH32[i].data; ++i) {
uint32_t digest;
memcpy(buffer_aligned, TEST_STRHASH32[i].data, strlen(TEST_STRHASH32[i].data) + 1);
digest = tommy_strhash_u32(seed32, buffer_aligned);
if (digest != TEST_STRHASH32[i].digest) {
/* LCOV_EXCL_START */
log_fatal("Failed strhash32 test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (i = 0; TEST_HASH64[i].data; ++i) {
uint64_t digest;
memcpy(buffer_aligned, TEST_HASH64[i].data, TEST_HASH64[i].len);
digest = tommy_hash_u64(seed64, buffer_aligned, TEST_HASH64[i].len);
if (digest != TEST_HASH64[i].digest) {
/* LCOV_EXCL_START */
log_fatal("Failed hash64 test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (i = 0; TEST_MURMUR3[i].data; ++i) {
unsigned char digest[HASH_MAX];
memcpy(buffer_aligned, TEST_MURMUR3[i].data, TEST_MURMUR3[i].len);
memhash(HASH_MURMUR3, seed_aligned, digest, buffer_aligned, TEST_MURMUR3[i].len);
if (memcmp(digest, TEST_MURMUR3[i].digest, HASH_MAX) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed Murmur3 test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (i = 0; TEST_SPOOKY2[i].data; ++i) {
unsigned char digest[HASH_MAX];
memcpy(buffer_aligned, TEST_SPOOKY2[i].data, TEST_SPOOKY2[i].len);
memhash(HASH_SPOOKY2, seed_aligned, digest, buffer_aligned, TEST_SPOOKY2[i].len);
if (memcmp(digest, TEST_SPOOKY2[i].digest, HASH_MAX) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed Spooky2 test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
free(buffer_alloc);
free(seed_alloc);
}
struct crc_test_vector {
const char* data;
int len;
uint32_t digest;
};
/**
* Test vectors for CRC32C (Castagnoli)
*/
static struct crc_test_vector TEST_CRC32C[] = {
{ "", 0, 0 },
{ "\x61", 1, 0xc1d04330 },
{ "\x66\x6f\x6f", 3, 0xcfc4ae1d },
{ "\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64", 11, 0xc99465aa },
{ "\x68\x65\x6c\x6c\x6f\x20", 6, 0x7e627e58 },
{ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32, 0x8a9136aa },
{ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 32, 0x62a8ab43 },
{ "\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18\x17\x16\x15\x14\x13\x12\x11\x10\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00", 32, 0x113fdb5c },
{ "\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x14\x00\x00\x00\x18\x28\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", 48, 0xd9963a56 },
{ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 32, 0x46dd794e },
{ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28", 40, 0x0e2c157f },
{ "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50", 40, 0xe980ebf6 },
{ "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78", 40, 0xde74bded },
{ "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0", 40, 0xd579c862 },
{ "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8", 40, 0xba979ad0 },
{ "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0", 40, 0x2b29d913 },
{ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0", 240, 0x24c5d375 },
{ 0, 0, 0 }
};
static void test_crc32c(void)
{
unsigned i;
for (i = 0; TEST_CRC32C[i].data; ++i) {
uint32_t digest;
uint32_t digest_gen;
digest = crc32c(0, (const unsigned char*)TEST_CRC32C[i].data, TEST_CRC32C[i].len);
digest_gen = crc32c_gen(0, (const unsigned char*)TEST_CRC32C[i].data, TEST_CRC32C[i].len);
if (digest != TEST_CRC32C[i].digest || digest_gen != TEST_CRC32C[i].digest) {
/* LCOV_EXCL_START */
log_fatal("Failed CRC32C test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
/**
* Size of tommy data structures.
*/
#define TOMMY_SIZE 256
static int tommy_test_search(const void* arg, const void* obj)
{
return arg != obj;
}
static int tommy_test_compare(const void* void_arg_a, const void* void_arg_b)
{
if (void_arg_a < void_arg_b)
return -1;
if (void_arg_a > void_arg_b)
return 1;
return 0;
}
static unsigned tommy_test_foreach_count;
static void tommy_test_foreach(void* obj)
{
(void)obj;
++tommy_test_foreach_count;
}
static void tommy_test_foreach_arg(void* void_arg, void* obj)
{
unsigned* arg = void_arg;
(void)obj;
++*arg;
}
static void test_tommy(void)
{
tommy_array array;
tommy_arrayblkof arrayblkof;
tommy_list list;
tommy_hashdyn hashdyn;
tommy_tree tree;
tommy_node node[TOMMY_SIZE + 1];
unsigned i;
tommy_array_init(&array);
tommy_arrayblkof_init(&arrayblkof, sizeof(unsigned));
for (i = 0; i < TOMMY_SIZE; ++i) {
tommy_array_insert(&array, &node[i]);
tommy_arrayblkof_grow(&arrayblkof, i + 1);
*(unsigned*)tommy_arrayblkof_ref(&arrayblkof, i) = i;
}
tommy_array_grow(&array, TOMMY_SIZE);
tommy_arrayblkof_grow(&arrayblkof, TOMMY_SIZE);
if (tommy_array_memory_usage(&array) < TOMMY_SIZE * sizeof(void*)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_arrayblkof_memory_usage(&arrayblkof) < TOMMY_SIZE * sizeof(unsigned)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
for (i = 0; i < TOMMY_SIZE; ++i) {
if (tommy_array_get(&array, i) != &node[i]) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (*(unsigned*)tommy_arrayblkof_ref(&arrayblkof, i) != i) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
tommy_arrayblkof_done(&arrayblkof);
tommy_array_done(&array);
tommy_list_init(&list);
if (!tommy_list_empty(&list)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_list_tail(&list)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_list_head(&list)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
tommy_list_insert_tail(&list, &node[0], &node[0]);
if (tommy_list_tail(&list) != tommy_list_head(&list)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
tommy_hashdyn_init(&hashdyn);
for (i = 0; i < TOMMY_SIZE; ++i)
tommy_hashdyn_insert(&hashdyn, &node[i], &node[i], i % 64);
if (tommy_hashdyn_count(&hashdyn) != TOMMY_SIZE) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_hashdyn_memory_usage(&hashdyn) < TOMMY_SIZE * sizeof(tommy_node)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
tommy_test_foreach_count = 0;
tommy_hashdyn_foreach(&hashdyn, tommy_test_foreach);
if (tommy_test_foreach_count != TOMMY_SIZE) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
tommy_test_foreach_count = 0;
tommy_hashdyn_foreach_arg(&hashdyn, tommy_test_foreach_arg, &tommy_test_foreach_count);
if (tommy_test_foreach_count != TOMMY_SIZE) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
for (i = 0; i < TOMMY_SIZE / 2; ++i)
tommy_hashdyn_remove_existing(&hashdyn, &node[i]);
for (i = 0; i < TOMMY_SIZE / 2; ++i) {
if (tommy_hashdyn_remove(&hashdyn, tommy_test_search, &node[i], i % 64) != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
for (i = TOMMY_SIZE / 2; i < TOMMY_SIZE; ++i) {
if (tommy_hashdyn_remove(&hashdyn, tommy_test_search, &node[i], i % 64) == 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
if (tommy_hashdyn_count(&hashdyn) != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
tommy_hashdyn_done(&hashdyn);
tommy_tree_init(&tree, tommy_test_compare);
for (i = 0; i < TOMMY_SIZE; ++i)
tommy_tree_insert(&tree, &node[i], (void*)(uintptr_t)(i + 1));
/* try to insert a duplicate, count should not change */
tommy_tree_insert(&tree, &node[TOMMY_SIZE], (void*)(uintptr_t)1);
if (tommy_tree_count(&tree) != TOMMY_SIZE) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_tree_memory_usage(&tree) < TOMMY_SIZE * sizeof(tommy_node)) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_tree_search(&tree, (void*)1) != (void*)1) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_tree_search(&tree, (void*)-1) != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_tree_search_compare(&tree, tommy_test_compare, (void*)1) != (void*)1) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_tree_search_compare(&tree, tommy_test_compare, (void*)-1) != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
tommy_test_foreach_count = 0;
tommy_tree_foreach(&tree, tommy_test_foreach);
if (tommy_test_foreach_count != TOMMY_SIZE) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
tommy_test_foreach_count = 0;
tommy_tree_foreach_arg(&tree, tommy_test_foreach_arg, &tommy_test_foreach_count);
if (tommy_test_foreach_count != TOMMY_SIZE) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
for (i = 0; i < TOMMY_SIZE / 2; ++i)
tommy_tree_remove_existing(&tree, &node[i]);
for (i = 0; i < TOMMY_SIZE / 2; ++i) {
if (tommy_tree_remove(&tree, (void*)(uintptr_t)(i + 1)) != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
for (i = TOMMY_SIZE / 2; i < TOMMY_SIZE; ++i) {
if (tommy_tree_remove(&tree, (void*)(uintptr_t)(i + 1)) == 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
if (tommy_tree_count(&tree) != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
return;
bail:
/* LCOV_EXCL_START */
log_fatal("Failed tommy test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
void selftest(void)
{
log_tag("selftest:\n");
log_flush();
msg_progress("Self test...\n");
/* large file check */
if (sizeof(off_t) < sizeof(uint64_t)) {
/* LCOV_EXCL_START */
log_fatal("Missing support for large files\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
test_hash();
test_crc32c();
test_tommy();
if (raid_selftest() != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed SELF test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_sort() != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed SORT test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_insert() != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed INSERT test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_combo() != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed COMBO test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_par(RAID_MODE_VANDERMONDE, 32, 256) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed GEN Vandermonde test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_rec(RAID_MODE_VANDERMONDE, 12, 256) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed REC Vandermonde test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_par(RAID_MODE_CAUCHY, 32, 256) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed GEN Cauchy test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_rec(RAID_MODE_CAUCHY, 12, 256) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed REC Cauchy test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (raid_test_par(RAID_MODE_CAUCHY, 1, 256) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed GEN Cauchy test single data disk\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
snapraid-12.1/cmdline/snapraid.c 0000664 0000000 0000000 00000122715 14166610522 0016631 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "snapraid.h"
#include "support.h"
#include "elem.h"
#include "import.h"
#include "search.h"
#include "state.h"
#include "io.h"
#include "raid/raid.h"
/****************************************************************************/
/* main */
void version(void)
{
msg_status(PACKAGE " v" VERSION " by Andrea Mazzoleni, " PACKAGE_URL "\n");
}
void usage(void)
{
version();
printf("Usage: " PACKAGE " status|diff|sync|scrub|list|dup|up|down|smart|pool|check|fix [options]\n");
printf("\n");
printf("Commands:\n");
printf(" status Print the status of the array\n");
printf(" diff Show the changes that needs to be synchronized\n");
printf(" sync Synchronize the state of the array\n");
printf(" scrub Scrub the array\n");
printf(" list List the array content\n");
printf(" dup Find duplicate files\n");
printf(" up Spin-up the array\n");
printf(" down Spin-down the array\n");
printf(" smart SMART attributes of the array\n");
printf(" pool Create or update the virtual view of the array\n");
printf(" check Check the array\n");
printf(" fix Fix the array\n");
printf("\n");
printf("Options:\n");
printf(" " SWITCH_GETOPT_LONG("-c, --conf FILE ", "-c") " Configuration file\n");
printf(" " SWITCH_GETOPT_LONG("-f, --filter PATTERN ", "-f") " Process only files matching the pattern\n");
printf(" " SWITCH_GETOPT_LONG("-d, --filter-disk NAME", "-f") " Process only files in the specified disk\n");
printf(" " SWITCH_GETOPT_LONG("-m, --filter-missing ", "-m") " Process only missing/deleted files\n");
printf(" " SWITCH_GETOPT_LONG("-e, --filter-error ", "-e") " Process only files with errors\n");
printf(" " SWITCH_GETOPT_LONG("-p, --plan PLAN ", "-p") " Define a scrub plan or percentage\n");
printf(" " SWITCH_GETOPT_LONG("-o, --older-than DAYS ", "-o") " Process only the older part of the array\n");
printf(" " SWITCH_GETOPT_LONG("-i, --import DIR ", "-i") " Import deleted files\n");
printf(" " SWITCH_GETOPT_LONG("-l, --log FILE ", "-l") " Log file. Default none\n");
printf(" " SWITCH_GETOPT_LONG("-a, --audit-only ", "-a") " Check only file data and not parity\n");
printf(" " SWITCH_GETOPT_LONG("-h, --pre-hash ", "-h") " Pre-hash all the new data\n");
printf(" " SWITCH_GETOPT_LONG("-Z, --force-zero ", "-Z") " Force syncing of files that get zero size\n");
printf(" " SWITCH_GETOPT_LONG("-E, --force-empty ", "-E") " Force syncing of disks that get empty\n");
printf(" " SWITCH_GETOPT_LONG("-U, --force-uuid ", "-U") " Force commands on disks with uuid changed\n");
printf(" " SWITCH_GETOPT_LONG("-D, --force-device ", "-D") " Force commands with inaccessible/shared disks\n");
printf(" " SWITCH_GETOPT_LONG("-N, --force-nocopy ", "-N") " Force commands disabling the copy detection\n");
printf(" " SWITCH_GETOPT_LONG("-F, --force-full ", "-F") " Force a full parity computation in sync\n");
printf(" " SWITCH_GETOPT_LONG("-R, --force-realloc ", "-R") " Force a full parity reallocation in sync\n");
printf(" " SWITCH_GETOPT_LONG("-v, --verbose ", "-v") " Verbose\n");
}
void memory(void)
{
log_tag("memory:used:%" PRIu64 "\n", (uint64_t)malloc_counter_get());
/* size of the block */
log_tag("memory:block:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_block)));
log_tag("memory:extent:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_extent)));
log_tag("memory:file:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_file)));
log_tag("memory:link:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_link)));
log_tag("memory:dir:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_dir)));
msg_progress("Using %u MiB of memory for the file-system.\n", (unsigned)(malloc_counter_get() / MEBI));
}
void test(int argc, char* argv[])
{
int i;
char buffer[ESC_MAX];
/* special testing code for quoting */
if (argc < 2 || strcmp(argv[1], "test") != 0)
return;
for (i = 2; i < argc; ++i) {
printf("argv[%d]\n", i);
printf("\t#%s#\n", argv[i]);
printf("\t#%s#\n", esc_shell(argv[i], buffer));
}
#ifdef _WIN32
assert(strcmp(esc_shell(" ", buffer), "\" \"") == 0);
assert(strcmp(esc_shell(" \" ", buffer), "\" \"\\\"\" \"") == 0);
assert(strcmp(esc_shell("&|()<>^", buffer), "^&^|^(^)^<^>^^") == 0);
assert(strcmp(esc_shell("&|()<>^ ", buffer), "\"&|()<>^ \"") == 0);
#else
assert(strcmp(esc_shell(",._+:@%%/-", buffer), ",._+:@%%/-") == 0);
assert(strcmp(esc_shell(" ", buffer), "\\ ") == 0);
#endif
printf("Everything OK\n");
exit(EXIT_SUCCESS);
}
/****************************************************************************/
/* log */
void log_open(const char* file)
{
char path[PATH_MAX];
const char* mode;
char text_T[32];
char text_D[32];
time_t t;
struct tm* tm;
#if HAVE_LOCALTIME_R
struct tm tm_res;
#endif
/* leave stdlog at 0 if not specified */
if (file == 0)
return;
t = time(0);
#if HAVE_LOCALTIME_R
tm = localtime_r(&t, &tm_res);
#else
tm = localtime(&t);
#endif
if (tm) {
strftime(text_T, sizeof(text_T), "%H%M%S", tm);
strftime(text_D, sizeof(text_T), "%Y%m%d", tm);
} else {
/* LCOV_EXCL_START */
strcpy(text_T, "invalid");
strcpy(text_D, "invalid");
/* LCOV_EXCL_STOP */
}
/* file mode */
mode = "wt";
if (*file == '>') {
++file;
if (*file == '>') {
mode = "at";
++file;
}
if (file[0] == '&' && file[1] == '1') {
stdlog = stdout;
return;
}
if (file[0] == '&' && file[1] == '2') {
stdlog = stderr;
return;
}
}
/* process the path */
for (*path = 0; *file != 0; ) {
switch (*file) {
case '%' :
++file;
switch (*file) {
case '%' :
pathcatc(path, sizeof(path), '%');
break;
case 'T' :
pathcat(path, sizeof(path), text_T);
break;
case 'D' :
pathcat(path, sizeof(path), text_D);
break;
default :
/* LCOV_EXCL_START */
log_fatal("Invalid type specifier '%c' in the log file.\n", *file);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
break;
default :
pathcatc(path, sizeof(path), *file);
break;
}
++file;
}
stdlog = fopen(path, mode);
if (!stdlog) {
/* LCOV_EXCL_START */
log_fatal("Error opening the log file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
void log_close(const char* file)
{
if (stdlog != stdout && stdlog != stderr && stdlog != 0) {
if (fclose(stdlog) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing the log file '%s'. %s.\n", file, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
stdlog = 0;
}
/****************************************************************************/
/* config */
void config(char* conf, size_t conf_size, const char* argv0)
{
#ifdef _WIN32
char* slash;
pathimport(conf, conf_size, argv0);
slash = strrchr(conf, '/');
if (slash) {
slash[1] = 0;
pathcat(conf, conf_size, PACKAGE ".conf");
} else {
pathcpy(conf, conf_size, PACKAGE ".conf");
}
#else
(void)argv0;
#ifdef SYSCONFDIR
/* if it exists, give precedence at sysconfdir, usually /usr/local/etc */
if (access(SYSCONFDIR "/" PACKAGE ".conf", F_OK) == 0)
pathcpy(conf, conf_size, SYSCONFDIR "/" PACKAGE ".conf");
else /* otherwise fallback to plain /etc */
#endif
pathcpy(conf, conf_size, "/etc/" PACKAGE ".conf");
#endif
}
/****************************************************************************/
/* main */
#define OPT_TEST_SKIP_SELF 256
#define OPT_TEST_KILL_AFTER_SYNC 257
#define OPT_TEST_EXPECT_UNRECOVERABLE 258
#define OPT_TEST_EXPECT_RECOVERABLE 259
#define OPT_TEST_SKIP_SIGN 260
#define OPT_TEST_SKIP_FALLOCATE 261
#define OPT_TEST_SKIP_DEVICE 262
#define OPT_TEST_FORCE_MURMUR3 264
#define OPT_TEST_FORCE_SPOOKY2 265
#define OPT_TEST_SKIP_LOCK 266
#define OPT_TEST_FORCE_ORDER_PHYSICAL 267
#define OPT_TEST_FORCE_ORDER_INODE 268
#define OPT_TEST_FORCE_ORDER_ALPHA 269
#define OPT_TEST_FORCE_ORDER_DIR 270
#define OPT_TEST_FORCE_SCRUB_AT 271
#define OPT_TEST_FORCE_SCRUB_EVEN 272
#define OPT_TEST_FORCE_CONTENT_WRITE 273
#define OPT_TEST_SKIP_CONTENT_CHECK 275
#define OPT_TEST_SKIP_PARITY_ACCESS 276
#define OPT_TEST_EXPECT_FAILURE 277
#define OPT_TEST_RUN 278
#define OPT_TEST_FORCE_SCAN_WINFIND 279
#define OPT_TEST_IMPORT_CONTENT 280
#define OPT_TEST_FORCE_PROGRESS 281
#define OPT_TEST_SKIP_DISK_ACCESS 282
#define OPT_TEST_FORCE_AUTOSAVE_AT 283
#define OPT_TEST_FAKE_DEVICE 284
#define OPT_TEST_EXPECT_NEED_SYNC 285
#define OPT_NO_WARNINGS 286
#define OPT_TEST_FAKE_UUID 287
#define OPT_TEST_MATCH_FIRST_UUID 288
#define OPT_TEST_FORCE_PARITY_UPDATE 289
#define OPT_TEST_IO_CACHE 290
#define OPT_TEST_IO_STATS 291
#define OPT_TEST_COND_SIGNAL_OUTSIDE 292
#define OPT_TEST_IO_ADVISE_NONE 293
#define OPT_TEST_IO_ADVISE_SEQUENTIAL 294
#define OPT_TEST_IO_ADVISE_FLUSH 295
#define OPT_TEST_IO_ADVISE_FLUSH_WINDOW 296
#define OPT_TEST_IO_ADVISE_DISCARD 297
#define OPT_TEST_IO_ADVISE_DISCARD_WINDOW 298
#define OPT_TEST_IO_ADVISE_DIRECT 299
#define OPT_TEST_PARITY_LIMIT 301
#define OPT_TEST_SKIP_CONTENT_WRITE 302
#define OPT_TEST_SKIP_SPACE_HOLDER 303
#define OPT_TEST_FORMAT 304
#define OPT_TEST_SKIP_MULTI_SCAN 305
#if HAVE_GETOPT_LONG
struct option long_options[] = {
{ "conf", 1, 0, 'c' },
{ "filter", 1, 0, 'f' },
{ "filter-disk", 1, 0, 'd' },
{ "filter-missing", 0, 0, 'm' },
{ "filter-error", 0, 0, 'e' },
{ "filter-block-error", 0, 0, 'b' },
{ "percentage", 1, 0, 'p' }, /* legacy name for --plan */
{ "plan", 1, 0, 'p' },
{ "older-than", 1, 0, 'o' },
{ "start", 1, 0, 'S' },
{ "count", 1, 0, 'B' },
{ "error-limit", 1, 0, 'L' },
{ "import", 1, 0, 'i' },
{ "log", 1, 0, 'l' },
{ "force-zero", 0, 0, 'Z' },
{ "force-empty", 0, 0, 'E' },
{ "force-uuid", 0, 0, 'U' },
{ "force-device", 0, 0, 'D' },
{ "force-nocopy", 0, 0, 'N' },
{ "force-full", 0, 0, 'F' },
{ "force-realloc", 0, 0, 'R' },
{ "audit-only", 0, 0, 'a' },
{ "pre-hash", 0, 0, 'h' },
{ "speed-test", 0, 0, 'T' }, /* undocumented speed test command */
{ "gen-conf", 1, 0, 'C' },
{ "verbose", 0, 0, 'v' },
{ "quiet", 0, 0, 'q' }, /* undocumented quiet option */
{ "gui", 0, 0, 'G' }, /* undocumented GUI interface option */
{ "help", 0, 0, 'H' },
{ "version", 0, 0, 'V' },
/* The following are test specific options, DO NOT USE! */
/* After syncing, do not write the new content file */
{ "test-kill-after-sync", 0, 0, OPT_TEST_KILL_AFTER_SYNC },
/* Exit with failure if after check/fix there ARE NOT unrecoverable errors. */
{ "test-expect-unrecoverable", 0, 0, OPT_TEST_EXPECT_UNRECOVERABLE },
/* Exit with failure if after check/fix there ARE NOT recoverable errors. */
{ "test-expect-recoverable", 0, 0, OPT_TEST_EXPECT_RECOVERABLE },
/* Skip the initial self test */
{ "test-skip-self", 0, 0, OPT_TEST_SKIP_SELF },
/* Skip the initial sign check when reading the content file */
{ "test-skip-sign", 0, 0, OPT_TEST_SKIP_SIGN },
/* Skip the fallocate() when growing the parity files */
{ "test-skip-fallocate", 0, 0, OPT_TEST_SKIP_FALLOCATE },
/* Skip the device check */
{ "test-skip-device", 0, 0, OPT_TEST_SKIP_DEVICE },
/* Force Murmur3 hash */
{ "test-force-murmur3", 0, 0, OPT_TEST_FORCE_MURMUR3 },
/* Force Spooky2 hash */
{ "test-force-spooky2", 0, 0, OPT_TEST_FORCE_SPOOKY2 },
/* Skip the use of lock file */
{ "test-skip-lock", 0, 0, OPT_TEST_SKIP_LOCK },
/* Force a sort order for files */
{ "test-force-order-physical", 0, 0, OPT_TEST_FORCE_ORDER_PHYSICAL },
{ "test-force-order-inode", 0, 0, OPT_TEST_FORCE_ORDER_INODE },
{ "test-force-order-alpha", 0, 0, OPT_TEST_FORCE_ORDER_ALPHA },
{ "test-force-order-dir", 0, 0, OPT_TEST_FORCE_ORDER_DIR },
/* Force scrub of the specified number of blocks */
{ "test-force-scrub-at", 1, 0, OPT_TEST_FORCE_SCRUB_AT },
/* Force scrub of all the even blocks. This is really for testing, don't try it */
{ "test-force-scrub-even", 0, 0, OPT_TEST_FORCE_SCRUB_EVEN },
/* Force write of the content file even if no modification is done */
{ "test-force-content-write", 0, 0, OPT_TEST_FORCE_CONTENT_WRITE },
/* Relax the checks done at the content file */
{ "test-skip-content-check", 0, 0, OPT_TEST_SKIP_CONTENT_CHECK },
/* Skip the parity access */
{ "test-skip-parity-access", 0, 0, OPT_TEST_SKIP_PARITY_ACCESS },
/* Exit generic failure */
{ "test-expect-failure", 0, 0, OPT_TEST_EXPECT_FAILURE },
/* Exit generic need sync */
{ "test-expect-need-sync", 0, 0, OPT_TEST_EXPECT_NEED_SYNC },
/* Run some command after loading the state and before the command */
{ "test-run", 1, 0, OPT_TEST_RUN },
/* Use the FindFirst/Next approach in Windows to list files */
{ "test-force-scan-winfind", 0, 0, OPT_TEST_FORCE_SCAN_WINFIND },
/* Alternative import working by data */
{ "test-import-content", 1, 0, OPT_TEST_IMPORT_CONTENT },
/* Force immediate progress state update */
{ "test-force-progress", 0, 0, OPT_TEST_FORCE_PROGRESS },
/* Skip the disk access */
{ "test-skip-disk-access", 0, 0, OPT_TEST_SKIP_DISK_ACCESS },
/* Force autosave at the specified block */
{ "test-force-autosave-at", 1, 0, OPT_TEST_FORCE_AUTOSAVE_AT },
/* Fake device data */
{ "test-fake-device", 0, 0, OPT_TEST_FAKE_DEVICE },
/* Disable annoying warnings */
{ "no-warnings", 0, 0, OPT_NO_WARNINGS },
/* Fake UUID */
{ "test-fake-uuid", 0, 0, OPT_TEST_FAKE_UUID },
/* Match first UUID */
{ "test-match-first-uuid", 0, 0, OPT_TEST_MATCH_FIRST_UUID },
/* Force parity update even if all the data hash is already matching */
{ "test-force-parity-update", 0, 0, OPT_TEST_FORCE_PARITY_UPDATE },
/* Number of IO buffers */
{ "test-io-cache", 1, 0, OPT_TEST_IO_CACHE },
/* Print IO stats */
{ "test-io-stats", 0, 0, OPT_TEST_IO_STATS },
/* Signal condition variable outside the mutex */
{ "test-cond-signal-outside", 0, 0, OPT_TEST_COND_SIGNAL_OUTSIDE },
/* Set the io advise to none */
{ "test-io-advise-none", 0, 0, OPT_TEST_IO_ADVISE_NONE },
/* Set the io advise to sequential */
{ "test-io-advise-sequential", 0, 0, OPT_TEST_IO_ADVISE_SEQUENTIAL },
/* Set the io advise to flush */
{ "test-io-advise-flush", 0, 0, OPT_TEST_IO_ADVISE_FLUSH },
/* Set the io advise to flush window */
{ "test-io-advise-flush-window", 0, 0, OPT_TEST_IO_ADVISE_FLUSH_WINDOW },
/* Set the io advise to discard */
{ "test-io-advise-discard", 0, 0, OPT_TEST_IO_ADVISE_DISCARD },
/* Set the io advise to discard window */
{ "test-io-advise-discard-window", 0, 0, OPT_TEST_IO_ADVISE_DISCARD_WINDOW },
/* Set the io advise to direct */
{ "test-io-advise-direct", 0, 0, OPT_TEST_IO_ADVISE_DIRECT },
/* Set an artificial parity limit */
{ "test-parity-limit", 1, 0, OPT_TEST_PARITY_LIMIT },
/* Skip content write */
{ "test-skip-content-write", 0, 0, OPT_TEST_SKIP_CONTENT_WRITE },
/* Skip space holder file in parity disks */
{ "test-skip-space-holder", 0, 0, OPT_TEST_SKIP_SPACE_HOLDER },
/* Set the output format */
{ "test-fmt", 1, 0, OPT_TEST_FORMAT },
/* Skip thread in disk scan */
{ "test-skip-multi-scan", 0, 0, OPT_TEST_SKIP_MULTI_SCAN },
{ 0, 0, 0, 0 }
};
#endif
#define OPTIONS "c:f:d:mebp:o:S:B:L:i:l:ZEUDNFRahTC:vqHVG"
volatile int global_interrupt = 0;
/* LCOV_EXCL_START */
void signal_handler(int signum)
{
(void)signum;
/* report the request of interruption */
global_interrupt = 1;
}
/* LCOV_EXCL_STOP */
void signal_init(void)
{
#if HAVE_SIGACTION
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
/* use the SA_RESTART to automatically restart interrupted system calls */
sa.sa_flags = SA_RESTART;
sigaction(SIGHUP, &sa, 0);
sigaction(SIGTERM, &sa, 0);
sigaction(SIGINT, &sa, 0);
sigaction(SIGQUIT, &sa, 0);
#else
signal(SIGINT, signal_handler);
#endif
}
#define OPERATION_DIFF 0
#define OPERATION_SYNC 1
#define OPERATION_CHECK 2
#define OPERATION_FIX 3
#define OPERATION_DRY 4
#define OPERATION_DUP 5
#define OPERATION_LIST 6
#define OPERATION_POOL 7
#define OPERATION_REHASH 8
#define OPERATION_SCRUB 9
#define OPERATION_STATUS 10
#define OPERATION_REWRITE 11
#define OPERATION_READ 12
#define OPERATION_TOUCH 13
#define OPERATION_SPINUP 14
#define OPERATION_SPINDOWN 15
#define OPERATION_DEVICES 16
#define OPERATION_SMART 17
int main(int argc, char* argv[])
{
int c;
struct snapraid_option opt;
char conf[PATH_MAX];
struct snapraid_state state;
int operation;
block_off_t blockstart;
block_off_t blockcount;
int ret;
tommy_list filterlist_file;
tommy_list filterlist_disk;
int filter_missing;
int filter_error;
int plan;
int olderthan;
char* e;
const char* command;
const char* import_timestamp;
const char* import_content;
const char* log_file;
int lock;
const char* gen_conf;
const char* run;
int speedtest;
int period;
time_t t;
struct tm* tm;
#if HAVE_LOCALTIME_R
struct tm tm_res;
#endif
int i;
test(argc, argv);
lock_init();
/* defaults */
config(conf, sizeof(conf), argv[0]);
memset(&opt, 0, sizeof(opt));
opt.io_error_limit = 100;
blockstart = 0;
blockcount = 0;
tommy_list_init(&filterlist_file);
tommy_list_init(&filterlist_disk);
period = 1000;
filter_missing = 0;
filter_error = 0;
plan = SCRUB_AUTO;
olderthan = SCRUB_AUTO;
import_timestamp = 0;
import_content = 0;
log_file = 0;
lock = 0;
gen_conf = 0;
speedtest = 0;
run = 0;
opterr = 0;
while ((c =
#if HAVE_GETOPT_LONG
getopt_long(argc, argv, OPTIONS, long_options, 0))
#else
getopt(argc, argv, OPTIONS))
#endif
!= EOF) {
switch (c) {
case 'c' :
pathimport(conf, sizeof(conf), optarg);
break;
case 'f' : {
struct snapraid_filter* filter = filter_alloc_file(1, optarg);
if (!filter) {
/* LCOV_EXCL_START */
log_fatal("Invalid filter specification '%s'\n", optarg);
log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
tommy_list_insert_tail(&filterlist_file, &filter->node, filter);
} break;
case 'd' : {
struct snapraid_filter* filter = filter_alloc_disk(1, optarg);
if (!filter) {
/* LCOV_EXCL_START */
log_fatal("Invalid filter specification '%s'\n", optarg);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
tommy_list_insert_tail(&filterlist_disk, &filter->node, filter);
} break;
case 'm' :
filter_missing = 1;
opt.expected_missing = 1;
break;
case 'e' :
/* when processing only error, we filter files */
/* and we apply fixes only to synced ones */
filter_error = 1;
opt.badfileonly = 1;
opt.syncedonly = 1;
break;
case 'b' :
/* when processing only block with error, we filter both files and blocks */
/* and we apply fixes only to synced ones */
filter_error = 1;
opt.badfileonly = 1;
opt.badblockonly = 1;
opt.syncedonly = 1;
break;
case 'p' :
if (strcmp(optarg, "bad") == 0) {
plan = SCRUB_BAD;
} else if (strcmp(optarg, "new") == 0) {
plan = SCRUB_NEW;
} else if (strcmp(optarg, "full") == 0) {
plan = SCRUB_FULL;
} else {
plan = strtoul(optarg, &e, 10);
if (!e || *e || plan > 100) {
/* LCOV_EXCL_START */
log_fatal("Invalid plan/percentage '%s'\n", optarg);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
break;
case 'o' :
olderthan = strtoul(optarg, &e, 10);
if (!e || *e || olderthan > 1000) {
/* LCOV_EXCL_START */
log_fatal("Invalid number of days '%s'\n", optarg);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
break;
case 'S' :
blockstart = strtoul(optarg, &e, 0);
if (!e || *e) {
/* LCOV_EXCL_START */
log_fatal("Invalid start position '%s'\n", optarg);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
break;
case 'B' :
blockcount = strtoul(optarg, &e, 0);
if (!e || *e) {
/* LCOV_EXCL_START */
log_fatal("Invalid count number '%s'\n", optarg);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
break;
case 'L' :
opt.io_error_limit = strtoul(optarg, &e, 0);
if (!e || *e) {
/* LCOV_EXCL_START */
log_fatal("Invalid error limit number '%s'\n", optarg);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
break;
case 'i' :
if (import_timestamp) {
/* LCOV_EXCL_START */
log_fatal("Import directory '%s' already specified as '%s'\n", optarg, import_timestamp);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
import_timestamp = optarg;
break;
case OPT_TEST_IMPORT_CONTENT :
if (import_content) {
/* LCOV_EXCL_START */
log_fatal("Import directory '%s' already specified as '%s'\n", optarg, import_content);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
import_content = optarg;
break;
case 'l' :
if (log_file) {
/* LCOV_EXCL_START */
log_fatal("Log file '%s' already specified as '%s'\n", optarg, log_file);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
log_file = optarg;
break;
case 'Z' :
opt.force_zero = 1;
break;
case 'E' :
opt.force_empty = 1;
break;
case 'U' :
opt.force_uuid = 1;
break;
case 'D' :
opt.force_device = 1;
break;
case 'N' :
opt.force_nocopy = 1;
break;
case 'F' :
opt.force_full = 1;
break;
case 'R' :
opt.force_realloc = 1;
break;
case 'a' :
opt.auditonly = 1;
break;
case 'h' :
opt.prehash = 1;
break;
case 'v' :
++msg_level;
break;
case 'q' :
--msg_level;
break;
case 'G' :
opt.gui = 1;
break;
case 'H' :
usage();
exit(EXIT_SUCCESS);
case 'V' :
version();
exit(EXIT_SUCCESS);
case 'T' :
speedtest = 1;
break;
case 'C' :
gen_conf = optarg;
break;
case OPT_TEST_KILL_AFTER_SYNC :
opt.kill_after_sync = 1;
break;
case OPT_TEST_EXPECT_UNRECOVERABLE :
opt.expect_unrecoverable = 1;
break;
case OPT_TEST_EXPECT_RECOVERABLE :
opt.expect_recoverable = 1;
break;
case OPT_TEST_SKIP_SELF :
opt.skip_self = 1;
break;
case OPT_TEST_SKIP_SIGN :
opt.skip_sign = 1;
break;
case OPT_TEST_SKIP_FALLOCATE :
opt.skip_fallocate = 1;
break;
case OPT_TEST_SKIP_DEVICE :
opt.skip_device = 1;
period = 50; /* reduce period of the speed test */
break;
case OPT_TEST_SKIP_CONTENT_CHECK :
opt.skip_content_check = 1;
break;
case OPT_TEST_SKIP_PARITY_ACCESS :
opt.skip_parity_access = 1;
break;
case OPT_TEST_SKIP_DISK_ACCESS :
opt.skip_disk_access = 1;
break;
case OPT_TEST_FORCE_MURMUR3 :
opt.force_murmur3 = 1;
break;
case OPT_TEST_FORCE_SPOOKY2 :
opt.force_spooky2 = 1;
break;
case OPT_TEST_SKIP_LOCK :
opt.skip_lock = 1;
break;
case OPT_TEST_FORCE_ORDER_PHYSICAL :
opt.force_order = SORT_PHYSICAL;
break;
case OPT_TEST_FORCE_ORDER_INODE :
opt.force_order = SORT_INODE;
break;
case OPT_TEST_FORCE_ORDER_ALPHA :
opt.force_order = SORT_ALPHA;
break;
case OPT_TEST_FORCE_ORDER_DIR :
opt.force_order = SORT_DIR;
break;
case OPT_TEST_FORCE_SCRUB_AT :
opt.force_scrub_at = atoi(optarg);
break;
case OPT_TEST_FORCE_SCRUB_EVEN :
opt.force_scrub_even = 1;
break;
case OPT_TEST_FORCE_CONTENT_WRITE :
opt.force_content_write = 1;
break;
case OPT_TEST_EXPECT_FAILURE :
/* invert the exit codes */
exit_success = 1;
exit_failure = 0;
break;
case OPT_TEST_EXPECT_NEED_SYNC :
/* invert the exit codes */
exit_success = 1;
exit_sync_needed = 0;
break;
case OPT_TEST_RUN :
run = optarg;
break;
case OPT_TEST_FORCE_SCAN_WINFIND :
opt.force_scan_winfind = 1;
break;
case OPT_TEST_FORCE_PROGRESS :
opt.force_progress = 1;
break;
case OPT_TEST_FORCE_AUTOSAVE_AT :
opt.force_autosave_at = atoi(optarg);
break;
case OPT_TEST_FAKE_DEVICE :
opt.fake_device = 1;
break;
case OPT_NO_WARNINGS :
opt.no_warnings = 1;
break;
case OPT_TEST_FAKE_UUID :
opt.fake_uuid = 2;
break;
case OPT_TEST_MATCH_FIRST_UUID :
opt.match_first_uuid = 1;
break;
case OPT_TEST_FORCE_PARITY_UPDATE :
opt.force_parity_update = 1;
break;
case OPT_TEST_IO_CACHE :
opt.io_cache = atoi(optarg);
if (opt.io_cache != 1 && (opt.io_cache < IO_MIN || opt.io_cache > IO_MAX)) {
/* LCOV_EXCL_START */
log_fatal("The IO cache should be between %u and %u.\n", IO_MIN, IO_MAX);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
break;
case OPT_TEST_IO_STATS :
opt.force_stats = 1;
break;
case OPT_TEST_COND_SIGNAL_OUTSIDE :
#if HAVE_THREAD
thread_cond_signal_outside = 1;
#endif
break;
case OPT_TEST_IO_ADVISE_NONE :
opt.file_mode = ADVISE_NONE;
break;
case OPT_TEST_IO_ADVISE_SEQUENTIAL :
opt.file_mode = ADVISE_SEQUENTIAL;
break;
case OPT_TEST_IO_ADVISE_FLUSH :
opt.file_mode = ADVISE_FLUSH;
break;
case OPT_TEST_IO_ADVISE_FLUSH_WINDOW :
opt.file_mode = ADVISE_FLUSH_WINDOW;
break;
case OPT_TEST_IO_ADVISE_DISCARD :
opt.file_mode = ADVISE_DISCARD;
break;
case OPT_TEST_IO_ADVISE_DISCARD_WINDOW :
opt.file_mode = ADVISE_DISCARD_WINDOW;
break;
case OPT_TEST_IO_ADVISE_DIRECT :
opt.file_mode = ADVISE_DIRECT;
break;
case OPT_TEST_PARITY_LIMIT :
opt.parity_limit_size = atoll(optarg);
break;
case OPT_TEST_SKIP_CONTENT_WRITE :
opt.skip_content_write = 1;
break;
case OPT_TEST_SKIP_SPACE_HOLDER :
opt.skip_space_holder = 1;
break;
case OPT_TEST_FORMAT :
if (strcmp(optarg, "file") == 0)
FMT_MODE = FMT_FILE;
else if (strcmp(optarg, "disk") == 0)
FMT_MODE = FMT_DISK;
else if (strcmp(optarg, "path") == 0)
FMT_MODE = FMT_PATH;
else {
/* LCOV_EXCL_START */
log_fatal("Unknown format '%s'\n", optarg);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
break;
case OPT_TEST_SKIP_MULTI_SCAN :
opt.skip_multi_scan = 1;
break;
default :
/* LCOV_EXCL_START */
log_fatal("Unknown option '%c'\n", (char)c);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
os_init(opt.force_scan_winfind);
raid_init();
crc32c_init();
if (speedtest != 0) {
speed(period);
os_done();
exit(EXIT_SUCCESS);
}
if (gen_conf != 0) {
generate_configuration(gen_conf);
os_done();
exit(EXIT_SUCCESS);
}
if (optind + 1 != argc) {
/* LCOV_EXCL_START */
usage();
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
command = argv[optind];
if (strcmp(command, "diff") == 0) {
operation = OPERATION_DIFF;
} else if (strcmp(argv[optind], "sync") == 0) {
operation = OPERATION_SYNC;
} else if (strcmp(argv[optind], "check") == 0) {
operation = OPERATION_CHECK;
} else if (strcmp(argv[optind], "fix") == 0) {
operation = OPERATION_FIX;
} else if (strcmp(argv[optind], "test-dry") == 0) {
operation = OPERATION_DRY;
} else if (strcmp(argv[optind], "dup") == 0) {
operation = OPERATION_DUP;
} else if (strcmp(argv[optind], "list") == 0) {
operation = OPERATION_LIST;
} else if (strcmp(argv[optind], "pool") == 0) {
operation = OPERATION_POOL;
} else if (strcmp(argv[optind], "rehash") == 0) {
operation = OPERATION_REHASH;
} else if (strcmp(argv[optind], "scrub") == 0) {
operation = OPERATION_SCRUB;
} else if (strcmp(argv[optind], "status") == 0) {
operation = OPERATION_STATUS;
} else if (strcmp(argv[optind], "test-rewrite") == 0) {
operation = OPERATION_REWRITE;
} else if (strcmp(argv[optind], "test-read") == 0) {
operation = OPERATION_READ;
} else if (strcmp(argv[optind], "touch") == 0) {
operation = OPERATION_TOUCH;
} else if (strcmp(argv[optind], "up") == 0) {
operation = OPERATION_SPINUP;
} else if (strcmp(argv[optind], "down") == 0) {
operation = OPERATION_SPINDOWN;
} else if (strcmp(argv[optind], "devices") == 0) {
operation = OPERATION_DEVICES;
} else if (strcmp(argv[optind], "smart") == 0) {
operation = OPERATION_SMART;
} else {
/* LCOV_EXCL_START */
log_fatal("Unknown command '%s'\n", argv[optind]);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* check options compatibility */
switch (operation) {
case OPERATION_CHECK :
break;
default :
if (opt.auditonly) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -a, --audit-only with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
switch (operation) {
case OPERATION_FIX :
case OPERATION_CHECK :
case OPERATION_SMART :
case OPERATION_DEVICES :
case OPERATION_SPINUP :
case OPERATION_SPINDOWN :
break;
default :
if (opt.force_device) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -D, --force-device with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
switch (operation) {
case OPERATION_SYNC :
case OPERATION_CHECK :
case OPERATION_FIX :
break;
default :
if (opt.force_nocopy) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -N, --force-nocopy with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
switch (operation) {
case OPERATION_SYNC :
break;
default :
if (opt.prehash) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -h, --pre-hash with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (opt.force_full) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -F, --force-full with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (opt.force_realloc) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -R, --force-realloc with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
if (opt.force_full && opt.force_nocopy) {
/* LCOV_EXCL_START */
log_fatal("You cannot use the -F, --force-full and -N, --force-nocopy options at the same time\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (opt.force_realloc && opt.force_nocopy) {
/* LCOV_EXCL_START */
log_fatal("You cannot use the -R, --force-realloc and -N, --force-nocopy options at the same time\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (opt.force_realloc && opt.force_full) {
/* LCOV_EXCL_START */
log_fatal("You cannot use the -R, --force-realloc and -F, --force-full options at the same time\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (opt.prehash && opt.force_nocopy) {
/* LCOV_EXCL_START */
log_fatal("You cannot use the -h, --pre-hash and -N, --force-nocopy options at the same time\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
switch (operation) {
case OPERATION_CHECK :
case OPERATION_FIX :
case OPERATION_DRY :
break;
default :
if (!tommy_list_empty(&filterlist_disk)) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -d, --filter-disk with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* fallthrough */
case OPERATION_SPINUP :
case OPERATION_SPINDOWN :
if (!tommy_list_empty(&filterlist_file)) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -f, --filter with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (filter_missing != 0) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -m, --filter-missing with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (filter_error != 0) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -e, --filter-error with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* errors must be always fixed on all disks */
/* because we don't keep the information on what disk is the error */
if (filter_error != 0 && !tommy_list_empty(&filterlist_disk)) {
/* LCOV_EXCL_START */
log_fatal("You cannot use -e, --filter-error and -d, --filter-disk at the same time\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
switch (operation) {
case OPERATION_CHECK :
case OPERATION_FIX :
break;
default :
if (import_timestamp != 0 || import_content != 0) {
/* LCOV_EXCL_START */
log_fatal("You cannot import with the '%s' command\n", command);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
switch (operation) {
case OPERATION_LIST :
case OPERATION_DUP :
case OPERATION_STATUS :
case OPERATION_REWRITE :
case OPERATION_READ :
case OPERATION_REHASH :
case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
/* avoid to check and access data disks if not needed */
opt.skip_disk_access = 1;
break;
}
switch (operation) {
case OPERATION_DIFF :
case OPERATION_LIST :
case OPERATION_DUP :
case OPERATION_POOL :
case OPERATION_STATUS :
case OPERATION_REWRITE :
case OPERATION_READ :
case OPERATION_REHASH :
case OPERATION_TOUCH :
case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
/* avoid to check and access parity disks if not needed */
opt.skip_parity_access = 1;
break;
}
switch (operation) {
case OPERATION_FIX :
case OPERATION_CHECK :
/* avoid to stop processing if a content file is not accessible */
opt.skip_content_access = 1;
break;
}
switch (operation) {
case OPERATION_DIFF :
case OPERATION_LIST :
case OPERATION_DUP :
case OPERATION_POOL :
case OPERATION_TOUCH :
case OPERATION_SPINUP :
case OPERATION_SPINDOWN :
case OPERATION_DEVICES :
case OPERATION_SMART :
opt.skip_self = 1;
break;
}
switch (operation) {
#if HAVE_DIRECT_IO
case OPERATION_SYNC :
case OPERATION_SCRUB :
case OPERATION_DRY :
break;
#endif
default:
/* we allow direct IO only on some commands */
if (opt.file_mode == ADVISE_DIRECT)
opt.file_mode = ADVISE_SEQUENTIAL;
break;
}
switch (operation) {
case OPERATION_DEVICES :
case OPERATION_SMART :
/* we may need to use these commands during operations */
opt.skip_lock = 1;
break;
}
switch (operation) {
case OPERATION_SMART :
/* allow to run without configuration file */
opt.auto_conf = 1;
break;
}
/* open the log file */
log_open(log_file);
/* print generic info into the log */
t = time(0);
#if HAVE_LOCALTIME_R
tm = localtime_r(&t, &tm_res);
#else
tm = localtime(&t);
#endif
log_tag("version:%s\n", PACKAGE_VERSION);
log_tag("unixtime:%" PRIi64 "\n", (int64_t)t);
if (tm) {
char datetime[64];
strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", tm);
log_tag("time:%s\n", datetime);
}
log_tag("command:%s\n", command);
for (i = 0; i < argc; ++i)
log_tag("argv:%u:%s\n", i, argv[i]);
log_flush();
if (!opt.skip_self)
selftest();
state_init(&state);
/* read the configuration file */
state_config(&state, conf, command, &opt, &filterlist_disk);
/* set the raid mode */
raid_mode(state.raid_mode);
#if HAVE_LOCKFILE
/* create the lock file */
if (!opt.skip_lock && state.lockfile[0]) {
lock = lock_lock(state.lockfile);
if (lock == -1) {
/* LCOV_EXCL_START */
if (errno != EWOULDBLOCK) {
log_fatal("Error creating the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
} else {
log_fatal("The lock file '%s' is already locked!\n", state.lockfile);
log_fatal("SnapRAID is already in use!\n");
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#else
(void)lock;
#endif
if (operation == OPERATION_DIFF) {
state_read(&state);
ret = state_diff(&state);
/* abort if sync needed */
if (ret > 0)
exit(EXIT_SYNC_NEEDED);
} else if (operation == OPERATION_SYNC) {
/* in the next state read ensures to clear all the past hashes in case */
/* we are reading from an incomplete sync */
/* The indeterminate hash are only for CHG/DELETED blocks for which we don't */
/* know if the previous interrupted sync was able to update or not the parity. */
/* The sync process instead needs to trust this information because it's used */
/* to avoid to recompute the parity if all the input are equals as before. */
/* In these cases we don't know if the old state is still the one */
/* stored inside the parity, because after an aborted sync, the parity */
/* may be or may be not have been updated with the data that may be now */
/* deleted. Then we reset the hash to a bogus value. */
/* An example for CHG blocks is: */
/* - One file is added creating a CHG block with ZERO state */
/* - Sync aborted after updating the parity to the new state, */
/* but without saving the content file representing this new BLK state. */
/* - File is now deleted after the aborted sync */
/* - Sync again, deleting the blocks over the CHG ones */
/* with the hash of CHG blocks not representing the real parity state */
/* An example for DELETED blocks is: */
/* - One file is deleted creating DELETED blocks */
/* - Sync aborted after, updating the parity to the new state, */
/* but without saving the content file representing this new EMPTY state. */
/* - Another file is added again over the DELETE ones */
/* with the hash of DELETED blocks not representing the real parity state */
state.clear_past_hash = 1;
state_read(&state);
state_scan(&state);
/* refresh the size info before the content write */
state_refresh(&state);
memory();
/* intercept signals while operating */
signal_init();
/* run a test command if required */
if (run != 0) {
ret = system(run); /* ignore error */
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error in running command '%s'.\n", run);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* waits some time to ensure that any concurrent modification done at the files, */
/* using the same mtime read by the scan process, will be read by sync. */
/* Note that any later modification done, potentially not read by sync, will have */
/* a different mtime, and it will be synchronized at the next sync. */
/* The worst case is the FAT file-system with a two seconds resolution for mtime. */
/* If you don't use FAT, the wait is not needed, because most file-systems have now */
/* at least microseconds resolution, but better to be safe. */
if (!opt.skip_self)
sleep(2);
ret = state_sync(&state, blockstart, blockcount);
/* save the new state if required */
if (!opt.kill_after_sync) {
if ((state.need_write || state.opt.force_content_write))
state_write(&state);
} else {
log_fatal("WARNING! Skipped state write for --test-kill-after-sync option.\n");
}
/* abort if required */
if (ret != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else if (operation == OPERATION_DRY) {
state_read(&state);
/* filter */
state_skip(&state);
state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
memory();
/* intercept signals while operating */
signal_init();
state_dry(&state, blockstart, blockcount);
} else if (operation == OPERATION_REHASH) {
state_read(&state);
/* intercept signals while operating */
signal_init();
state_rehash(&state);
/* save the new state if required */
if (state.need_write)
state_write(&state);
} else if (operation == OPERATION_SCRUB) {
state_read(&state);
memory();
/* intercept signals while operating */
signal_init();
ret = state_scrub(&state, plan, olderthan);
/* save the new state if required */
if (state.need_write || state.opt.force_content_write)
state_write(&state);
/* abort if required */
if (ret != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else if (operation == OPERATION_REWRITE) {
state_read(&state);
/* intercept signals while operating */
signal_init();
state_write(&state);
memory();
} else if (operation == OPERATION_READ) {
state_read(&state);
memory();
} else if (operation == OPERATION_TOUCH) {
state_read(&state);
state_touch(&state);
/* intercept signals while operating */
signal_init();
state_write(&state);
memory();
} else if (operation == OPERATION_SPINUP) {
state_device(&state, DEVICE_UP, &filterlist_disk);
} else if (operation == OPERATION_SPINDOWN) {
state_device(&state, DEVICE_DOWN, &filterlist_disk);
} else if (operation == OPERATION_DEVICES) {
state_device(&state, DEVICE_LIST, 0);
} else if (operation == OPERATION_SMART) {
state_device(&state, DEVICE_SMART, 0);
} else if (operation == OPERATION_STATUS) {
state_read(&state);
memory();
state_status(&state);
} else if (operation == OPERATION_DUP) {
state_read(&state);
state_dup(&state);
} else if (operation == OPERATION_LIST) {
state_read(&state);
state_list(&state);
} else if (operation == OPERATION_POOL) {
state_read(&state);
state_pool(&state);
} else {
state_read(&state);
/* if we are also trying to recover */
if (!state.opt.auditonly) {
/* import the user specified dirs */
if (import_timestamp != 0)
state_search(&state, import_timestamp);
if (import_content != 0)
state_import(&state, import_content);
/* import from all the array */
if (!state.opt.force_nocopy)
state_search_array(&state);
}
/* filter */
state_skip(&state);
state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
memory();
/* intercept signals while operating */
signal_init();
if (operation == OPERATION_CHECK) {
ret = state_check(&state, 0, blockstart, blockcount);
} else { /* it's fix */
ret = state_check(&state, 1, blockstart, blockcount);
}
/* abort if required */
if (ret != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* close log file */
log_close(log_file);
#if HAVE_LOCKFILE
if (!opt.skip_lock && state.lockfile[0]) {
if (lock_unlock(lock) == -1) {
/* LCOV_EXCL_START */
log_fatal("Error closing the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#endif
state_done(&state);
tommy_list_foreach(&filterlist_file, (tommy_foreach_func*)filter_free);
tommy_list_foreach(&filterlist_disk, (tommy_foreach_func*)filter_free);
os_done();
lock_done();
return EXIT_SUCCESS;
}
snapraid-12.1/cmdline/snapraid.h 0000664 0000000 0000000 00000001570 14166610522 0016631 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __SNAPRAID_H
#define __SNAPRAID_H
/****************************************************************************/
/* snapraid */
void speed(int period);
void selftest(void);
#endif
snapraid-12.1/cmdline/speed.c 0000664 0000000 0000000 00000042336 14166610522 0016130 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "snapraid.h"
#include "util.h"
#include "raid/raid.h"
#include "raid/cpu.h"
#include "raid/internal.h"
#include "raid/memory.h"
#include "state.h"
/*
* Size of the blocks to test.
*/
#define TEST_SIZE (256 * KIBI)
/*
* Number of data blocks to test.
*/
#define TEST_COUNT (8)
/**
* Differential us of two timeval.
*/
static int64_t diffgettimeofday(struct timeval *start, struct timeval *stop)
{
int64_t d;
d = 1000000LL * (stop->tv_sec - start->tv_sec);
d += stop->tv_usec - start->tv_usec;
return d;
}
/**
* Start time measurement.
*/
#define SPEED_START \
count = 0; \
gettimeofday(&start, 0); \
do { \
for (i = 0; i < delta; ++i)
/**
* Stop time measurement.
*/
#define SPEED_STOP \
count += delta; \
gettimeofday(&stop, 0); \
} while (diffgettimeofday(&start, &stop) < period * 1000LL) ; \
ds = size * (int64_t)count * nd; \
dt = diffgettimeofday(&start, &stop);
/**
* Global variable used to propagate side effects.
*
* This is required to avoid optimizing compilers
* to remove code without side effects.
*/
static unsigned side_effect;
void speed(int period)
{
struct timeval start;
struct timeval stop;
int64_t ds;
int64_t dt;
int i, j;
unsigned char digest[HASH_MAX];
unsigned char seed[HASH_MAX];
int id[RAID_PARITY_MAX];
int ip[RAID_PARITY_MAX];
int count;
int delta = period >= 1000 ? 10 : 1;
int size = TEST_SIZE;
int nd = TEST_COUNT;
int nv;
void *v_alloc;
void **v;
nv = nd + RAID_PARITY_MAX + 1;
v = malloc_nofail_vector_align(nd, nv, size, &v_alloc);
/* initialize disks with fixed data */
for (i = 0; i < nd; ++i)
memset(v[i], i, size);
/* zero buffer */
memset(v[nd + RAID_PARITY_MAX], 0, size);
raid_zero(v[nd + RAID_PARITY_MAX]);
/* hash seed */
for (i = 0; i < HASH_MAX; ++i)
seed[i] = i;
/* basic disks and parity mapping */
for (i = 0; i < RAID_PARITY_MAX; ++i) {
id[i] = i;
ip[i] = i;
}
printf(PACKAGE " v" VERSION " by Andrea Mazzoleni, " PACKAGE_URL "\n");
#ifdef __GNUC__
printf("Compiler gcc " __VERSION__ "\n");
#endif
#ifdef CONFIG_X86
{
char vendor[CPU_VENDOR_MAX];
unsigned family;
unsigned model;
raid_cpu_info(vendor, &family, &model);
printf("CPU %s, family %u, model %u, flags%s%s%s%s%s%s\n", vendor, family, model,
raid_cpu_has_sse2() ? " sse2" : "",
raid_cpu_has_ssse3() ? " ssse3" : "",
raid_cpu_has_crc32() ? " crc32" : "",
raid_cpu_has_avx2() ? " avx2" : "",
raid_cpu_has_slowmult() ? " slowmult" : "",
raid_cpu_has_slowextendedreg() ? " slowext" : ""
);
}
#else
printf("CPU is not a x86/x64\n");
#endif
#if WORDS_BIGENDIAN
printf("Memory is big-endian %d-bit\n", (int)sizeof(void *) * 8);
#else
printf("Memory is little-endian %d-bit\n", (int)sizeof(void *) * 8);
#endif
#if HAVE_FUTIMENS
printf("Support nanosecond timestamps with futimens()\n");
#elif HAVE_FUTIMES
printf("Support nanosecond timestamps with futimes()\n");
#elif HAVE_FUTIMESAT
printf("Support nanosecond timestamps with futimesat()\n");
#else
printf("Does not support nanosecond timestamps\n");
#endif
printf("\n");
printf("Speed test using %u data buffers of %u bytes, for a total of %u KiB.\n", nd, size, nd * size / KIBI);
printf("Memory blocks have a displacement of %u bytes to improve cache performance.\n", RAID_MALLOC_DISPLACEMENT);
printf("The reported values are the aggregate bandwidth of all data blocks in MB/s,\n");
printf("not counting parity blocks.\n");
printf("\n");
printf("Memory write speed using the C memset() function:\n");
printf("%8s", "memset");
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
memset(v[j], j, size);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
printf("\n");
printf("\n");
/* crc table */
printf("CRC used to check the content file integrity:\n");
printf("%8s", "table");
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
side_effect += crc32c_gen(0, v[j], size);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
printf("\n");
printf("%8s", "intel");
fflush(stdout);
#if HAVE_SSE42
if (raid_cpu_has_crc32()) {
SPEED_START {
for (j = 0; j < nd; ++j)
side_effect += crc32c_x86(0, v[j], size);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
printf("\n");
printf("\n");
/* hash table */
printf("Hash used to check the data blocks integrity:\n");
printf("%8s", "");
printf("%8s", "best");
printf("%8s", "murmur3");
printf("%8s", "spooky2");
printf("%8s", "metro");
printf("\n");
printf("%8s", "hash");
#ifdef CONFIG_X86
if (sizeof(void *) == 4 && !raid_cpu_has_slowmult())
printf("%8s", "murmur3");
else
printf("%8s", "spooky2");
#else
if (sizeof(void *) == 4)
printf("%8s", "murmur3");
else
printf("%8s", "spooky2");
#endif
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
memhash(HASH_MURMUR3, seed, digest, v[j], size);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
memhash(HASH_SPOOKY2, seed, digest, v[j], size);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
memhash(HASH_METRO, seed, digest, v[j], size);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
printf("\n");
printf("\n");
/* RAID table */
printf("RAID functions used for computing the parity with 'sync':\n");
printf("%8s", "");
printf("%8s", "best");
printf("%8s", "int8");
printf("%8s", "int32");
printf("%8s", "int64");
#ifdef CONFIG_X86
printf("%8s", "sse2");
#ifdef CONFIG_X86_64
printf("%8s", "sse2e");
#endif
printf("%8s", "ssse3");
#ifdef CONFIG_X86_64
printf("%8s", "ssse3e");
#endif
printf("%8s", "avx2");
#ifdef CONFIG_X86_64
printf("%8s", "avx2e");
#endif
#endif
printf("\n");
/* GEN1 */
printf("%8s", "gen1");
printf("%8s", raid_gen1_tag());
fflush(stdout);
printf("%8s", "");
SPEED_START {
raid_gen1_int32(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
raid_gen1_int64(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSE2
if (raid_cpu_has_sse2()) {
SPEED_START {
raid_gen1_sse2(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
raid_gen1_avx2(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#endif
printf("\n");
/* GEN2 */
printf("%8s", "gen2");
printf("%8s", raid_gen2_tag());
fflush(stdout);
printf("%8s", "");
SPEED_START {
raid_gen2_int32(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
raid_gen2_int64(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSE2
if (raid_cpu_has_sse2()) {
SPEED_START {
raid_gen2_sse2(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86_64
SPEED_START {
raid_gen2_sse2ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
raid_gen2_avx2(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#endif
printf("\n");
/* GENz */
printf("%8s", "genz");
printf("%8s", raid_genz_tag());
fflush(stdout);
printf("%8s", "");
SPEED_START {
raid_genz_int32(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
raid_genz_int64(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSE2
if (raid_cpu_has_sse2()) {
SPEED_START {
raid_genz_sse2(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86_64
SPEED_START {
raid_genz_sse2ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
raid_genz_avx2ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#endif
#endif
printf("\n");
/* GEN3 */
printf("%8s", "gen3");
printf("%8s", raid_gen3_tag());
fflush(stdout);
SPEED_START {
raid_gen3_int8(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
printf("%8s", "");
printf("%8s", "");
#ifdef CONFIG_X86
if (raid_cpu_has_sse2()) {
printf("%8s", "");
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
}
#endif
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
raid_gen3_ssse3(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86_64
SPEED_START {
raid_gen3_ssse3ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
raid_gen3_avx2ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#endif
#endif
printf("\n");
/* GEN4 */
printf("%8s", "gen4");
printf("%8s", raid_gen4_tag());
fflush(stdout);
SPEED_START {
raid_gen4_int8(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
printf("%8s", "");
printf("%8s", "");
#ifdef CONFIG_X86
#ifdef CONFIG_SSE2
if (raid_cpu_has_sse2()) {
printf("%8s", "");
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
}
#endif
#endif
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
raid_gen4_ssse3(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86_64
SPEED_START {
raid_gen4_ssse3ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
raid_gen4_avx2ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#endif
#endif
printf("\n");
/* GEN5 */
printf("%8s", "gen5");
printf("%8s", raid_gen5_tag());
fflush(stdout);
SPEED_START {
raid_gen5_int8(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
printf("%8s", "");
printf("%8s", "");
#ifdef CONFIG_X86
#ifdef CONFIG_SSE2
if (raid_cpu_has_sse2()) {
printf("%8s", "");
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
}
#endif
#endif
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
raid_gen5_ssse3(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86_64
SPEED_START {
raid_gen5_ssse3ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
raid_gen5_avx2ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#endif
#endif
printf("\n");
/* GEN6 */
printf("%8s", "gen6");
printf("%8s", raid_gen6_tag());
fflush(stdout);
SPEED_START {
raid_gen6_int8(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
printf("%8s", "");
printf("%8s", "");
#ifdef CONFIG_X86
#ifdef CONFIG_SSE2
if (raid_cpu_has_sse2()) {
printf("%8s", "");
#ifdef CONFIG_X86_64
printf("%8s", "");
#endif
}
#endif
#endif
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
raid_gen6_ssse3(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86_64
SPEED_START {
raid_gen6_ssse3ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("%8s", "");
#ifdef CONFIG_X86_64
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
raid_gen6_avx2ext(nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
}
#endif
#endif
#endif
printf("\n");
printf("\n");
/* recover table */
printf("RAID functions used for recovering with 'fix':\n");
printf("%8s", "");
printf("%8s", "best");
printf("%8s", "int8");
#ifdef CONFIG_X86
printf("%8s", "ssse3");
printf("%8s", "avx2");
#endif
printf("\n");
printf("%8s", "rec1");
printf("%8s", raid_rec1_tag());
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
/* +1 to avoid GEN1 optimized case */
raid_rec1_int8(1, id, ip + 1, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
for (j = 0; j < nd; ++j)
/* +1 to avoid GEN1 optimized case */
raid_rec1_ssse3(1, id, ip + 1, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
for (j = 0; j < nd; ++j)
/* +1 to avoid GEN1 optimized case */
raid_rec1_avx2(1, id, ip + 1, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#endif
printf("\n");
printf("%8s", "rec2");
printf("%8s", raid_rec2_tag());
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
/* +1 to avoid GEN2 optimized case */
raid_rec2_int8(2, id, ip + 1, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
for (j = 0; j < nd; ++j)
/* +1 to avoid GEN2 optimized case */
raid_rec2_ssse3(2, id, ip + 1, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
for (j = 0; j < nd; ++j)
/* +1 to avoid GEN1 optimized case */
raid_rec2_avx2(2, id, ip + 1, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#endif
printf("\n");
printf("%8s", "rec3");
printf("%8s", raid_recX_tag());
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_int8(3, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_ssse3(3, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_avx2(3, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#endif
printf("\n");
printf("%8s", "rec4");
printf("%8s", raid_recX_tag());
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_int8(4, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_ssse3(4, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_avx2(4, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#endif
printf("\n");
printf("%8s", "rec5");
printf("%8s", raid_recX_tag());
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_int8(5, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_ssse3(5, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_avx2(5, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#endif
printf("\n");
printf("%8s", "rec6");
printf("%8s", raid_recX_tag());
fflush(stdout);
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_int8(6, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
fflush(stdout);
#ifdef CONFIG_X86
#ifdef CONFIG_SSSE3
if (raid_cpu_has_ssse3()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_ssse3(6, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#ifdef CONFIG_AVX2
if (raid_cpu_has_avx2()) {
SPEED_START {
for (j = 0; j < nd; ++j)
raid_recX_avx2(6, id, ip, nd, size, v);
} SPEED_STOP
printf("%8" PRIu64, ds / dt);
}
#endif
#endif
printf("\n");
printf("\n");
printf("If the 'best' expectations are wrong, please report it in the SnapRAID forum\n\n");
free(v_alloc);
free(v);
}
snapraid-12.1/cmdline/spooky2.c 0000664 0000000 0000000 00000014606 14166610522 0016435 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* Derivative work from SpookyV2.cpp/h
*
* WARNING!!!! Note that this implementation doesn't use the short hash optimization
* resulting in different hashes for any length shorter than 192 bytes
*
* SpookyHash
* http://burtleburtle.net/bob/hash/spooky.html
*
* Exact source used as reference:
* http://burtleburtle.net/bob/c/SpookyV2.h
* http://burtleburtle.net/bob/c/SpookyV2.cpp
*/
// Spooky Hash
// A 128-bit noncryptographic hash, for checksums and table lookup
// By Bob Jenkins. Public domain.
// Oct 31 2010: published framework, disclaimer ShortHash isn't right
// Nov 7 2010: disabled ShortHash
// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
// April 10 2012: buffer overflow on platforms without unaligned reads
// July 12 2012: was passing out variables in final to in/out in short
// July 30 2012: I reintroduced the buffer overflow
// August 5 2012: SpookyV2: d = should be d += in short hash, and remove extra mix from long hash
//
// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages.
// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.
//
// This was developed for and tested on 64-bit x86-compatible processors.
// It assumes the processor is little-endian. There is a macro
// controlling whether unaligned reads are allowed (by default they are).
// This should be an equally good hash on big-endian machines, but it will
// compute different results on them than on little-endian machines.
//
// Google's CityHash has similar specs to SpookyHash, and CityHash is faster
// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders
// of magnitude slower. CRCs are two or more times slower, but unlike
// SpookyHash, they have nice math for combining the CRCs of pieces to form
// the CRCs of wholes. There are also cryptographic hashes, but those are even
// slower than MD5.
//
#define Mix(data, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11) \
s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = util_rotl64(s0, 11); s11 += s1; \
s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = util_rotl64(s1, 32); s0 += s2; \
s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = util_rotl64(s2, 43); s1 += s3; \
s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = util_rotl64(s3, 31); s2 += s4; \
s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = util_rotl64(s4, 17); s3 += s5; \
s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = util_rotl64(s5, 28); s4 += s6; \
s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = util_rotl64(s6, 39); s5 += s7; \
s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = util_rotl64(s7, 57); s6 += s8; \
s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = util_rotl64(s8, 55); s7 += s9; \
s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = util_rotl64(s9, 54); s8 += s10; \
s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = util_rotl64(s10, 22); s9 += s11; \
s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = util_rotl64(s11, 46); s10 += s0;
#define EndPartial(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \
h11 += h1; h2 ^= h11; h1 = util_rotl64(h1, 44); \
h0 += h2; h3 ^= h0; h2 = util_rotl64(h2, 15); \
h1 += h3; h4 ^= h1; h3 = util_rotl64(h3, 34); \
h2 += h4; h5 ^= h2; h4 = util_rotl64(h4, 21); \
h3 += h5; h6 ^= h3; h5 = util_rotl64(h5, 38); \
h4 += h6; h7 ^= h4; h6 = util_rotl64(h6, 33); \
h5 += h7; h8 ^= h5; h7 = util_rotl64(h7, 10); \
h6 += h8; h9 ^= h6; h8 = util_rotl64(h8, 13); \
h7 += h9; h10 ^= h7; h9 = util_rotl64(h9, 38); \
h8 += h10; h11 ^= h8; h10 = util_rotl64(h10, 53); \
h9 += h11; h0 ^= h9; h11 = util_rotl64(h11, 42); \
h10 += h0; h1 ^= h10; h0 = util_rotl64(h0, 54);
#define End(data, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \
h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3]; \
h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7]; \
h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11]; \
EndPartial(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11); \
EndPartial(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11); \
EndPartial(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
// number of uint64_t's in internal state
#define sc_numVars 12
// size of the internal state
#define sc_blockSize (sc_numVars * 8)
//
// sc_const: a constant which:
// * is not zero
// * is odd
// * is a not-very-regular mix of 1's and 0's
// * does not need any other special mathematical properties
//
#define sc_const 0xdeadbeefdeadbeefLL
void SpookyHash128(const void* data, size_t size, const uint8_t* seed, uint8_t* digest)
{
uint64_t h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11;
uint64_t buf[sc_numVars];
size_t nblocks;
const uint64_t* blocks;
const uint64_t* end;
size_t size_remainder;
#if WORDS_BIGENDIAN
unsigned i;
#endif
h9 = util_read64(seed + 0);
h10 = util_read64(seed + 8);
h0 = h3 = h6 = h9;
h1 = h4 = h7 = h10;
h2 = h5 = h8 = h11 = sc_const;
nblocks = size / sc_blockSize;
blocks = data;
end = blocks + nblocks * sc_numVars;
/* body */
while (blocks < end) {
#if WORDS_BIGENDIAN
for (i = 0; i < sc_numVars; ++i)
buf[i] = util_swap64(blocks[i]);
Mix(buf, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
#else
Mix(blocks, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
#endif
blocks += sc_numVars;
}
/* tail */
size_remainder = (size - ((const uint8_t*)end - (const uint8_t*)data));
memcpy(buf, end, size_remainder);
memset(((uint8_t*)buf) + size_remainder, 0, sc_blockSize - size_remainder);
((uint8_t*)buf)[sc_blockSize - 1] = size_remainder;
/* finalization */
#if WORDS_BIGENDIAN
for (i = 0; i < sc_numVars; ++i)
buf[i] = util_swap64(buf[i]);
#endif
End(buf, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
util_write64(digest + 0, h0);
util_write64(digest + 8, h1);
}
snapraid-12.1/cmdline/spooky2test.c 0000664 0000000 0000000 00000473067 14166610522 0017347 0 ustar 00root root 0000000 0000000 { "", 0, { 0xc7, 0xd7, 0x7b, 0x13, 0xfc, 0xa2, 0x7b, 0x21, 0x00, 0x95, 0xf6, 0xa9, 0x6f, 0x84, 0xaa, 0xc9 } },
{ "a", 1, { 0x85, 0x34, 0x3d, 0x36, 0xf4, 0x56, 0x79, 0x60, 0xa7, 0x96, 0x5f, 0x16, 0x44, 0x1e, 0x8c, 0xea } },
{ "abc", 3, { 0xb0, 0x41, 0x6b, 0xce, 0xba, 0xdf, 0x5d, 0xe3, 0x09, 0x38, 0xcb, 0x1f, 0x25, 0xdc, 0x44, 0xa2 } },
{ "message digest", 14, { 0xdf, 0xe5, 0xcb, 0x3f, 0x51, 0xa8, 0xa2, 0xd4, 0x8d, 0xa0, 0xd5, 0x48, 0x26, 0x1c, 0x17, 0x74 } },
{ "abcdefghijklmnopqrstuvwxyz", 26, { 0x95, 0xe3, 0x9c, 0x85, 0xb8, 0x70, 0x3d, 0x24, 0x51, 0x1c, 0x3c, 0x85, 0x8c, 0xd7, 0x57, 0x2c } },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, { 0x7d, 0x0d, 0xb7, 0xae, 0x85, 0xce, 0xa1, 0x13, 0xf8, 0xfc, 0xb4, 0xf4, 0x05, 0xab, 0x68, 0xb0 } },
{ "The quick brown fox jumps over the lazy dog", 43, { 0xda, 0xfa, 0x43, 0x97, 0xaf, 0x0b, 0xdc, 0x06, 0x1e, 0xc2, 0x77, 0x12, 0xeb, 0x36, 0xd4, 0x4d } },
{ "\x00", 1, { 0x51, 0xe3, 0xc3, 0x5c, 0x6d, 0x8f, 0xb3, 0xc8, 0x01, 0x19, 0x29, 0xcb, 0x9d, 0x98, 0xa3, 0xa0 } },
{ "\x16\x27", 2, { 0xbb, 0x96, 0x86, 0x56, 0xcc, 0x62, 0x37, 0xe0, 0xe8, 0xf0, 0x5b, 0x63, 0x3b, 0xd1, 0x6c, 0x42 } },
{ "\xe2\x56\xb4", 3, { 0xf6, 0xb2, 0x17, 0x1a, 0x27, 0x85, 0x2b, 0x8a, 0x45, 0x4e, 0x26, 0xf1, 0xeb, 0xb9, 0x60, 0x64 } },
{ "\xc9\x4d\x9c\xda", 4, { 0x94, 0xdb, 0x3e, 0x5b, 0x3a, 0x4c, 0x0e, 0xf9, 0x4a, 0xe6, 0x42, 0xfa, 0xc6, 0xb5, 0xa6, 0x1d } },
{ "\x79\xf1\x29\x69\x5d", 5, { 0x0c, 0x5e, 0x36, 0x05, 0x04, 0xd5, 0xf2, 0x53, 0x7b, 0x56, 0x03, 0xc7, 0x6f, 0x08, 0x45, 0x07 } },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, { 0xd5, 0x65, 0x58, 0x3d, 0xa3, 0x96, 0x8a, 0xa2, 0x6e, 0xe5, 0x8d, 0x84, 0xa5, 0x3f, 0xd7, 0x6c } },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, { 0x19, 0x00, 0x13, 0x2a, 0xd9, 0x50, 0x80, 0x5e, 0xb9, 0x19, 0x36, 0x5a, 0xb6, 0x36, 0x2a, 0x0f } },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, { 0x13, 0xc7, 0xac, 0xcb, 0x7a, 0x5d, 0xe5, 0x14, 0x4b, 0xf6, 0xee, 0xfd, 0x52, 0x86, 0xb7, 0x5f } },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, { 0xe4, 0x12, 0x97, 0x2c, 0xe2, 0xca, 0x21, 0x67, 0xec, 0xe8, 0x63, 0xf7, 0x20, 0x7d, 0xae, 0xad } },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, { 0x11, 0x14, 0x7d, 0xd2, 0x79, 0xd5, 0x32, 0x30, 0x43, 0x83, 0x26, 0x60, 0x47, 0xed, 0x63, 0x2b } },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, { 0x59, 0xa5, 0x00, 0x96, 0x3f, 0x01, 0xd8, 0x90, 0x00, 0x46, 0x2f, 0x11, 0xf1, 0xf9, 0xb8, 0x7d } },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, { 0x21, 0x3e, 0x1d, 0x1b, 0x44, 0xb7, 0x71, 0xc0, 0xde, 0x2b, 0xb4, 0x6d, 0x82, 0x6d, 0x8c, 0x1e } },
{ "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 13, { 0xe3, 0xa9, 0x25, 0xc9, 0x53, 0x3c, 0xa2, 0xa5, 0x69, 0x62, 0x31, 0xde, 0x10, 0xbb, 0x54, 0xd3 } },
{ "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 14, { 0x91, 0x14, 0xf0, 0x97, 0x98, 0x3b, 0x8e, 0x97, 0x33, 0x33, 0x8e, 0xb9, 0xca, 0x3a, 0x1d, 0x82 } },
{ "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 15, { 0x06, 0x33, 0x47, 0x61, 0xe7, 0xc9, 0xc1, 0x0e, 0x23, 0x36, 0x86, 0x2b, 0x2d, 0x21, 0x89, 0x57 } },
{ "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 16, { 0x66, 0x36, 0x7d, 0xee, 0x47, 0x92, 0xa4, 0x4f, 0x62, 0xf8, 0xd9, 0x36, 0x8e, 0xe7, 0x69, 0x53 } },
{ "\x59\xa9\x9f\xa6\xdb\xb7\x02\xc5\x95\x65\x34\x17\xde\xe5\xbb\xdf\xc5", 17, { 0xd7, 0x7b, 0x00, 0x79, 0xae, 0x0f, 0xc9, 0x16, 0x02, 0x28, 0xab, 0x6a, 0xdf, 0x3e, 0x30, 0x03 } },
{ "\x0d\x52\x83\x32\x6a\x92\xf9\x9a\x6e\x3c\x3d\x5e\xc4\x25\xfe\xc1\xc4\xbe", 18, { 0x46, 0x32, 0xb2, 0xa0, 0x1b, 0xa8, 0xea, 0xce, 0x66, 0x9f, 0xc1, 0x58, 0xc9, 0x94, 0xba, 0x3b } },
{ "\x59\x84\x02\x3f\xbc\x20\x01\x70\xed\xa0\x05\x14\x23\x18\x06\xf2\x52\xc5\xc1", 19, { 0x98, 0xfc, 0x24, 0x88, 0x92, 0xbd, 0xad, 0x17, 0xcc, 0x07, 0x11, 0x2b, 0x37, 0x4e, 0xb1, 0x47 } },
{ "\x81\x84\xc3\xe8\x2f\x63\x65\x79\x4e\xd3\x34\x2c\x9c\xbc\x87\x05\x6d\xe5\xbc\x0a", 20, { 0x9f, 0x9d, 0x76, 0xcf, 0x19, 0x6b, 0xae, 0x90, 0x61, 0x27, 0x76, 0x9d, 0xe4, 0x7b, 0x29, 0x4c } },
{ "\x77\xfc\x10\x0f\x3a\xb2\x20\xad\x0a\x03\xfd\x51\xba\x93\xe6\x70\xe7\x34\xa4\xd8\xde", 21, { 0xb2, 0xfe, 0xf1, 0xb3, 0x6c, 0x1d, 0x2a, 0x88, 0x2a, 0x02, 0x4e, 0x25, 0x12, 0xa8, 0x6a, 0x6d } },
{ "\x84\x87\x22\x2b\xb3\xf8\x44\xbf\x63\xbb\x43\xbd\xa8\xc4\x05\xe1\x2f\xb2\x44\x8d\x7a\xec", 22, { 0x2e, 0x1f, 0xd3, 0x56, 0xce, 0x43, 0x56, 0x70, 0xb8, 0x84, 0x6d, 0xd9, 0x24, 0x49, 0x36, 0x4d } },
{ "\x9f\x72\x49\x57\xca\xd7\x15\xb1\xe6\x89\xb5\x8d\xec\x5f\x24\xeb\x42\x69\x5a\x73\x70\xb5\x56", 23, { 0xf2, 0xbf, 0x7a, 0x59, 0x6e, 0x6c, 0x2f, 0xcf, 0x5b, 0xaa, 0x02, 0x31, 0xac, 0x48, 0x5d, 0xe9 } },
{ "\x4e\xec\xbd\x3d\xa2\x11\x70\x9c\xa8\x2e\xdb\xca\x6b\xbb\x74\x69\x1e\xa7\x03\x0b\x1b\xcd\x2e\xf0", 24, { 0x68, 0x75, 0x89, 0x8f, 0xaf, 0xdb, 0x8e, 0x21, 0x92, 0xab, 0x3a, 0xd2, 0x25, 0xad, 0xc1, 0x26 } },
{ "\x74\x84\x9f\xed\x38\x55\xd4\x69\x44\xc8\x82\x82\xc2\x57\xa7\x4d\x43\x84\x2b\x3a\x75\x06\x32\x95\x81", 25, { 0x03, 0x4c, 0x88, 0xfc, 0x08, 0x2e, 0x21, 0x1e, 0x9f, 0xac, 0x56, 0x0c, 0xfc, 0x96, 0xbf, 0xec } },
{ "\x1e\x01\x28\x03\x09\x8a\xfe\xa7\x8e\x95\x42\x5d\xb7\x8d\x46\x38\x9c\xe5\xa1\xe8\x5a\x25\x70\xd2\x23\x95", 26, { 0x0d, 0xb4, 0xe1, 0xa3, 0x6a, 0xed, 0xb5, 0x0b, 0x0f, 0xa1, 0x90, 0xf1, 0x48, 0xb7, 0x47, 0xd8 } },
{ "\xbc\xc9\x70\x84\xfc\x6c\x51\x35\xb1\x1c\xe4\x67\x2f\x97\xe4\x80\x7c\x36\x59\x55\x51\xbf\x6d\x98\x3d\xe8\x5f", 27, { 0x6b, 0x15, 0x3d, 0x7d, 0xe7, 0x46, 0x1f, 0xe1, 0xb8, 0x15, 0xe7, 0xc8, 0x75, 0x66, 0x19, 0xf5 } },
{ "\xdf\xe9\x69\x90\x4d\x76\xbc\xbb\xdf\x03\x74\x42\x55\x4a\x37\xa3\xba\x6a\x5a\x09\x92\xbf\x17\xff\xa0\xed\x6d\x3f", 28, { 0x6d, 0x27, 0xc4, 0x1a, 0xa8, 0xa4, 0x76, 0x37, 0x72, 0x9d, 0x84, 0xbe, 0x00, 0x7b, 0x7b, 0xf6 } },
{ "\x51\xfc\x95\xa9\xc8\x9d\x1c\x4f\x87\x8b\xa2\xad\xb7\x0d\x2d\xf6\x14\x98\x2b\x89\x77\x91\x02\x83\x01\x2f\x56\x6e\xe1", 29, { 0x65, 0xa5, 0xce, 0x1b, 0x75, 0x9d, 0x8f, 0xd3, 0x5f, 0x84, 0x26, 0x59, 0x32, 0x0d, 0x9c, 0x5f } },
{ "\x7f\xd6\x16\xc3\x81\xc3\x7c\xd6\x70\xff\xe4\x77\x1f\xcd\x09\x7f\x7f\x2b\x71\x26\x3d\xc9\xdb\x92\x88\xa5\xd4\x00\xf0\x44", 30, { 0x99, 0xfa, 0xec, 0x52, 0x11, 0x1b, 0xfc, 0x3a, 0xba, 0xe5, 0x96, 0xbc, 0xa7, 0xb4, 0xa8, 0x3c } },
{ "\x68\x8e\x6a\x8e\xf6\xa2\x70\x47\x1d\xfb\x45\x26\xd2\x52\x56\x94\x94\xac\xbc\x02\xb6\x3f\xde\xe7\xdb\xfe\x34\x55\x81\xc3\x26", 31, { 0x14, 0x6f, 0xe7, 0x1d, 0x1c, 0xa6, 0x6d, 0xc6, 0xd5, 0x33, 0xbb, 0xf5, 0xdc, 0x90, 0x83, 0xf7 } },
{ "\x37\xb3\x18\x13\x29\xe2\xa2\x6d\xf4\xce\x2b\x01\xa5\x9f\x4b\x54\x48\x10\xb1\x29\x46\xcb\x13\x20\x58\xcf\xb0\x78\x27\x0d\x7e\xf5", 32, { 0x41, 0x97, 0x27, 0x98, 0x5b, 0x0a, 0x50, 0x29, 0x3f, 0x71, 0xa0, 0x20, 0xfa, 0x18, 0x13, 0x9b } },
{ "\x82\x13\xf6\xdd\x3b\xdf\x78\x1c\x9e\xd6\x5b\x87\x8f\xcd\x95\x3d\x3f\x43\x04\x2a\x8b\xb2\x57\xa5\xf1\xfa\x9c\x39\x2f\xfe\x66\x81\x7a", 33, { 0x96, 0xda, 0xe6, 0x6e, 0xda, 0xaf, 0x1c, 0x5c, 0xb6, 0x68, 0x6c, 0xb5, 0x2a, 0x53, 0x52, 0x21 } },
{ "\x4a\x9a\x2c\x58\xf1\xd6\x21\x1a\x76\x7f\xbc\xfa\xe0\x66\x35\xcd\xf1\x17\x22\x64\x6f\xbd\x13\x85\xa1\xb5\x5b\x81\xd6\xad\xee\x72\x1e\x79", 34, { 0x2c, 0x13, 0x32, 0xbe, 0x4b, 0xee, 0x1e, 0x36, 0x8a, 0xcd, 0x5c, 0x1c, 0x5f, 0x5a, 0x7e, 0xce } },
{ "\x66\x13\x1c\x72\x20\x3c\xf3\x8b\x83\xf8\x5e\xf2\x60\x44\xdc\x5e\x75\x67\x08\xfb\x0c\xe9\x07\xf0\xc1\x31\x1a\x89\x60\xbf\x53\x06\xed\x02\x64", 35, { 0x8a, 0x9e, 0x07, 0x96, 0x6b, 0xeb, 0x7e, 0xc2, 0x3f, 0xca, 0xe4, 0x74, 0x0f, 0x8a, 0xd0, 0xb1 } },
{ "\xff\xbd\xd6\x92\x72\xc1\x9e\xc9\x6b\xe3\xfb\xca\x4e\x88\x26\x7f\xc4\x36\xf0\x70\x40\x4f\x53\x4d\x2d\xfc\xb3\xab\xb6\x25\xb7\xcc\x31\x9c\xbf\x97", 36, { 0x6d, 0x72, 0xeb, 0xd3, 0xf4, 0x77, 0x52, 0x8b, 0xb9, 0x10, 0x42, 0x47, 0x1d, 0xcc, 0xa3, 0xa4 } },
{ "\x29\x59\x01\xa6\x06\xe0\x43\x7e\x5b\xbf\x37\xda\xcc\x33\x6e\x20\x9a\xeb\xfa\xf5\xcd\xe4\xa5\xec\xfd\x73\xc7\x59\xbc\x61\xc8\x44\xa3\x30\x33\x79\xf8", 37, { 0xd1, 0x39, 0xc1, 0x5b, 0x60, 0xb6, 0x53, 0x6e, 0xda, 0x86, 0xc6, 0x34, 0xa7, 0x8f, 0xe9, 0x66 } },
{ "\x7c\x72\xbd\xf9\x7d\x2c\x1b\xc8\xa9\x38\x5c\xf2\x76\x3c\x94\x9d\x3d\xe7\xd9\x4e\x3a\xc7\x0f\x55\xb0\xc7\xdf\xd5\x29\x49\xc6\x74\xdb\xdc\x49\x9b\x27\x9a", 38, { 0x8e, 0x6e, 0x12, 0xe2, 0x8f, 0xd3, 0x0d, 0x34, 0xac, 0x81, 0xc6, 0xe6, 0xe8, 0xf2, 0x84, 0x03 } },
{ "\x46\x6a\x3b\x21\x66\x3c\xd6\x2c\xaf\xd2\x2b\xcd\x36\x1e\xdf\x49\xf2\xae\x2d\x8f\xab\xb3\x29\x57\x10\xae\x22\xd4\xe8\xb6\x20\x15\x61\x68\x8c\xfd\x85\x6b\xef", 39, { 0x73, 0x15, 0x7b, 0xcb, 0xa7, 0x03, 0x3b, 0xb0, 0xac, 0x14, 0x75, 0x41, 0xe5, 0x83, 0xb4, 0x66 } },
{ "\xdd\xca\x9e\xd3\xab\xc8\xe1\xc5\xe2\x05\x7d\x7c\xf5\xec\x31\x14\x71\xdd\xda\x73\xae\x5e\xbd\xcd\x31\x6e\x74\x42\xa1\xfa\x74\xa4\x64\x69\x37\x57\xd6\x52\x0a\x51", 40, { 0x41, 0x74, 0x00, 0x0d, 0xee, 0x8b, 0x3e, 0x10, 0xac, 0x8e, 0x4e, 0x41, 0x25, 0x38, 0xa6, 0xf0 } },
{ "\x26\x4d\x75\x42\xcc\xe7\x42\xcd\xaa\xaf\x2c\xf3\xbf\x6d\x26\x91\x82\x72\x44\x00\xe1\x0b\x4a\xda\xd5\x0f\xd0\xdc\xa2\x98\x0e\xe7\xa8\xce\x4a\x21\xe9\xdf\xdb\x38\x18", 41, { 0x46, 0x5e, 0x28, 0x3e, 0x2d, 0x14, 0x30, 0xbf, 0x07, 0x39, 0x12, 0x33, 0x8e, 0x1b, 0xcd, 0x05 } },
{ "\x0b\xd8\xc7\x5a\xdb\x62\xd1\x9c\x71\x8b\x69\x90\xfb\xe2\x74\x45\x41\x9d\x87\x61\x5e\x61\xea\xfe\x8c\xb0\xbe\x1f\x18\xef\x3a\xce\x74\x13\x11\xd3\x6b\xe8\xf0\x12\x6f\xfe", 42, { 0x19, 0xae, 0x68, 0x51, 0xdb, 0xf7, 0xfa, 0xe7, 0x3c, 0xdc, 0x83, 0x35, 0x95, 0xb7, 0x9e, 0xd8 } },
{ "\x23\x82\x0e\x30\x57\xc6\x83\xd2\x7a\x69\x69\x76\xbf\xbb\x8b\x5b\xbd\x42\xd6\x73\xb7\xdf\x70\x10\xf6\x7f\xe1\x2a\x53\x7a\x6f\x4f\xd1\x8b\x89\x27\x66\x6d\x59\xa3\xdb\x0e\x7c", 43, { 0xfc, 0xe5, 0x55, 0x88, 0x24, 0xff, 0x12, 0x50, 0x72, 0x7d, 0xbb, 0x96, 0xda, 0xfa, 0x2a, 0xd0 } },
{ "\xc2\xc9\x72\xce\xff\xc3\x27\x5e\x5b\x92\x60\x81\x02\x9b\xb1\x02\x9d\x5d\x66\x77\x33\x17\x80\xaf\xbc\x86\x4d\x47\x44\xcc\xa3\x5b\xb9\x6c\xb9\x5a\x51\x72\xa1\xf3\x9a\xb0\x66\x2c", 44, { 0x4e, 0x95, 0x9e, 0x13, 0xba, 0x8e, 0x68, 0xa8, 0xd2, 0xcb, 0x9c, 0xd6, 0xb0, 0x91, 0x48, 0xc9 } },
{ "\x1f\x22\xbe\x91\xb4\x73\x6c\x89\x5f\x99\x35\xc4\xe3\x9b\x7c\xd0\xea\x73\x7c\xcb\xce\x0e\x22\x33\x04\x6f\x85\xd9\x25\xf4\x03\xf4\x9b\xb1\xf9\x52\xe4\xf8\x1e\xa9\x86\xfb\x7d\x0c\x8f", 45, { 0x32, 0x7d, 0xcc, 0x9a, 0x8b, 0x86, 0x41, 0xe3, 0x94, 0x5d, 0xb2, 0xa1, 0xc4, 0xf2, 0xb9, 0x9a } },
{ "\x88\x0d\x79\xc8\xf2\xee\x8f\x76\x10\x42\x5d\xcc\x5f\xa6\x55\xf0\x43\x4c\x5f\xa8\x6b\xb7\x0a\xa1\x51\x11\xdd\x5c\xe1\x2c\xcc\xb6\x31\x13\xc6\x12\x6f\x4c\x6b\x24\xd9\xae\xdb\x5d\xe4\xd9", 46, { 0x9d, 0x7d, 0x73, 0xcf, 0x4b, 0x16, 0x28, 0xd1, 0x06, 0x3a, 0xde, 0xde, 0xf5, 0xdd, 0x8a, 0xda } },
{ "\xaa\xee\x80\x55\xef\x7b\x73\x4a\x7c\x5e\xe2\xd9\x27\x95\xef\x17\x47\x49\x7b\xa4\x3b\xfc\x0c\xeb\x24\x4e\xca\xf8\xb2\x6b\xa4\xf0\x1c\x14\x90\x32\x29\x49\xef\x09\x6c\x91\x0e\x0a\x8d\xe5\xc9", 47, { 0x00, 0xf1, 0x9c, 0x62, 0x0b, 0xe5, 0x13, 0xde, 0x01, 0x5e, 0x1c, 0xe9, 0xf5, 0x8d, 0x5e, 0x54 } },
{ "\x11\xc1\x3f\x4a\xd2\x5b\xba\xef\x1e\x7c\x66\x4a\xb5\xe6\xaa\x41\xc2\xef\x56\x56\xf7\x91\x45\x01\x45\x9e\xab\x3a\x38\x10\x01\x13\xee\x7f\x8a\x81\xb2\xab\x80\x3b\x39\x39\xe0\xba\x78\x7b\xcf\x61", 48, { 0xa9, 0x50, 0x1d, 0x59, 0x8d, 0x20, 0x34, 0xca, 0x14, 0xc3, 0xfe, 0xc6, 0xd2, 0x01, 0xcb, 0xc6 } },
{ "\x20\x68\xa7\xb7\xb8\xab\xa3\xcf\x55\xd7\x23\xc9\xf7\xb7\x9b\x71\x83\xc3\x1e\x04\x59\xaf\x83\x13\x91\x1e\x31\x81\xd7\x75\x8d\xa6\xe0\xca\xfc\x96\x88\xfa\x97\x7c\x8a\xd9\x6b\x1f\xdb\x85\x69\x87\xa3", 49, { 0xc3, 0xf7, 0x89, 0x76, 0x95, 0xe3, 0xbd, 0x8c, 0xca, 0x78, 0xfd, 0x14, 0x43, 0xd5, 0x3b, 0xc0 } },
{ "\xd4\xf1\xb9\xd9\xeb\xf5\x9d\x7a\xf0\xcd\x01\x65\xd7\x98\xb6\xd6\x59\x49\xd8\x7d\x03\x55\x2c\x3a\x6b\xf7\xa1\x78\x7d\x1e\xf9\x23\xf3\xf5\x81\x47\xe3\x0c\xfc\x46\x72\x28\x9e\xb6\xa6\xa4\x34\xd5\x5a\x81", 50, { 0x1c, 0x9d, 0x43, 0x52, 0xba, 0x7f, 0xc6, 0x30, 0x6b, 0x72, 0xd7, 0x96, 0x5b, 0xd1, 0x2d, 0xbf } },
{ "\x61\xa9\x4a\x12\x02\xb8\x4e\x3d\xb3\x61\xa4\x6e\x6b\xd6\x66\x1e\x42\xb7\x1a\xfb\x54\x4b\x68\xb5\xbd\x5d\xe6\x65\xc3\xb1\x0f\x99\x13\x22\x53\x00\x24\x59\x48\xaf\xb8\x2c\xfe\x0d\x81\x90\x70\x62\xe0\x3c\x15", 51, { 0x7e, 0x5a, 0xb7, 0x6b, 0xb6, 0x54, 0xdf, 0x9e, 0x2f, 0xb5, 0x77, 0xf1, 0x9a, 0x6a, 0x59, 0x79 } },
{ "\xf6\xc8\xdd\x96\xe9\xc2\xef\x9b\x8f\x09\x3f\xbf\x85\xd5\xfa\xa2\x55\xb5\x70\x1c\xc1\x15\x6b\x8e\xb0\xdf\x26\x55\xb2\x3e\xec\x58\x32\x7e\x4f\xc1\x37\x10\x01\xc8\xd6\xa9\x52\xab\x38\x89\x46\xba\x44\xd9\x52\x8e", 52, { 0xb9, 0x98, 0x85, 0xa3, 0x53, 0x84, 0x15, 0xf4, 0x7d, 0xbe, 0x68, 0xff, 0x96, 0x56, 0x6a, 0xc5 } },
{ "\xe9\x2d\xb3\x5a\x09\x2a\x6c\x59\x05\x41\xda\x67\xe1\x99\xf5\xac\x14\x0b\x25\x73\xef\x47\xbe\x19\xa7\x14\x1a\x20\x01\xb4\x52\x63\x99\x65\x98\x64\xce\x04\x34\xc1\x4d\xcd\x19\xc5\x39\x3d\x24\x1b\xf4\x18\xf0\x9e\x8d", 53, { 0x78, 0x0a, 0x2e, 0xce, 0x91, 0xb5, 0xd9, 0xa2, 0xc1, 0x7f, 0xab, 0xd3, 0x93, 0x73, 0xc5, 0xf2 } },
{ "\x86\x58\x4b\xc6\x59\x8a\x58\xc0\x6a\xc4\x5e\x45\x21\xaf\xb1\xb2\x12\x54\xd0\x7f\xc4\xbf\xf8\x6d\x8e\x2f\xd3\x4b\x9b\xf6\x4e\x64\x0c\xf3\x88\x88\x3c\xaa\xe6\xb5\x1f\xfd\x43\x63\xc3\x89\x45\x69\xf9\xa0\xcb\x8f\x0d\xde", 54, { 0x20, 0x66, 0xd7, 0xd2, 0x54, 0xf6, 0x4a, 0x99, 0xbd, 0x73, 0xde, 0x75, 0x3b, 0x07, 0xaa, 0x2a } },
{ "\x82\xb1\xb0\x6a\x97\x8c\xf7\xcb\x86\x28\x7c\x64\x11\xe2\xa2\x8e\x4d\x15\xf7\x50\xd6\x64\xb2\xbd\x23\xa7\x5b\xeb\xf4\x70\x8a\x8b\xe8\x39\xc7\x2a\x2b\x2b\x91\x03\x4c\x8d\x7a\x7e\x2c\xc8\x6f\x49\x12\x13\x16\x12\xdc\xbf\x50", 55, { 0x0b, 0x6a, 0x58, 0xcd, 0x5c, 0x4b, 0xa5, 0xbe, 0xff, 0xdb, 0xff, 0x98, 0x89, 0x58, 0xe4, 0xe9 } },
{ "\xde\x50\x21\xdd\x09\x91\x17\x1f\xda\xad\x39\xf7\xc2\x53\x9e\xcc\x32\xa2\x48\xaa\x16\x1c\x2d\x86\xf1\xb9\xe2\xa0\x96\x18\xe6\x01\x80\xd0\x89\x24\xcf\xe5\x77\xb1\xe0\x57\xe5\x64\x87\xd4\x57\x0d\xeb\x8d\x0b\xb0\xff\x25\x06\xcc", 56, { 0xcb, 0x43, 0x10, 0xa9, 0x26, 0xff, 0xaf, 0x74, 0xd9, 0xde, 0xc4, 0xab, 0xa2, 0x97, 0x12, 0x12 } },
{ "\x99\xc6\xb8\xb9\x30\x4e\x4e\x2a\x37\xa7\x95\xd2\x10\x35\x86\x24\xaf\x68\x72\x32\xcc\x71\xcb\xb7\xc4\x11\x8c\x30\x0d\x8c\x46\x24\x54\xbd\xc8\x85\x35\x43\x6b\x2f\x96\xd9\xf7\x8d\xa0\xc4\x36\x5b\x5b\x65\x13\x85\xe6\xee\x6d\x2f\x4f", 57, { 0x69, 0x64, 0x39, 0x12, 0xc7, 0x7b, 0x98, 0x65, 0xf1, 0x8b, 0xc8, 0x61, 0xad, 0x18, 0x5e, 0x44 } },
{ "\x31\x7e\xce\x15\x94\xe2\x73\x37\x21\x47\x28\xf8\xba\xb0\x73\x32\x69\x65\xf7\x54\xd6\x7b\x3a\xfa\xd2\xfc\x82\x28\x0f\xb2\xd8\x22\xf2\x95\x2e\xea\x81\xce\x9c\x57\x77\x67\xc6\xce\x77\x92\x5a\xa0\x5d\x93\xb6\x00\xaf\x4a\xa6\x9f\x32\x61", 58, { 0xc3, 0xc8, 0x1d, 0xfd, 0x52, 0x98, 0x05, 0x1b, 0xe1, 0x01, 0x1e, 0xbf, 0x18, 0xbd, 0x55, 0x5d } },
{ "\x4b\xe6\x99\x0e\x17\xcf\x58\x85\xf4\x60\x98\xfe\x84\xc6\x43\x5e\x84\x0a\x3e\x84\x4a\x72\xb7\x33\x6d\x77\x94\x97\x83\x5d\x0a\x76\x05\x75\x4f\xf4\x4f\xeb\x67\x02\xc4\x4c\x1b\x65\x0f\x99\x09\x5f\x84\x00\xd4\xfc\xa4\x3d\x7b\xfa\xbf\x8d\xc7", 59, { 0x81, 0x50, 0x46, 0x50, 0xc1, 0x0e, 0x9f, 0xb5, 0x07, 0xaf, 0xde, 0xd3, 0x59, 0xfe, 0xcc, 0x22 } },
{ "\x55\xb4\x4a\xde\x6f\xd4\x28\xf6\xd9\xe5\x0a\x23\xc1\x42\x82\x50\xf8\x20\xe4\x4c\x9e\xeb\x05\xfc\x6b\xce\x95\xaf\x9d\x52\x16\x8f\x7a\x1e\x78\x32\x81\xdd\x53\x50\x31\x3d\xc0\x8f\x75\x13\xd5\x7b\xaa\x9b\xbb\x91\x4f\xdd\x6c\x7a\x48\x2c\x0e\x55", 60, { 0xb4, 0xc3, 0xbe, 0x48, 0x13, 0x1d, 0x4c, 0xa6, 0x77, 0x96, 0xfb, 0x46, 0x00, 0xaa, 0xe0, 0x23 } },
{ "\xcf\xc9\xe7\x2d\xf7\xa4\xfd\x8a\xde\x42\x4b\x23\x6f\xba\xa1\xb1\xd1\xd9\x19\xde\x70\x65\x31\x5c\xa6\x7f\x8a\x13\x05\x21\x3c\x72\x43\xc7\xd4\xe7\xc3\x2b\xeb\x69\xbc\x28\x64\x32\x08\xc0\x41\x8b\x8b\x39\x22\xbf\x3e\x62\x5c\x31\xd7\xf3\xb2\x8a\x17", 61, { 0xae, 0x50, 0xbd, 0x11, 0xb4, 0x2e, 0x1b, 0x32, 0xbc, 0x0a, 0x61, 0xb0, 0x07, 0x0c, 0xb7, 0x6e } },
{ "\x12\xb3\x0a\xb0\xf2\xaf\x00\xd0\x96\x57\x55\x4e\xb4\xf0\x42\x70\xb4\x34\x21\x56\xd7\xc5\x61\x07\x75\x4f\x94\x17\x5e\x39\xa3\xf1\x62\x07\x21\xc7\xed\x4f\xbc\x13\xca\x55\xd0\xc8\x08\x46\x15\x1a\xfa\x0d\x79\xe7\x58\xd6\x09\xc0\x82\x1d\x08\x98\xe5\x72", 62, { 0xb6, 0x1f, 0xc7, 0x47, 0x6f, 0x2c, 0x1a, 0x33, 0xcd, 0x50, 0x40, 0xd0, 0x30, 0xd4, 0x96, 0x9f } },
{ "\x22\x09\x76\xcb\xba\x3d\x5c\x85\x60\x7f\xaa\xf9\x5e\x5e\x4a\xb7\x71\x7b\xf5\x62\x95\xf0\x5e\x28\xf9\x5d\x6e\xdb\x12\x90\xaa\xb1\xcc\xd0\xb2\x95\xdb\xc7\xe3\x27\x2f\x09\x1b\x57\x85\x45\x9c\x99\x1a\x07\x09\xc5\x7a\x27\x8e\x8f\x77\xd2\x1d\x9e\x36\x32\xd3", 63, { 0x54, 0xbb, 0x99, 0xe2, 0xff, 0x14, 0xe1, 0xce, 0xa6, 0xb9, 0x6b, 0x77, 0xf9, 0x1f, 0x52, 0xd7 } },
{ "\x4a\x64\xbd\x79\xf7\x86\x17\x25\x09\x6d\xa5\x01\x83\xc7\xaf\x23\x3f\xd2\x31\x9c\xcc\x2c\x3f\x8d\xdf\xc7\x12\x72\x78\xf8\xb3\x82\x93\xae\x42\x2f\x86\xbf\xe3\xc8\xfd\x5e\x46\x3f\x90\xa9\xd2\x04\xfe\x5f\x6e\x0f\x09\xaf\xeb\xfd\xed\x2b\x11\x52\x7e\xdc\x45\xdd", 64, { 0xa7, 0x71, 0x25, 0x07, 0x06, 0x4e, 0x0a, 0x46, 0xbb, 0x2f, 0xd6, 0x8a, 0x0b, 0x19, 0xda, 0x2f } },
{ "\xdd\x6e\xd4\x39\xd6\x8c\xde\x6f\xda\x47\xe1\xb9\x94\xaf\xe1\x62\x29\x84\x32\xc9\x11\x06\xe2\x84\x8d\xe9\xc5\xa0\xc4\x65\x1d\x07\x7e\x69\xe6\xfb\x7c\xef\xbc\xbe\x71\x6b\x6a\x54\xa1\x5d\x10\x60\x15\x06\xf0\x2b\x78\x37\xd5\xc4\x92\x44\x20\x41\x5e\x18\x70\x23\xc9", 65, { 0x7e, 0x44, 0xc6, 0xdd, 0x7f, 0x97, 0x6b, 0x87, 0x68, 0x0a, 0xd6, 0x98, 0xec, 0x78, 0x88, 0x0b } },
{ "\x62\xed\xd8\x0a\xed\x32\x59\x98\x0e\xd4\xf4\xcd\x36\x93\x24\x15\xa7\x1d\x9c\xd2\x44\x79\x63\xd0\x81\x16\x18\x60\x79\x71\x57\x13\x1e\x5d\x34\x15\x8a\xf2\xe4\x23\x75\x14\x7c\x2a\xc0\x9f\xd1\x7e\x2d\x2c\x7d\xb3\x32\x83\x03\x1c\xe2\x9d\x0a\xdd\x0b\x54\xc6\xaf\x5f\xa9", 66, { 0x1e, 0x1d, 0x15, 0x6e, 0x61, 0xaf, 0x88, 0x11, 0x0d, 0x63, 0xfc, 0x25, 0x8a, 0x13, 0x66, 0xb9 } },
{ "\x74\x30\xca\xb2\x03\xe5\xe2\x1e\xd0\xcd\x7d\x66\x8a\xa2\x5c\x92\x35\xaf\x04\xa5\x4a\x49\xad\xa7\xfb\xeb\x54\x4d\x93\xf2\xeb\x46\xfc\xf1\xb1\x24\x5a\xb2\x9c\xe5\xd5\xca\xf1\x1e\x75\xd8\xf6\xc3\x26\x5a\xc0\x95\xda\x2b\x26\x28\xcd\x9d\xd6\x90\xe4\x2f\x85\xa8\x2f\xeb\x42", 67, { 0x8a, 0x89, 0xe2, 0xa9, 0xd2, 0xbb, 0xeb, 0x0c, 0xd6, 0x6c, 0xe7, 0x6b, 0xbd, 0xe1, 0xd3, 0xa7 } },
{ "\xfe\x8e\x5f\xe4\x5e\x6f\x35\xa2\xfa\xb5\x71\xc1\x33\xb4\x47\x68\x06\x59\x97\x8f\x16\x67\x06\x16\x52\xdc\xba\xf2\x42\x72\xb1\x82\xf3\x41\xbf\x00\x7d\x81\x22\x12\x2e\x6e\xc3\x80\x55\x44\x0c\x01\xac\x6a\x60\x2e\x63\xab\x3b\x98\xc5\x9f\xe8\xf5\x89\x28\xd9\x75\xb8\x18\xa6\x33", 68, { 0x27, 0x2d, 0x93, 0x98, 0x27, 0x31, 0xa7, 0x1c, 0x09, 0x80, 0xd8, 0x3b, 0xcf, 0xde, 0x6f, 0x7a } },
{ "\x35\xf0\xa5\xc6\xee\xc3\x22\xf8\x9e\x3a\x3b\x8e\x3c\xb0\x5b\x46\x03\x7d\x51\xdf\x1f\x50\x0c\x52\xf1\xb6\xf5\x1e\xff\xb7\xe9\xc1\x7b\x9f\xd2\x42\x6e\xda\xe1\xeb\x81\xf9\x69\x15\x68\xd1\x5b\x1b\xec\xc2\x5a\x71\x93\x15\x7a\xcd\xed\x7f\x9a\x83\x7e\xc2\x5b\xee\x43\x73\xbc\x0a\xe3", 69, { 0x00, 0x4e, 0xc2, 0xb8, 0x3e, 0xdf, 0x71, 0x7c, 0xc0, 0x9c, 0xd3, 0x98, 0x85, 0x4d, 0xc7, 0x95 } },
{ "\xd6\x54\x1f\x73\xe5\x2b\x98\xe6\x70\x34\xf2\x10\x5e\x6b\xd0\x55\x11\x87\x2b\xf2\xe8\x5d\xa6\xe7\xde\xd1\xff\x80\x4f\x79\xa2\x7f\xdb\xd0\x58\xe8\x63\x13\x1c\x69\x31\xbb\x0e\x63\x34\x98\x81\x99\x68\x87\x2f\x74\x54\xd0\x8c\xf0\xad\x1e\x37\x27\x18\x1b\x8d\x78\x0d\xa8\x58\x54\xd7\xb6", 70, { 0x81, 0x58, 0x80, 0xad, 0x21, 0x1f, 0x75, 0x90, 0x18, 0x1c, 0x0a, 0xb5, 0xf2, 0xa3, 0x72, 0x34 } },
{ "\xa5\xa5\xb7\xc4\xb3\xda\x29\xf5\x07\x93\x3e\xcd\x9d\xe6\x28\x88\x38\x4c\xd4\x17\xdb\x2c\xb2\xb8\x50\x01\x90\x30\xe5\x2a\xa4\xa8\x37\x4e\xa8\x21\x26\x69\x20\x96\xe8\x78\xb7\xad\x39\xbd\xc7\x19\xb0\xaf\xf8\x8f\x2d\x82\x00\x62\x6f\x4e\x88\xbd\xc2\xdf\x2f\x7d\xd5\x18\xa7\xa9\xa9\x04\xfd", 71, { 0xfb, 0x0d, 0x18, 0xc6, 0xe4, 0x89, 0x48, 0xb1, 0xf6, 0xfd, 0xeb, 0xe2, 0xc1, 0xf1, 0xf3, 0x23 } },
{ "\x8e\xa6\x7b\xf7\x7b\xc8\x6d\x96\x16\xf6\xce\xbb\x5a\x73\x1e\xe6\x2e\x6d\x93\x19\xa2\xb3\xe6\xdb\xc8\x84\xb7\x58\x2d\x83\x9e\xa6\xf3\xc6\xcf\x22\x8c\x0d\x69\xf9\x15\x0f\x5b\xc3\x4d\xf0\xf3\x49\x30\xd0\x05\x67\x34\x7d\xb4\x0d\x1f\x48\xe0\x5f\x83\xf5\x2c\xfc\xd7\xcc\xaa\x5c\x34\xcc\x26\x53", 72, { 0x36, 0x75, 0xbc, 0x48, 0xda, 0x88, 0x50, 0x72, 0x0e, 0xf7, 0x29, 0x63, 0x24, 0x34, 0xe3, 0x39 } },
{ "\xa9\xff\x0c\xb2\x3b\xe9\xf7\x0c\x7c\x1a\xd6\x96\xc6\xe3\xbd\x28\x15\x25\x46\x60\x7e\x45\xa1\xe6\x50\x3b\x3f\xc9\xc6\xca\x17\x08\x65\xca\x94\x2e\xf8\xa1\x1b\x14\x26\xd0\x3c\xca\xad\xa9\x74\x91\x3d\x1e\x9d\xff\xf3\xb5\xf3\x45\x70\x99\xc3\x8e\x96\xca\xb2\x7d\xdf\x73\xa7\xd9\x59\x5c\x56\x45\x42", 73, { 0x2e, 0x33, 0xf2, 0x55, 0x54, 0x86, 0xb7, 0xa7, 0x53, 0x25, 0xf2, 0xe5, 0x8a, 0x28, 0xf6, 0x82 } },
{ "\x76\x6a\x28\x3c\x94\x4e\xab\x2c\x39\x99\xe0\xb5\x8c\x14\x00\x4c\x68\x68\xcd\xf8\x90\x69\x16\xa9\xff\xf9\x59\x32\x5e\x9a\x82\xe3\x8f\x94\xd2\xc7\x66\xbf\xdb\x77\x78\xee\xa7\x99\x0b\xf8\xf9\xf9\x2d\x64\xab\x41\xe0\xb0\x36\x58\x72\x3e\x50\x55\xec\xdc\xb0\x1f\xa0\xee\xed\xa3\xe5\x3a\x84\xb0\xe0\x50", 74, { 0xfd, 0xff, 0x52, 0x9f, 0xba, 0xe5, 0xe2, 0x49, 0xa8, 0xfa, 0x24, 0xec, 0xa0, 0xc7, 0x0b, 0x65 } },
{ "\xf8\x72\x78\x94\x14\x89\x6b\xea\x4c\xc5\x7f\xfc\x42\x80\xd0\x2a\x64\x98\x48\x0f\xb2\x08\x2e\xad\x9d\x23\xd5\x34\x0b\x6c\x74\x74\x78\x14\x16\xdc\x36\x11\xa8\x2e\xd4\xd7\x30\xc1\x9d\xb5\x1d\x72\x38\xcf\xb9\x28\x6d\xb4\xba\x03\x07\x9b\xf8\x21\xd4\x3f\x3a\x0c\xc9\x54\x29\xc3\x42\xa4\x66\x64\x68\xcb\xb7", 75, { 0x17, 0x43, 0xcd, 0x6e, 0xf2, 0xfb, 0x65, 0xa6, 0x27, 0x7f, 0x7b, 0x30, 0xa9, 0x73, 0xb9, 0xe3 } },
{ "\x73\xac\x8b\x5c\xc9\x6f\xcd\x80\x1b\xc4\x35\xcb\x26\x4d\x60\x14\x74\x42\xbd\x34\xce\x90\x05\xf6\x3f\x1d\x58\x2a\xc0\x2a\xf6\xd9\x85\xd2\x96\x92\xe3\xaf\x66\xe3\xdc\x9c\xa0\xe3\x49\x94\xc6\xec\xa2\xb9\x7a\x67\x6d\x71\xfc\xc0\x41\xab\x40\x78\x76\x40\xab\x58\x6c\x74\x6a\xc3\x74\x9b\x6f\x25\x5e\xd8\x09\x88", 76, { 0x1d, 0xc9, 0xfa, 0xc0, 0x76, 0x7a, 0xd0, 0x00, 0x3b, 0xec, 0xae, 0x60, 0x8f, 0xb8, 0x72, 0xb3 } },
{ "\x9f\x01\x1f\x45\x1f\x9e\x09\xd7\x9d\x10\x0c\xcb\xc0\xcf\x6f\xd3\xbb\xcc\x3e\xfb\x9b\xf5\x9b\xf3\x30\xd7\xbb\x8a\x96\x5d\x84\x3c\x5f\xb6\xc2\xe2\x5e\x55\x00\x87\x12\x12\x09\x5b\xae\x6e\xfb\xc1\xc1\xf3\x04\x83\x87\xba\xbc\x25\xcb\x06\x1f\x57\x77\x0e\x25\x83\xba\xd8\x00\x87\xd4\x41\xf2\x7d\x81\x19\xc8\xb5\x00", 77, { 0x7e, 0x0a, 0x91, 0xef, 0x91, 0x65, 0x79, 0x1f, 0x66, 0x8b, 0x7e, 0xb7, 0xb3, 0xe1, 0x02, 0x61 } },
{ "\x6b\xf6\xc4\xbe\xda\x50\xb6\xa4\x26\x63\x5e\xfa\xce\x6e\xcf\xf6\xd0\x00\xe3\xe5\x8c\x2f\xf8\xf5\xc7\x72\xbb\x9d\xca\x1c\x1c\xa9\xaa\x76\x61\xe5\xa3\x77\x56\x65\x9e\x22\xa2\x70\xd2\x7c\x36\x07\x86\x29\xde\x15\xf4\xa3\xfa\x34\x4a\x15\x46\x19\xca\xda\xd4\x9e\x11\x8d\x1c\x6b\x74\xcc\x2a\x3f\x86\x8e\xf5\xe0\xb6\x1e", 78, { 0xd2, 0x81, 0xce, 0x6f, 0x24, 0x4b, 0x96, 0x1b, 0x08, 0x0c, 0x6e, 0x2e, 0x55, 0xd5, 0x40, 0x8b } },
{ "\xd0\x9d\x15\xb4\xf9\x7d\x36\xae\x4f\x06\x2e\x70\x21\xab\xbb\xd5\x22\xd3\x81\x50\x78\x82\x6f\xa4\xf3\xa6\x41\x3c\x5b\x00\x1e\x27\xe2\xad\x61\xb4\xb8\x51\x75\xa7\xbe\xdf\xd9\x15\x52\x06\x43\xe1\x49\x3b\xe1\xd3\xfa\x5f\xba\x52\x77\x0e\x33\xbe\xd7\x84\xae\x6c\xaf\x2b\x4d\x3f\x9f\xc5\xd4\xdf\x08\x32\xac\x99\xdb\xdf\x04", 79, { 0xb0, 0xc7, 0xe4, 0x19, 0x13, 0x5d, 0x32, 0x5b, 0x90, 0x39, 0x3c, 0x85, 0x8c, 0xd8, 0x61, 0xfd } },
{ "\xd3\x8d\xcb\x12\x2a\x27\x37\x78\x2a\x79\xea\xc1\xa9\x6e\x1e\x0a\xc0\x7b\x0a\xbd\x67\x31\x0d\x79\xac\xf3\x74\x0e\x90\xc1\xe8\xa4\x7e\x32\x77\xfb\x69\x80\x3d\xcc\x81\xbd\xae\x4f\x91\x24\x83\xd6\xf8\x8b\x3f\x96\x41\xf4\x5d\x86\x62\x42\x18\x61\x8e\x08\xd6\xc5\xb7\x7d\x32\x7d\xa3\x8c\xa5\xdc\xaa\x08\xd0\x40\x0a\xfd\x68\xb6", 80, { 0x6a, 0xa7, 0xac, 0x63, 0x95, 0x32, 0x9d, 0xb9, 0x93, 0x8b, 0x41, 0xfe, 0xd5, 0x77, 0x03, 0x84 } },
{ "\xe6\x21\xbe\xbc\xe1\x6a\xea\x21\x53\xb9\x93\x09\x27\xb4\x5e\x0d\x51\xb1\xf1\x7f\xee\xcd\xa3\xcb\xf9\x1d\xc2\xf8\xa7\xfc\x80\x4e\x61\x84\x7a\x12\x5c\x18\xe6\x6d\xd6\x1d\x59\x6b\xee\xc8\x33\x1e\x20\x12\xa8\xe3\x5d\xc9\x6b\xbc\xc3\xc0\xf9\xdd\xfe\x27\x7f\x42\x3a\xf5\x68\x7d\xc4\x81\x87\x8e\x3c\x30\xde\xea\x79\x49\x09\x9e\xf9", 81, { 0xa4, 0x46, 0x74, 0x55, 0xda, 0x5c, 0x22, 0x46, 0xf2, 0xb8, 0xd1, 0x7a, 0x5e, 0xa9, 0x22, 0x09 } },
{ "\xe5\x4a\x8d\x03\x02\x32\xd5\x5c\xb6\xe8\x50\xa1\x80\x19\x36\x47\xba\x7c\xe0\x2d\x2a\x00\xa7\xdb\xb9\x95\xeb\x5a\x3b\x94\x30\xaf\x6c\xcc\x62\xf5\xfc\x2a\x39\x16\xc1\x04\xb7\x26\x0c\x02\xcf\x5c\x16\xa9\x20\xad\x98\x85\xde\x07\x79\xf3\xd2\x27\xa2\x88\x78\x17\xee\x22\x46\x48\x3d\x89\x0c\x47\xa7\xc1\x76\x1f\xce\xee\xaf\x4c\x4d\xe4", 82, { 0x67, 0x67, 0xb5, 0x70, 0xa1, 0xd2, 0x3b, 0x5c, 0xb5, 0xea, 0xb7, 0xf3, 0x39, 0xa9, 0xef, 0xf7 } },
{ "\x20\x57\x4d\x4b\x56\x13\x36\x6b\x02\x72\x81\x9a\x19\x04\xac\x3f\x1c\x0e\x47\x82\x72\x43\x2c\x92\xf1\x22\xcc\x92\x7b\xeb\xa3\x21\xc5\x3f\xcd\x84\x33\x53\xb3\x73\xdf\xd0\xdd\x7d\xb9\x4b\xec\x18\xc8\x5c\x9f\x49\x8f\x5d\x12\xec\x8a\xc1\x08\xa1\xd7\x0e\x5d\x53\xf2\x78\x9e\x66\x99\x1c\xb4\x7c\xce\xd5\xbb\xe4\xfb\xbf\xf0\xa1\x2f\x46\xc9", 83, { 0xb9, 0x7f, 0x77, 0xa3, 0xd3, 0x37, 0x5f, 0x32, 0xbd, 0x36, 0x3b, 0x21, 0xb8, 0xd0, 0x42, 0xef } },
{ "\x12\x87\xe9\x1f\x42\xa8\x91\x87\xce\x68\x88\xbe\x6f\x8c\x18\x42\x24\xac\xc1\xfd\xfd\x33\x15\x1c\x85\x83\x34\xff\xcd\x35\xe0\x49\x11\x7e\x3c\x16\x0a\xad\x26\x4c\xcf\xd0\x2d\x0b\x69\x76\x31\x3e\xf2\x90\x96\x53\x68\x71\x24\x07\x13\xf5\x7f\x5a\x91\xbd\x3e\xa2\x7b\x98\xa9\xbc\xb6\xfd\x14\x5d\x58\xb4\xd2\x86\x34\x95\xd8\x16\x04\x69\xf0\x79", 84, { 0x1d, 0x33, 0x53, 0x15, 0xa2, 0x3a, 0x5b, 0x3d, 0x37, 0x92, 0x65, 0xc7, 0x04, 0xb6, 0x7a, 0xd8 } },
{ "\x32\xed\x96\x7e\x43\xf4\x73\x47\xe0\xac\x73\x59\x83\xf9\x85\xd4\xba\xfd\xef\xbc\x1b\xb9\x4b\x75\xc5\xcd\x21\x4e\x8e\x5b\x22\x34\xce\x0a\xff\x87\xc7\x26\xe4\x09\xaf\xe5\x95\xf2\x5c\xc5\x23\x22\x8c\x10\x4a\x6e\xcd\x81\x6a\x1b\x0f\x01\x20\x69\xc4\x8a\x81\x46\xb1\x2d\xf3\x24\x55\x2e\xa7\xe4\xf2\x4d\xb6\xf1\x3c\x20\xd7\x6a\x9d\x9b\xbb\x77\x23", 85, { 0x2d, 0x58, 0x23, 0x43, 0xe5, 0xa1, 0xc3, 0x4b, 0x28, 0x16, 0x9e, 0x76, 0xb5, 0x07, 0xd4, 0x3a } },
{ "\xb2\x05\xbd\x07\x15\xe9\xec\xdb\x1a\x60\x75\x4f\xb9\x05\x59\x9f\xb0\x90\x1d\x9d\xc7\xec\xda\x56\x2e\xf6\x70\x64\x02\xd4\xdb\xd0\xee\xf0\x9e\xa1\xee\x90\x5b\x06\x2f\x14\x84\x1b\x13\xcb\x2c\x5b\x50\x71\xf7\xa3\x38\x49\x30\xdf\x13\xd3\xf9\x53\xa8\x2b\x9d\x88\xdb\xfe\x02\xd1\x70\x29\x3a\x78\xed\xf0\x38\x8a\x9e\xd7\x9f\x3c\xb2\x20\xb6\xf9\x83\xe2", 86, { 0x88, 0xef, 0x98, 0xec, 0xda, 0x19, 0xbe, 0xbe, 0x17, 0xb8, 0xe4, 0x4d, 0xea, 0x79, 0xc2, 0x82 } },
{ "\x78\x09\xf3\xc3\xc8\xdf\xf2\x52\xc2\xff\x1b\x03\x2d\x8a\x7e\xc8\x0c\x67\x79\x48\x54\x10\xbf\xbe\xb7\xf6\x7a\x71\x9f\x92\x8d\xcb\x36\x0a\xf2\x19\xa2\x7c\x43\xde\x4e\xe9\x54\xd4\x64\xc1\xfd\x80\x57\x07\xf5\x71\x9c\x20\xd9\x15\x78\x3e\x4d\x37\xf4\x64\x39\x19\xee\x15\x35\x29\x4a\x9d\xff\x64\x45\xfb\x29\x44\xe9\x69\xd0\x67\x9d\x8a\x86\xbe\xd4\x2f\x51", 87, { 0x5a, 0x35, 0x4a, 0x9b, 0x4a, 0x1f, 0x23, 0x3b, 0x70, 0x42, 0xbb, 0x52, 0x31, 0x27, 0xee, 0xee } },
{ "\x6e\xe6\xc6\xcd\x46\xfb\x60\x6c\xdd\x23\x5d\xde\x48\x84\xb7\x8c\x76\xab\xe7\x3d\x28\x03\x77\xdb\x8f\x63\x26\x50\x83\xc1\xb1\x8b\x5e\x04\x44\x9f\x73\xf8\x7d\x0a\x2e\x5b\x19\x12\xca\x14\x3d\x4b\xa9\x83\x63\x36\x53\xf3\xdf\x04\x0d\x2c\x0d\x78\x15\x26\x19\xea\x79\xd7\x6b\x67\x91\x2d\xad\xf6\x1f\x18\x7a\xf6\x01\xbe\xa4\xa3\x90\xd2\x22\xb7\x99\xff\x95\x2c", 88, { 0xdd, 0x18, 0xb1, 0x72, 0xbb, 0x74, 0x5b, 0xfc, 0x9b, 0x3f, 0x66, 0xec, 0xc2, 0x0a, 0x43, 0xcc } },
{ "\xf6\x7e\x75\x20\xc8\xc7\xdc\xd2\x52\x63\xef\xc9\x75\xe0\xe5\x14\xc4\xde\xb5\xac\x43\x47\x60\xf5\xc1\x9c\xd8\x63\xcf\x6a\x8c\x3a\x5c\xdb\x91\x6e\xee\x68\x6e\xa8\x7f\xac\x84\x6a\xf2\x54\x18\x49\x30\x33\xff\x59\xe4\x72\xe5\xa8\xcf\xe5\x39\xe0\xc8\x78\xb8\x10\x54\xc8\x95\x84\xde\xce\x42\xd0\x93\xb6\xde\xec\xdc\xce\x3b\x79\x79\x99\x8a\x22\x37\x04\xa6\x1d\x4d", 89, { 0x95, 0xd2, 0xf3, 0xfa, 0x24, 0xcf, 0xd0, 0x6d, 0xca, 0x74, 0x7b, 0x13, 0x05, 0x68, 0xcd, 0x51 } },
{ "\xb0\xfa\xd1\x59\x6e\x0f\x92\xf2\xc9\xb5\x87\xe9\xbc\xcd\xad\x21\x84\xb2\xf4\x08\x90\x42\x87\xb1\x96\x8c\x29\x90\xbf\x18\xbb\xd1\x02\xcd\xda\x55\x8f\x83\xa5\x4a\x61\x26\x9c\x65\xf6\x83\xa4\xbf\x1b\xb2\x27\x49\xe0\x20\x58\xee\xac\x38\x94\x44\x69\xae\xe4\xed\xdf\x68\x9f\x90\x1f\x09\x2c\x6f\x15\x1a\xe5\xa6\x41\xd7\x18\xff\x7f\x94\x27\x74\x02\xca\xcf\x42\x5f\xc2", 90, { 0x1c, 0x07, 0x39, 0x77, 0xf3, 0x7e, 0xb5, 0x17, 0xb5, 0x1c, 0x09, 0x97, 0xbb, 0x47, 0x34, 0xe3 } },
{ "\x85\xa6\xc5\xdd\xfc\x37\x4b\x7e\xc4\xdf\x62\x94\x0c\x77\x6a\xc7\x88\xfe\x60\x3e\x6d\x76\xb4\x0b\x99\x5c\x38\x34\xd1\xc2\x35\x5f\x22\x0a\x98\x19\x68\x41\x4a\xc3\xed\x15\x9d\x19\x29\x75\xe1\x60\xa8\xc4\x17\x2c\x09\xb9\xbd\xcb\x22\xd5\xc8\x51\x41\x82\xb0\x41\x8d\xc5\xa5\xd5\x8c\xa0\xdf\xeb\xbe\x07\x13\x8c\x66\x7c\x01\x19\x68\x49\x2a\x14\x4d\x5a\xa0\x88\x64\xb4\xf5", 91, { 0x6b, 0xcf, 0x3d, 0x4f, 0x7a, 0x79, 0xf2, 0x5e, 0xe8, 0xd1, 0xda, 0xe9, 0x5e, 0xb1, 0x9c, 0xc5 } },
{ "\x17\x04\x96\x55\x9e\xec\x2a\xb0\x86\xcb\xe8\x0f\xaa\xdb\x88\x08\xe0\xa7\xa1\x4d\x26\xee\xe3\x5e\x6b\xa0\xee\x5f\xf7\x05\xcc\x04\x77\xfd\x12\xa8\xa9\x62\x8e\x7d\xa0\x89\xad\xde\xb4\xe8\x6c\xc1\x0a\xd1\xf9\x48\xbc\x5c\xe6\x76\xa4\x64\x08\xfe\xca\xa7\xf2\x37\x68\x11\x09\x2d\x96\x12\x44\xd1\x96\x2b\x83\x50\xbe\x89\x88\x80\xfe\xc8\x96\x77\x0d\xd8\xa4\x36\x35\xfd\x3e\x83", 92, { 0x0a, 0x40, 0xa5, 0x22, 0x7b, 0xe3, 0x34, 0x40, 0xb1, 0x02, 0xa4, 0xf4, 0x4a, 0x1d, 0x26, 0xcb } },
{ "\xb7\xf0\xf9\xd4\xdc\x23\x91\x67\x99\x22\xa1\x6f\xdd\x15\xfd\x68\x7b\x62\xaf\x8c\xaf\x6f\x8d\x5e\x4f\x85\x74\x63\x8f\xd0\x23\xf0\x71\x83\xb6\x1f\xc7\x18\xbc\xdb\xe6\xb5\xf8\x1b\xe9\xaa\xb1\x2c\x9f\x17\xb6\x26\x12\xab\xf2\x80\xb5\x87\xa7\xc0\x29\x40\x4b\x4a\xac\x03\xfc\x5a\x1e\xd3\xa0\x0d\xb1\xef\x9d\x99\x2d\x4f\x32\x9e\xde\x4c\x70\x94\x1b\x5f\xd9\x63\x73\x01\x93\x11\x75", 93, { 0x83, 0x24, 0x5e, 0x67, 0xff, 0x3f, 0xf3, 0xac, 0x7f, 0x5d, 0x83, 0xda, 0x0f, 0x0f, 0x3b, 0x66 } },
{ "\xb2\x49\x95\x1c\x16\x9f\xcb\xd5\x39\x4f\x81\x2f\xf3\xe5\xae\x1e\x67\x13\xc2\xec\xa5\xb2\x19\xbd\x70\x56\x89\xbd\x19\x8c\x1a\xe8\x8a\xb2\xd5\x75\x5f\x73\xb5\xe0\x2e\xdf\xba\x93\xe7\x23\x1d\x30\x57\xe9\x9e\x7e\xc7\x4f\xf9\xed\xc1\x7c\x77\xc7\xf2\xe5\xa0\xf5\x2b\x1b\x0e\x6c\x81\x4e\x5c\x32\x95\x62\x3e\x11\xee\xac\xab\xd6\x82\x49\x34\x46\x54\x1b\x78\x9f\x37\x61\xb0\xc6\xb0\xf2", 94, { 0xc1, 0xa1, 0x23, 0x7f, 0xf0, 0x11, 0x23, 0xcf, 0x64, 0xb1, 0x96, 0x5d, 0xb6, 0xb5, 0x34, 0xe0 } },
{ "\x66\x12\xf9\x16\x43\xb3\x22\x01\x05\xc0\x53\xed\x8f\xdd\x2c\xd2\x1a\x85\x4d\xc5\x8a\xfa\xf1\xeb\xdb\xc6\xb5\x18\xba\xec\x77\x08\x30\xb0\x59\x38\x79\x00\xe5\x62\xf3\x7e\x4b\x94\x32\x2b\xc5\xec\x30\xb1\x1b\xf2\xa2\x4d\x42\x92\x65\x57\x20\xb4\x17\x6f\x6b\x7a\xc3\x46\xaf\x15\xb4\x06\x6e\x44\x6a\x5f\x61\x6b\xaa\x2f\xdd\x4c\xb0\xcd\x51\x64\x80\xf3\xec\xe4\x5b\x45\x5b\x4a\x4c\x82\xb0", 95, { 0x5c, 0xe8, 0x8d, 0x8c, 0xb0, 0x07, 0x9f, 0xc3, 0x82, 0x01, 0x92, 0x4f, 0x7d, 0xb2, 0x46, 0x76 } },
{ "\x9c\xe1\x46\x5e\x41\xf7\x79\x61\x7c\xaf\x58\x7a\x85\x7d\x1e\x98\x46\x5f\x4e\x53\xaf\x2a\xa0\x91\xb6\xff\x86\x4b\xef\xdd\x59\x6a\xc3\x50\x25\xcb\x50\x48\x0d\x62\xdd\x04\x5d\x7a\xf8\x2d\x2a\x1a\x2e\xff\xa8\x83\x67\xa2\x08\xd7\xdb\x97\x0b\xd4\xb5\x4e\x28\xbd\x42\xd4\x56\x59\xd6\xa9\x77\x15\x3c\xb0\x61\x44\xd7\x3f\x89\x87\x59\x56\x4e\x48\x1f\xed\xe0\x60\x28\x66\xcb\xd9\xb1\x22\x24\xcd", 96, { 0x13, 0x2f, 0xc8, 0x0f, 0xaf, 0x52, 0x90, 0x3f, 0x45, 0x2f, 0x80, 0x39, 0xa5, 0x04, 0x8c, 0xd4 } },
{ "\x6e\x2f\x4e\x6d\x66\x1f\x69\x1e\x6c\xf4\x59\xbd\x35\x37\x6e\x03\x1b\x28\xb5\x04\x6d\x0f\xda\xd1\x70\xb1\xb8\x3e\xf7\xde\x7f\xbe\x9a\x27\x81\x6a\x96\xae\x99\x20\x41\x16\x38\xc8\x28\x98\x0d\xb2\xc7\x37\x51\x1b\xe4\x0c\xac\xcf\x88\xbb\xc7\xe8\x9b\x06\x6f\xb4\x1b\x80\x62\xb5\xf8\x59\xba\xc2\x90\x58\xf3\x4a\xc2\xe8\x9b\xb8\xb1\x49\x95\xf3\x17\xe7\x09\xfd\x43\xc8\xec\xbb\xb0\x1e\x27\xd3\x44", 97, { 0xe5, 0x04, 0x88, 0xc9, 0xa8, 0x97, 0xde, 0x0f, 0xc3, 0x2b, 0xee, 0x13, 0xc6, 0x06, 0x7c, 0x2d } },
{ "\xfc\x66\xb1\x22\x26\x80\xa5\x2c\x01\x7e\x26\x18\xa9\x4a\x3a\x2d\x21\xf6\xed\x9b\xa5\xa5\xfe\x75\x1c\x1a\x9a\x4c\xa9\xdb\x40\x69\x61\x01\xc3\xa6\x17\x9f\x6e\xe9\x52\xcd\x41\x3e\x60\x50\x5a\x91\x84\xe7\x6f\x05\x4a\x34\x78\xf3\xfc\x46\x69\x82\x2d\xbf\x1d\xdf\x72\xa7\x4e\x19\x30\xcc\xc1\x9c\x91\xd3\x7f\x48\x91\xe0\x3d\x2e\x34\xdd\xdd\xe7\xda\x6b\x0d\x90\xaa\x63\x0e\x65\x2c\x07\x8f\xf9\xf3\xcb", 98, { 0xf8, 0x09, 0xb6, 0x5e, 0xb8, 0xfb, 0xde, 0x39, 0xd6, 0xfe, 0x1c, 0xd8, 0x44, 0x48, 0x7f, 0x01 } },
{ "\xb7\x0f\x77\xb9\x42\x19\x01\x85\xc3\x4a\xbf\x2d\x07\x5a\xf7\x4c\x9a\xd6\x59\xfd\x63\x21\x6d\x66\x6a\x0b\x2b\xe8\xba\x0b\xa9\xe1\x62\x1c\xef\x1c\x95\x32\xe6\xd6\xf1\x58\x6f\x6e\xdd\x68\x91\xa8\x0b\x66\xca\x9d\x1d\x79\x86\x43\x64\x86\xad\x95\xc3\x6a\x57\x5f\xb2\xd4\x9b\xa8\x5e\xbe\x85\xbb\xee\x86\x25\xd4\xe1\xad\xc4\x64\x93\x2b\x32\x01\xde\x6c\x3b\xed\xdd\xeb\x38\x41\xcc\x6a\xc1\xb7\x0b\xcb\x80", 99, { 0x4e, 0x3f, 0xa9, 0x33, 0xaf, 0x6e, 0x3a, 0xac, 0xc0, 0x29, 0x88, 0x8f, 0x91, 0x86, 0xc9, 0x7c } },
{ "\x11\x42\x8e\x21\x28\x9e\xe8\x65\x94\x38\x7c\x56\xf5\x97\xb8\x95\xdc\x4c\xcf\xcf\x5a\xbc\x4e\x4e\x22\xfa\x5d\xab\x5d\xd2\x95\xa2\x0b\xe3\x78\xfe\x10\xe4\x91\xb3\xe3\x07\x29\x0a\xce\x7a\xa3\xf7\xe2\x0a\x75\x86\x7b\xe2\xc3\x74\x6c\x72\x9a\xda\xc6\x6b\xc5\x04\x4a\xe6\x50\x60\x69\xa4\x97\x92\xf3\x70\x09\xa4\x64\xa1\x7f\x5b\xc8\xbc\x33\xa5\x47\x36\x5e\x2e\x51\x56\x72\x11\x66\x39\xf1\xab\x82\xef\xe8\x8c", 100, { 0xbe, 0x63, 0xc5, 0x40, 0x5e, 0x75, 0x2c, 0x1d, 0x52, 0xb3, 0x28, 0x39, 0x09, 0x05, 0x7b, 0x4d } },
{ "\x24\xc3\x0c\x87\xfa\x99\x6f\xe9\x2b\x6f\xdf\xc6\x40\x06\xdc\x6f\xb0\x9a\x33\xff\x06\xcc\x3b\x15\xb1\xa1\xab\x9c\x68\xa3\x17\xc5\x38\x25\x05\x16\x2b\xa4\xb0\x9c\x97\x49\x58\x3c\x00\x8c\x44\x28\xab\xf2\x56\x6d\xc0\xf6\x49\xa8\x1a\x06\x89\xd8\xaf\xa4\xae\x4f\x97\xad\x97\xc3\xba\xb7\x1f\x81\x1b\x93\x7f\xc1\xbb\xf1\x40\x14\x2b\x23\xda\xfa\xb2\xfa\xee\x90\x16\xcd\x1d\x66\x0b\x98\x83\x6f\x35\x40\xd4\x11\xdc", 101, { 0xd6, 0x38, 0x85, 0xcf, 0x1e, 0xaf, 0x84, 0xf1, 0x6a, 0x8f, 0x77, 0xc8, 0xab, 0x9e, 0x1a, 0x64 } },
{ "\xf4\x11\x5d\x2b\xb0\xe6\x59\x25\xdb\xa3\x78\x25\x10\xd5\x2b\x10\xfa\x81\x90\x19\x59\xfc\x49\x30\x21\x6a\x68\x08\xc1\xd1\x86\xc3\x81\xd9\xf3\x3d\x04\x22\x07\xf2\xed\x42\x97\x8d\x9e\x59\x83\x0e\x1c\x53\x6d\x96\x0f\xba\x84\xd2\xe5\x36\x7e\x02\x15\xa2\xa4\xa5\x81\xcb\x07\xc3\xf4\xc7\xd0\xd6\xcb\xf0\xb5\x56\xb1\x7c\x88\xe0\x83\xba\xe9\x02\x52\xeb\x1c\xa5\x86\xbb\xf5\x18\x1f\x5a\xf0\xb5\x17\x3c\x5d\x32\xe4\x69", 102, { 0x7b, 0xe8, 0xa1, 0x02, 0xa6, 0x52, 0xe9, 0x3b, 0x9d, 0xb4, 0xbc, 0x95, 0xba, 0x9e, 0xd1, 0x07 } },
{ "\xb8\x86\x19\x81\x17\x75\x2c\x50\xbc\xba\x52\x0a\xca\xd0\x87\x23\x15\x5e\xaa\xcd\x12\x3d\xc4\x0f\xed\x27\x69\x7f\xba\xfd\x37\x9c\xc3\xb4\x7b\xca\xff\xde\xf4\xea\xa4\xd1\xaf\x95\xd4\x76\x11\xf6\x72\xb0\xf3\x41\x18\x66\xe7\xd4\x21\x2f\x9a\xe8\xe1\x99\x52\xf4\xf4\x16\x96\x9e\xb4\x84\xec\x5e\xc4\x57\xbd\xb2\xc9\x66\x55\xf8\x39\x4e\x07\xeb\x4b\x87\x6d\xbc\x0e\x01\xcd\xcc\x96\x7a\x98\x04\x50\x5f\x0c\xd2\x77\x5c\xf9", 103, { 0x60, 0xd0, 0x9d, 0x98, 0xae, 0xef, 0x2b, 0x51, 0xa0, 0x3c, 0x5d, 0x3c, 0x71, 0xa6, 0x4a, 0x5b } },
{ "\xc0\x9f\x82\xa0\xb2\x6f\x14\xb6\xbc\x6c\xee\x89\x9e\xf0\x93\x3a\xdd\x39\xd1\x28\x7f\x9d\x13\x6d\x62\xdb\x43\x82\x39\x8e\x5a\xfe\x24\xbb\x85\x7b\xeb\x89\xf5\x99\xd8\x10\x44\x1c\x2e\x49\x7f\x31\x49\x26\xd4\xce\x53\xbb\xd9\x37\xa0\x99\x76\x9c\xa7\x3c\x71\xdf\x9e\x9a\x94\x74\xb1\xdb\x7d\x62\xde\x28\x02\x66\x70\x9d\x3e\x36\x15\x58\x3d\xe3\x9e\xb3\x40\x57\x54\x8e\xad\xfe\xf1\xc5\x96\x82\x42\xc1\xe4\xae\x57\x2a\x20\x74", 104, { 0x1a, 0xe2, 0xe9, 0xa9, 0xb1, 0xf0, 0x27, 0x90, 0x7d, 0xf3, 0x66, 0x14, 0x07, 0xe0, 0xe9, 0xdc } },
{ "\x63\xba\xdd\x0a\x37\x2d\xc6\x2e\x56\x54\x40\x8b\xb4\x49\xbb\x34\x10\xbd\x9a\x4d\xeb\x6d\xbc\xc8\x23\xb4\xa7\x4d\x31\x39\x51\x33\xfc\xc8\x8a\xab\x57\x3f\x09\xfd\x1e\x9f\x11\xea\xe8\x42\xbf\x2f\x69\x2b\xf3\x2b\x67\x79\xd9\x98\x57\xf6\x13\x75\x94\xcc\x85\x04\x3b\x57\xbc\xef\xa5\x16\xfb\x86\x82\xba\xe4\x23\x8e\x61\xc0\xaf\xf3\xdc\x6b\x3d\x3b\x2c\xdf\xd4\xf0\x0c\x5b\x4a\xd3\xa8\x2f\x4b\xb5\x6b\x27\x79\xf5\xf6\x91\xae\x96", 105, { 0x0c, 0x2a, 0x47, 0x52, 0xba, 0xb6, 0xca, 0x3e, 0xb2, 0x6e, 0x64, 0x53, 0x9f, 0x3d, 0x3f, 0xf2 } },
{ "\x2c\xed\x5c\xfb\x6a\x31\x16\x42\xd4\xb6\x27\x3b\xcb\xc2\x60\x04\x7a\xb3\xf0\x42\x90\xc4\x6b\xfe\x08\x7f\xed\x19\x23\xbf\x58\x6d\x78\x61\xb8\x82\x21\x87\xc8\xea\x17\x88\x8e\x3a\x98\x77\x21\xa5\xc4\x4f\x8b\x36\x48\xb8\xc9\xaa\x31\x78\xef\xe7\xe2\x79\x68\x1d\x21\x72\x5b\x78\x4b\x35\x2a\x7f\xa8\x95\x14\x0c\xd9\xf2\xfa\xe8\x6e\x63\x3f\x02\x94\x7e\xc8\x4c\xeb\xc7\x23\x33\x76\xb2\xc4\xb9\xac\x56\x6a\x30\xab\xb1\xa0\x95\x8c\x92", 106, { 0x73, 0x8a, 0xe1, 0x0f, 0xcf, 0x84, 0x6e, 0xf5, 0x84, 0xab, 0x8b, 0x58, 0xd5, 0x1b, 0xc8, 0x95 } },
{ "\x5f\x86\xd1\x27\xd0\xe1\xfb\x23\x30\xfb\x39\x8b\xcd\x7a\x3a\x1e\x2d\xd0\x23\x5f\x4d\x54\x9d\x40\x07\xfe\x05\x6d\x8d\xbf\xc7\x32\x11\x7b\xc5\x09\x87\xa4\xf0\xc4\x82\x74\xfa\x53\x3b\xc7\x22\x33\xb1\x92\x2e\x74\xea\x04\x77\x64\x57\x37\x1e\xdd\x55\x93\x5c\x28\xd0\xc0\xf8\x8d\x02\x45\xd1\x79\x59\xc2\x9b\x49\x77\xc6\xa7\xb9\x53\x4e\xda\xe4\x7c\xdb\xbf\xf7\x7e\x2e\xb9\x76\x5d\xa3\x51\x2a\x3e\x28\xae\xa6\x26\xd8\x22\x75\xd9\x38\xe0", 107, { 0x4e, 0x61, 0xed, 0x55, 0xbd, 0x31, 0x31, 0x6f, 0xa6, 0xa6, 0x8f, 0xde, 0x2b, 0x8f, 0x51, 0x60 } },
{ "\xe3\xe4\x0c\xcc\x64\xb7\xd7\x6b\xa4\xea\xee\xfa\x2b\xa0\x38\x9a\xac\x09\x84\xa8\xeb\x01\x87\x2b\x4a\xd6\x71\x67\xee\x27\x2a\x8e\x92\xe7\x2e\x96\xe8\x81\x02\x26\xa7\x16\x51\xa9\x36\xe1\xa8\x85\xf3\xbc\xcb\x66\x20\xbb\xb2\xb4\x6a\xf6\x23\x23\x18\x65\xed\x68\xcd\xe3\xbe\x09\xf9\x55\xa2\x4d\xe2\xe4\x18\x53\x4b\x66\xd3\x3f\xbe\xda\x0a\x8f\x7b\x12\x7c\x8b\xfd\x6b\x04\xdb\x25\xb8\xd4\x33\x06\x3d\x51\x29\x4c\xd7\x8b\x26\x49\x39\x57\x7b", 108, { 0x35, 0x50, 0xf9, 0xfe, 0xb2, 0x42, 0x8e, 0x9b, 0x20, 0x2d, 0x1f, 0xd7, 0x1b, 0x32, 0x8a, 0x93 } },
{ "\x9d\x51\x0b\x8b\x82\xc9\xd6\x26\xb2\xa9\xb9\xf7\xf5\x19\x26\x13\x4a\x44\x33\xb1\xb1\x59\x7b\xf9\x93\xab\x92\xf5\xcf\xf8\x22\xa4\x63\xc7\xa7\x2d\xb2\xea\xc3\x31\x77\x23\x3e\x39\x47\xe3\x9e\x4e\xbc\x2e\x5f\xa8\x44\x9a\xd0\x7e\x84\x75\x8a\xfc\x6a\x06\x8d\x53\xce\xec\xf8\xea\xc4\xa4\x65\xb2\x80\x26\xdf\x97\xe6\xae\x81\x12\xae\x98\xac\xdb\x31\x64\xe4\xbc\xd2\xda\x47\x81\x0b\x47\xac\x0c\x13\xe5\x54\x85\x29\x58\x4f\xae\x80\x55\x6f\x54\xc7", 109, { 0x3a, 0xf8, 0x64, 0x70, 0xe9, 0x73, 0x58, 0x6b, 0x98, 0xb5, 0xdd, 0xbd, 0xfc, 0x56, 0x38, 0xc1 } },
{ "\x5d\xe1\xe1\x54\xa7\x6d\x0f\xec\x1c\x4a\xb7\x31\x7c\x9e\xc7\xa9\x9e\x92\x52\x67\xd4\x0f\xc2\x58\x6d\x17\x28\x2c\x54\xc2\xb4\xde\xd5\xd3\x40\xe2\x80\xf6\x06\xeb\x26\x98\x73\x56\x00\x63\x13\x36\xf0\x55\xab\xfc\xdf\x7c\x65\xc3\x45\x50\x26\x36\xc6\xac\xfc\x1f\xfa\x6b\xb3\x8c\x45\x9b\x86\xa0\xe5\x61\xf3\xf3\x0b\x69\xa7\xa7\x20\x07\x99\x08\x28\xef\x33\xdf\x44\x8d\xaa\x54\x51\x02\x6f\x7d\xae\xbc\xbd\x87\x1c\xb1\x53\x7f\xbe\x38\x3c\xbe\x3f\x84", 110, { 0x96, 0x3d, 0x85, 0x1f, 0xc8, 0xcf, 0x7e, 0x10, 0xe1, 0x2c, 0x33, 0xe0, 0x8a, 0x3b, 0x50, 0x98 } },
{ "\xc9\xef\x12\xbd\xa1\xbe\xd5\xbd\xef\x1f\xcf\x64\xb9\x38\x98\x98\x51\xec\xd8\xdc\xe4\x05\x27\x8c\x2f\xfd\x14\xb2\x52\x69\x41\x89\xbd\x03\xac\x8c\x47\x52\x08\x39\x5d\xf8\x49\x67\x57\x98\x3f\x41\xe6\x62\x5a\xde\xaa\x3c\x8c\x7e\xe0\x8e\x4c\x64\x39\xaa\xb6\x4b\xc5\xd7\xcf\x86\x0e\xf9\xe7\xb7\x42\xde\x17\x2b\x87\x27\xea\xd1\x73\xd1\x18\xd5\x94\x5f\x6d\xde\x29\xa6\xc9\xe0\xf4\x34\x40\x9e\x27\x5e\x61\xc0\x7b\xe5\x94\x8c\x60\x44\x9d\x44\x4f\x99\x3d", 111, { 0x50, 0x81, 0x50, 0xba, 0x15, 0x74, 0xa5, 0xf8, 0x00, 0x23, 0x1f, 0xe5, 0xf0, 0x1a, 0xef, 0x6f } },
{ "\x1c\x3b\x2d\xf7\xd7\x25\xa2\xf0\xfd\xcf\xb8\xf0\xbb\x88\xbc\x85\x57\x26\x8d\x46\x4e\x12\x4c\x35\x0b\x7d\xa0\x3e\x46\xb1\xa2\xdc\xf8\x82\x6c\xdb\xf6\xe3\x39\x38\x31\x95\x39\x24\x89\xf9\xf4\x27\x49\x07\x58\x62\x43\x57\x61\xed\x89\x5d\x63\x5e\xc5\xb2\xf7\x6b\x36\x8d\x80\xa7\x48\x50\x59\x68\xce\x3e\x9d\xca\xde\x4b\x92\xcc\x49\x0c\xb2\x97\xb5\xce\x58\xdf\x59\xc2\x04\x62\x55\x56\x4b\x8e\xac\x9e\x5e\x40\xdf\xf1\x34\xa6\x27\x91\x57\x45\x4e\x82\x48\x16", 112, { 0x24, 0xfa, 0x3a, 0xa0, 0x34, 0xeb, 0x16, 0x59, 0xd6, 0x98, 0xf1, 0x66, 0xad, 0xfb, 0xf7, 0x93 } },
{ "\x2a\x7b\x90\xcc\xb7\xfa\x65\x31\xd0\x72\xf5\xae\x8a\xa0\x51\xe9\x2d\xfc\xf9\x89\xd0\x4a\x00\x15\x90\x4f\xdc\xfa\x6c\xa1\xcc\xab\xc0\x98\xe6\xe3\x5c\x61\xbc\x06\x41\x30\xaa\xa5\xf7\x95\xbf\x20\x8e\xe8\x46\x66\x2f\xdf\xf0\xd9\x5d\x3e\x9f\x4c\xce\xad\xd1\x2e\xe0\xa5\xa7\xc0\xba\x84\x91\x82\x00\xc1\x99\xac\x32\x39\x48\xd8\xa2\xa8\x38\xbd\x10\x33\x38\x15\xe3\x21\x15\xa0\x06\xaa\x0b\x42\x5d\xe8\xc8\x48\xe3\xea\x19\xc8\x62\xe8\x34\x26\xcd\x90\xa1\xb3\x3d", 113, { 0xa1, 0xc5, 0xd1, 0x2c, 0x14, 0xcf, 0xb3, 0xd5, 0x90, 0xa0, 0xe8, 0x8d, 0xef, 0x19, 0xa2, 0xdc } },
{ "\x7f\xc5\x6f\x87\xcb\x0c\xef\x76\xbd\xb2\x5a\xaa\x9c\x2f\x8d\x0c\xa4\x3d\x5f\xec\x16\x87\xfe\xba\x69\xef\x78\x5e\x9e\x7c\x56\x34\xb1\xdf\x63\xa7\x2b\xa0\x8a\x69\xd4\xea\xdd\x4c\x86\xef\xb2\xc0\x1d\xf9\xe8\xea\x8b\x0f\x47\x5d\x08\x40\x05\x77\x66\x8f\x65\x5a\x82\x7a\x7a\x86\xd7\x29\x0a\x10\x2c\x30\x8d\x81\x6e\x01\x55\x4e\x98\xf1\xc7\xef\xce\xe5\xc7\x9e\x8a\x99\x32\xad\xed\x8c\x85\x84\x37\x8c\x9b\x36\x52\xd9\x93\xc0\x89\xf9\xd0\xdd\x56\x18\x19\x89\x58\x19", 114, { 0x65, 0x16, 0xd9, 0xfd, 0x97, 0x36, 0x97, 0x2c, 0x1d, 0x90, 0xe6, 0xb9, 0x11, 0x75, 0x7e, 0xd1 } },
{ "\xe1\x10\x63\x3d\xa2\xd1\xb2\x6e\x62\x94\x37\x29\x58\x85\x13\x06\xa7\xcd\x21\xe6\x49\xcc\xad\xb8\x07\xf4\x43\xe7\xa4\x45\xa1\x64\x1a\x61\xce\x4b\xfc\x4b\x44\x35\xfa\xc0\x48\x19\x83\x32\x5b\xdf\x85\x5d\xc8\x83\x50\x88\x5e\xe2\x98\x5a\x38\x25\x99\x57\xb8\xc7\x55\xf5\x92\x44\xf9\x5f\x04\x5f\x5e\xc5\x24\x10\xab\x5e\x51\x09\x17\xfb\xcb\xe4\xcc\x49\x5f\xeb\xe7\xa3\x3b\x83\x9c\x92\xe0\x35\x77\xe2\x34\x5a\xbd\x62\xb7\x63\xf1\x37\xce\xc3\x72\xdd\x3b\x79\x41\xbc\xae", 115, { 0xdd, 0xa6, 0xd8, 0x85, 0x1a, 0x69, 0x15, 0x50, 0xbb, 0x0b, 0x98, 0x6a, 0x04, 0x95, 0x94, 0x7b } },
{ "\x3d\xe1\x62\x74\x46\x57\x63\x4e\xb6\x51\xca\x5d\xa3\x63\x3b\x38\xc3\x6c\xa7\x20\xb3\x17\xaa\x4b\xe4\x7d\x84\x5c\x23\xe8\xb2\xf4\xc3\xb3\x28\x62\x68\x4d\x2e\x76\x73\x5c\xd4\x73\x05\xfe\x13\x22\xb2\x2a\x82\x03\xc4\x35\xb1\x9f\x29\x71\x26\xf9\xfa\xf0\xf2\x22\xa8\x66\xee\xec\xc5\x2c\x97\xb6\x6d\x61\x83\x67\x4f\x2b\x80\x76\x5b\x1b\x48\x25\x0a\xaf\xe2\xcd\x45\xf0\x97\x55\xf3\x3c\x8f\xbc\x22\x1e\x09\xd6\xd1\x59\x34\x14\x57\x04\xac\x7b\x74\xcf\x94\xb7\xf3\x63\x4c\x49", 116, { 0xc8, 0x45, 0x92, 0xb3, 0xa3, 0xee, 0xed, 0x4c, 0x7d, 0xad, 0x54, 0x75, 0xbd, 0x5e, 0xc8, 0x88 } },
{ "\xf8\xe8\xcf\xdb\xec\xa0\xac\xb4\x01\xb0\x9f\x46\x64\xff\xcc\xe5\xff\x37\x97\x92\xe7\xe9\x22\xf6\x69\xcd\x64\x6a\xac\x27\xe3\x33\x03\x44\x0e\xcb\xd2\x23\x39\x5a\x19\x31\x35\x44\xa2\x2d\x8b\xdb\xc3\x2b\x55\x35\xd1\xb4\xba\x19\x21\x0a\x04\x13\xbc\x89\x60\xa7\x9e\x28\x31\xa2\xab\x1f\x10\x8c\x2f\xa3\x65\x39\x10\xcd\x9b\x7e\x93\x99\x03\x01\xc7\x09\x47\x2a\x92\x69\x88\x36\x56\xae\x17\x6a\x3f\xf8\xcd\x64\x2b\x37\x08\x8c\x37\xe9\x42\xaa\xe2\x01\x4f\x92\xe1\xe3\x33\xfa\x7f", 117, { 0xce, 0x59, 0x2a, 0x66, 0xbd, 0x8e, 0xcc, 0x02, 0x56, 0xa9, 0xb2, 0x02, 0x08, 0x00, 0x60, 0xf2 } },
{ "\x8c\x9d\x6c\xad\xcf\x04\x56\xad\xba\x5d\x3f\x57\x17\x76\x14\x07\x0e\xf2\xa1\x24\xe8\xe1\x1b\x4d\xee\xfb\xd9\x21\x70\x7a\x23\xab\xe1\x91\x23\x69\x20\x8c\xf9\xf8\xd2\x85\xea\x5d\xea\xc0\xb8\xf2\x4a\xa4\x0c\xeb\x83\x57\x10\x84\xb9\xf4\x19\xc9\xa2\x6c\x82\x01\xad\xf6\x94\xb8\x3f\x34\xa1\x68\x18\xe4\x30\xc3\xd4\x3f\x52\xa0\x8e\xf2\x13\x7f\x9f\xb6\x0c\xba\x84\x8e\x15\x4b\xdd\x9c\x19\x34\x92\xa1\x02\x8f\x10\x10\xd2\x32\xb1\xcd\xd3\xfe\x3a\x87\xe7\xc5\x7e\xae\xf9\xd5\x1f\x13", 118, { 0x21, 0xc8, 0x1d, 0xc6, 0x45, 0xbe, 0xfc, 0x87, 0x21, 0x04, 0xc5, 0xc2, 0x14, 0x3b, 0x40, 0x91 } },
{ "\x94\x25\x74\x1c\x92\x1b\x86\xa0\xec\xf8\x35\x65\xb1\xe1\x78\x33\x12\x8b\xc2\x94\x9a\x81\x7f\x2b\x7a\x15\xbd\xaf\x02\xe1\xe8\x82\x2c\xf9\xae\xf2\x53\xaf\x01\x0b\x01\x01\x3b\x16\xe5\xa3\x5b\xb3\xe3\xa5\x6d\x8e\x46\xc2\x08\xc1\x11\x44\xf1\xc6\x73\x96\xdd\x17\x58\x68\x54\x64\x1c\x79\xb1\x70\x5a\x04\x46\x89\xe3\xc9\x9c\xa2\xcb\xb6\xd8\x0e\x9d\x32\x39\xdb\xae\x07\xbd\xc9\x8f\xe9\xe3\xe6\x9c\xa7\x8c\xc7\xb1\xed\xfb\x65\xfc\xb3\xfb\x91\xee\x46\x20\x15\x4b\xf1\x25\x69\x62\x48\x74", 119, { 0xc1, 0x1f, 0x44, 0xbf, 0x5d, 0x95, 0xee, 0x5f, 0x2e, 0x4f, 0xef, 0x28, 0x62, 0xf6, 0xe7, 0x5c } },
{ "\x0f\x56\x10\xc5\x8c\x9a\xce\xde\x03\x7b\xeb\x78\x6f\xd7\x81\x42\x7c\xeb\xc4\xff\x03\x4a\x0f\xca\x20\xea\x8a\x7a\xf2\x59\x76\x20\xef\x0d\x15\xad\xe1\xd8\x39\xb1\x81\x7a\x67\x3e\xae\x50\xa6\xeb\x4a\x2b\xea\xf4\xb2\x3c\x18\x7f\xd8\x2b\xb6\xf9\xfe\x46\x31\x9f\x10\xd6\xc9\x19\x9f\x8e\x1d\x40\x76\x1d\x4e\x00\xdb\xe3\xd3\x59\x63\xbf\xd9\x7f\x72\x07\x55\x22\x4f\x91\xa7\xc8\xe0\xee\xec\x55\x06\xb7\xe0\xad\x97\xff\x6e\x70\xf4\xe8\xd7\xe4\x47\x51\x7a\xf1\x5c\xad\x45\x45\x18\xef\xb9\x98", 120, { 0xf6, 0x28, 0x50, 0x22, 0xdd, 0x55, 0xfc, 0xba, 0xb4, 0x4c, 0xc4, 0x1f, 0xa7, 0x92, 0x8a, 0x03 } },
{ "\x9a\xa1\x93\x09\xec\x14\x1c\xa7\x65\xb2\x0f\x0d\x9f\x6f\x22\x25\x11\x5e\x33\x0d\x48\x60\x10\x16\xc7\xf7\xe3\xe9\x77\x38\x38\xc4\xcc\xdf\xad\xe2\x77\x7c\x35\xf9\xc1\xcc\x08\xdd\x8b\x23\x2b\x42\xdf\x04\x97\x9e\x32\xd3\x09\x2d\x38\xa1\x65\x0e\x64\x27\xc8\x8b\xfb\xcf\x29\x76\xd4\xeb\xaa\xae\xae\x08\x81\xc1\x2e\x5d\x7d\xab\x73\x5e\x38\xbd\x58\x6e\xd8\x99\x45\xb1\x81\x5a\xb2\xff\x5b\xd0\x3a\xf4\x3b\xe8\x57\x24\xf0\x2b\xc0\x6c\x2d\x5d\x5c\x64\x0e\x45\xe1\xf0\x48\x8d\x0e\xf6\xf2\xbf\x81", 121, { 0xec, 0x78, 0x08, 0x73, 0x5a, 0xc1, 0xb9, 0x65, 0xd2, 0xa0, 0x7c, 0x7c, 0xfb, 0x45, 0x7b, 0xb2 } },
{ "\x29\xea\x6f\x36\x04\xe5\x78\x9c\xd3\x17\x5e\x55\xeb\x7b\xd3\x8b\xfa\xbf\x55\xea\x79\xd0\xf4\x3d\x3e\xfd\x31\xa8\x2d\xca\x02\x7f\x0f\x54\xf3\xc2\x7b\x5c\x66\x37\xf0\xf1\xfb\x22\x05\xbe\x0b\xa2\xb7\xee\x4d\xab\xe2\xb7\x9b\x9b\xcb\x8a\xcf\x7b\xda\xd5\xc7\xd5\x65\x73\x89\x2d\xa6\xb2\x7f\x1d\xcf\xfe\xe3\x10\x34\x2e\x36\x9b\xa7\x6b\xe9\x73\xe2\xb9\x1f\x0f\x1c\x23\x8a\xdb\xbe\x87\x72\x15\x2f\xfb\xd4\x48\xcc\xdb\xa7\x63\xf3\x71\x3a\x76\x3e\x3f\xb9\x08\xce\xeb\xce\x17\xbd\xc8\x63\xad\xb5\xfd", 122, { 0x41, 0x22, 0x16, 0xa5, 0xec, 0x77, 0xcc, 0x6c, 0x26, 0x63, 0x46, 0xa1, 0xf4, 0xe3, 0x94, 0x6e } },
{ "\x86\x1b\x9f\x54\x66\xd5\x73\xa1\x7a\xe9\x2e\xcc\xc1\x1d\xe2\x7e\x24\xa5\xe7\x64\xf7\x7e\x2f\x23\x9e\x6a\xb7\xd8\x4c\x88\x1a\x4a\xe7\x8f\x40\xaf\x08\xa7\x33\x17\x1e\xe4\x12\x79\xb1\x60\x1e\x59\xc4\xf3\xf1\x12\x55\x91\xcf\xc5\xfe\x41\x15\xde\xbd\xb6\xce\x40\xd1\x8c\x65\x0d\xbb\x20\x74\x13\x64\x0c\xbb\xd6\x5d\x3e\x2c\x36\x40\xb3\x22\xbd\x36\xd5\xb2\x87\x93\x6f\x1a\xce\x9b\x49\x57\x12\x68\x65\xd2\xe1\xe3\xd4\x3a\x48\xef\x35\x6d\xd6\xa6\xcb\x8f\x49\xbb\x3a\x3d\xd8\xff\xde\xdc\x1c\xff\xc9\x0b", 123, { 0x09, 0xff, 0x3e, 0x2d, 0x99, 0x55, 0xc5, 0x1e, 0x17, 0xcc, 0xc1, 0xdd, 0x07, 0x11, 0x8a, 0x62 } },
{ "\x9c\xca\x57\x37\xf3\xd0\x6e\x4c\xa0\xe5\x57\x89\x6a\x65\x34\x6c\x6e\x72\x1d\xcd\xc7\x59\xdb\x07\xd8\x13\x40\x50\x39\xe7\x21\x2a\x3b\x2d\xf2\xf2\x1a\x2d\xfc\x96\xbe\x25\x3d\x64\x2e\x69\xdc\xfd\x92\xa5\x47\x68\xe2\x3e\xeb\x43\x31\xd7\x8f\x14\x90\xf0\x4e\xbd\xa0\xa8\x2f\x0e\xb8\xa3\x62\x75\xae\x06\x1a\xd0\x46\x9f\x01\x63\x35\x22\x5d\xe5\xd0\x8e\xbd\xb5\x56\xaf\x5f\x2a\xd6\xbc\x22\x07\xbf\x20\x22\x0d\x02\x56\xf5\xab\xe6\xed\x81\xd1\x68\xab\x78\xe2\x4c\xce\x72\xc8\xc4\x6d\xa5\x21\xbc\xfe\x43\x97", 124, { 0xe3, 0xc6, 0x50, 0x53, 0x82, 0x8e, 0xf8, 0xd1, 0x42, 0x08, 0xdc, 0xf3, 0x3b, 0x60, 0x8d, 0xc7 } },
{ "\xc0\xf1\x71\x4d\x8b\x79\xdf\x75\x2d\x6a\x08\xfe\xd7\x3d\x08\x6b\x46\x31\x15\xbf\xca\x8c\x9b\x94\xf2\x00\xf8\x4c\xd6\x28\xd1\x5e\x01\x31\x0f\xd2\xf9\x96\x7a\xc8\x6b\x03\xf0\x31\xf8\x5b\x41\xa1\x96\xd5\xaa\x3d\xa4\x41\xed\xcf\x8f\x69\x09\xf8\x1a\x92\x9b\x85\x4d\x22\xd1\xda\xfc\x5b\x07\x8a\xf2\x45\x00\x09\xbb\xaa\xc2\x79\x0b\x3b\x0e\xa0\xce\xd0\x7a\xfb\xcd\xcd\x2d\xeb\xfa\xa0\x37\x0e\x58\x66\x8a\xa9\x89\xad\x99\x41\xf5\x54\x8c\x49\x94\x8f\x1d\xf5\x59\x07\x12\x2d\x3c\x1e\x57\x9d\xe2\x50\xb7\xe9\xea", 125, { 0x8e, 0xb8, 0x75, 0x22, 0x66, 0x98, 0xd0, 0x80, 0x2d, 0xcc, 0x7a, 0x6e, 0x92, 0x96, 0x2e, 0xe8 } },
{ "\xb5\x37\xfe\xd6\xa3\x0f\x84\x94\x70\x46\x6f\xa9\x55\xe9\xb5\xf9\x6e\xe7\x1a\x35\xdd\x8c\x26\xe1\x0f\x98\x38\x00\x16\xfc\xb5\x5f\x36\x30\x59\x7c\x7b\x33\xad\x11\x87\x20\x99\x40\x6a\x6a\x11\x5c\xaa\xb4\xeb\x51\x62\x50\xd1\xb2\x86\x51\x52\x5d\x44\x4e\x13\xcd\x86\xb6\x22\xfc\x94\xcb\x6b\xf3\xd7\x3d\x43\xef\xb8\x64\x22\x32\xa7\x18\x6e\x63\x38\x30\x72\xa2\x67\x96\x6d\x2c\xfc\x04\xc7\xa8\x0a\x5d\x5e\x0c\x91\xaa\xff\x2f\x43\xaf\xf1\xeb\x64\x29\xab\xee\xca\xa7\xa5\x1e\x04\x02\x4b\xa6\x97\x7b\x0e\xa2\x63\x6f", 126, { 0x97, 0xa7, 0x1a, 0x12, 0xd4, 0x55, 0xc2, 0x52, 0x09, 0xa9, 0x14, 0xbd, 0x78, 0x17, 0xf5, 0xd7 } },
{ "\x95\x63\x25\xb9\x12\x5f\x16\xa4\xaf\xb8\xb0\x8b\x26\x67\x90\x10\x70\x05\x76\xf5\x95\x36\x6a\x9a\xa2\xb2\xfa\x13\xb9\xf1\x9e\xe5\x42\x73\x3c\x5e\x3f\xa9\xc6\x8e\xbe\x83\x01\xe5\x67\x97\x61\x6b\x35\xea\x11\x96\x42\x5f\x0e\xcb\xba\xba\x73\x74\xf2\x4f\xcf\xba\x91\x4b\xb2\xdf\xec\x9e\x47\x3b\x70\x84\x1b\xd2\x38\xaf\xfc\x8e\xbf\x13\xfc\x1d\xaf\x4d\x95\x69\xd8\xb1\xe6\xb0\x3c\xee\x1c\x41\x47\x60\xec\xd2\x1c\xf2\x3c\x80\x0a\xae\xe1\x63\x1d\xe3\x83\xcd\xd1\xf2\x9d\x20\xe2\xb5\xa1\x49\x3e\x8b\x38\xdd\x1c\x04\xa7", 127, { 0x94, 0x7d, 0x6b, 0x70, 0x52, 0xe6, 0x4a, 0xbd, 0xc1, 0x5b, 0xbc, 0x11, 0x26, 0x8c, 0x48, 0xb3 } },
{ "\xce\x26\x26\x4d\xca\xd2\x5a\x49\x30\xcf\xf6\x38\xaf\x9a\x68\x1c\x7d\x2f\xfb\x58\x31\xdd\x49\xd7\x3e\x32\x3e\x4d\x0d\x16\xc4\x96\xb6\xf4\x10\x3a\x5a\x13\x89\x12\x1f\x03\x50\x04\xc9\x32\x70\xe9\xf2\x9e\xa4\x90\xe6\xa5\xbf\xdc\x1d\xf8\xbc\x08\x55\xae\x62\x0b\x4c\x75\x93\x16\x17\xe3\x32\x3b\x22\xea\xaf\x27\xc5\x6a\x31\x10\x7f\xe1\x5f\xaa\xd1\x3d\xca\x52\xb9\xd2\xfa\x4e\xc9\x67\x13\x2c\xe4\x6b\x23\x46\x95\x45\x0b\x67\x0c\xc9\x08\x88\xb6\xc6\xde\xb3\x78\xbc\xa0\x09\x87\xab\x1e\xdf\xe7\x06\xeb\x02\x7d\xc7\x09\x1b", 128, { 0x14, 0x39, 0x8a, 0x7a, 0x34, 0x95, 0xeb, 0x11, 0x5f, 0xf6, 0x04, 0x23, 0x14, 0xa9, 0xb1, 0x51 } },
{ "\xd3\x9c\x34\x2e\x69\x3f\xc8\x3c\xb2\xe3\x4f\x09\xb2\xca\xab\xf8\x31\xf3\xdc\x12\x9c\xf1\x6f\x25\x79\xd7\x84\x09\x85\x50\x7a\xfe\x6d\xcb\x39\x31\x25\xd3\x1b\x5d\xe7\x7c\x78\x8e\xcb\xf9\xcc\x02\xff\x4b\x87\x28\xa4\x14\x72\xca\x46\x8a\xb9\x46\xf5\x87\x99\xf7\x04\xbc\xa6\xb4\x5e\x06\xb9\x6e\x80\xd9\x76\xfd\x16\xd8\x76\xf4\x36\x87\x15\xb0\x33\x18\xd9\x70\x1f\x61\x7d\x9e\xe1\xef\x9a\x2c\xee\x34\xf1\x1a\xa7\xdb\x57\x14\x4f\x3c\x3d\x37\xa8\xeb\xdb\xf4\x29\x6b\xf9\x0d\xdd\x00\x5a\xbd\xda\xa2\xc5\xf4\x5d\x0e\xb1\xc0\x7f", 129, { 0xa6, 0x03, 0x6e, 0x71, 0xb9, 0x65, 0xc3, 0x1c, 0xaa, 0x8e, 0x3b, 0xc0, 0xab, 0x74, 0x07, 0xf0 } },
{ "\xf6\x8c\xc7\x96\x58\xa8\xf1\x2b\xec\xc3\x22\x93\xb6\x31\x25\x2c\xbc\xa8\xa4\x36\xd2\xa8\x53\x4b\x91\x85\x2d\x7c\x66\x12\xd7\x0a\xc6\xec\x20\xbe\x7f\x60\xaa\xe5\x2a\xfa\xa2\xec\xbd\xab\xaa\x93\x3d\x95\xd9\xd1\x90\x77\xd8\x45\x70\xb0\x2d\x54\x7c\xf1\x94\xe3\x68\x84\x89\xb2\x55\x33\xe3\x53\x3c\xd6\x9a\xc7\x83\x7d\xa9\xb4\xb2\x36\x0f\x44\x3f\x7b\xef\x9c\x85\x3b\xd7\xf7\xd3\x83\x1d\x5f\xa1\xc9\x65\x08\xde\xd5\x40\x49\x65\x4c\xef\x37\x8d\xdb\x45\xe0\xdf\xfc\xaa\x21\xe3\x68\x3b\x25\x13\x19\x0f\x7a\xf1\xfb\x95\xd1\x34\x2f", 130, { 0x93, 0xfb, 0xf2, 0x73, 0x89, 0x24, 0x2e, 0x2d, 0xf7, 0xf6, 0x94, 0x0b, 0xfe, 0x0f, 0xfc, 0x89 } },
{ "\x19\x52\x1e\xfa\x65\x9a\xe9\x50\x84\x52\x5f\xf9\xa2\x6d\x89\x5e\x0f\xfd\x7f\xf3\x62\xb3\x5e\x40\xba\xf1\x58\x8d\x20\x8e\xe6\x29\x08\x25\x18\x57\xf7\x1a\x0c\xd6\x3e\x2b\x7d\x0c\xe4\xae\x73\xce\xa2\x6d\x18\xce\x07\x1a\xab\xa2\xbc\x70\x8d\x6d\xe2\xe9\x79\x2c\x97\x16\xd1\x9f\x98\x9e\x13\xd1\x00\xd5\x6a\x46\x2f\xf8\x61\xc1\xc6\x03\xb2\xaf\xce\x2f\x3d\x33\xf8\x0b\x14\xcf\xff\x36\xb3\xab\x2a\xb7\x4d\x86\xed\xf9\x41\x36\xaf\x66\xac\xdd\xa7\x9e\x18\xaa\xdf\x54\x51\x49\x5b\xc5\x58\xe9\x53\xd6\x71\xe7\x9b\xca\x57\x1c\x23\x9d\x90", 131, { 0xf0, 0x21, 0xdf, 0x96, 0x22, 0x89, 0xf7, 0xca, 0x7b, 0x3d, 0x32, 0xbe, 0xc3, 0x70, 0x87, 0x87 } },
{ "\xc0\x9b\xff\xdb\xf9\x2f\xf0\xc5\x04\x70\xf4\x5a\xfe\x52\xf4\xf9\x50\x52\xb1\x41\xb5\xb0\xe5\x27\xea\xda\xf8\x2a\xf1\xe9\x5c\x9d\x01\x44\x85\x23\x0d\x62\x88\x3a\xef\xae\x4f\xed\x31\x83\x77\xad\x78\x56\xc6\x3b\x8e\xf3\x4c\xbc\x0a\xe0\x15\xee\x9e\xde\x87\x7a\xfd\x8d\x5f\x5f\x67\x2f\x42\x8e\xd2\x85\x03\x95\xb7\xd5\x70\x73\x76\x0d\xd9\x8a\x66\x02\x1e\xb2\x7d\xd1\x74\x69\x99\x66\xb4\x29\x69\x1b\x5f\xd2\xfa\x78\x32\x47\xe2\x19\x62\x15\x03\xad\x75\x4c\xfc\x1a\xbd\x72\x32\xbe\x71\x8d\x76\xd1\x69\x5f\x53\xe6\x76\xaf\xf7\x90\x5b\xc1", 132, { 0x6e, 0x38, 0x9a, 0xae, 0x82, 0xdf, 0x52, 0xff, 0xe8, 0xed, 0xfc, 0x75, 0xe6, 0x9a, 0xdc, 0x14 } },
{ "\xbc\x21\x6c\xe7\x51\x8e\xc2\x30\x89\x6e\x19\x3f\xc0\x21\x46\x38\xe6\x0e\x57\xd3\x29\x04\x49\x92\x43\xc2\x60\x0f\x5d\x92\x27\x6f\x9e\x0f\x04\xf3\x55\x08\x77\xad\xbf\x7d\xef\x4f\x75\xc6\x49\x1e\x75\xd3\xe6\x06\xcb\x8e\x67\xc8\xdb\x5d\x08\x4f\x4e\xc3\x96\x20\x97\x26\x0e\xee\x21\xab\xd4\x4d\x17\x3d\x8c\x7f\xcf\xad\x99\x6b\xd4\xf4\x30\xab\x8e\x93\x18\xc4\x90\x1b\x00\x71\x7e\xc9\x7c\x18\x99\x49\x9e\x5e\xe9\x9b\x2d\xd6\x06\x13\x85\xd4\x82\x7a\x0a\x60\xf6\x53\x4a\x46\xa8\x38\xaf\x4b\xd6\x62\xdd\x7a\xa1\x46\xae\x9c\x99\x5d\xc7\xc5\xe1", 133, { 0xc4, 0xd1, 0x37, 0xe5, 0xf5, 0x29, 0xa4, 0xa2, 0xf0, 0xf2, 0x27, 0x54, 0x17, 0x1f, 0x12, 0x2c } },
{ "\xfd\xd7\x0b\xff\x63\x6c\x52\x42\xd2\x71\x43\xd0\xd4\x48\x5b\x4b\x9f\x80\x1f\x20\x93\x33\x6e\x6c\xe0\xff\xee\x8a\x45\x9f\xa8\x3d\xf3\x25\xb0\x77\x90\xd6\xfd\xc4\x57\xa2\x57\x56\x5c\x3e\x6e\xad\xed\xe0\x06\xe3\x14\x96\x50\x91\x3a\x44\x55\x62\xe6\x38\x8b\x32\xa2\x6c\x8a\xe2\xfe\x57\xd8\xbb\xae\x70\xe0\x7c\xce\x40\x02\x01\x46\x22\xc4\x92\x49\x9a\x25\xc6\xf7\x50\x12\x12\x23\xa8\xf2\xf3\x2e\xfe\x5c\xb3\x12\x83\xe8\xda\x7b\xaf\x23\x35\x0f\x62\x9c\x7c\xcf\x9b\x1b\xa2\x95\xd3\xf1\xbe\xbd\xf7\x6b\x91\xe1\x01\x60\xb3\xbc\x32\xea\x5f\x30\xee", 134, { 0xec, 0x22, 0x50, 0xb3, 0x22, 0x24, 0x26, 0x6d, 0x36, 0xda, 0xd2, 0xa4, 0xc6, 0xc7, 0xd1, 0x20 } },
{ "\x31\xd7\x62\x33\x63\x75\x03\xb7\xc0\x50\xaa\x9e\xd1\x87\x5d\xd5\xb8\x2d\x2f\x0e\xa3\xd1\x03\x58\x5a\xa8\x6e\x5a\xf8\x5a\xbb\x2b\xb7\x66\x08\xd1\xe4\x32\x8d\x55\xf1\xb3\xfd\x7f\xa9\xb5\x04\x34\x7e\xc7\xf1\x68\xfe\xc7\x6e\xc1\x64\x05\x6a\xca\x4b\x17\x17\xd0\x7e\x39\x0f\x5d\xea\x5e\x92\x4e\xb5\xd7\xea\x93\x67\x9f\xef\x83\x46\x41\xa7\xda\xc1\x66\x05\x50\x02\xff\xd2\xd6\xa6\x0b\xa9\x70\x89\x05\x1c\xaa\xba\xee\xf5\xb8\x8e\xf2\x96\x2e\xd0\xba\x82\x58\x16\x4d\xf4\x37\x2f\xa3\xad\x19\xb8\xc8\xcc\xd3\xce\xa9\xd5\x9e\xdd\x7f\xd4\x8c\x97\xd5\x9a", 135, { 0x24, 0x9d, 0xa1, 0xd8, 0x65, 0x77, 0x2f, 0x84, 0x7f, 0x6b, 0x6a, 0xc7, 0xec, 0x38, 0x7c, 0x68 } },
{ "\xa5\x01\x37\x26\xa2\xa7\x79\x20\x45\xf0\xa1\x7e\x53\x8c\x72\x49\x2f\x09\x96\x7a\x15\x85\x67\xfe\xef\x7e\x5a\xd9\xd7\xc5\x08\x66\x2a\x91\xda\xbd\x45\xb0\x51\x2d\xdf\xd9\xf0\xe8\x03\x1c\xc6\xbe\xa8\x7a\x9c\x02\xef\x91\xb7\x89\xf8\x70\x4a\xd0\x60\x89\x7b\x3d\x5b\xc4\x10\x7e\x6b\xb0\xb6\x0e\xbb\xd4\xee\xd6\x1a\x24\x94\xf0\x97\x8f\x0d\x86\xb5\xb5\x0d\xd9\x4b\xb6\x03\x5e\xfb\x26\x21\x02\x4c\x1c\x0b\x8f\x67\x6a\x1b\x27\x6b\xe6\x4f\xec\x6d\xe7\xd0\xc2\x0f\xcc\x1f\x2c\xbb\xb6\xde\x53\x7d\x55\x39\x25\x7b\xe0\xef\x9a\x11\x1e\x01\x12\x8d\xa2\xf5\xdb", 136, { 0x77, 0xfc, 0x95, 0x17, 0xdd, 0xeb, 0xc5, 0xa5, 0x2f, 0x8d, 0x90, 0xd8, 0xd1, 0x38, 0x80, 0x09 } },
{ "\x84\x14\xc7\xce\xcf\xa9\x6d\x18\x26\xb4\x06\x16\x56\x56\x9e\x5a\x22\x51\xa0\xcb\xb4\xfb\xd9\xe9\xbe\x4e\x25\x2d\x32\x1c\xb8\x8e\x9a\x60\x0b\x20\x14\xaf\x60\xd7\xee\xcd\xf4\x6a\xda\x5b\xc1\x53\xce\xae\xed\xf2\x7b\xbc\xd2\xd1\x67\x30\xab\x03\xa9\x9d\xd7\xa5\x41\xce\xcd\x86\x11\x3b\x9d\xe3\x7c\x99\x1f\x4b\x9a\x89\xba\xa1\x15\x70\xd2\x40\xa3\x66\xcf\x39\x20\x47\xc7\xb7\x46\xe8\xc7\x84\x0c\x64\xc3\xa4\x99\x94\x17\x1f\xe4\x9c\xb9\xdd\xea\xa2\xfe\xa9\x8a\x9a\x05\x58\x00\x3d\xc4\x03\xfc\x18\xad\x6f\x5e\xc1\xfc\x8e\x91\x24\xa0\x1e\x81\xfb\xc3\x70\x3a", 137, { 0xd2, 0x39, 0x29, 0x3a, 0xe7, 0x3c, 0xc8, 0x48, 0x93, 0x9d, 0x84, 0x3d, 0x11, 0x7c, 0xd9, 0x72 } },
{ "\x5c\xf8\x43\x1f\x6c\x00\xcf\xc3\x31\x39\xdd\x67\x86\xa4\x13\x11\x27\x91\x4e\x45\xec\xe9\x28\x62\x13\x18\x99\x9c\xb6\x95\xb9\x92\x5b\x0f\xa3\x8c\xaa\x36\x76\x52\x39\x23\x75\xab\x83\x64\x4e\x71\xf8\xa8\x78\x4d\x2e\x03\xb5\x15\x35\xdf\xb7\xbf\x08\x80\xdf\x00\x1e\x32\x20\x85\x20\x11\x02\xcd\xb6\x75\xc3\xa1\x7b\xf8\x98\x31\x0f\x25\x11\xcd\x4a\xbe\x9a\x3c\x8d\xaa\x1d\xbd\x35\x79\xc5\x97\x29\x96\xde\x5f\x93\x08\xd8\xc6\xac\xe4\x6a\x1c\xaf\x53\xd4\x65\xef\x3c\x3c\x16\x04\x8d\x3a\x6d\x21\x2b\x6f\x6a\x81\x74\x50\x6d\x00\x6d\x01\x6d\xc6\x8d\x5c\xd2\x1e\x25", 138, { 0x0e, 0xfb, 0x6c, 0xcd, 0xb4, 0x7f, 0xb9, 0xa7, 0x2b, 0xcc, 0x94, 0x5f, 0x1f, 0x1c, 0x9b, 0x52 } },
{ "\x10\xf0\x65\x6a\xe6\x21\x1d\x21\x1f\x7e\x21\xe5\xfa\x3a\xf3\x18\x52\x9b\x31\x64\x63\x95\x27\xed\xad\x04\x7d\x15\xf1\x85\x11\xeb\x58\xf2\xe0\x31\xb3\x79\x1d\x08\xdd\x59\x64\x3a\x3d\x38\x08\x24\x68\x23\x88\x3e\xe3\x22\x21\x48\x06\x77\x7d\x13\xfb\x73\x89\xea\xe6\xf6\x64\x9b\x1f\x81\x73\x25\x9a\xf9\x91\xff\x68\xfb\x64\x03\x56\xd6\xcb\xf6\xb3\x29\x73\xb4\x30\x1a\x89\xfc\xdf\x30\x89\xd6\x5e\xce\x35\x9d\x0d\x4d\xa2\xad\x7a\xb5\x6c\xa9\xde\x17\x0a\x69\xc1\x89\x3c\x7f\xb8\xbf\xa1\x65\x4f\x42\x65\x44\x01\x50\x17\x63\x64\x51\x98\x2a\x62\xf1\x2f\xd2\xa1\xde\xba", 139, { 0x7c, 0xea, 0xd6, 0xf2, 0x79, 0xc5, 0xbc, 0xa4, 0x89, 0x86, 0xfc, 0x29, 0x94, 0x1c, 0x8b, 0xee } },
{ "\xac\x04\x20\xff\x0a\x4b\x0f\x21\xce\xd6\xf6\x2e\x8d\x87\x43\xd5\x5f\xc4\x67\x35\x45\x9c\x50\xd0\x38\x01\x80\x9e\xca\x33\xca\x3e\x7b\x3e\xa9\x48\x9b\x99\x3d\xbd\xd6\xf0\xe3\xa0\x61\xfc\x6f\x9e\xc0\x8d\x09\xe8\x31\xa9\xa1\x21\xb8\xcf\x10\x73\xc8\x54\xcd\xbc\x8b\xef\x48\xe6\xce\x50\xe6\x55\x8c\xea\x9a\x79\x16\xd2\x1c\x83\xdc\xbf\xc9\x34\xda\x31\x17\xd0\xa1\xaf\xb3\x32\x00\x29\x39\xf9\x50\x7b\x8f\xe0\x59\x12\xdf\x2c\xe4\xa9\x2f\x3e\xde\x2d\x9e\x48\x26\xbf\x3d\x1c\xf4\xd7\x87\x20\xe5\x86\x7f\x5e\xa7\xd4\x65\xb8\x3d\x47\x14\x38\xef\x85\xfb\x86\xd2\x11\xb5\x73", 140, { 0x2d, 0xe4, 0xcd, 0x76, 0x59, 0x59, 0x50, 0x08, 0xc3, 0xaf, 0xda, 0xd6, 0x68, 0x92, 0x96, 0xd3 } },
{ "\xef\x31\x48\xb1\x13\xf0\xf7\xa5\x33\x40\xc4\x55\x79\x37\x86\x7c\xef\x7a\xa2\xbd\xc7\xb7\x69\xf4\x44\x4c\x0e\xa7\x49\x05\x42\x9b\x7c\x02\x6e\x83\x17\xb7\x8c\xd4\x4b\x0e\xa3\xb4\x50\xa7\xdd\x10\x0e\x3f\x46\xcc\x61\x2e\x23\x16\x0d\x0a\xed\xd4\x3e\x6a\xe9\x3b\xba\xc5\x65\x81\xa5\x78\x91\xaa\xf0\xe6\xf7\x7f\x60\xb9\x98\x9e\x36\x47\xa5\xaa\x80\x11\xd6\xa2\xfc\x65\x6b\x4f\xa4\x23\xbd\xd7\xdb\x9c\x80\x70\x32\x96\x98\x2e\xd7\x6c\x94\xfc\x9a\x52\xcb\xa9\x9d\xb7\x12\x1a\x98\xc3\x17\x9e\xc7\xff\x5d\x5f\x70\x14\xd4\xf3\x14\xac\x14\x12\x32\x75\x36\x62\xb2\x44\x4f\x6f\xf5", 141, { 0x5c, 0xf1, 0xaa, 0x0f, 0x1a, 0x4b, 0xef, 0x0b, 0x9d, 0x3e, 0xba, 0x20, 0x09, 0xb6, 0xbe, 0x59 } },
{ "\x56\x8a\xb6\x76\xb1\xe1\xe0\x1d\xa9\x78\x0c\x20\x7e\x96\x45\x96\x23\x40\x13\x9a\x19\x74\x2d\x18\x7a\xff\x4c\x37\x12\xfb\x1a\x63\xa8\xe9\x49\xf6\x5a\x66\xc1\x82\x26\x8d\xf1\xbd\x85\xea\x47\x0a\x31\x16\xba\xa0\x08\xf4\x84\x59\x09\x86\xee\x19\x7b\x6d\x43\xd9\x2c\xfb\xe6\x4f\xd7\xf6\x80\x3c\x9f\xec\x51\x51\xb8\x2e\xa8\xbf\x25\x6a\x6e\x5a\x9b\xe9\xdc\x69\x85\x61\x4c\x0c\x21\x78\x2d\x4a\xc7\xd6\x11\xb7\x4a\xe5\xe1\xbe\x77\x28\x30\x91\xba\x35\xac\xaa\xe1\x50\xb1\xfc\xf8\xa6\xf7\xbb\x52\x23\x6c\xc5\xa9\xf0\x1d\xab\x5d\x8c\x4d\x60\xd8\x86\xa8\x7d\x13\xd4\x91\x2f\x31\xd4", 142, { 0xcb, 0x47, 0xda, 0xb2, 0x8e, 0x25, 0x2b, 0x75, 0xc9, 0x37, 0x45, 0x95, 0x31, 0x20, 0x1f, 0x6c } },
{ "\x98\xbe\x49\xdc\x07\xba\x41\x7f\x9b\xc4\xd5\x5f\x50\xf6\xb7\xaa\x56\xf0\xd1\x33\x1f\x80\xa6\x2d\x9a\xed\xd6\x86\x7a\xe0\xc0\xde\xaf\xf0\x42\x22\xf6\xc9\x9c\xc9\x8c\xf8\x72\xab\xfe\x55\xf0\x79\x1c\x0c\x08\xdd\x0b\x4a\xd6\x2f\x7a\x82\x25\xd0\xed\x59\x0b\x27\x35\x34\xaf\x36\x00\x5b\x2b\xd8\x8c\xca\x8c\x99\x77\x94\xf6\x32\xb3\xf5\xe5\x9f\x95\xf2\x8e\xdf\x0c\xaf\x64\x48\xee\x4d\xb6\x84\x6e\x7d\x75\x2c\xaa\xa6\x14\xff\x61\xaf\x88\xbe\xc2\x69\x6f\xa8\x5f\x9c\x4d\x8d\x41\xad\xf1\x91\x15\xe8\xf6\x80\x83\x70\x65\xc8\x9f\xc0\x78\x17\x78\xdd\x79\x92\xce\x1a\xc9\xae\xe2\x87\xfd", 143, { 0x25, 0x55, 0x2f, 0xfb, 0x95, 0x22, 0x2a, 0xea, 0xac, 0x33, 0xb7, 0x28, 0x61, 0xa0, 0x65, 0x9b } },
{ "\xaf\x65\xf8\x23\xbb\x92\xad\x22\x9a\x57\xa3\x3f\x0d\xae\x76\xbc\xc8\x0b\xf1\x9b\x0d\xee\x34\x80\xe5\x98\x81\xb4\xfe\xda\xa3\x46\x1c\x08\xfb\x4c\x3d\x0d\x28\x47\x4e\x98\x52\xa4\x83\x74\x13\x5f\x57\xf6\x03\xc2\x20\x8f\xdf\x4b\x4d\x82\x55\xac\x40\xaf\x6f\xec\xc2\x8d\x99\xab\x27\x36\x51\x82\xff\x9c\x6a\x89\x76\xd9\xfc\xf2\x49\xa5\xeb\xd2\x65\xe1\x13\x00\x1e\x50\x0d\x16\x08\x65\xa1\x95\x76\x36\xc8\x25\x8d\x90\x5c\xf9\x03\x25\x5e\x51\x7a\xe1\xe3\x19\x73\x5a\x9d\xaf\xf0\x66\x02\xc2\xab\xc6\x1b\x55\xec\xff\xeb\xe3\x0b\x49\x7a\x9d\x82\xa1\x7d\xcf\xae\xf9\xa3\x60\x22\x90\x7e\x78", 144, { 0x6d, 0xea, 0x3a, 0xd4, 0x25, 0x47, 0x84, 0x4e, 0x6a, 0x6c, 0x75, 0xcd, 0x94, 0xd4, 0x93, 0xef } },
{ "\xa8\x5c\x0c\x3c\xd5\xa2\xe2\xb5\x48\xa8\xf4\xce\x7f\x83\x03\x7c\x55\x0a\xa3\xf8\x1c\xeb\xe8\x28\x53\xdb\xad\x1c\x04\xe9\x80\xb3\xbd\xec\x2d\x5e\x28\x1b\xe6\xfc\x4a\xbb\x0c\xe5\x54\xf3\x9c\x02\x29\xbb\x39\x19\x6d\xf3\xd1\x27\x46\x90\xef\xe6\xb3\xf1\x9d\x6e\x85\x50\xf4\xf8\xfd\x53\x42\xbd\x04\xc2\xd6\xfd\x01\x54\x6a\x1b\x5b\xa2\x2e\xe5\x8b\x3d\x6d\xf2\xdc\xdb\xde\x24\xc2\xac\x89\x4e\xd4\xcb\x54\xef\x68\xd0\x2a\x1b\xca\x82\xc7\x7a\x18\xa9\x22\x6a\xbd\x02\x76\x40\x88\x4c\xef\xac\x68\x80\xee\x3a\xc6\x1d\xf9\xf5\x7f\xa1\x42\x26\x7d\x13\xd2\x3a\xf1\xbd\x52\xc1\x2f\xf8\x76\x93\x25", 145, { 0xdb, 0x35, 0x6a, 0x9a, 0x9f, 0x39, 0xbc, 0x4e, 0xdc, 0xf9, 0x16, 0x44, 0xc7, 0xfb, 0x4d, 0xb9 } },
{ "\x1f\x06\x0d\x79\xa6\x8b\x79\x3f\x43\x92\x8c\x54\x4a\x9f\x08\x5a\x16\xb2\x82\x50\xa3\x6f\x3e\xcc\x39\xec\x36\xd8\x43\x1c\x39\x67\x3e\xd2\x30\x72\xcd\x75\x7d\xb4\xe9\x3f\x7c\xfe\x35\x31\x2b\x37\x6f\x97\xe6\xf4\x03\x33\x4b\xb0\xba\x09\x3c\xa8\x8f\xc6\x02\x56\xa2\xce\x8f\x87\x46\xe1\xdc\x1b\x35\x69\x71\x59\xe3\x62\x03\xec\xef\xe6\x37\x7e\xb6\x54\x85\xf0\x02\x1c\x37\x33\xe0\x2a\x91\xc6\x8f\xed\x0b\xcc\x94\x03\xba\xc9\xeb\x83\xcd\xf9\x58\xe6\x32\x4d\xdb\x92\x58\x03\x41\x05\x10\xe0\xd7\x9b\x8d\x0d\x3a\xfb\x8a\x8c\x4a\x24\x8a\x55\x3d\x10\x3b\x11\xcf\x02\xf4\x72\x97\x71\x5d\x2d\x75\x91", 146, { 0x31, 0x6e, 0xe6, 0x46, 0x65, 0xc8, 0x63, 0x25, 0x2b, 0x8f, 0x01, 0xbd, 0x64, 0x08, 0x2c, 0x6e } },
{ "\x2e\x95\x28\x7a\x10\xd5\xfc\xf7\x9f\xca\xa0\xee\x91\x7b\x16\x67\xf0\x36\xc3\x3a\x83\x48\xa6\x59\x4b\x58\xa5\xb8\x29\x60\x03\xd5\x9d\x49\xee\xe7\xa9\x23\x35\xa7\xd3\xfe\x17\xb5\x4a\x67\x37\xa9\x20\x82\xab\xb7\xb6\xc1\x33\xfe\x35\x3e\x86\x6d\x38\xbb\xc8\x52\x87\x33\x19\x81\xff\x1c\x47\x1d\x8d\x3c\xa3\x06\x59\x25\xf1\xdf\xff\x4f\x79\xba\xf8\xd0\x3a\x63\x17\xba\x3e\x46\x30\x11\x09\xfd\xd3\x67\x2b\x7a\x36\x16\xf5\xce\x30\x1a\x48\x93\x62\x89\x89\xfc\x70\xaf\xb0\x77\x6d\xca\x80\xfc\x55\x5f\xd1\xf6\xb3\x37\x09\xca\x63\xf9\xa9\x08\x72\x65\x03\x2d\x21\x2a\x0a\x12\x09\x65\x41\xf5\x58\xb8\xd6", 147, { 0xea, 0x55, 0x9c, 0xa7, 0x61, 0xf9, 0x9e, 0xc7, 0x2a, 0x9a, 0x54, 0xba, 0xa7, 0x5b, 0x37, 0xfd } },
{ "\x23\x39\x02\xc8\x3e\x52\xc0\x42\x30\x67\x00\x0c\xad\x1c\xfd\x17\x5e\xd7\x5c\x36\xaf\xc4\x02\xec\x36\xf2\x90\x60\xbe\x9a\x7c\x6d\xf0\x80\xcd\xd6\x9d\x73\x72\x97\xab\xee\x40\x56\x99\xe1\x87\xf8\xb0\x89\x4f\x50\xc8\x7b\x34\xf3\xb4\xc1\xdb\x27\x4b\x1b\x10\xfa\x14\x67\x7e\x6e\x8d\x1b\x0a\xd2\x18\xee\xcc\x2c\x83\x96\xaa\xd2\x32\xad\x93\x17\xeb\xad\x55\x23\x3e\x1a\x1c\xdc\x8f\xbf\x88\x00\xc1\x10\x69\x55\x81\xae\x1a\xf7\x2c\x0a\x77\xd0\x5e\x21\x7c\x27\x18\x65\x7b\x4f\x8d\xbc\xf9\x7f\x89\xa1\x26\xc2\x7f\x69\xf8\x05\x2d\xa0\xe3\x4e\xee\x92\x37\x0c\x9c\xe5\x15\x89\x1f\x63\x0f\x7b\x97\xd6\x5c\xba", 148, { 0x0f, 0x9d, 0xb8, 0x04, 0x8a, 0x9f, 0x75, 0x74, 0x4a, 0xcd, 0xdd, 0x47, 0xcf, 0x76, 0x81, 0xfa } },
{ "\x22\x0c\xb4\x0d\x4a\xfa\xce\x1d\x0e\xfd\x74\x8b\x8b\x3b\x3f\x1d\x47\x28\xf5\x13\x1b\x25\x7b\x98\xba\x42\x78\x54\xe2\x24\x89\x1e\x1d\x02\x1a\xcf\x34\xc9\xe7\x32\x31\x60\x10\x17\x10\x06\xd2\x87\x02\xd7\xe8\x11\x5d\x6d\x7d\x43\x23\xa2\xcc\x35\x2c\x74\x56\x3f\xf3\x02\xbf\xca\xfb\xb3\x46\x44\xdc\x76\xdf\x2d\xee\x23\xef\x4e\x90\x00\xa3\x0a\x16\x60\xee\xcd\x4d\x67\x1d\xa1\xab\xe8\x18\xdf\x18\x6f\x37\x02\x53\x5a\xbe\x97\x03\x22\xf7\x51\x5b\xb7\xea\x39\x68\x0a\xbc\x02\xfa\xa4\xa2\x7a\x2c\x73\x80\x1d\x92\xa6\x22\xc4\xff\xad\x15\x7a\xf0\x63\x23\x6f\x99\x48\x6a\x06\x89\xe7\x18\x09\xfc\x56\xc6\xfc\xbd", 149, { 0x76, 0x46, 0xa2, 0x5b, 0x70, 0xce, 0xd9, 0x5e, 0xe2, 0x86, 0xf5, 0xb1, 0xc2, 0x39, 0xd9, 0x4d } },
{ "\xdc\x6d\x8f\xb6\xad\x09\x2d\x16\xd0\xc8\xb1\x1d\x21\xef\x38\x87\x73\x4a\x11\x92\xcd\x4e\xd1\xae\xd5\xcd\x84\xc1\x4b\x54\xfd\x14\xac\x24\x4f\xdd\xf7\xcc\x54\x69\x8b\x5f\x6a\xe6\x2f\x57\x3e\xca\x2c\x06\xc0\xe4\x95\xb5\x36\xfd\xa7\x5b\x6d\x2a\x4b\xfb\x09\xb1\xb8\x9b\xfe\x96\x35\xdc\x17\xc1\xfc\x3b\xb4\xcd\x3a\xe3\x91\x6f\x33\x2c\xc0\x81\x83\xb4\xb9\xaa\x7f\x18\x88\xac\xba\x50\x24\x4a\xa4\xa5\xe0\xd4\x4c\x4f\xfb\x50\x46\xaf\x52\x47\xa7\x25\x34\x29\x2d\x8f\x56\x5e\x7c\x5f\xdd\xea\x83\x58\x99\xbb\xfd\xe5\x52\x92\x14\x16\x3a\x8c\x1f\x37\x81\x4c\x8c\x0f\x08\xc7\xd9\xb2\x2d\xac\xbc\x03\xc5\x6e\x63\xca", 150, { 0x63, 0xf7, 0x3c, 0x3f, 0x15, 0xc6, 0x1c, 0x37, 0x20, 0x84, 0x1a, 0x45, 0x07, 0x6f, 0x04, 0x5b } },
{ "\x28\xef\xd6\x6e\x65\xca\x78\x4f\x96\x3d\xac\xc2\x4f\xb2\x93\xaa\x30\xe8\xf4\xaf\x9a\xc3\x35\x1e\x7e\xac\x86\x5d\x51\xa6\x1d\x09\x1c\xef\x9b\xae\xaf\x4f\x8e\x22\xf5\x00\x10\x7e\x63\x39\x8c\xba\x8b\x59\xa0\xe4\xcd\xad\x1e\xfd\x2c\xde\x2d\x70\x3e\xfa\x8d\x30\x3d\x1d\xf8\x6d\x3c\xbb\xa3\xf2\x73\x8d\xe4\x1e\xbb\x16\xed\x7d\x15\xd1\xb6\x02\x64\xf9\xf9\xe3\x3b\xf4\x57\x11\xd1\x5d\x98\x53\x11\xad\x10\xfe\xce\x85\x1c\x53\x14\x9a\xcc\x75\x99\x3d\x9b\x05\x53\x86\x59\x5c\x23\x1c\x29\x64\xaf\xa4\xa6\x13\x4d\xc4\x21\x85\x1a\xd3\x06\xb6\x2b\x1f\x5d\xd9\xdb\xd9\x6c\x57\x44\xa1\x79\x67\xc9\xaa\xac\x46\xcf\x8a\x13", 151, { 0x62, 0xc8, 0xb6, 0x40, 0xbc, 0xc6, 0xa0, 0x0a, 0xa7, 0xd0, 0x3e, 0x39, 0xf2, 0xb0, 0xea, 0x37 } },
{ "\x14\x8b\xa1\xc0\x4b\xf6\x23\x0d\xdc\xde\xc3\x00\xe7\x16\xfd\xc9\x17\xce\x00\x68\x99\xfd\x37\x6b\xb7\xfa\x73\xd5\x15\x2a\xb7\x1b\x86\xd4\x9f\x48\x8c\x11\x6d\x40\x6a\xdf\xb1\x21\xe8\x54\x95\xc5\xa3\xef\xc2\x64\x0e\x0a\xf3\x57\x09\x6c\x14\xf7\x1c\xfb\x16\xa4\x50\x8e\x52\xe1\xaa\xe0\x97\x9d\x45\xa1\xd2\x8d\x0b\xa7\x59\xb4\x0f\x43\xd4\x04\x8a\xae\xc8\x1e\x71\xa1\xc1\x36\xaf\x03\x1c\x12\x04\xbd\x6e\x31\x79\xaa\x95\x08\x7f\xaa\x59\x67\xa4\xd6\xbd\xfb\xf1\xcd\xe8\xec\xe2\x2d\xab\xa7\x02\x1e\xaf\xb6\x23\x08\x3c\xca\x37\x64\xa8\xdb\xcf\xb0\x5a\x66\x2d\x7c\x7a\xd5\x07\xa2\x37\xfd\xdb\x93\xb4\xc1\xe9\xcb\x90\xd3", 152, { 0x37, 0x36, 0xc5, 0xe7, 0x36, 0x49, 0xee, 0x57, 0x1d, 0x40, 0xa0, 0x4c, 0x9e, 0xde, 0x94, 0x3a } },
{ "\xd2\x66\x10\x9b\xcb\xcd\xeb\x30\x7e\x89\xb2\x83\x7d\x38\xdb\x9b\x63\x9c\x69\xfa\x89\xd0\x39\x1f\x62\x97\xea\x25\x74\xcd\x6a\x89\xf3\xff\x1a\x09\xfc\x16\x9d\xa7\x6b\x2e\x42\xcc\x59\x85\x0b\x8a\x35\x8e\x5a\xfa\x7e\x25\x37\xc4\x1a\xde\x40\xbd\x56\x76\x2e\xab\x7b\x6b\xff\x23\x09\xa7\xc6\x93\x93\x57\x0b\x5c\x36\xdb\xe0\x17\xb7\xd6\x81\xf9\x38\x64\xa7\x51\x97\x6b\x69\x2e\x64\x0b\xcf\x1d\x7c\x2f\xf5\x0f\x46\x45\xd9\x5a\x8a\x0a\xc1\xd6\xe9\x7e\x4b\x28\xfd\xf7\x13\x1b\x0e\x52\xfa\x2a\x6d\x44\x19\x1a\x71\xce\x43\xc4\x0b\xcf\x2f\xf0\x08\xb3\x4a\x5d\xe4\x49\x18\xde\x45\xb3\x43\x9e\x1b\x77\x42\x84\x51\xb2\xa7\xb1\x30", 153, { 0x1b, 0xaf, 0xb0, 0x43, 0x8c, 0x29, 0x50, 0xc6, 0x5f, 0x88, 0x3c, 0x47, 0xe5, 0x59, 0x28, 0xf0 } },
{ "\x6b\x05\x26\x51\x05\x7b\x83\x3d\xcf\xe2\xeb\xa3\xb6\x8f\x03\x34\x1a\xc5\x18\x1f\xbd\xba\x60\x24\xd8\x44\x58\x57\x48\x20\x4d\x74\xe5\xdf\xf7\xd9\xf3\x6e\x3f\x24\xb2\x40\x22\x69\x10\x1a\xad\x10\x7f\x7a\x28\x4a\xe0\xa5\x4f\x2e\x9e\x4c\xbb\x74\xd8\xda\x60\xcc\xb6\x5d\x2f\xdc\xdd\x0e\xdc\xd5\xfd\x7f\xba\xb0\x87\x60\xc2\x0b\x7c\xed\xb2\x9a\x61\xf8\x52\x4b\x4f\x8e\xd1\xfa\x27\x49\x4e\xce\xe2\x32\x74\x2e\x06\x50\x3d\x64\x34\xd1\xd7\xcc\xde\xd4\xa3\xb8\x17\xd1\x5a\xe4\x83\xa6\x4a\x90\x6d\x3f\xbf\x40\xf7\xe0\x7d\x0c\x6c\x12\x68\xa4\xb2\x28\x46\xe4\xdb\x6c\x9d\x10\xda\xeb\xb7\xac\x52\xda\xc4\xfb\x8a\xa4\x1e\x12\x7d\x91", 154, { 0xee, 0xd2, 0x3f, 0xfe, 0x5e, 0xf6, 0x9b, 0x93, 0x25, 0x36, 0x5e, 0xa2, 0x89, 0x96, 0x1b, 0xe0 } },
{ "\xa7\x97\x0f\x14\x4e\x1b\x59\xb1\xb1\x12\x25\x89\xdd\x6b\x75\x83\x30\xd0\x3e\x19\x5f\x7c\x32\xbc\x94\xb3\xbb\xe5\xb0\xe3\x03\xba\xae\x55\x30\x58\x27\x90\xad\xc3\xf2\x4a\xa4\x68\xe3\x0c\x88\x4a\xb4\x61\xce\xd1\x02\xba\xbb\x2c\x6e\xe1\x58\x5e\xe4\x18\x83\xec\xf8\xce\x20\x22\x6c\xfd\x6c\xfd\xce\x23\x72\xb8\x3e\xda\x96\xf6\x4e\x16\x4e\x88\x02\xfb\xf1\xdc\xf6\x59\xa7\x03\x9f\xc5\x80\x5d\xa9\x55\xa2\xe3\x80\xf7\x9a\x11\xeb\x6e\xd3\x6e\xd2\xea\x24\xb9\x20\x44\x83\xb2\xc3\xd3\xd7\x82\xd0\xed\xec\xc8\xc4\xfe\x80\x40\xe6\x3e\x7a\x12\xc8\x12\x3a\xb5\xec\x01\x0b\x7e\x82\x51\xb5\xc9\x4f\x3e\x30\xc2\xaa\xd6\x72\xd1\xa1\x74\x69", 155, { 0x71, 0x8e, 0xdb, 0xc9, 0x57, 0x5f, 0x0d, 0x0c, 0xec, 0xbe, 0xde, 0xdc, 0xa1, 0x2e, 0x69, 0xb9 } },
{ "\xa0\x38\xd6\x05\xca\xd1\xdd\x6c\x21\xa7\xe2\x51\x9c\x74\xb0\x5f\xdb\x33\x21\xcf\x59\x00\x58\x19\x2d\x1b\x98\xc6\x7d\x0b\xbf\x64\x7f\xdf\x63\x94\x2d\x90\x88\x3d\x85\x82\xfa\xe3\x7a\x29\x4d\x12\x7a\xc8\x6f\xf4\x9d\x55\xe7\x02\x67\x79\xac\xd7\x3a\xb3\xa4\x20\x5b\x9c\xb8\xb0\x9f\x45\x90\xb0\xb1\xbc\xf0\xf4\x03\xae\xae\x68\x4f\x26\x4f\xa9\xc9\x74\x3e\xb0\xe3\x28\xa8\xa9\xbc\x3d\xf7\xe2\x26\x54\xe8\xdf\x52\x15\x4b\x8a\x1b\xba\x57\x87\xeb\xa7\xa7\xa6\x4e\x31\xd5\x72\x11\x7f\xb1\xe6\x16\x8e\x1f\x3f\xb7\x4e\x8a\xed\xd5\xea\x09\xa3\x7c\x25\x0c\x8d\x34\xdf\xc2\xa1\xe7\xb8\x0b\x0f\x6a\xcb\x15\xd2\xaa\x9b\x95\x68\x74\x0c\xa4\x9e", 156, { 0x28, 0x35, 0xde, 0xdd, 0xce, 0x7d, 0xf6, 0x36, 0x34, 0x58, 0x67, 0x85, 0xef, 0xd3, 0xd0, 0x4b } },
{ "\xe0\x88\x34\x85\x5b\x42\x2d\x81\x50\x97\x28\x7f\x73\x90\xc7\x46\xaa\x84\xaf\xe7\x97\xdb\x23\x4f\xc6\xed\x3e\xfb\x70\x08\xcc\xca\xea\x91\xc6\xee\xad\x41\x69\xfc\x02\x91\xf2\x24\x4a\x31\xf8\x7a\xe7\xb1\x65\x72\xcb\x43\x12\x6b\x9b\x97\xff\x62\x7f\xe6\x2c\xc7\x89\x0b\x16\x6c\xbf\xcb\xd1\x9a\xc7\x35\xbe\x3e\x2e\x25\xea\x41\x54\xe2\x04\xf5\xf8\xe7\xf8\xab\x5c\xbf\x2c\x61\x15\x09\x56\x98\x71\x9b\xf8\x44\x84\xc3\x79\xdd\xd1\xa9\xe1\x93\x92\xd0\x31\x9e\xa5\xbb\x5d\xb3\x13\xac\xe7\x92\x3d\x88\x19\xbc\xa5\xd6\xdf\x43\x56\xe6\x3f\xb9\xf1\x0e\x75\x4a\x56\x10\xaf\xe6\xeb\x97\x61\x96\x8c\xe0\x46\xf0\x0f\x76\xf5\xa6\x72\x15\x1c\x38\xa4", 157, { 0x11, 0xb8, 0xb0, 0x6e, 0xcf, 0xbf, 0xc1, 0x00, 0x35, 0x81, 0x35, 0x3d, 0x73, 0x40, 0x71, 0xab } },
{ "\xb0\x5b\xca\xec\x8e\xbf\x10\xfa\x8f\xd9\x65\x89\x8d\x7a\xfb\xad\xde\x0e\x2d\xbe\xf5\x93\xf1\xe1\x28\x34\x69\x30\x8c\x86\x98\x85\xfc\x5e\x31\xe8\x39\x4c\x8b\x92\x2b\xb9\xb2\x9e\x46\x99\x97\x4b\x08\xcc\x67\xf0\x9e\x17\xf9\x7d\xa6\xb9\x60\xa9\x10\xad\xa0\xbd\x1e\x7c\x7e\xfd\x8a\xbb\x70\xae\xc6\x28\xb4\xc9\x5e\x5d\x7d\x3a\x7a\x2f\x47\xd5\x7f\xa6\x4c\xd6\xd6\x98\x0f\x13\xc4\xe4\x15\xc0\x78\x48\xb3\xdf\x24\xe0\x03\x42\x43\x3c\xf0\x3e\xf8\x1c\x71\xee\x97\xca\xd3\x21\x3d\x14\x2e\xe1\x99\xe5\xf9\xa1\xce\x80\xba\x02\x71\x58\xd6\x42\xad\x8e\x86\x41\x86\x07\xea\x31\x3a\x29\x37\xda\xc3\x33\x0d\x88\x7a\x37\xe4\x92\xd5\xb4\xa4\x87\x51\xd4", 158, { 0x88, 0xe5, 0x0c, 0x22, 0xb2, 0x63, 0x28, 0xbd, 0x76, 0x62, 0x67, 0x97, 0x24, 0xc6, 0x2a, 0x19 } },
{ "\x53\xf6\x6b\x3e\xe8\xda\xc8\x99\x3f\xa0\x74\x9f\x26\xd7\x47\xd9\x94\x73\x64\xfc\xf9\xbe\xdb\xe0\xb1\xdc\x6e\x19\x92\xf9\x71\x34\xa4\x11\xca\x90\xf0\x5b\x18\xd6\x71\x53\xf0\x16\x28\x63\x43\x7f\x4b\x2d\xdb\xbb\x9d\x77\x04\xe5\xd9\xb4\x47\x28\x48\x2b\x52\xf5\x72\xc6\xf3\xe0\xf1\x79\x43\x18\x60\x4d\xe0\x81\x73\x08\x52\x70\x21\x7b\x8f\x02\xfb\x68\x99\x19\xa0\x0d\x45\xf4\x49\x52\x18\x6a\x80\x8a\x4a\xc3\xee\xe9\xec\x33\x51\x83\x25\xf4\x8c\xfd\x98\xff\xd8\xd2\xe1\x66\x19\x44\x3f\x51\x4b\xdd\x4c\x93\x1b\xa0\xe6\xe8\x92\xd1\x32\xcd\xec\x5e\xb7\xfc\x87\xe9\xa5\x83\xf7\x73\x39\x80\x36\xa6\x38\x7b\xf9\xbe\x98\x60\x1d\x16\x3e\x17\x40\x4b\xe2", 159, { 0x5a, 0xc3, 0xb5, 0xfd, 0x87, 0xbf, 0x56, 0xbf, 0x6f, 0x69, 0xe7, 0xac, 0xf6, 0xb7, 0x50, 0x26 } },
{ "\xc2\x6d\x0f\x10\x92\xef\x8c\x47\x47\x46\x72\x44\x42\x38\x9f\x59\x48\xfe\x6a\xf6\xd5\x9f\x8c\x49\x1a\x5b\xac\x02\x96\x3d\x86\x2f\x4c\xd3\xb7\x47\xa6\xfa\x27\x42\xe6\xd3\x13\xe5\x45\xd2\xb6\x1c\xaa\xf5\x93\x7f\x08\x11\x62\xf7\x54\x47\x94\x7a\x22\x96\x85\xf1\xdb\x8b\x3e\x3b\x9d\x13\xe3\x4b\xaf\x71\xbf\x6d\x9f\x4a\xea\xa6\xfb\xdd\x95\x38\xa8\x51\xf2\x44\xe2\x27\xc2\x8a\xd0\xcf\x7c\x4c\xc3\x56\x17\x52\x0b\x3c\x75\x06\x76\x64\x6c\xbf\x66\x24\x71\x1b\x8e\x7c\xe3\x85\x49\x64\xf2\xd6\x96\x3e\x2a\x17\xb4\x6b\xa0\x65\x56\xfa\xb7\xed\x84\x7a\x8f\x17\x0e\xf0\x0b\xc0\xad\xe4\x7e\x9f\xb7\x96\xf2\xc9\x7e\x4e\x14\x4f\x47\xd1\xbf\x05\xe2\xef\x23\x5e", 160, { 0x59, 0x21, 0x8f, 0x6b, 0x10, 0x2a, 0x92, 0x6c, 0x6c, 0xbd, 0x52, 0x03, 0x5c, 0x47, 0xa6, 0xc0 } },
{ "\xeb\x39\x92\x73\x9b\xd1\xe7\x22\x4b\x5a\x93\x53\x87\x0e\x45\x56\xcb\xed\x35\x68\xdd\x13\x0e\x55\xc6\x23\x76\x37\x6e\xdf\x3b\x5c\xd3\xda\x1b\x45\xe6\x44\x30\xcb\x02\xe4\xf6\x5d\x09\x25\xfb\x64\x1d\x47\x22\xc7\xf8\xb6\x93\x8a\xe9\x78\x42\x91\x6c\xbf\x1b\x83\x64\x9e\xc7\xca\xf7\xd9\x1e\xb6\x06\xd5\x29\xc1\x48\xae\xd2\xed\x02\x67\x2b\x4a\x65\x3c\x57\x94\x83\x14\x19\xf8\xef\x12\xf7\xf7\xf1\x4b\x0a\x64\x63\x92\x65\x87\x2c\x6e\x20\x64\x56\x2d\x00\x15\xcd\x12\xc4\x54\xb6\xa9\x0e\x15\x69\x3c\xec\x50\x0d\x5e\x03\xdc\xcf\xa4\x57\x77\xbf\x74\xb9\xe2\x47\xf5\xce\x29\x48\x26\xb7\x01\xd2\x0a\x62\x49\x80\x01\x6d\xb3\x6e\x33\xb2\x7c\xd9\x25\x73\x57\x51", 161, { 0xfd, 0x5f, 0x53, 0x55, 0x7c, 0xa6, 0xcc, 0x9a, 0x93, 0x96, 0x50, 0x2d, 0xe3, 0xbc, 0xde, 0x90 } },
{ "\x6d\x23\x68\x9d\x82\xcf\x6b\x2b\xad\x27\xf5\x32\x1c\x2d\xd3\x66\x15\x79\x8f\x57\x48\x26\x11\x67\x3d\x5d\x61\x66\xec\x7c\x8a\xcc\x6b\xe2\x9a\x92\xc2\x5a\xc7\xad\xda\x21\xac\x28\x94\x95\xb0\xdc\xf7\x7d\x87\xcf\x81\xef\xd2\x27\x9e\xe2\xe2\xc9\x36\x50\x9a\x93\x61\x07\x72\x32\x36\x0d\x98\xa0\xc1\xae\x31\x3d\x12\x24\xbd\x89\x72\xe1\x98\x7c\xb1\x7b\x9c\x82\x9b\x34\xe4\x16\x89\x25\xac\xfa\x13\x07\x54\x10\xe3\x9e\x83\xd9\xa5\xc3\x68\x87\x1a\x0c\x1c\xc0\x4c\x1a\x23\xf2\xdc\x7e\x12\x4d\x77\x48\x4a\x62\x67\x2e\xe2\x56\x45\x56\xa3\xc2\xcd\xc0\x2c\x2b\xca\x53\x36\x19\x30\x83\xf2\xd6\x48\x9b\x8f\xc4\x06\xac\xd8\x5b\x76\x12\xf9\xbf\x55\x9f\x61\xfa\xbc\x67", 162, { 0xb5, 0xc0, 0xce, 0xe9, 0x9a, 0x70, 0x42, 0x5b, 0xc4, 0x6d, 0xf4, 0x0e, 0x8c, 0x2a, 0xf1, 0x63 } },
{ "\xc3\xdb\xf7\x28\x38\x6a\x74\x53\x7f\x06\xd7\xbb\x64\x1d\x2a\xd9\x3e\x88\x32\xba\x91\x81\xdd\x86\xd4\x42\xd7\xad\x4e\x3b\x3b\xaf\x1a\xee\x67\x88\x49\x6b\xe8\xb7\x26\x63\x94\xea\x94\xec\xb7\x48\x94\xd0\x65\x5a\xe3\x92\xef\x97\xc0\xfe\x70\x8a\xcb\x6c\x87\xa2\x09\x91\x1f\x96\x04\x01\x69\x73\x10\x45\xeb\x43\xa8\x94\xb2\x5b\xf6\x32\xa3\x42\x71\x62\xf0\x39\xa1\x09\xa6\x6c\xfe\xe1\x62\xb3\xf6\x56\x24\x05\x0e\x01\x3b\x7a\x20\xbe\x60\xfa\xc2\x6c\xdf\x87\xc7\x40\xf0\x25\xdf\xc6\x24\x62\x5a\x76\xfb\x85\x09\xef\x92\x57\x45\xd2\x79\x88\x09\x3e\xa3\xc0\x3a\xe3\x77\x44\x08\xc5\x03\x40\x6c\x8f\x50\xb7\xd1\x91\xd0\x04\xcf\x58\xf4\x0b\x12\xe9\xda\x02\x59\x99\x24", 163, { 0x62, 0xcf, 0x51, 0xd0, 0xdd, 0x5e, 0xaa, 0x1d, 0x2e, 0xbc, 0x45, 0xe8, 0xbe, 0x1e, 0x27, 0xc5 } },
{ "\xd6\x9b\x8c\xe4\x3b\x44\xc8\xb3\x53\x9c\xf5\xe1\xfa\x49\xb3\xc6\xac\xeb\x98\x37\x13\xe5\xc5\x14\x31\x3c\x24\xff\x27\x97\x40\x27\xa0\x66\xc0\x42\xdd\x20\x81\x99\x5d\xa4\x4f\x3d\x28\xc1\x40\x57\xb8\xd1\xae\x9d\x85\x80\xcf\xe1\x2c\xaa\xf3\xc3\x3b\x62\x59\x87\x9b\x10\xad\x01\x9a\xb2\x22\xad\x95\x43\x28\xdb\x3e\x38\xca\x07\x90\x27\xa7\x47\x4c\x83\x8b\xdd\x2e\x82\xaa\xec\xb1\x1f\x78\x48\x7c\x3b\x28\x66\x68\xdf\xbf\x72\x2e\x9c\xd3\x80\xb2\x13\xe5\xda\xe6\x91\x58\x78\x5c\xd2\x0e\x8d\x6d\xe7\x9e\x3e\xff\x32\x33\x6d\xf5\x8d\x04\xb4\x39\x8a\x6b\x6e\x5f\xd5\xa5\xef\xf6\xb6\x25\xd3\x70\xb3\x95\x74\xab\x52\x6a\x75\x1b\xfc\x22\x7b\x96\xfd\x1d\xfc\xbc\xa8\x15\x2f", 164, { 0xd4, 0x4e, 0xcd, 0x21, 0xa5, 0x2f, 0xe7, 0xf0, 0xee, 0x4c, 0x55, 0xcd, 0x8a, 0xa7, 0xdc, 0x4c } },
{ "\x01\x98\x3c\x23\x59\x11\xf1\xec\x7f\x84\x1e\xf7\xe1\x31\x30\x7f\x2b\xe7\xb4\x18\x6e\xe7\x8b\x69\xed\xe7\xc9\xf7\x84\x32\x30\x1c\xb6\xec\x44\x16\x51\x74\x0b\x3b\xc0\xe2\x63\x4b\xed\xaf\xff\xde\xd0\x74\x00\xfc\x99\xcb\x2c\xcb\x76\x52\xcd\x63\x01\xce\x28\xbe\x4a\x6b\x99\xcb\x7c\x21\x48\xa1\x2e\x33\x8a\xd0\x48\xd2\xd6\xd4\x90\xde\x61\xa3\xd0\xcc\x59\x65\xa6\xa2\xbb\x44\xe8\x1f\xd2\x59\xb1\xf9\x4d\xc5\x0d\x3e\xfb\xe1\x3d\xdc\x25\x8d\xa7\x3c\x88\xa5\x5d\x08\xed\x92\x48\x0e\x67\xfc\xf0\x3c\x29\x9d\xeb\xcb\x01\x31\xe1\x79\x75\x8a\x37\xee\x78\xbf\x60\x40\xc9\x84\xbc\x92\xe9\x95\x2b\xd7\xb7\x4d\x33\xb4\xa9\x53\xca\x84\xa9\x73\xc0\x75\x8a\x8b\xcb\xcf\x25\x9c\x31", 165, { 0x92, 0x9b, 0xb5, 0xb5, 0x6a, 0x86, 0x10, 0xb0, 0xb7, 0xe5, 0x9a, 0xc2, 0x89, 0x26, 0x9a, 0x48 } },
{ "\x55\x14\x00\x0c\xc4\x0a\xbb\x3d\x78\xce\xe9\xf0\x2e\xd2\x57\xc7\xe4\x74\x2e\xa5\xdd\xd0\xca\x1a\xc1\x40\xaa\x66\xe0\x71\x7f\x2c\x97\x23\x67\xb4\xcb\x7c\x33\xdd\x93\x0a\xe4\x9d\xf2\x54\x35\x36\xc1\x1b\x52\xf8\xac\x32\xa6\xad\x53\xf7\xd2\xa4\x90\x6d\xb9\x5d\xd8\xf7\xb8\xce\xba\xb3\xf3\x50\x85\x71\xcb\x29\x07\x4f\x6b\xb6\x6f\xf3\x82\x35\x54\x63\x0b\x2d\xce\x84\x47\x7a\xc2\x2d\xcd\xf9\x3c\xe7\xb7\xcc\xf5\x43\xfe\x4a\xf3\xd8\xe0\x86\x50\xd8\x7d\x7a\x12\x4e\x82\xd1\x39\xf7\xfc\x4e\xd8\xba\x4e\xdc\x5b\xc4\x3e\x32\xe7\x44\x29\x22\xdf\xc0\x57\x7f\x82\x13\x69\xa9\xb1\x03\xef\xb7\xce\x83\x16\x3f\xc1\x82\x7e\xc4\x14\x6d\x2a\xbd\x3e\x48\x91\x3e\xfd\x64\xd1\x46\xdc\xbe", 166, { 0x34, 0x76, 0xd8, 0x8f, 0x60, 0x2b, 0x1e, 0x31, 0x24, 0x84, 0xf7, 0xc0, 0x6c, 0xe6, 0x88, 0x4d } },
{ "\x03\xa4\xd4\xf4\xa9\x86\x0f\xe5\x44\x9f\xc7\xe3\x03\xf4\x4d\x97\x95\x44\x26\x72\x1f\x12\x50\xcc\x4a\x50\xa2\x9b\x73\xa9\x51\xd0\x06\x6b\x8f\x51\xe5\x10\x4d\x8f\x01\x68\x22\xc5\x0c\x64\x44\xcc\x45\x81\xb2\x9c\x72\xce\x74\x63\xec\x9c\xfa\x3b\xd4\xc2\xa2\x8c\x64\x8a\x55\xfe\x60\x3c\x51\x18\xaa\x44\x01\x7a\xf5\x02\x07\xb3\x92\x2f\x5c\xc0\x66\xe7\x8f\x22\xfd\x57\x29\x9b\xb7\x03\x32\x84\x20\xb4\xcc\xce\x5e\xfd\xfc\x93\xc3\x69\x89\x58\x82\x43\xfd\xe2\x7f\x02\xc8\xb1\x3f\x4e\x84\x1d\xff\xb3\x54\x0c\xe0\xe1\x65\x4e\x3f\x9d\x96\x95\x23\x48\x34\x14\xd0\x0a\xde\xb2\x78\x9b\x88\xeb\x11\xae\x9a\x44\x42\xfa\x38\x69\x77\xe6\x9d\xe4\x13\xd0\xa0\x7c\xc5\xfa\x59\x28\xf4\x11\xdd", 167, { 0x8e, 0x0a, 0xc8, 0xfc, 0x3e, 0xdd, 0xb3, 0xf5, 0x3b, 0x8d, 0x4d, 0xfd, 0xac, 0xbe, 0x7e, 0x2e } },
{ "\xae\x54\x5b\x24\xdd\x9a\x7d\x0c\x63\x4c\xe7\x77\x4c\xb1\xdd\x8f\x18\xe8\x22\x29\x77\x15\x43\x47\xa3\xb6\x7d\xb8\x5a\x14\x4c\xda\x77\xd4\x91\x80\x2c\xad\x5e\xee\xde\x34\x62\x01\x9d\xd2\xec\x6c\x3f\xd8\x9d\x1c\x18\xa9\xaf\xbd\x57\x15\xdc\x56\x00\xc7\xec\x10\x81\xd4\xde\x14\xf4\x73\xb2\x91\xf0\xcc\xd1\xdd\x0c\xe9\x1a\xb3\xf1\xc9\x8a\x9b\x1b\x93\x87\x67\x2c\xe8\xc9\xd9\xed\x51\xe6\x62\xe2\xd8\x78\x05\x88\xb2\xec\x5a\x2d\x19\xea\xaf\x6c\x38\x5c\x49\x44\x40\x1e\xc8\xd5\x98\x40\xa8\xb6\x31\xfa\xe4\xf5\xf7\x2d\xb5\xac\x63\x92\x78\x3c\x2d\x81\xad\x29\x1f\x60\x1b\x92\x05\xa6\x12\x4b\xc1\x8b\xc8\x99\x7b\x4e\xe5\x89\xf5\x22\x1a\xed\xfc\xb6\xec\xf4\xfa\x60\x8f\x65\xa9\xe5\xed", 168, { 0x77, 0x8c, 0x9e, 0x7c, 0x8c, 0x2b, 0x00, 0xa3, 0x26, 0x62, 0x11, 0x70, 0x3a, 0xe1, 0x62, 0x5b } },
{ "\x78\xe2\xe7\xc6\x51\x93\xec\xf1\x19\xcf\x07\xc0\xbb\x00\x25\x81\x38\x37\xf5\x21\xa8\xa4\x75\xec\xce\x21\x16\x6f\x56\xe8\x8b\x7f\xad\x66\x33\x52\x7d\x27\x21\xca\xc4\xf0\xc4\xd2\x90\xeb\x38\xe1\x59\xfd\x28\x9c\xfb\x34\x5d\x98\x4e\x5c\xe8\x3d\x64\xb1\xe8\xc6\x5e\xae\xf9\x64\xeb\x04\x39\x82\x5e\xa6\xf8\x24\x6b\x01\xfc\x69\x7f\x49\x6d\x2f\xb9\xef\x63\xd5\x88\x2e\x0b\x1b\xe2\xc5\x70\x26\x1d\xbf\xec\xa1\x6e\x6e\x2a\xfd\xfd\x76\xd6\xd8\xa1\x05\xe4\x7b\x3d\x20\x7a\x7e\xb6\x19\x7b\x86\x90\x1d\xb1\x2f\x24\xe9\x96\x04\x80\x9d\xbf\xab\xdb\xa9\xe6\x1e\xb3\xe4\x92\x14\x85\xbb\x65\x7e\x28\x86\x02\x2d\xc7\xf6\x99\x90\x79\xab\x10\x9b\x7f\xe2\xcf\xc4\x19\x4c\x28\x27\x05\xf9\x62\xca\x95", 169, { 0x0c, 0xf1, 0xe5, 0x46, 0x88, 0x85, 0xe9, 0x7a, 0x00, 0x08, 0xbf, 0xe5, 0x0b, 0x81, 0x55, 0xec } },
{ "\x73\x61\xfa\x6c\xe8\xf3\xd4\xd4\x7f\xb9\xe0\xbf\xcb\xb0\xd7\x59\x5d\x5b\x85\x46\x73\x8f\xc9\x7d\xcf\xda\xba\xc0\xa3\x91\xe4\xb7\xa8\x75\xb0\xa8\x4e\x01\xe1\xd6\x0e\x53\x3b\x73\xdb\xb4\x3e\x42\xb6\xc6\x10\xce\x61\x49\x78\x40\x2b\x8a\x06\xe1\xea\x68\x51\x2d\x07\x04\x59\x90\xb3\x04\x0a\xc0\x38\x84\xe2\xb6\x6a\x9b\xa9\x4a\x3c\x85\x5f\x6a\x6f\x72\x34\xf6\x64\xea\xb1\x3b\x13\xdb\xf4\x0b\x14\x41\x18\x75\xdb\x70\xb3\x27\x01\x01\x79\x5c\xbd\x57\xfd\x83\x71\xcc\x98\x6e\x61\x7d\x62\x33\x7e\xaf\x5d\xa3\x60\xdd\xb2\x64\x53\x5f\x13\xae\x88\xb8\x3f\x9e\x6d\x7c\xa4\x3a\x17\xdc\x1e\x02\xda\xd0\xde\x2f\xfb\xe0\x66\x8b\x7d\x8e\xb0\xec\x17\x63\x6e\x4e\x47\x95\x6d\x8c\x80\x51\x13\xbb\x7f\xab", 170, { 0x46, 0x56, 0xb9, 0xc5, 0xa9, 0x38, 0x86, 0xec, 0xb8, 0x20, 0x03, 0xdb, 0xb1, 0xc2, 0x84, 0xd8 } },
{ "\x07\x23\xbc\x20\xef\xdb\xfb\xc4\x54\x00\x01\x0c\xa3\x92\x64\x3a\x4d\xeb\x7c\x61\x0d\xdc\x76\x14\x49\x65\x87\xaf\x92\x11\x3c\x41\x46\xec\xf0\x15\x55\x22\x58\xdc\x20\x36\x38\x78\x7d\xdb\x38\x67\xd7\x72\xd1\xca\x73\x21\x62\x11\xcd\x8c\x5f\x45\x21\x33\xa8\xf2\x05\x68\xf8\xaf\x33\xeb\x74\x4c\x65\x24\x63\x96\x59\xfc\xfd\xc9\xf4\x58\x5c\x93\x83\x32\x8f\xc1\x1c\x56\xce\x88\x23\xb7\xc7\x72\xe8\x6c\x17\xe4\x6e\x4a\xd4\x48\x47\x1e\x47\xdb\x9a\x87\xb7\x14\x47\x6e\x60\xb0\x21\x24\x83\x57\x5a\x16\x97\xec\xfd\x2f\x9d\x76\x94\xca\x91\xa6\xe9\x53\xfc\x04\xea\x79\xa6\xba\xa5\x11\x69\xfd\x73\x8a\x21\x14\x32\x09\xc0\x06\xab\x21\x7e\xe4\x12\x30\x2d\xb0\xab\x59\xaa\xe9\x89\x19\x70\xb4\x71\x88\x46", 171, { 0xea, 0xdb, 0xc3, 0x9a, 0xf7, 0x45, 0x25, 0xa6, 0xbb, 0x14, 0x02, 0x97, 0x30, 0xae, 0x75, 0xc7 } },
{ "\xa9\xde\x2f\x19\x37\x1e\x80\xe4\xb4\x9a\xf6\xcb\x04\x33\xca\x48\xe5\xc7\x4f\x7c\xd6\xd2\xea\xa7\xa2\x31\xb2\xb3\x8d\x02\xa0\xeb\x19\xa8\x73\xc9\x75\xeb\x23\xec\x83\x3c\xff\x28\x85\x15\x65\xb8\x63\x7f\x1e\x8e\x9b\xad\x54\xcb\xc5\xc6\x30\x4a\xc2\xc0\x14\x57\x81\x68\x72\x7e\x6d\x7e\x47\x7d\x77\xfc\x38\x5b\xbb\x77\x47\x92\xd1\x9f\x32\x67\xb3\xe1\x68\x5b\x46\x2b\xa8\xba\x87\xcf\x39\x50\x53\x81\xc0\x3b\xd2\x7b\xc1\xdc\x82\xc0\xb5\xe7\xdc\x7c\xc3\x9a\xa4\x8a\x1f\x0b\xd2\x10\xfc\x99\x18\x45\x2f\x84\x10\x53\x61\x99\x04\x58\xf1\x06\x59\x86\x64\x4c\x98\x69\x89\x51\x1a\x48\x2e\x95\x50\xa5\x78\x7d\xac\xe0\xe3\xcb\x30\xf8\xd7\x2f\x91\x70\xe3\xf6\x07\x50\x98\xe1\xb4\x42\x04\x11\xae\xdd\xca\x1d", 172, { 0x6d, 0xb0, 0x2c, 0x2a, 0xe2, 0xf9, 0xc9, 0xa2, 0x0a, 0x36, 0xf9, 0x94, 0x5f, 0x28, 0x33, 0xf3 } },
{ "\xab\x00\xdb\x46\x48\x54\xd3\xc2\xf6\xf3\xf2\x34\x82\x27\xb5\x3d\x3f\x4a\x10\x2c\xd1\xcd\x4b\xd1\x99\x55\x76\x6f\xb8\x00\x8a\xcf\xc2\xc6\x1e\x71\x01\xfa\xc3\xde\x63\xee\xfc\x19\x01\xb6\xdd\x34\x4c\x06\x3f\xfe\xd6\x35\x9d\xda\xba\x62\x8e\xab\xaa\xb5\xdf\xeb\x93\xbf\x4c\xdb\xef\xfb\xdb\xd4\xa6\x76\xd6\xbd\xa2\x8a\x63\x96\xee\xc6\xc1\x30\x89\xea\x21\xff\xcd\x0d\x1f\x08\x77\xe1\xdb\xf4\x52\x0f\xb8\x47\x85\xbc\xb1\xaa\x75\x2d\x53\x64\x58\x87\x5b\xe7\x58\xaf\xec\x87\x5f\x50\x6c\x45\x85\xfb\xfd\xca\x14\x68\x93\x6f\x34\xda\xb7\x79\x38\xa1\xd7\x62\x83\xb9\x47\x52\x18\x90\xd8\xad\xbe\xe5\x13\xc5\xcc\xcc\x13\xb0\x96\xce\xfc\x35\x74\x2d\x1c\xe0\x6c\x44\x9c\x35\x7b\x0d\xe2\x01\x85\xbd\x87\xcc\xd6", 173, { 0xfb, 0x6c, 0x0f, 0xbd, 0xe3, 0x87, 0x1b, 0x0a, 0xb2, 0x35, 0xf1, 0xb1, 0x53, 0x60, 0xd6, 0xd2 } },
{ "\x8a\xf0\x6a\x54\x8c\x8b\xb1\x44\xc1\xa8\x44\xb5\x2b\xf1\x8e\x8c\x14\x88\xcb\x2d\x72\xbb\x40\xc3\x65\x66\x8b\x2d\xdc\xe6\x15\x86\x58\xb5\xa3\x4e\xc9\xa7\x0c\x3a\x94\xc0\x05\x94\xb6\xb0\x18\x50\x02\xec\xb3\xad\x86\x69\x5d\x84\x0c\xf7\x03\x31\xbc\x39\x71\x1b\xdf\x3d\xdc\xe1\xbe\xbc\x9b\x22\xa8\xef\xf6\xe9\x13\x0b\x17\xb4\xda\x5b\x1e\x1f\xa5\xf9\x50\x32\x67\x29\x6f\x44\x00\x52\x24\x89\x02\x9a\x99\x3f\x90\x1d\x23\x72\x62\xc9\x1d\x67\xe6\xd9\xd0\xab\x81\xeb\x8e\xb8\xf3\xc0\xde\x40\xd9\x90\xd1\x19\x4b\x08\x73\xc6\xa5\xe1\x5d\x9e\x64\x1e\x68\x9c\x26\xe2\x7c\xc2\xd3\xeb\x86\x2a\xdb\xaf\x87\xaa\x95\x11\xc9\x23\xc7\xc0\x2e\x66\x43\x2d\xa1\xc4\xae\x26\xad\x31\x5c\x14\x2c\x14\x57\xcd\x17\xae\x7f\x17", 174, { 0xf9, 0x28, 0x28, 0x66, 0x7b, 0xe7, 0x4e, 0x18, 0xda, 0xce, 0xe9, 0x8d, 0xff, 0xab, 0x12, 0x98 } },
{ "\x7d\x24\xcb\xba\x8f\x2a\xad\x41\xd8\x4e\x94\x4d\x89\xdf\x8b\x95\xf2\x78\xff\x7d\x0d\x2c\x9d\x52\x35\x4f\x5a\x20\xf4\xdf\x8c\x30\xf9\x8e\x35\x22\x28\x6d\x61\xa3\xcc\x36\xa5\xca\xe8\x36\xc7\x14\xab\xd5\x7c\xfa\x01\xc4\x4c\x2d\x46\xc1\x92\x6e\x15\x0a\x9f\x0b\x3f\x5c\xff\xf9\xd8\xa6\xd3\x8b\x6b\x4f\x5d\xcd\x4d\x21\x9b\x7f\x0f\xd0\x0a\xb1\x0d\x2b\x8b\xf8\x23\xde\x63\x4a\x7f\xe1\x5d\x7b\x92\x81\x0a\x55\x21\x09\x29\x4d\x78\x0d\x21\xe8\xbd\x52\xaa\xaa\x62\x5d\x8c\xb6\xb4\x97\x80\x07\x91\x19\x33\x49\x36\x1a\x68\x55\x36\xf2\x3c\x48\x87\xca\x85\x3f\xb7\xe3\x54\xb0\x3c\x7f\x9a\x68\xe8\x6f\xe7\x1d\x7b\x3a\x4d\xaf\x53\xe7\x63\x00\x3e\x68\x66\x6c\x70\xa3\x79\xe7\x90\x1e\x0d\xb2\xec\x45\x5b\xba\xcd\x5b\x0e", 175, { 0xeb, 0xa1, 0xc8, 0x38, 0xbc, 0x82, 0x3d, 0x39, 0xc0, 0x92, 0xec, 0xdb, 0xa3, 0xa7, 0xc6, 0x56 } },
{ "\x8d\xe6\xfb\xff\xf9\xe6\x4a\x18\x43\x2d\xbc\x59\x01\x9a\x7f\xf9\x95\x83\x87\xa4\x46\xbe\x37\xe3\xfc\xdc\xa9\x9a\x98\x76\x9a\xaa\xee\x9f\xf5\xb7\x60\x79\xfa\x04\x18\xe3\x0b\x79\xe1\x50\x13\xc7\xa2\xb3\x93\xa2\x79\x96\x4b\x2d\x70\x4a\x81\x74\xdf\x39\xf1\x12\x5e\x57\x51\xf3\x64\xb7\x7f\x34\x81\x3a\x49\x27\x34\x62\xd9\x72\x6a\x44\xbb\x56\x94\x9c\x8c\x0e\x63\xfb\x42\x4e\x23\x1b\x12\xea\xb1\x53\x02\x98\x1a\x25\x0b\xce\xd2\xf5\xea\xc5\x8e\xd5\x83\xa0\xbe\x7b\xf2\xa5\xaa\x54\x30\xa9\x28\xb1\x5f\x8b\xf8\x1f\xb3\xd6\xbc\xd6\xfc\x8a\x96\x47\xd3\xf5\x60\xd8\x61\xba\x83\xac\xc7\xf3\x74\x9b\x2d\x73\xd1\x41\x6b\x24\x14\x32\x4d\x8f\xf1\x4b\x86\x7b\x52\x24\x35\xac\xed\xa1\x0c\xcc\x7e\xa8\x23\x68\xa6\xed\x58\x11", 176, { 0x48, 0xe5, 0x26, 0x3b, 0x57, 0x15, 0x53, 0xb3, 0x89, 0x25, 0x12, 0x09, 0x49, 0xd9, 0xd6, 0xd2 } },
{ "\x60\xe8\x60\xa3\x1c\xe8\x6f\xa2\x7a\x41\x80\xbf\xa4\xa1\x41\x7b\x48\x81\x9e\xe3\x31\xb7\xe9\x79\x81\x27\xa3\x33\x8d\x9a\xea\x9c\x55\xa7\x7e\x19\x9e\xcd\x71\x47\xae\xa3\x6b\x7f\xa3\x26\x97\x7b\x71\x32\x2e\x12\x76\x0c\x9e\x1f\xbc\x1c\x56\x80\xca\xb8\x31\x3a\x27\x1b\x7c\xba\x6c\x74\xa6\x36\x05\x1b\x83\x32\x84\xde\x3d\x1f\xa1\x7a\xd7\x1e\xd2\x25\x5f\xe2\x83\x48\xb0\xb3\xf3\x4a\xe1\x8e\xab\x88\x4e\x69\x9b\x60\x41\x95\xd2\x6d\x3c\x3d\xd9\xcd\xe5\x0b\xad\x9d\x8e\xea\x58\x86\x60\xe6\x2b\x71\x25\x2f\x9a\x56\xaf\x3c\xb4\x32\xe7\x0b\x3d\x17\x75\x87\x69\x5d\x09\x03\x38\xf6\x45\xe3\x69\xdf\x47\x5b\x1c\xb3\xd6\x4e\x07\x5a\x28\x58\x54\xde\x4d\xe7\x16\x5e\x3c\x84\x67\x1b\x78\x30\x1f\xaf\x5f\xe9\x33\x5e\x0f\x4c\xa7", 177, { 0x3d, 0x14, 0xce, 0xb4, 0x3a, 0x69, 0xc6, 0xb9, 0x35, 0x2a, 0x07, 0x20, 0x11, 0x8a, 0x7d, 0x87 } },
{ "\x85\xf5\x54\x31\x2f\xf4\x40\x6c\xc7\x2e\x93\xb5\xe7\x71\x35\xa6\x4f\x41\xb7\x2d\xf1\x7e\xb4\x48\x28\xb2\x53\x5f\x09\xf9\xe8\x3d\xd2\x8d\xba\xad\x80\xed\xdf\xad\x7c\xaa\x44\x51\x75\xce\xc9\x37\x49\xe9\x89\xa3\x1c\xb9\x37\x37\x8c\x75\xa3\x50\xfb\xb6\x5c\xff\x0b\x03\x70\xa2\x28\xf7\x4f\x1f\x3e\x11\xce\xbc\x8c\x18\x47\x9e\x90\x29\xdd\xdd\x22\x5f\xdd\x40\x9d\x1d\x40\x9a\x37\xfa\x4d\xc0\x72\x4a\x5b\x25\xab\x45\xb4\x15\xd0\xd7\x96\x8a\x2f\x03\x53\xae\x69\xf4\x98\xee\x85\xa2\xca\xb7\x21\x1d\x8c\xd0\xc3\x7a\x5d\x54\x4d\x57\x5f\x4a\x8f\x0c\x32\xe3\xf7\x6f\xff\x4f\x63\x44\x91\x3c\x17\x40\x9d\xec\xca\xb8\x22\xf1\xdb\xeb\xeb\x88\xa1\xe8\x32\x90\xdf\x1d\x5f\x25\x57\x7e\xed\xde\x75\x4e\x6e\x9f\x2c\x8d\xa5\x1b\xde\xff", 178, { 0xaa, 0x31, 0x4b, 0xfa, 0x31, 0x2f, 0x20, 0xf7, 0x63, 0xcf, 0x19, 0xbd, 0x14, 0xf2, 0xac, 0xda } },
{ "\xd1\xe9\x75\xd4\x09\x42\x8f\xf9\xc2\x55\xd6\xfa\x6e\x47\x6d\x92\x3f\xca\x86\xd1\x09\x59\x10\xe8\x46\x0d\x8e\x94\x33\x1f\x03\x25\x17\xfb\x09\x2a\xa3\xfd\x02\xbb\x75\xca\x65\x63\xb7\x4e\x3a\xa7\xe4\x4d\xa1\x37\xab\xa8\x8b\xb1\xec\x2f\x8c\x0c\xdf\x1d\xc9\xa7\x54\x34\x0c\xee\x14\x76\xf6\xa6\x67\x7d\x54\xd7\xaf\x77\x8a\x53\x32\xc2\x2b\xe6\xf5\x20\xab\x1c\xc3\x97\x2c\xa9\x4d\xe8\x74\x79\x6b\xa9\x00\x55\x81\x01\x32\x2e\xfc\x00\xa5\x0a\xfc\x99\xa1\x88\x0c\x3d\xaa\x11\x0c\x14\x33\x9d\x30\xda\x70\x1f\x21\x55\x49\x8f\x41\x6e\x6a\x92\x0c\xf3\x77\xc7\x9a\xe8\x5d\xb0\x40\x86\xc4\x3b\x56\xd1\xa0\xec\x14\xd9\xe7\xaa\x96\x8d\x5d\x23\xff\x36\x8a\xdc\xb9\x98\xce\xd8\xda\xa0\x8b\xe4\xa2\xc9\x80\x7d\x21\x12\x36\x5f\xf6\x94\x92", 179, { 0x24, 0x4a, 0x76, 0x30, 0xfe, 0x50, 0x3c, 0xd3, 0x96, 0x8d, 0xed, 0x72, 0xd2, 0xd1, 0x92, 0x63 } },
{ "\xac\x31\xc2\x8b\xb5\x5a\x42\xf6\x67\x8b\x62\x7d\x55\xb8\x38\xaf\x5d\x0f\x5b\x31\xfa\x7a\x38\x11\x26\x42\x11\x3b\xea\xcd\x98\x04\x83\x88\x24\xe4\x32\xe0\x8e\x41\xa1\x69\xab\x66\xed\x22\x65\x92\x54\xd0\x78\x2d\x7c\x86\xc6\x16\x5e\x46\x58\x17\xcd\xc2\xf2\x7a\xa7\x3b\x52\xb5\x97\x8b\x05\x40\x84\x3d\xe5\x87\x99\xda\x32\xfb\xf2\x3f\x4c\x43\xe0\x29\x0a\x91\xd9\xd3\xbb\x0f\xff\xb6\xb7\x77\x4b\x6f\xa0\xc2\x56\xbf\x3a\xf8\xc4\xac\xe4\x26\x4d\xc4\xb3\x6e\x69\x81\x2a\x38\x97\xc8\x97\x87\x4b\x8c\x0c\x66\x72\x90\xf9\x80\xa3\x49\x63\xcf\xe3\xe1\xc3\x6d\x15\x58\x7d\x86\xfc\xc5\xfb\x6f\xee\xbb\x66\xcf\x18\xc6\x01\xfb\x68\x15\x22\x60\x1b\x31\xcd\x19\xe3\xeb\xee\xa1\xb4\x55\x33\xa2\x2b\xe6\x84\xec\x9b\xc1\x20\x81\xb6\x0f\x55\xcd", 180, { 0x74, 0x83, 0x94, 0xea, 0x7a, 0x82, 0x27, 0x87, 0x79, 0x88, 0xa8, 0x23, 0x8f, 0xc2, 0x4e, 0xaa } },
{ "\xd5\x88\xdb\xf3\xe4\x11\xce\x42\xed\x80\x47\xc6\x3f\x7b\x96\xfb\x3b\x7e\x1d\x9d\xba\xfb\xcc\x9b\x1e\x9b\x34\x29\xf4\xa3\x4a\xf4\x43\x72\xbb\x71\x74\x26\xe6\x4f\xdd\x5f\x7b\x0b\xec\x1b\xae\xa1\xec\xc0\x17\x60\x77\x39\x29\xd7\x79\x38\x4c\xeb\xdc\x99\x9a\x0a\xd5\xe7\x33\x7a\xca\xf7\x3e\xcb\xdf\xb2\x7c\x6b\x1c\xf1\x8a\x58\x3c\x81\xb3\xf0\x15\x45\x6f\xe4\x9f\x7b\x7d\x4b\xa2\x17\x98\xd3\x00\x4b\xd9\x12\x9c\x28\xa8\xfa\xe6\x5e\x60\x6b\x05\x1f\x7f\xe3\x9f\x22\x86\x50\x48\xc4\x73\x06\x84\x48\xd9\xcc\x7b\x3f\x99\x11\x38\x03\x3f\x3c\x9d\x5d\xfb\x21\xe7\x47\x3a\x8f\xda\xcb\xe0\x06\x89\x0a\x24\x86\xc4\x58\x09\x82\x1e\x85\x75\xf4\x99\x37\xf0\x8a\xf0\x72\xfa\xfe\x81\x3a\x08\x83\xd6\x50\x1d\x5b\xcf\x17\x02\x85\x6d\x9a\x22\x94\x3d", 181, { 0x15, 0xb7, 0x30, 0xcc, 0x2e, 0x0b, 0x5f, 0x0f, 0x3f, 0x7d, 0x9a, 0x52, 0x62, 0x75, 0x4d, 0x08 } },
{ "\xa7\x04\x9b\x5f\x13\x65\x45\x32\x21\xf1\x01\x9e\xfe\x9e\x5a\xfd\x63\xa5\x64\xa6\x5d\x1e\x52\x18\x29\x38\x25\xc0\x39\x12\x7f\x67\x53\x38\x96\x3b\xd9\xbd\x44\x78\x23\xf1\x3a\xb3\x08\xbf\x55\xc3\x7c\xa6\x09\x4c\x53\x52\xf9\xe9\x24\xd6\xa9\xf6\x48\x88\x4b\xf7\x02\x7a\xb5\xa8\xb9\x90\x74\xa1\x60\x43\xfa\xa6\xf6\xf1\xf2\x89\x29\xde\xb5\xbc\x16\xcb\xd4\x77\x2b\x31\xd5\x82\x2e\x1a\xfc\xa0\x56\x9d\x3e\x98\x97\xf4\x5b\xe1\xfb\x3b\x04\xe9\x2c\xc7\x37\x02\x0e\x21\xac\xe9\x89\x9e\x67\xf5\x64\x9c\x6e\xd9\x4d\x5b\x95\x15\xf5\x75\x75\xff\x58\xfb\x7b\x6a\x1a\x2e\x1c\xf0\x0d\xd7\x26\xe2\xcf\x44\x32\xc8\x91\xdf\x36\x96\xf2\x6c\x37\x6e\x09\xde\x1b\x0c\x82\xd7\x9c\xd3\xdf\xbb\x59\x71\xe0\x70\x07\x31\xfe\xb4\xc4\xdd\x10\x1a\x8c\x4d\x11\xa2", 182, { 0x96, 0x00, 0xc5, 0x60, 0xaa, 0xdd, 0xfd, 0x65, 0xc4, 0x77, 0x58, 0x7b, 0x27, 0x5e, 0xcd, 0xef } },
{ "\xc4\x90\xf9\xf4\xf4\xb6\xc7\x39\x92\x39\xa0\x96\xf9\x06\x11\x46\x79\x2b\x71\x53\x87\x1e\x62\xf1\x15\xa7\x76\x77\xfd\x6b\xed\xfb\x96\x17\x9d\x88\x69\x25\xeb\xfe\xfe\x4e\xf5\x87\xcf\x8c\xd2\xb2\x5e\xcb\xf1\x2d\x62\x2b\x9e\x23\x1d\xf4\xa3\x30\xc9\x17\x0b\xfd\x30\x5d\x01\x7d\x5b\x2f\xd8\xc3\x62\x00\x24\x7d\x62\x5b\x05\x11\x8d\x84\x84\x52\x5c\xce\x15\xdf\xdf\x79\x3c\x18\x34\x45\x4d\xbe\x16\x97\x4b\x26\x8d\x47\xf2\x1a\xc3\x04\xd1\x4d\xf7\x65\x8e\x78\x8b\x8c\x4d\x15\x37\x79\x2c\xb7\x60\xe9\xe5\x04\x83\xe8\x97\x51\xcb\xfa\x9e\xd8\xc3\xe2\x9e\x98\x26\x0d\x9f\xc9\xd1\x9e\x52\xfb\xd9\x17\x72\xca\xb9\xa0\x46\x40\xa4\xf7\x43\xc7\xf9\xf5\xce\xc9\xd5\xb9\x1e\xe6\xc2\x73\x40\xd1\x8c\xcf\x34\xc8\x34\xfb\x35\xae\xfe\x57\x16\xc6\xa5\xb9\xc8", 183, { 0xef, 0xfd, 0x81, 0xc1, 0x6c, 0x60, 0x89, 0x07, 0x98, 0xf3, 0x9c, 0xe6, 0x82, 0x4e, 0x17, 0xaa } },
{ "\x0e\xaf\x59\xf8\x36\xdb\x60\xd5\x3b\xb6\x08\xef\xa5\x4c\x6f\x3b\x59\xfc\xfe\x33\x1b\x65\x70\x1a\xa4\x7c\x82\x5d\x5c\xc0\x36\x15\xb5\x84\xc3\xaa\x24\x6b\x1c\x91\xbc\xf3\x1b\x35\x68\x28\x4a\xf4\xc4\x78\x4e\x40\x99\xa7\xf1\xe6\xf3\xd9\xca\x6b\xe1\xcc\x8b\x92\xc9\x29\xe7\xfb\x65\xef\x1e\xe5\x5e\x0f\x26\x14\x83\x17\x77\xce\xa6\x06\x74\xff\x13\x3e\x71\x7c\xae\x97\x65\x64\xa8\x8f\x2d\xa3\xdd\xa0\x7a\x90\xce\xaa\x59\xb6\x36\x90\x5d\xb0\x4b\xdf\x6b\x2e\x92\xa8\x22\x14\x5f\x6e\xe5\xc1\xe8\x3b\x86\x6d\x05\xcd\x90\xab\x87\xee\x31\x0e\x32\xe1\xc2\xe8\x58\xf8\xf5\x2d\x13\x43\x9a\x77\x1e\x62\x0e\x23\x50\x7c\x40\x98\xb7\x48\x61\xa8\x4d\x48\x28\x0e\x59\x16\xbf\x17\x65\xd7\x4d\x5e\xd8\xcc\x21\xb0\x2f\x07\x78\x0e\xdc\x3a\xeb\xd0\xbb\xab\x78\xe6", 184, { 0xe5, 0xf6, 0xa3, 0x02, 0x8d, 0x9c, 0x95, 0x97, 0xf0, 0x29, 0x88, 0x10, 0x26, 0xed, 0xac, 0x58 } },
{ "\x2d\x43\x2a\x3a\xd3\xc6\x68\x31\xe9\x1e\xd8\x51\x3c\xd0\xee\xfd\xc9\x90\x15\x5b\xf4\xef\x78\x56\x62\x32\x6d\xbf\x73\x3f\x7e\x8e\x5f\xb1\x16\x47\xec\x0a\xc5\x78\xc9\x08\x30\x5e\x8b\x10\x98\xa8\x41\xc7\x05\x53\xb5\xc0\x0e\xb4\x42\x4f\x48\x94\x4d\x7c\x49\x75\x61\x13\x58\xf3\xeb\xd9\xb2\x46\x8e\xd9\x7b\xe4\x24\xdc\x40\x43\x53\xf6\x25\xbc\xc7\x3d\xb0\x8b\x11\x95\x30\xf3\x1c\xa7\xcb\x7f\x47\xf0\x23\x62\x24\xf5\xa5\x00\xcd\x95\x6e\x86\xde\x77\xd9\xb3\x12\xa1\xa9\xba\x7d\x2a\xd0\x40\x63\x08\xda\x80\xb4\x03\xf9\x8e\x25\xcb\xad\xb9\xec\x24\x10\x09\x18\x3f\xbb\xe7\x8a\x25\x58\xdc\x94\x4c\xc6\x72\x2c\x4c\xe2\x37\xcc\xab\xf8\xdf\xea\xd4\xc6\x89\x0f\x27\x29\x1a\x97\x2a\x67\xc6\x04\xdc\x18\xad\xfe\x2a\xb1\xa1\xeb\x7b\xae\x06\x27\x66\x5c\xd3\xe0", 185, { 0x57, 0xa9, 0xed, 0x9a, 0xe3, 0x2c, 0x62, 0x78, 0x43, 0x4c, 0x1b, 0x41, 0xd3, 0x3c, 0x09, 0xe6 } },
{ "\x83\x40\xb5\x91\x5f\x27\x52\x19\x89\xcc\xaa\x77\xc9\x1f\x9a\x25\x71\x38\x7b\x0d\xcb\xd8\xe7\x2c\xb1\x97\x9b\xc2\x3c\xb0\x78\x33\x94\x65\xa4\x7e\xe7\x10\xcd\x57\x75\xc8\x8e\xe2\xc7\xac\xea\x0e\xff\xcf\xf5\x8c\x4b\xc0\x91\x6b\x74\xb4\x56\x52\x93\x52\x8b\x59\xe2\x1b\x51\x84\xb0\x75\xe0\xdc\x6e\x52\xe8\x2c\xe7\x81\x19\x55\x8d\x91\xbe\x4e\xee\x5e\x84\xbc\x46\x39\x59\x2a\x2d\xb8\x70\xe5\x71\x17\x60\x77\xfe\x49\x6c\xbe\xfa\x9f\xde\xa8\xed\x14\x8c\x8d\x1e\x32\x7c\x28\xf9\xa5\xa4\x33\xab\x5b\xca\x9f\xd0\x54\x8a\x6d\x44\x0b\x76\x2c\x16\x81\x57\x0f\x9b\xe2\x92\xaa\x9e\xd6\xa6\x49\xb5\x67\xd2\xed\xaf\x8a\xd3\xe4\x29\x4c\xcb\x04\x40\x9a\x3e\x83\x60\xad\x35\x76\x3b\x10\x85\xe4\xd6\x4f\x2a\x87\xbd\x3e\xcc\x1e\x57\xe3\x64\x71\x54\x01\xf8\xe3\x42\x42", 186, { 0xcb, 0x49, 0xbe, 0x32, 0xa6, 0x87, 0x50, 0x00, 0x6b, 0x95, 0x12, 0xf2, 0x40, 0x2b, 0x3e, 0x10 } },
{ "\x82\xad\x3f\x00\xef\xbd\xc9\xaf\x43\x2a\xb1\xeb\xd9\x52\x2f\x1a\xc8\x92\x0e\xbe\x62\xa1\x08\x5a\xd6\x89\x2c\xfd\xf5\x43\x7b\x6f\x11\xef\x93\xf7\x01\xad\x83\xc7\x42\x1b\xbd\x06\x38\x6b\x29\x78\x92\x1f\x56\xcb\xcb\x64\xcb\x80\xcc\x09\x7c\x73\xae\x1a\x58\x09\x9e\x1d\xff\xf6\xda\xe8\x91\xa8\xd7\x1d\xa2\x54\x85\x44\x49\x9b\xaa\x83\x58\xd8\x6b\xfc\xa6\x09\xea\xc3\x87\x57\x08\x7a\x5d\x43\x4b\x8c\x48\x6f\xdb\x02\xf8\x07\xa7\x05\xa4\xca\xa5\xf1\x13\xb9\x36\x11\xd8\x5a\xa7\xfd\x9b\xa9\xd4\x8d\x91\x9c\xe7\x79\x0d\x52\x3e\x8f\x30\xd7\x6b\x8f\xbd\x96\x54\xa6\x07\x5c\x7b\x85\x0b\x04\x59\x1e\x9a\xc5\xe3\xfb\xaf\x14\x2e\x3b\xdf\x37\x2e\x29\xee\x68\x9a\x7a\x7d\xa2\xec\x23\xe1\x0b\x84\xd5\x10\xfd\xec\x16\xb5\xb2\x36\xfd\x63\x8c\x82\x8a\xe5\xff\x9c\x9f\xb4", 187, { 0x19, 0x6a, 0xcb, 0x09, 0x35, 0x27, 0xb3, 0x8d, 0xd4, 0xcb, 0x04, 0x0f, 0x85, 0xa6, 0x19, 0x58 } },
{ "\x1c\xc2\x9f\x82\xba\x82\xa2\xf3\x43\xc2\x52\x6b\x18\xda\x65\x02\xa2\xdb\xb7\x94\xf6\xf3\x03\xf6\x24\xe8\x3e\x43\x1d\xc2\x90\x54\x3e\x86\xef\x4d\x7d\x1c\x5a\x54\x2f\x52\xb0\x5d\x73\xf9\x2a\x21\x3f\x2d\x7b\x2d\x05\xb3\x83\x17\x01\x7b\xaa\xcb\x22\x44\xdf\x03\x92\x5e\x2d\xd5\xe4\x8b\xf7\x66\xd9\xff\xd0\xbc\xcf\x87\xf9\xb2\xc7\xc6\x78\x04\x11\x99\xd1\x8a\x96\x9c\x64\x82\xc2\xc6\x62\x06\x0c\x30\x6d\x9c\x23\xf3\xcc\xec\xb6\x2e\x48\x8c\xe0\x27\xa6\xf8\x2f\x89\xc0\x9d\x50\xcb\x42\x14\x58\xb1\xfe\x6c\xb1\xec\xed\xa0\x9f\xdd\xc3\x27\x6e\x78\x2d\xab\x3a\x43\xaa\xd1\x5b\x7c\x03\x37\x6b\x5a\x68\x7c\xf4\x6c\x29\x78\xbb\x1e\x0e\x8c\xa3\x5b\x53\x15\x40\x4c\x43\xfa\xf9\x3d\x6f\x65\xd9\xa1\x8b\x8f\xf2\x2c\xe6\xb3\x6e\x1e\x85\x24\x33\xa0\xdb\x7a\x32\x46\x26\xe8", 188, { 0x6e, 0xa3, 0x09, 0xda, 0xde, 0xad, 0x44, 0xc1, 0x7b, 0x3f, 0xcd, 0x70, 0x94, 0x3c, 0x2a, 0x39 } },
{ "\x4d\xb5\x68\xa9\xe3\x5e\x00\x99\xf9\x68\xe5\xe0\x06\xac\x80\xc9\x5b\xda\x23\xb1\x2d\xb9\x29\x1a\x40\x8b\xaa\xdf\x32\x8e\xdb\x82\x62\x9f\x01\xa1\x7b\xfa\x10\x88\xa1\x1c\xe4\xdf\x36\xc9\x1b\x84\x49\x2a\x8d\x74\x9b\x0e\x69\xe2\x50\x51\xed\x6a\x2f\x5a\xc1\x5f\x96\xbd\xf1\x40\xfd\x02\x85\x4a\x24\x7d\xbf\x27\xdc\x5c\xf1\xc4\x6f\xbf\xf8\x40\xae\x01\x59\x00\x95\x16\xf6\xdf\x9c\x0d\x6c\x83\x9d\xb4\x0c\xcb\x5c\x5a\x75\x64\xb8\xaf\xcb\x7b\x6d\xc8\x96\x8c\x84\x3f\x39\x59\x58\x78\x0c\xf7\x45\xfe\x19\x02\xee\x0b\x70\xf1\x2b\x26\x27\x84\x8e\xe7\xf2\x5a\x90\xd6\x5b\x9f\x5d\x72\x9e\xe2\x39\x4d\x23\x04\x8d\x54\x98\x4c\x92\x2d\xaa\x98\x99\xb9\x61\xaf\xf4\xb9\x16\xb1\x85\x8c\x11\xaf\xe1\x16\xc3\x57\x1f\xbd\x9c\xc8\x43\x8d\x8a\x84\x42\x7b\x10\x83\xb2\xc7\xfe\x64\x10", 189, { 0x84, 0x35, 0x9a, 0xad, 0x8c, 0xa1, 0x8d, 0xfc, 0x2a, 0xaf, 0xbc, 0xa9, 0x59, 0x79, 0xf1, 0xae } },
{ "\x7a\xd3\xbf\xad\x0f\xab\x95\x35\x2e\xe6\xe9\xdd\x93\x58\x68\x29\x0e\x26\x43\x57\xa3\x43\x1e\x6b\xd1\x87\x20\xd8\xf0\x69\x2b\xc8\xb3\x59\x25\x08\xce\xbd\x75\x93\xb1\x85\x8a\xba\x71\x6d\x95\xec\xc8\xcf\x57\x28\x33\x22\x14\xfc\x39\x13\xa7\x38\xcc\x3e\xaf\x34\xc8\x89\xe7\xbb\x98\x1d\x94\x93\xf8\x02\x17\xaa\xd5\x56\xa2\xdf\x50\x2a\x07\x69\xe1\xf9\xac\xe8\x98\xc6\x9b\x06\x7f\xd1\xb6\xca\x1c\xf5\x08\x79\x13\xa1\x36\x29\xe7\x71\x14\xe3\xe1\x68\x53\x74\x09\xcc\x59\xd0\x51\x9f\x29\x24\xd2\xb5\x81\x77\x2d\x77\x03\xfc\x14\x32\x8b\xf6\xe1\x1c\x9f\x48\x63\xe5\x04\x98\xba\xf6\x6d\x1f\x58\x4a\x4f\x11\x27\xe5\xd0\xa3\xc9\xf4\xdd\xbd\xfb\x83\x5a\x3e\x13\xd5\x75\xe6\xa8\x63\x14\xe5\x50\x74\x6c\xfd\x84\x55\xb3\x56\xa0\x36\xed\xde\x07\xa1\xe3\x5a\x64\x97\x33\x0e\x0b\x49", 190, { 0x15, 0x6b, 0xec, 0xbe, 0xf4, 0xb1, 0x19, 0x29, 0x9b, 0x9c, 0x5a, 0x59, 0xd9, 0x1c, 0x8f, 0x68 } },
{ "\x80\x05\x7d\x70\xc9\xfe\x0a\x49\xd8\xf3\x91\x31\xd1\x47\x63\xd8\xea\x8b\x46\x25\x39\xed\x95\xa6\xa8\x3d\x85\xb2\x06\x9d\x82\x1e\x38\xc5\xb8\x98\x8d\xf0\xad\xa3\x2f\x67\x0b\x2f\xb4\xa8\x7c\xd4\xd4\x33\xae\xab\x36\xf0\x76\xc9\x63\x40\x10\x59\xdc\x9e\x84\x55\x46\xf2\x25\xe1\xc3\x72\x34\x44\x5d\x35\xa1\x83\x9c\x56\xf1\x9c\x37\xc1\x3b\x5c\xe7\x9d\x4c\xdd\x56\x56\xea\xa0\x37\x45\x36\x43\x6f\xff\x2a\xdf\x70\x37\x42\x00\xa7\x51\x38\x42\x0b\x84\xe7\x84\xd6\xaa\x51\xcc\x51\xa7\xbc\xb7\xc7\x74\xf7\x9b\x9b\xc7\xba\x3b\x76\x61\x2b\x25\x98\xf3\x7d\xac\x50\xae\xb3\x7e\x87\x66\x83\xe1\x76\x61\x55\x8e\x78\xc2\x6a\x2a\xf9\x8d\x1a\x5d\x8e\x56\x36\x91\x1f\x8b\x8c\x49\x3f\x5a\x88\x1b\x9c\x40\xf7\x4c\x92\x30\xe7\x06\xda\x5c\xab\x38\x5f\xa2\xfb\x0a\x31\x96\x37\xfc\x92\x28\xcc", 191, { 0xaf, 0x3e, 0x62, 0xef, 0x2b, 0x45, 0x27, 0x17, 0x20, 0x4f, 0x50, 0xb2, 0xb9, 0xa7, 0x9b, 0x48 } },
{ "\x9a\x2e\xf2\xaf\xde\x68\x21\x07\x22\xd0\xfd\xe7\xc0\xb0\x16\x39\x48\xc6\x0d\xb6\x5d\x0b\xfc\x11\x2c\xb2\x83\x34\x8a\x2c\x70\xa1\xa9\x68\x0a\xfb\x77\x19\xfa\x9e\x94\x2f\xd7\x68\xed\x67\x4c\x9b\xfa\xfe\xe8\xdb\x90\x81\x82\x51\x63\xc0\x47\xf2\x1c\xe0\x62\xda\x13\x90\x46\xd2\xaa\x54\x9b\xf1\x45\xfd\xc3\x5e\xe9\x39\x07\x33\x70\x46\x63\x7d\x66\xc2\xea\x60\x84\x9f\xd7\x57\xc7\x32\x32\x9a\x2b\x6a\x1c\x98\x09\x1d\xb7\xa3\x0a\x7e\xb4\xac\xc7\xab\x7a\x23\xff\x63\xf0\x00\x74\xd5\xe7\xe0\x62\x93\x27\x47\x9d\xa6\x14\x04\x46\xbb\xfd\xb2\x29\x7a\x02\x5a\xb6\xf7\x9f\x36\x9a\x41\xd9\x91\xfa\x18\x03\xca\x4c\xbd\x2d\x77\xd1\xe1\x2a\xa5\xb3\xbf\x3d\x3e\xb7\x45\x60\x84\x9e\x6d\x30\xb9\x1e\xab\x78\xe1\x78\x7f\x58\x9f\xb6\x2a\x11\x3c\x83\x7d\x70\xc7\xa6\xcc\xd5\x56\x1d\x02\xe0\x6a", 192, { 0xc8, 0x67, 0xcd, 0x31, 0xf4, 0xb8, 0x95, 0x60, 0x67, 0xc3, 0xcd, 0x10, 0xe4, 0x31, 0xe5, 0x8e } },
{ "\xd2\xce\xb7\x71\xfc\xfc\xf5\x64\x15\xde\x32\x91\x73\xe8\x2b\x73\x86\x5c\x8e\xbd\xad\x1a\x65\x76\xb5\x99\x1c\x3d\xee\xf3\x30\xae\xec\x1a\x76\xdd\xd7\x28\x06\xff\x6f\xd5\xc2\x29\xa1\x02\x86\x30\xbe\x72\xee\xaf\x5e\x98\xbe\x26\xd0\x8a\xf2\x3f\x3c\x15\x63\x2d\x58\xeb\x13\x2d\xc3\xf8\x15\x2e\x01\xd3\x1c\x8d\x14\x4f\x9e\xf9\xb3\x30\xa4\x76\x2a\xfa\x31\x7d\xcd\xe5\x4c\x40\x1a\xee\xac\x0a\x6a\x07\x00\xc5\x54\x6f\xd8\x96\x9f\x1c\x33\xa3\xe1\x54\xa6\xf4\xb8\x5a\x25\x77\xa4\x46\x71\x1d\x80\xee\x1e\x23\x9b\x00\x40\x6b\x77\x32\xb2\x08\x1d\x90\x02\xd9\x1b\xf4\xfc\x4c\x1c\x94\xd1\x44\x22\xb5\xe4\x15\x62\x7e\x32\xac\xbc\xe7\x5b\xcc\xd5\xd0\x51\x73\xd3\x2e\x9c\x5b\xb4\x60\x47\x9c\x4b\xa0\x6b\x6e\xd9\x94\x55\x0d\x44\x04\x57\xb5\xf6\xa7\x28\xcd\x55\x16\xf8\x20\xda\xe2\x15\x46\xd0", 193, { 0x0e, 0x63, 0xa1, 0x87, 0xec, 0x2a, 0x99, 0xd9, 0x1d, 0xec, 0x28, 0x37, 0x05, 0xb3, 0xe9, 0x44 } },
{ "\xc1\x33\x21\xab\xe3\x5b\x83\x03\x63\x73\xed\x2b\xd9\x66\x72\x72\x0c\xef\xbb\xf6\xc4\x07\x23\x20\xbe\xe9\x02\xbf\xb9\xbe\x08\xc4\xae\xeb\xbf\x98\x1c\xf3\x32\x16\x81\x09\xac\x28\xe5\x36\x99\x01\x8f\x23\x8a\xf4\xdb\x84\x54\xf0\x24\x21\x57\x99\xdb\x82\xdc\x03\xb9\x37\x98\x0d\x11\xc1\x4e\x58\x3e\x21\x90\xe3\x3c\xfa\xa5\x44\x53\x65\x86\x85\xee\x51\x03\x99\x8a\x95\x03\x7a\xca\x94\xe8\xe0\xa7\x8c\x11\x9f\xc4\x7b\x79\x9e\xdf\xae\x7c\x6f\x64\xe7\xba\x90\x1c\x2a\x14\x3d\x02\x24\xd4\xf0\x36\x39\xe9\x77\x0b\x29\x4f\xaf\x3b\x5d\x8d\xe2\x3a\xd4\x58\xbb\xa7\xa5\x55\xe2\xdf\x30\xfc\x38\xac\xe5\x98\xb0\x1b\xb0\x6b\xc8\x6b\x00\xee\xa3\x21\x73\x35\xc5\x39\x20\x71\x6a\x77\x80\x90\x83\xdb\xf9\xff\x4e\x32\x09\x8f\x91\xc9\x0b\x75\x72\x3d\xa9\x6b\xf6\x6e\xdf\xf5\x14\x92\xa7\xbe\x75\x65\x47", 194, { 0xa3, 0x61, 0x94, 0x86, 0x67, 0x56, 0xab, 0x69, 0x85, 0x9b, 0xbc, 0xa2, 0x1c, 0x09, 0x1e, 0x31 } },
{ "\x21\xcb\x7e\x33\xc3\xcb\xbd\xa0\x5d\xc8\xe1\xa6\x97\xee\x36\x10\x10\x17\x6b\xc4\x7a\x4d\x82\xc9\xe3\xdd\xe0\xfa\x0e\x14\x84\x46\xff\x99\x54\xa1\x96\x66\x93\x8b\x53\x65\x70\x3b\x38\xa3\xb7\x68\xcc\x33\xaa\xb3\x3b\xa2\xeb\xb4\x9b\x12\x90\x9f\x49\xf5\x59\x93\x72\x68\xfd\x7f\xae\x29\xa0\xb1\xc6\x37\x62\xfc\x96\x05\x11\x86\x0e\x5a\xfe\x2c\x52\xc8\xed\x92\x01\xc6\x26\xca\x93\x6c\xa8\x9f\xdc\xcb\x7d\x80\xad\xe7\x29\x04\x9a\x53\x3c\x1e\xd5\x67\x07\xde\x39\x1f\x6b\xe1\x63\x93\xcd\x57\xfb\x0f\x25\xaf\x11\xce\x36\xe1\xa1\x58\xd8\x57\x39\x75\x71\x79\xb2\xcc\x82\xd4\x19\x1d\x5d\xe6\xb2\x18\xf5\x88\x12\xd8\xce\xf8\x6b\xff\x13\x82\xe5\x6e\xc6\xcb\x27\xa1\x11\xba\xf4\xa6\xbc\x04\xf2\xb8\xb8\x52\x87\x7c\xd8\x10\xdc\xd7\x9f\xd4\x03\x6a\x34\x69\x35\xab\x72\x78\x34\xa1\x1c\xd2\xcf\x3c\x2e", 195, { 0x6f, 0x1c, 0xed, 0x06, 0x68, 0x7c, 0x13, 0x93, 0x1d, 0x84, 0xbc, 0xd5, 0x40, 0xbb, 0x5a, 0x78 } },
{ "\x5c\x84\x4e\x4e\x98\x3f\x2a\x61\xcc\x41\xd8\x3a\xd1\x1c\xf1\x6e\x79\xda\x1d\x43\x9e\x3e\x27\xc7\xc3\x22\xba\xfc\x6a\xff\xbb\x31\xf2\x8b\x42\x6c\x29\x7d\x35\x03\x76\x6c\x83\x4a\x9c\xd5\xfb\x66\x2c\x3c\xc6\x40\x8a\x69\x87\x95\x99\xd3\x0e\x2b\x06\x1b\xb3\x1e\x2e\xaf\x55\x59\xad\x8f\xef\x20\x84\x2c\xd3\xc9\xe6\x6c\x87\x8b\x9f\xcb\x39\x6e\x22\x9b\xdf\x62\x2d\x6c\xef\x6c\x1b\x86\xb8\xfb\xc6\x93\x5c\x59\x16\x5a\x6a\x3d\x2b\xa6\x1c\x7d\x23\x45\x2a\xe0\x88\x2c\x48\x11\x59\xb8\x43\xb0\xb3\x0f\xb4\x83\x1c\xa5\x5e\xca\x6c\xda\x2a\xb0\x59\xc1\xbc\xdd\x9d\xfc\xb1\x28\xc6\xc3\x78\x6a\x9a\x03\xca\x6e\x24\xa3\xc7\x04\x5f\xe1\xae\x35\x7e\xdb\x39\x90\xd6\x2a\x93\xa3\x69\xa9\xf7\x86\x10\x53\xe6\x91\x44\x4a\x04\x2d\x89\xf4\x90\xc8\x77\x40\x7e\xe2\x66\x07\x33\x45\xb5\x8d\xdd\x51\xb7\x26\x6c\x75", 196, { 0x26, 0xa3, 0xf1, 0x62, 0xb9, 0x04, 0xc2, 0x92, 0x0c, 0x08, 0x6f, 0x8a, 0x4a, 0x9e, 0x80, 0xaf } },
{ "\x3b\xf5\x30\xba\xb7\xd0\x10\x79\x11\x3f\x64\x2d\x09\xa4\x70\x63\x45\x74\x7e\xce\xfa\xa3\x97\x77\x35\x1e\xdd\x11\xc4\x72\x88\x6a\xc3\x8a\x7b\xfe\xc6\x95\x82\xa6\xa0\x06\x2b\x6d\xce\xb5\x3e\x83\x83\x23\xda\x4b\x51\xda\x2d\x8f\x71\xf3\xcf\xd3\xaf\xb2\xbc\xc7\xf5\x4b\xec\xd6\x72\xc8\x91\xdb\x66\x02\xec\xf3\x8d\xcc\xcc\x6d\x25\x30\xa5\xde\x9e\xd1\x49\x52\xde\x6f\x45\x9d\x2d\x89\xdb\xcf\xc4\x1d\x97\xc5\xed\x8b\x90\xdc\xd6\x98\x3d\xc1\xf8\x8e\xf1\x64\x1f\x80\xf4\x0b\x15\xaa\x40\x83\xef\xf7\xd5\x71\xf3\x9d\xb9\xc6\x24\xe4\x90\x50\x6d\x04\xd3\x6e\x66\x2b\xb0\xdc\xc5\x9d\x7e\xac\x64\xf6\xdb\x56\xea\x8b\x65\xe6\x19\xef\x11\x53\xb4\x91\x2b\xf0\x0b\x82\xea\xfc\x24\x55\xaf\x54\x88\x20\xda\x48\xa7\x9e\x49\x8a\xe8\x76\x6f\x42\x51\x97\x0c\x3f\xd0\xba\x8e\x49\x24\x09\x04\x92\x6b\xde\xbb\x4a\x48", 197, { 0x8e, 0x2a, 0x66, 0x4b, 0x0e, 0xe7, 0x3f, 0xaa, 0x95, 0xa8, 0x6c, 0x1d, 0xeb, 0x22, 0xdb, 0xd8 } },
{ "\xa4\x77\x52\xb0\xda\x4f\x08\x52\x36\x26\x41\xa1\xe6\xc2\x55\x7f\xf1\x8a\x53\x87\xbc\xe0\x55\xf7\xa9\x19\xef\x39\xda\x15\xc1\x0c\x13\x80\x2c\x53\xbe\xa4\x21\x7a\x07\xe8\x15\x81\x27\xe8\x11\xa7\xbf\x32\xe5\xb3\x5a\x9b\x7c\xe1\x15\x3d\x4b\x68\x5b\x0e\xe4\xa4\xc8\x1d\xa7\xe5\x2f\x6b\x97\xd4\xb7\x63\x4a\x7c\x20\xf7\xfa\xfc\x23\x59\xba\xc8\xf8\x53\xc2\x97\xf1\x44\xeb\xed\x44\xb8\x36\x45\xe6\xa2\x86\xda\x92\x38\x6e\x12\xe8\x6b\x25\x88\xb3\x02\x96\xb4\x43\x52\x94\x39\xf9\x9c\x2b\xcc\xe1\x03\x12\xbc\x79\x28\x3c\x21\x90\x64\x8d\xa5\x4a\xa1\xaa\xea\x40\xd6\xe9\x97\xc4\x1d\x68\x02\x42\x72\x39\x7b\xc2\x0a\xbb\x33\x89\x4d\x04\xc8\xdf\x72\x7a\x6e\xec\xb6\x81\xbb\xbc\x39\x4e\x0f\x62\x75\xda\x9d\x38\x5b\xf3\x1b\x44\x0c\x6c\x02\xb6\x31\x75\x82\x8d\xf7\x05\x06\x5a\xaa\x73\x5f\x1d\xed\x25\x8f\x4b\x93", 198, { 0xfd, 0x93, 0x65, 0x5a, 0xe8, 0xb0, 0xde, 0x62, 0x06, 0x84, 0x76, 0x4f, 0x5b, 0x47, 0xf7, 0xd4 } },
{ "\x5f\x35\xf1\x1e\x3d\x90\xf2\xd2\xbc\x31\x6c\x74\xf2\x42\x39\xa4\x5e\x6c\x92\xd4\x5a\x6a\xcd\xe4\xad\x28\x47\x5c\x3d\x97\x5c\x45\xe1\x10\x93\xa4\x55\x62\xd7\x94\x46\x7a\xe0\xff\x8e\xae\xb1\xf9\x7a\xa6\x3a\xb9\x46\xe7\x1d\x34\xaa\xfd\x8d\x57\x8d\x45\x53\xe1\xd8\x54\xeb\xdc\x66\x07\xcb\xb6\x17\x28\xc3\x00\x04\xba\x7f\xc2\xcc\xe2\x2a\x78\x0d\x72\x2d\xae\xef\x12\x15\x33\xda\x0d\x93\xfd\x47\xb6\x9c\x99\xb4\x75\xb1\x4c\xb1\x71\x39\xcc\x18\xdb\x0a\x94\x5a\xd5\x06\xe8\xf3\xfe\xe2\x65\xff\x9c\x02\x44\xe7\x64\x80\x2b\x34\xe8\x4c\xaf\x84\x9e\x6d\x6b\x99\x88\x66\xb6\x8f\x85\xb3\x03\x26\x34\x73\xda\x3d\x81\x1f\x6f\x60\xcd\x78\xdc\x78\xbe\x7f\x00\xa1\xa0\x9e\xf3\x19\x76\xe4\x25\x53\xa2\x6e\x12\x2b\x2c\xe1\xa3\x35\xb2\x13\x25\x2e\xed\xc9\xde\x94\xdb\x9b\x51\x51\x8e\xf4\x10\x93\x91\x36\x39\xb7\xf2\xfa", 199, { 0x05, 0x5a, 0x5c, 0xa0, 0xac, 0x17, 0x39, 0x3b, 0x11, 0x90, 0x08, 0xf7, 0x91, 0x12, 0x33, 0xbd } },
{ "\x8e\x63\xf1\x75\x35\xb3\x43\xf6\x08\x8a\x03\x8b\x9a\x0b\x36\xc4\xc8\x20\xf9\x8f\xf7\x37\x4a\x42\xef\x0d\x6f\xb8\x53\xa2\x06\x92\xbc\x4c\xaa\x9f\x72\xe8\x3f\x56\xc6\x2b\xdb\x80\x0f\x16\x51\xf2\x3f\x88\x5b\x78\x21\xed\x63\xce\x31\x15\xee\xc8\x17\x1f\xa6\x91\xc2\x94\x02\x10\x1e\x90\x94\x66\x71\x1a\xef\x94\x57\x95\x32\x3a\x58\x50\x36\x7a\x23\x38\x50\xfb\x6a\xad\xc3\x09\x59\x71\xc5\xda\xb3\xbd\x2c\xec\x8c\x6e\xb5\x89\x9d\x5c\xf1\x6c\x47\xcb\xcd\x7e\x27\xfb\xbc\xb9\x52\xda\x83\x2e\x2d\xda\xa0\x21\xec\xdd\x58\x52\xa5\x4b\x5c\x57\x10\x46\x17\x24\xdd\xf5\x97\xad\xb2\xfd\x15\xa2\xc0\x0e\x22\x59\x01\x99\x71\xca\x10\x9f\x3b\xb3\xa4\xa5\x52\xdc\xaa\xc4\xc6\x75\xff\xdd\x2e\x9b\xc7\xe9\x94\xf9\x6d\x6e\xff\x8b\x37\x0d\x9d\x7e\x84\x38\x8d\x34\xa5\x02\x47\x63\x56\x0f\xa9\x5d\xd8\xaa\x9e\x6a\xac\xf5\x6d\x51", 200, { 0x09, 0x82, 0x5e, 0x9d, 0x4d, 0x7d, 0x4f, 0xf4, 0xcb, 0xc6, 0x86, 0xe7, 0xc4, 0xdb, 0x1a, 0xb4 } },
{ "\xed\x3b\x9c\xf6\x4b\x62\x7e\x1d\xa0\x7c\x60\x4d\x30\x7c\x4c\xcf\x82\x05\x78\xd6\xd5\x5e\x4e\xb8\x41\x82\x19\x5a\x6c\x55\x49\xab\xe5\xf0\x63\x47\x20\x1d\x88\x3b\x0e\xde\x9f\xe8\x59\x28\x22\x00\x39\xad\x82\xae\xf6\xd7\x38\xd2\xfa\xd0\x69\x6a\x92\xbe\x35\x0c\x41\x0c\x9d\x8f\xc1\xe4\x0e\xca\x97\xb9\x8e\x74\x51\x00\x82\x2a\x5f\xfe\x19\x90\x8c\xbc\x59\x8f\x17\x18\xc4\xbc\x72\xf6\xa6\xd8\x96\x93\xfe\x74\x01\xfa\x07\xad\x4d\x8f\x62\x15\x6e\xc8\xe1\xb2\x88\xfc\xf2\x20\x6b\x53\xa6\xd1\xac\xde\x5d\x75\x61\xc0\x10\x75\x78\x89\x3b\x98\xb4\xa3\x65\xc9\x46\xe5\x4d\xf0\x04\x45\xb3\xfc\x48\xaa\xc0\x02\x68\xe0\x12\x7f\xcd\xa5\x68\xb9\xb2\xe0\xe7\x44\x7b\xf1\x07\xa1\xaf\x23\x1d\x01\x94\x3e\x85\x27\x66\x3a\x6b\x6b\x33\x0e\x36\xda\x56\xa5\x93\x7b\x8e\xf2\x19\xad\xba\x1a\x9e\xac\x33\xd0\x16\x32\xc6\xbf\x22\x3a\x4c", 201, { 0x67, 0xe6, 0x8a, 0xc1, 0xa4, 0x4c, 0x07, 0xb7, 0xd2, 0x7e, 0x82, 0x85, 0x0f, 0x8c, 0x27, 0xed } },
{ "\x20\x3d\xdf\xe8\x6b\x7e\x63\xdd\x2a\x0a\x4c\x0a\xe8\x1a\xa9\x02\x49\xa5\x73\xcc\x33\xaa\x0e\x34\x2a\x1c\xef\xcc\xba\x69\x57\x82\x0d\xa9\x3d\xdf\x9c\x60\x49\xda\x02\xf0\xfd\x57\xec\x9e\xee\x3f\x2d\x3e\x30\x3c\xee\x7e\xd1\x11\x03\xcd\x7b\x95\x58\xe6\x3d\x4a\x8a\xfd\x63\x9e\x92\x84\x81\xbd\x9c\x9a\x8f\x11\xf6\x11\x2e\x57\x24\x1a\x09\x5f\x10\x8f\x57\x60\x5e\xdd\x7c\xf5\xde\x8c\xcf\xb8\x1b\x6d\x77\x7a\x10\x5f\x6e\x1c\xfa\xbd\xa7\x0d\x49\x68\x4c\x60\xb0\x6c\x20\x88\x5b\x51\x04\xb4\x40\x01\x95\xc1\x8f\x51\xf2\xe0\x43\x2d\x9b\xc6\xda\x65\x75\x89\x21\x0e\xea\x1e\x29\x96\x2d\x6c\x56\x68\x9b\x0c\x95\x38\x3d\xa0\xad\xeb\x6a\xcd\xaf\x26\x89\xd6\x88\x72\xf5\xd6\xb5\x09\xf9\x9a\x15\x40\xfa\x19\xda\x90\xb4\x90\x99\x42\x9c\x3e\x82\xb7\x65\xb9\xa9\x51\x9f\xec\x82\x02\x79\xae\x6c\x6f\xa7\xff\x64\xc0\x5f\xe1\xda\x07", 202, { 0xee, 0x3e, 0xeb, 0xd8, 0x7a, 0x38, 0xa2, 0x68, 0xcc, 0xf5, 0x88, 0x6a, 0xe0, 0x3a, 0xba, 0xac } },
{ "\x7d\xd1\xa0\x4a\xc6\xe0\xff\x2e\x49\x73\xe4\x42\xe1\x93\x38\xe6\xd8\xf2\x4d\xd7\xa6\x7b\x74\x58\xd7\x94\xab\xfd\x0a\xf3\x73\x17\x15\x1a\xc0\xb7\x46\x70\x0a\x61\xc5\xfe\x81\x4f\x15\xc3\x5d\x5e\xb9\xb4\x53\x99\xf3\x53\x23\x61\xa7\xea\x4e\x36\x5f\x64\xe6\x24\x68\xc9\x7d\xcc\x19\x54\x3f\x0e\x33\x33\x1c\x50\x64\xdb\x1d\x6e\xe6\x05\xd8\x3e\x44\x48\xff\xbe\x3d\x54\x12\xdc\xc1\xc8\x35\xe2\x18\xb1\x1c\x7d\x22\xa0\x22\x68\xb9\x67\x79\xba\x32\x6f\x7c\xc8\x03\xb9\x21\xb8\x7e\x6f\x8a\xa3\xbc\x26\xba\x66\x95\xb3\x64\x06\xcb\xa7\xdf\xbd\x46\x68\x37\xa5\x7a\xed\x5d\x00\xe4\x15\x7e\x22\xb4\xa5\x71\xfb\x85\xdd\x49\x45\x47\xb5\x0a\x46\x3e\xb9\x79\x42\x23\x7e\x0c\x81\x68\xc0\x01\xf8\x99\x19\x8b\x97\xde\x60\x26\x2f\x9d\x9c\x0c\xfd\x3c\xa4\xc0\xd7\x04\x54\xc7\xf1\x21\x6e\x76\x4c\xc6\x0a\xe7\xbd\x6d\xbb\x05\x96\x3c\x40\xc7", 203, { 0x5d, 0x3b, 0xc3, 0x5e, 0xdc, 0x45, 0x22, 0x74, 0x96, 0x59, 0x61, 0x19, 0xb7, 0x97, 0x5f, 0x60 } },
{ "\xf3\x83\xe4\x7a\xa2\x62\x73\x36\xb0\x88\xd9\x72\x8c\x16\x58\xb4\xdb\xa1\x65\x61\xd7\x56\x20\xb2\x64\x39\x6f\xc7\xb1\x86\xb6\xd6\x87\x38\x34\x7c\x32\xa7\xfd\x34\x08\x4c\x90\xe5\x9a\xa1\x14\x95\x77\x23\x34\x3c\x97\x79\x93\xb3\x6b\xaf\xee\xcb\x7f\x9b\xcd\x7a\xc8\x60\xe6\x31\x90\x10\x0e\x49\xfb\x6d\xdc\x9b\x35\xc8\xdc\x2e\x3a\x0b\x6d\x0b\x41\xd2\x38\x2d\xc6\xb3\x4d\x95\x32\x9e\xdc\x79\x2a\x60\x8c\x9c\x71\x42\x7b\xb9\x7b\xce\xd3\x19\x8f\xb1\x05\x44\x97\xbc\xa5\xd4\x87\x05\xe2\x65\x68\x2a\xa0\xa8\x00\xb5\x34\x97\x20\x9b\xbb\xc3\x8a\xdf\x17\xc8\x7c\x54\x88\xe3\xdd\x7f\xe3\x9a\x03\x9a\x71\x99\x1f\xb5\x66\x9d\x46\xf8\xfb\x89\x1c\x03\x2b\x96\x1f\x76\x08\xa8\x8d\x8c\xb7\xbb\xf3\xe2\x0e\x7c\x54\x56\xc8\xf4\xf2\x0b\x63\x5f\xbc\x88\x97\x1b\x53\x00\x72\xbc\xbb\xac\x14\x3c\x9b\x54\x05\x50\x30\xee\x2e\xd5\xd4\x5d\x7b\x69", 204, { 0x91, 0x24, 0x52, 0x7f, 0x19, 0x7f, 0xae, 0x81, 0x27, 0xa6, 0x98, 0xe8, 0xa1, 0xc3, 0x1d, 0xf4 } },
{ "\x27\x5a\x0a\x17\xd7\x70\x10\x2a\x12\x21\x49\x22\x85\x26\x19\xc5\x0f\xd4\x44\x4c\x07\x9a\x47\xbe\x26\xa7\x51\x5b\x13\xa8\xe1\x2e\x8a\xaf\xfd\xc6\x28\x2f\x0c\xfe\xd5\x24\x51\xf7\xce\x50\x04\x27\x4d\x9f\x0e\x8b\xd8\xac\x62\xf8\x23\x5c\xf3\x8f\xa3\xa8\x55\x4f\xb1\x79\xf4\xc5\x56\xac\xeb\xde\xb9\x35\x82\xdd\x22\x5f\x47\x67\xaa\x31\xc7\xbb\x82\xed\xe9\x00\xdc\xb2\xe8\xb7\x79\x41\xeb\x50\xd0\xdc\x43\xd8\xd8\x4a\x40\xcf\x72\xf8\xb0\x18\x76\x39\xf5\x09\x59\xae\xc2\xa2\x78\xc1\x72\xdb\x03\x4b\x05\x16\x89\x56\xb7\xb4\x1b\xfc\x3f\xc4\x20\x6e\xa1\xd5\xb5\x11\xb0\xec\xbe\xc2\x24\x91\x8e\x3a\x53\x04\x2f\x8d\x90\x8d\x4e\xcd\x1d\xf1\xc6\xcb\xcd\x00\xc7\xfd\x3b\x4c\xa3\x7b\xa1\xf4\x35\x24\x56\x9e\xee\xdd\xe6\x83\x7d\xf9\xcf\xa3\x1a\xb5\xd6\x1a\x70\xda\x04\x8b\x25\x85\x41\xb8\x07\x03\x8b\x34\xd4\xd6\xd3\x2f\xa6\xd5\x74\x71\xf9", 205, { 0xaf, 0x74, 0x5b, 0xa6, 0xbe, 0xb5, 0x95, 0xdd, 0x3a, 0xa9, 0x3e, 0x1c, 0x12, 0x8f, 0x4a, 0x81 } },
{ "\x6f\x51\x62\x5a\x10\x89\x45\xae\x9c\xda\x85\x1d\x18\x8f\x28\x99\x68\x27\x60\x0f\x33\x84\x40\x28\xe2\xcc\x8c\xbc\xc8\xe0\xa9\xd4\x5c\x77\xb3\x5a\xa1\xd6\xad\x5f\xa6\x2e\xd3\x09\x29\x92\x0c\x17\x57\x93\x7c\x13\xaf\x7a\x35\x13\x04\x21\x0d\x2b\xa1\x6e\x8a\x72\x86\x6d\xfa\xaf\xd1\x09\xa0\xa1\x38\x67\x08\xe8\xb3\xe0\x7c\x93\x37\x98\x8f\x47\x9c\xbf\xd6\x08\xa0\x64\xb7\xa7\x04\xf1\x59\xc8\xd4\x47\xbb\x8f\xc4\x77\xe0\xe7\xb6\x19\x28\x6f\x58\x4d\xbb\x01\xeb\x4c\x1e\xdf\x1e\xa9\xe7\x7c\x18\x2a\x8d\xe5\x95\x3d\x59\xca\x28\x19\x79\x2d\x9e\x72\x33\xa6\x83\xd8\x37\x50\xbe\xad\x0d\x54\x57\xc1\xad\x10\x5a\x8c\x2d\xe3\xd3\x07\x95\x97\xf8\x27\xce\x6c\x66\xf7\xb9\xbd\x84\x51\x5d\x51\x04\x38\x38\x41\x88\xd5\xb6\x81\x61\x0d\xbf\x0c\x72\xbb\x6b\xb0\x33\x8f\xd1\x73\xd1\x82\xfd\xa1\x73\xf5\xff\x73\x98\x65\x20\x5e\x9c\xcd\x30\xf5\x5a\x99", 206, { 0x69, 0x2b, 0x76, 0x76, 0x70, 0x75, 0xc8, 0x00, 0xd0, 0x62, 0xd9, 0xc8, 0xd8, 0x37, 0xe6, 0xe6 } },
{ "\xfb\x7b\xea\x42\xda\x09\x8b\x8a\x65\x58\x9c\x56\x46\x2c\xc5\x23\x29\x5e\x33\x26\xcf\x84\x00\x04\x42\x3e\xb0\x2b\x23\x20\xd7\xcb\x1f\x37\xd9\x75\x80\x3a\xca\x4e\xe0\x4f\x73\xef\xf8\x76\x76\xdd\x96\x96\x89\xa0\xad\x22\xde\x82\x86\x68\xa3\xe6\x15\x76\xa5\x42\x66\xa9\x10\xba\x36\xd3\x51\x5a\x9e\x08\x1c\xf0\xea\x06\x89\x84\x88\x3e\x59\x75\x1c\x83\x57\x32\xb1\x4e\xda\x91\x09\xab\x67\xcf\x15\xc4\x73\x25\x80\x08\x45\x03\x65\xf8\xfa\xec\x22\x8e\xa3\xed\x44\x4a\x89\xbb\xa1\xda\x90\x68\x85\x65\xb9\xc2\x04\x74\xc1\x48\x6f\x7d\xe7\xca\xe1\x0e\xcb\x9c\xf9\x93\x72\x76\xa2\xc4\x66\xeb\x0d\xad\xfa\x84\xc0\x5b\xab\x79\xc8\x20\xa2\x0b\x0a\x84\x57\x81\xb8\xc8\x4f\xbc\xdf\x17\x05\x79\x1c\x4c\xe7\x23\x6f\x5a\x77\x53\x27\x5c\x92\xe5\xfd\x3a\xce\xb8\x3d\xf4\xfc\x01\x1f\x8e\xcd\x4c\x34\x99\x90\x11\xfc\x59\x19\xef\x94\x98\xbe\x88\x8c\x06\x7b", 207, { 0xdc, 0x06, 0x1a, 0x7c, 0xe0, 0x3b, 0x42, 0x40, 0xac, 0xe3, 0xdd, 0xf7, 0x63, 0xae, 0xaa, 0x36 } },
{ "\xf8\xd7\x4b\x35\xf2\xdc\xab\x1b\x79\xa9\x55\x29\x39\x47\x48\xc6\x80\x28\xe3\x8e\xdc\xbd\x07\x2a\x51\x24\xea\x5a\x37\xff\x7b\x14\xae\x60\x6d\xc6\xbf\xe0\xe3\xb8\x11\xcf\xb6\x8d\x45\x85\x66\xe8\xee\xd7\x9a\x2c\x30\xa5\x55\x5b\xf4\x91\xb8\x20\xc5\xca\x6e\xe8\x4a\x06\xb7\x2a\x60\x8e\x15\xc8\xd4\x73\x8d\x8d\xba\xde\x9a\xd6\x6c\x85\xb4\x4e\x22\x3a\x77\xd2\x2b\x9d\x74\x73\xc6\xf2\x91\x99\x9f\x0d\x1d\x44\xe9\x6a\x74\x6e\x14\x59\x4b\x8d\x2c\x56\x99\x35\xce\x77\x23\xd9\xc7\xfe\xa2\xb1\x4a\x0e\x92\xb8\xce\x7b\x9a\xcd\x82\xba\x93\xf9\x6e\xf7\x36\xd0\x27\x46\x67\xf0\x2e\xf1\x18\xa7\xe7\xf0\xdb\xb1\x31\x76\x08\x1e\xa6\xa8\x87\x52\x70\x68\x3b\x26\xc6\x50\x0c\x2d\x02\xbb\x8e\x11\x61\xfd\x53\x1b\x56\xb2\xca\xd1\x8b\x34\xd2\xb9\x75\x26\xdf\x3c\x92\x2d\xc7\xa6\x42\xbf\x2a\x4a\x40\x13\x7c\xc2\xbb\x38\xb1\x54\x15\x42\x83\x71\x37\x9f\x63\x57", 208, { 0x90, 0x71, 0x3e, 0x92, 0x18, 0x0b, 0x7f, 0xec, 0xf7, 0x44, 0x3a, 0xd3, 0x5c, 0x05, 0xb4, 0xfe } },
{ "\x8f\x87\xfa\xd6\xa7\x92\x5a\x2f\x63\x63\xc6\x17\xd7\x82\x1a\xdc\xc2\x48\xd8\x9f\xab\xf3\xd1\xbf\x97\xd9\x6d\x57\x64\xba\x97\xdd\xc6\x2e\x47\xeb\xdb\x3d\xad\x1a\x6c\x0d\xf7\x0a\xc2\xb6\xbf\x7f\x23\x32\x14\xa6\xe8\x70\x24\x75\x3c\x87\x83\x30\x73\x07\x1a\x07\x04\x6c\xaf\xdd\x25\xac\x0c\x23\x01\xf0\xcf\xe3\x99\x5f\xce\xd9\x34\x15\x24\xbb\x84\x32\xdc\x9a\x57\x0f\x39\x60\xf6\x8c\xa0\x79\x1e\x85\x23\x8f\x98\x63\xab\x6d\x77\xce\xc1\x05\xee\x80\xf9\x8d\xcb\x35\xfb\xc3\x94\xbf\x2f\x52\x3d\x35\x05\x4d\x83\x4b\xde\xd8\xe7\xbd\x9a\xe6\x4a\xe6\xbf\x1c\x22\x6d\x42\xd4\x56\x1e\xf6\x3f\xbc\xd7\x8e\xa2\x2c\x99\x50\xc1\x41\xbe\x59\x59\xac\x4a\x87\xc6\x34\x59\x06\xc5\x4e\xb8\x7a\x54\x54\x90\xc6\xb6\x65\x3d\x77\x92\xda\x3e\xd1\x3b\x60\x45\x74\x0b\xb7\x6d\xa9\xe8\x06\x8b\x4f\xe8\xd8\x9c\x5c\x11\xb7\x5e\x12\x39\x63\xfc\xc1\x0c\xaf\xe1\x32\x2c\xf9", 209, { 0x72, 0x27, 0x02, 0xd5, 0x86, 0x5b, 0x0b, 0x2b, 0x77, 0x13, 0x3a, 0x71, 0x39, 0x5b, 0x88, 0x5d } },
{ "\xae\xb7\x73\x44\x46\xcd\x54\xf4\x06\x6b\x5f\x25\x12\xea\xe3\xa8\xcb\x90\xad\x9a\x5c\xba\xef\xa3\x74\xa6\x3c\xc8\x0e\xda\xb0\xee\x6b\x35\x2d\xec\x22\x90\xc0\x4b\x4e\x11\x21\x9d\xe5\x0c\x59\x77\x28\x95\x7e\x0c\x36\xa6\x9a\x67\xbc\xe9\xaa\x44\xc7\x24\xc2\x8c\xba\x3f\x4e\x6c\x5b\xf2\x73\x11\x07\x0f\x93\x01\x06\x69\xef\x19\xf9\x60\x68\x1f\x70\x0a\x5e\x03\x98\x00\xb9\xb7\x11\xc2\x06\xa8\xde\xc8\xb9\xd7\x76\x26\x91\x99\xf7\xda\x19\xe7\x7a\xb6\x4a\x63\x81\xe4\x4e\xe8\x8d\x1b\x5f\xcc\xcd\x5d\xce\xb5\xf0\x6a\x20\x14\x1c\xc5\x52\x43\xf7\x60\x3e\x37\xe2\xe6\x16\xe2\x45\xa5\x0c\x28\x05\x17\x14\x7b\x12\x0b\xc1\x15\x1e\x75\x4c\xd1\x68\xce\xb4\xa7\xb6\x29\xff\xc2\x61\xd4\x9e\x40\x8a\xa7\xee\x85\x6b\xec\xdb\x3c\xc8\xeb\x9f\xec\x83\x10\xa8\x32\x4f\xbb\x98\xa1\x7d\xa4\x66\x33\xf2\xe9\xa2\x6a\x3a\xb6\xd5\x07\xb5\x90\x06\x66\xef\x3e\x59\x74\x0e\x54", 210, { 0x00, 0xbf, 0x87, 0x36, 0xd5, 0xe8, 0x54, 0x2f, 0x00, 0xca, 0x28, 0x9c, 0xe1, 0x28, 0xe5, 0x7e } },
{ "\xd3\x4e\x64\x5d\x38\x78\x24\xa4\x15\xb7\x32\x9f\xda\x8c\x13\xf9\x1f\x4d\x04\x3b\x07\xe1\x26\x16\xc2\x17\xd6\x4f\x2c\xa4\x1b\x47\x93\xb7\x39\x26\x96\x3b\x62\xa4\x28\xa8\x8c\x74\xb4\x86\x5e\x4d\x5b\x80\x44\x95\x58\x21\xa2\xac\x85\x2d\x24\xd3\xf7\x9e\x34\xb8\xc3\x3a\x85\x9f\xe4\xcd\xcc\x2e\x35\x09\x8f\xd5\x98\xf6\x15\xd8\x39\x17\x7d\xfa\xed\x1b\xeb\xc8\xb4\x3c\xdd\x7f\x5d\x38\x9c\x9b\x49\xea\xcd\xfd\x47\x85\x27\xb7\x9f\x6c\x3f\x77\x2a\xb0\x7f\x8f\x9e\x26\x35\x9d\xe8\x24\xc7\xdc\xb2\xd9\x05\x05\x04\x46\x97\xe3\x06\xdd\x95\x72\xe3\xb4\x35\xb3\xca\x66\x9f\x0f\x0e\x86\xdd\x65\x6e\xc1\xee\xb2\x6e\x8f\xf9\x5c\xe4\x95\x9f\x1b\x8f\x9b\x69\x84\xe0\x25\x61\x34\xdd\xb0\xbc\x95\x1b\x8b\xb4\x50\xe9\x4f\x74\x4f\x8c\xfc\x2d\x6d\xa4\xb1\x46\xbd\xad\x07\xb0\x74\xa1\x0b\x57\x74\xbb\xd7\xb4\x76\x57\xcc\x7f\xd2\xf5\x6e\x82\xee\x5d\xdc\x89\x41\xdc\xea\x76", 211, { 0xfa, 0xb4, 0x08, 0xeb, 0xfa, 0x54, 0xe3, 0x20, 0xe9, 0x99, 0x17, 0x29, 0x29, 0xf5, 0xeb, 0xb1 } },
{ "\x03\xac\xdd\x71\x73\x72\xc2\x3c\xc4\xe0\x6b\xa4\xa5\x7b\x07\x13\x6c\x77\x5d\x43\xde\xbe\xa5\x0d\x9d\x0f\x03\x5f\x2e\x43\xa1\x4c\xa6\xab\x67\x3e\xf1\x96\x5a\x47\xbf\xef\x8e\x94\x0c\x02\xc9\x02\x4a\x5f\xb6\xcb\x2c\xd7\xc3\x59\x11\xa3\x98\x3b\x0c\xa5\x33\x35\x8c\xc6\xa4\x71\xad\x7e\x62\xd4\x1a\x72\x49\x6c\x9c\x37\x31\x05\x44\x34\x2c\xdb\x31\xa6\x5b\x69\xff\xbe\x6d\x60\xcc\xe5\xb6\xb3\xe8\x1f\xe8\xcf\x18\x8d\x70\xe8\x87\x0c\x6f\x6f\x4a\xfc\x53\xa0\x8e\x1b\x12\x37\x61\x8f\x03\x42\x19\x02\x59\x12\x65\xe7\x3c\x4b\xee\xdc\x85\x1b\xa9\x28\x16\x87\xbc\x63\xd4\xe1\x0a\x35\x43\x56\x5d\x36\xbc\xa3\x2f\xf5\x4d\xe8\x15\x15\x2d\x95\xc9\x91\xda\x81\xd2\x1f\xd1\x19\xd2\xb2\xea\xb5\x6c\x1a\x4d\x06\xcf\xc7\x14\xac\xe4\xab\x7f\xe4\x10\x3d\xce\x5f\xa6\x99\xbf\x2d\xec\xc4\xa4\xd8\xc8\x0e\x20\x8e\x08\x90\x7e\x76\x4b\xd5\xad\x23\x8e\x95\xdc\x26\x57\x9d\xae\xbd", 212, { 0xe8, 0x18, 0xd0, 0x40, 0x9b, 0xb0, 0x23, 0xd1, 0x12, 0xb7, 0xa8, 0xcc, 0xdb, 0xb8, 0x5e, 0xb0 } },
{ "\x78\xca\x1a\xaa\x80\x33\xe3\x1f\xc4\x67\xae\xd5\x05\xcb\xc5\x3c\xbe\x26\x67\xcd\x0d\x38\xc9\x7b\x3b\x84\xae\x48\xea\x2f\x9e\xf3\xda\x01\xc6\xce\x57\x88\x6b\xae\x43\x5b\x0c\xfd\xbc\x7c\x14\xe9\x69\xd7\x39\xbf\x66\xe7\x74\x52\xc9\x78\x9d\x95\xd1\x87\xa2\x49\xab\x45\x63\xcf\xe0\x3a\x2e\x1f\x5e\x87\xd2\xd1\x20\x44\x62\x59\x7d\x08\x88\x50\x0b\x86\x83\xed\x2d\x54\xbe\x92\x40\xc7\x0e\x83\x5e\xfb\x88\xb1\xcd\xef\xfd\xb5\x08\xcd\x14\xd8\x67\x46\x02\x4e\x6d\x1c\xe8\x84\xae\xb9\xc8\x8f\x5b\xfd\x25\xc3\x6c\x15\x37\x65\x68\x86\xc8\x78\xd4\x4a\xae\xb3\x36\x11\xe5\x94\xc4\xa8\x48\x8e\xae\x77\xec\x5e\x05\x24\x1a\x7c\x46\x56\xe6\xac\x87\x94\x70\xb3\x3d\x4b\xb7\x27\xa8\xee\x15\x60\xdc\x38\x5b\x8b\x6c\x8d\x89\xdd\xb4\x7e\x2a\xe3\xc3\x6c\x4a\xf8\xe3\x43\x15\xd1\xc7\x68\x0e\x32\x51\xae\xe8\xc3\xfd\x81\x05\xfd\xed\x25\x88\x3f\x91\x19\x19\xfd\xf2\x95\x35\x17\x9c", 213, { 0xc0, 0xa9, 0x36, 0x91, 0xae, 0x59, 0x54, 0xe1, 0x90, 0xbf, 0x8f, 0x65, 0xc7, 0x08, 0x89, 0x62 } },
{ "\x33\x2e\x48\x26\xaa\xc6\xb5\x5f\xb4\x64\xa5\x17\x12\xf1\x50\x87\xa6\x52\xd0\x5e\xbd\x82\x34\x69\x6f\x4a\xb4\xdd\x1f\xa0\xfe\x44\x1e\x9a\xaf\x02\x6c\x0d\xdd\x83\x9e\xc7\xd1\x04\xdb\x64\xd2\xfa\x00\xd1\x1c\x22\xe4\x5b\xf0\x26\xb5\x73\xc5\xe9\x81\x00\x9c\x70\xd1\x61\x71\x6c\x70\xfe\xcd\x68\x49\x89\x78\x8b\x6e\xd7\x4c\x0b\x35\x55\x26\x2f\x7d\x28\xf5\xfe\x0e\xe5\xf4\x89\x1e\xea\xf4\xd2\xf0\x57\xd2\x58\x97\xac\x09\x40\xd9\x01\x60\x21\x2f\xdc\xc4\x6c\xe8\xb2\x30\x77\x6c\xfd\xc8\x24\x9e\xf6\x06\x32\x5b\xf0\x0b\x20\x20\x51\x17\xb9\xc8\x2d\x14\x41\x72\x80\x4d\x3a\x81\x08\x3c\xd3\xbd\xbd\x80\xee\x96\xee\xed\xcd\x89\xfa\xbe\x58\x9c\xa7\xe5\x0d\x03\x22\x83\x84\xe5\x93\x74\x9e\x38\x5c\x01\x4a\xc8\xef\xb9\xf3\x57\x49\x59\x89\xdf\x0f\xe8\x2b\xf3\x43\xe0\x6e\x43\xa3\x86\x4d\x3e\x9e\x5b\xad\xd2\xf4\xab\x8b\x4f\xe9\x42\xc4\x69\x25\x3e\x80\x5c\x2b\x00\x3c\x2a\x74", 214, { 0x60, 0x47, 0x87, 0xbd, 0xbc, 0x4a, 0x9d, 0xcc, 0x51, 0x19, 0xbe, 0xfd, 0x54, 0x82, 0x87, 0x82 } },
{ "\x3c\x73\x00\x28\x30\xe9\xb7\x78\xf2\x94\x76\x9a\xe1\x27\x74\x62\x3d\x3c\xdb\x7a\xd3\x1d\xc8\x3e\xd1\x2e\x6f\x36\xb8\xfb\x5c\x31\x6f\xda\xd4\xfa\x73\x3d\x5a\xba\x2c\x96\x4d\xea\x5b\xe7\xae\xf2\xb0\xe5\x00\x63\x78\xc8\x48\xce\x74\xb2\x34\x3f\xc5\xb9\x58\x59\x03\x93\x93\x0f\x6c\xdd\x90\xfc\x39\x08\x69\x60\x0c\xe0\x65\xb8\x86\xba\xe2\xf9\xf6\x3a\x4e\x68\x2c\xbe\x4f\x19\x6b\x6b\x03\x02\x7c\xd2\x61\xbb\xdf\x3e\xbe\xd4\x1d\x9c\x6c\xd2\x39\x08\x7d\xc8\x45\xf0\xa5\x8f\x10\xe7\x3d\xa2\xfd\x08\x64\x98\xef\x05\x40\x3e\x60\xcb\x62\x50\x91\xd3\x48\xd4\xdc\x08\xfc\x14\x25\x50\xd9\x36\x6f\xba\x6d\x79\x8a\x42\x7a\x0e\xea\x43\x17\x91\x94\x7a\xf4\x22\x31\xb2\xba\x25\x45\x12\x91\x92\x79\xff\xbb\xf9\x14\xaf\x5d\x16\x88\x4c\x5e\x0c\x29\xc0\x68\x42\xf8\x23\x0c\xd7\x9e\xbf\x02\xc3\x74\xbc\x8e\x8b\xbf\x6d\xfd\xa0\xf9\x35\x4f\x55\x4a\x17\xef\x7c\x16\xda\x9d\x9c\xb0\xd5\x6c", 215, { 0x9f, 0x07, 0x30, 0xc6, 0x81, 0xe5, 0xb3, 0x2c, 0x28, 0x8e, 0x78, 0x51, 0x93, 0xde, 0x42, 0x5f } },
{ "\xd0\x2c\x5c\xe7\x3f\x51\xc7\x4f\x33\xad\x1b\x00\xd8\xc3\x58\xf2\x93\x60\x5f\x5a\xfd\x0c\x7d\x70\x7b\x9b\x98\x4c\x7b\xe3\xf4\xea\x4d\x87\xa3\x4c\x9b\xf2\x87\xbe\x87\x60\x05\x35\x60\x16\x48\xd1\x00\xb2\x2f\x82\xc4\x9d\xd4\xc6\x29\x9c\xba\x90\x00\x86\x92\x45\x4d\x10\xaa\xdf\xc2\xc4\xf3\x34\xd3\x10\xad\x51\x76\x7b\xb1\x00\x79\xf2\x5b\x6a\x7f\x41\x12\xd5\x98\x9d\x75\x8d\x7c\x5e\x23\xe1\x64\xc9\x27\x45\x86\x0b\xfe\x95\x2b\x43\x47\x79\x6e\xb6\x07\xe3\x93\x26\x95\xb5\x01\x3c\x28\x80\xdd\x22\xfb\x68\x45\x2d\x1a\x23\x26\xb8\xcd\x20\xbb\x0c\x9e\xc4\xa2\x7b\x07\xcf\x9c\x8c\xbd\xdd\xe1\x50\x93\x09\x1e\xd3\x0d\xac\x0d\xae\x82\x43\xae\xba\xec\x6f\x27\xdf\x50\x15\x35\x0a\xc4\xa5\x4e\x3d\x22\x78\x55\xc0\x48\x53\xf9\x75\x08\x09\xab\x49\xb3\x3c\x3a\xc6\x3d\x43\x0c\xc6\x0d\x28\xfb\x42\x64\xc8\xc9\x49\x67\x1d\x42\x0c\xca\x99\xed\x16\x1b\xa8\x6e\x98\xa8\x58\x7b\xe2\x0f\x15", 216, { 0xa6, 0xa5, 0x85, 0xaa, 0x5a, 0xae, 0x5b, 0xdc, 0xc7, 0x29, 0x53, 0x55, 0x13, 0x71, 0x93, 0xef } },
{ "\x79\xee\x60\x8d\xb2\x17\xf0\xb2\x35\xe7\xbd\xde\x4b\x0d\x79\x10\x91\x6d\x35\x54\xb7\x52\xab\x41\xd4\xbf\x28\x9c\x63\xe3\x14\x1b\xde\xaa\x1f\x43\xf5\x70\x0a\x72\x6b\xd0\x0f\xf9\x8e\x9b\xef\x61\x51\xe5\x96\xcf\x07\x39\x6c\x82\xbe\xec\x3a\x78\x36\x8f\xb7\x30\x7d\x7e\xaf\x8b\x28\x95\xcf\xcc\x2f\x02\x0f\xbe\x66\x36\xbc\xf5\x94\xf6\x21\x2c\x32\x8e\xd1\xce\xc1\x24\x94\x90\xc8\x2a\xec\x3b\x69\xed\x42\x87\x9f\x4b\xb2\x23\x17\x81\x70\xd2\xa7\x22\xd5\xaf\x6f\x24\x0a\x91\x8b\x15\x50\x89\x72\x6b\xe9\x88\xee\xf8\xa6\x1e\xb8\x7c\x0e\x5d\xaf\x55\x28\xdc\xb5\x51\xe6\xd7\x26\xa4\x2e\x74\xf6\xf5\x85\x20\x66\x92\x5a\x18\x63\xc4\xed\x04\x7b\x3e\xda\xbd\x7c\xaf\x46\x2f\xab\x77\xa5\x5a\x9e\x76\x25\x73\xb7\xff\xba\x27\x3d\xeb\xc7\xff\x18\xca\x66\xde\x29\x35\x54\xa8\x44\x3f\x7c\xfd\xcb\x0a\x1e\x23\xe8\x75\x9c\x02\x66\xf3\xe1\x48\x2d\x77\x6e\xad\x58\x8b\x02\x51\xf5\x80\xe6\x41\x58", 217, { 0xf2, 0xd7, 0x83, 0x16, 0xed, 0x22, 0x8c, 0xde, 0xa0, 0xe8, 0x95, 0x2a, 0x6c, 0x79, 0xc1, 0x12 } },
{ "\xc0\xd2\x18\x1c\x3f\x7f\xc2\xb7\x10\xca\x5c\xb9\x14\x82\x37\xa1\xcd\xce\xfc\x5c\x50\x3e\xe9\x5b\x59\x6c\x39\x4f\x79\x4e\xa2\x84\x6a\x1c\xf8\xd7\x83\xd8\xef\x56\x46\x7f\xfd\x72\x17\xca\x5e\xe3\x08\xa1\x31\x19\xcf\x1d\xc0\x5a\xd9\x38\xdd\x7c\x77\x14\x67\x41\x37\xe4\xec\x4a\x4e\x7b\x68\xba\x30\x6f\x2c\x68\x42\xea\xda\xc0\xa7\x0d\x8d\x37\x37\x00\x15\x68\x8e\x9a\x60\xd0\x40\xaa\xf4\x23\x2a\xe0\x98\x48\xe4\x5b\x13\xf8\x52\xd0\x0c\xee\x5a\x53\x10\xcc\xb0\xc6\xb2\x8a\xcc\xdb\xcd\x9b\xfd\xc7\xe5\x9e\x97\x85\xac\xbd\x48\xc3\xa0\xa6\x89\x6d\xd1\x24\x30\xd5\xc1\x59\x22\x7c\x64\x9a\x25\x80\x46\xe7\xd3\xa1\xcc\x05\xaa\xf1\x92\x3a\x41\x18\xf4\xa5\x95\x41\xa8\x37\x82\xa5\x88\x52\x4d\xb2\x6e\xfc\x5e\xd7\x39\xc0\x0b\xef\xec\x21\x8f\x47\xf5\xbc\x19\x4e\x8a\xf9\x01\xd8\x2f\x6d\x15\x0e\x55\xd0\x12\xf6\x1c\x96\x2e\x9b\x39\x2c\xd2\xd9\xf3\xd0\x65\xc7\x47\x0d\x44\xc1\x2c\xd2\xce\x10", 218, { 0xdf, 0x39, 0x06, 0xcc, 0xa4, 0x24, 0x46, 0xc5, 0xa8, 0xd7, 0xba, 0x98, 0x18, 0xc2, 0xff, 0x44 } },
{ "\xf1\x2e\x9b\x39\xc2\xab\xcc\xdf\x49\x44\x7d\x55\x70\x03\x57\xc4\x45\x3b\x43\xf5\xf7\x5f\x39\xbc\xfd\x7e\x36\x02\x56\x5c\x4c\x6d\xeb\x2e\xd3\xae\x10\xf1\xbd\x66\x29\x67\x07\xb6\x69\xaf\x4c\x62\xb6\x80\x74\xa1\xa9\x02\x89\x83\xb1\x49\xaa\xff\x57\x6e\x18\x98\x9c\x4c\x6e\xac\x7e\xd4\xed\x59\x99\x0e\xf5\x0c\xeb\xac\x90\xa7\xb0\x4c\x29\x4e\x6c\x4b\x5a\xcf\x07\x40\xe3\xac\x12\x1c\xb6\xc5\x27\xdc\x2e\xa6\x6f\xe8\x60\x6b\x3e\x0c\x8a\x4c\xd8\x10\xa9\xc9\x57\xf5\x26\x73\xa4\xcf\xb0\x54\xa3\x3a\x25\xfc\x6b\x7a\xa9\x29\xb2\x33\xda\x12\x48\xc1\x1a\x50\xba\x35\x0b\x6a\x17\xed\x19\xfb\xcf\xcf\x30\x82\xb6\x22\x8f\xc6\x28\x08\xb4\xe4\x47\x9c\x77\xf6\x60\xa4\x33\x62\x6c\xf3\x1f\xbc\xb2\x5e\xba\x59\x8a\x58\x65\x71\x3c\x2d\x0a\xf0\x06\xd8\xfa\xd9\x3c\x81\xd5\x57\x8a\x96\xa2\x62\x55\x84\xff\x0a\x40\x12\x72\x64\xcb\x5a\xdd\x0e\x15\x66\x10\x5f\xc2\x63\x79\x30\xf7\x84\xf6\x42\x82\x76\xcb", 219, { 0x9a, 0x70, 0x70, 0xa9, 0xb4, 0x61, 0xe5, 0x09, 0x0c, 0xb2, 0x96, 0x1f, 0xf2, 0x98, 0x95, 0xe3 } },
{ "\x4a\x17\x7b\xaf\xa0\xb6\x26\x01\x80\x28\xb2\x18\xfa\xa7\xfe\xb7\x7c\x9d\x29\xa0\xfa\x85\xc6\x3f\xb7\xee\xbc\x87\xb3\x9e\xbd\xdb\xb1\xd3\x61\x22\xe4\xa5\xde\xfd\x35\x87\x66\x15\x34\xec\x1d\x8d\x98\x06\xb6\x97\x74\xa9\xa9\x22\xf9\x4e\x13\xb1\xe2\x34\x29\x47\x80\xa1\x5a\x7e\x62\x47\x01\xb8\xa0\xc1\xc4\x6c\xc4\x3c\x8c\xa2\x56\x33\x21\x7a\x03\x52\x60\xe6\x69\x7c\x1c\x77\x82\xe8\x8f\x55\xaa\x66\x7b\x49\x4e\xc0\xe2\xf4\x33\x3b\x5e\x23\x60\x3d\x1e\x3a\xaa\x08\x7d\xcc\xda\xa4\x40\x4d\x7c\xdb\x6c\xdf\x47\x5e\xc2\x48\x24\x06\xd9\x05\xdd\x34\xd6\x78\x63\x59\xc9\x3c\xbc\x91\xa7\x99\x87\x6a\xe0\x13\x2d\xc4\x9d\x1d\xa6\xd0\x98\xeb\xe5\x3a\x97\x65\xe1\x63\x86\x37\x41\xc7\x30\x05\xa4\x7e\x9a\xbc\x8a\xde\xfc\x04\xe2\x3d\x5d\xd1\x30\xd4\xc9\x19\x5a\xbf\x32\xa0\x10\x20\xe1\xee\xd7\x64\x97\x63\xb0\x9e\x44\x61\x69\x0f\x63\xa7\x7f\xc7\xf0\xbe\xae\x64\x07\x8b\x18\xf9\x1b\x05\x0d\x0b\xcf\x01", 220, { 0x8a, 0xd2, 0x4d, 0x87, 0x81, 0x67, 0x56, 0xc3, 0xa6, 0x51, 0x29, 0xe9, 0xa8, 0x9f, 0x1d, 0xc2 } },
{ "\x7d\xb5\x74\x2a\xf7\xeb\x20\x0c\x5d\xdb\x67\x4f\x27\x6f\xb9\x00\x50\xef\x58\x0b\xe0\xc4\xf3\xc5\x17\x57\xe4\xa6\x33\x18\x03\x14\xdf\x9b\x25\x5a\x38\xdc\x35\xb7\x7c\xa3\xec\x93\x51\x03\x30\xcd\xb0\xfc\x07\x2f\x6c\x40\x36\x03\x63\x13\x7a\xbf\x3f\x4b\x62\xdb\xe9\x77\xc7\xc3\x8c\x1c\xe9\xe8\x41\x1a\x28\xf1\x5c\x2e\x5c\x10\x52\x91\xd1\x6f\x46\xbd\xe2\xc8\x2f\x4f\x30\x14\xa6\x08\x80\x5b\x51\xed\x2f\xaa\xe8\x8f\x52\x23\xe1\x65\x6e\x2e\xbc\x55\xd0\xce\xed\x51\x8b\x0e\x61\x1e\x0c\x99\xae\x30\x5b\x69\xdc\xb2\xf0\x98\xca\xa2\x30\x52\x3f\x18\x83\xec\x8e\xa6\x12\x0d\xe5\xdb\x22\x0a\x32\xe2\x08\x50\xb1\x48\xc8\xfa\x17\xdb\x4e\x83\x54\xad\x48\x95\x09\x38\x9e\x00\x86\x9b\x8f\xc6\x7e\x03\x69\x53\x4a\x25\xe5\xca\xd6\xe7\x1d\x7a\x2c\x1e\x2a\x6f\x9b\x72\x5e\x25\x83\x5e\x1b\x58\xc0\x54\x4e\xf7\xb1\xfd\x8c\x36\xe4\x9e\xc7\xc2\x60\x96\xac\x10\x8e\x04\x9d\xcf\xc4\x85\x46\x7c\x6f\x1b\xde\x13\xdd", 221, { 0x11, 0xa3, 0xea, 0x6a, 0x2d, 0xee, 0x6c, 0xd5, 0xd4, 0xd9, 0xde, 0x9e, 0x00, 0x76, 0x0c, 0xd3 } },
{ "\x29\xfc\xe3\xa5\x9b\x13\x7b\xb3\x22\x8c\xc0\xf4\x9d\x7e\x20\x9a\x93\x0d\xe1\x0c\xdd\x5c\x93\x6e\x61\x30\xa4\xc8\xf8\x22\x12\xd5\x71\x80\x3b\x6b\xaf\xc2\xd0\x81\xed\xa4\xe8\xe2\x22\xe4\x31\x6d\x80\x9a\x41\x06\x16\x2f\x0a\x1d\x95\xfb\x6c\xd9\x2f\x7f\xe5\xe0\x9b\x3c\x63\xc5\x35\x2e\x3a\x0f\x43\xe8\x31\xe2\x53\x4d\xb7\x4d\xc0\xe8\x33\x2f\x53\xe9\x79\xc6\x05\xbd\x58\xd6\xe2\x48\x50\x72\x0a\xc2\x61\x6a\xd2\x2b\xfe\x51\x07\x6c\x21\x29\x52\x0c\x78\xad\xb7\x8b\xbd\x3b\x38\x5f\x23\x5d\x96\x88\xec\xf3\x37\xd2\x17\xfe\x23\xa7\x37\x0b\x28\x8e\x5f\x3a\x1e\x44\x3a\xe9\x45\x75\x37\x02\xca\x17\xe1\xdf\x69\x64\x4c\xd5\x2c\x72\xab\x64\x51\x70\xff\xf9\x49\x7f\xea\x5d\xdd\x2a\xa2\xbe\x6b\x84\x01\x58\x4f\xdd\xb8\xc0\x5d\x20\xa4\xcd\x8a\xcc\x39\x23\xaa\x8d\x1b\x5d\x53\x76\xcb\xd6\xe1\xde\x7e\x15\x93\xd4\x0a\xd3\xd3\x40\xf9\xa9\x93\x97\xc0\x5a\xd3\x3a\x09\xbb\x3f\x6c\x2d\x3b\xf4\xee\xbe\x13\x63\x39", 222, { 0x88, 0x92, 0xcd, 0x7f, 0x51, 0x5f, 0xd0, 0x0d, 0x4e, 0x0f, 0x7b, 0x98, 0x75, 0xa3, 0xbc, 0x7e } },
{ "\x3f\x0b\x4e\x57\xf7\xe5\xf0\x5f\xda\x6b\x3a\x8b\xa8\x10\x37\xf8\x12\x45\x90\xf9\x16\x5b\x2c\x17\x6c\x10\xef\xa9\x18\xb8\xa4\x0c\x20\x09\xf5\x80\x58\x83\x97\x9f\x59\x7a\x82\x7b\x90\xc2\x5e\x52\x72\xf5\xfa\xf0\xcd\x63\xf5\xa2\x3a\x97\x7f\xd2\xaf\x82\x3a\x44\x31\x47\x3a\xec\xa6\xa2\x2b\x69\xc8\xf6\x92\x22\x36\xf1\x2c\xfc\xa5\xde\x72\xb5\x33\x86\x3e\xe0\xdc\xc4\x87\x7d\x05\xa4\x48\x34\x36\x37\x80\x2e\xe5\xf6\x52\x91\x6d\xd0\x4c\x96\x1e\xd3\xc4\x48\x6d\x73\x5e\xda\x4c\x79\xab\xa5\x95\x8a\xec\xbe\x9f\x52\xf4\x31\xf3\x4e\x2b\xdc\x19\x34\xaf\xe9\x8b\xff\x94\xe9\xcb\x9a\x4c\x8a\x90\x28\x22\xf5\x1c\xee\xe0\xc6\xa9\xde\x91\xa9\x01\xc1\x61\x8d\x2d\x00\xa0\x88\x45\x0e\xe5\x47\x67\x75\x4f\x30\xc2\x7c\xe7\x16\xd2\x72\x21\x98\xa6\x9b\x82\x17\x5c\xb4\x5b\x51\xaf\x23\xb3\x9f\xa7\xc0\x43\xc7\x6f\x3a\x9a\x7f\x43\x27\x61\x7f\x88\xac\xb9\x41\xf5\xc5\xc0\x58\xef\x32\x33\x6a\xf3\xff\x2d\xcc\x2d\xae\x0e", 223, { 0x01, 0xee, 0xfc, 0xcf, 0xf4, 0x83, 0xfd, 0x73, 0xc0, 0x07, 0x8d, 0x6d, 0xe4, 0x05, 0xae, 0xc8 } },
{ "\xb8\x25\x02\x77\x2d\x99\x6b\x71\x64\x65\x09\xad\xea\x9e\x5e\xe6\x5f\xbf\x95\x63\xc9\xfb\x69\x98\x95\xb1\xb4\xec\x2c\xfe\x95\x96\x2e\x6c\x3c\xa0\x4e\xc7\x55\x0e\xca\x10\x0e\x18\x85\x8b\xc7\x92\xc2\xf3\x55\x6d\x7f\xce\xf3\x56\x6c\xdb\xc6\x7c\x87\xa7\x0c\x6f\x55\x3f\xe0\x24\xbe\xff\xac\x15\x86\x89\x2b\x85\x64\xd2\x19\xe1\xc1\x56\x7a\x42\x0e\x6b\x84\x0f\xc2\xb3\x2b\xc0\xef\xac\x47\xbd\x80\x62\x63\x64\x11\x43\x24\x2a\x6b\x13\x54\xd8\x9a\xf6\x8a\xf7\xad\xab\x78\x47\x0a\xd8\xd1\x68\x6b\x7f\xd5\x9c\x79\x90\x7a\xa4\x03\x99\x38\xc6\xdb\x99\x71\xf0\x4d\xf2\x7c\xf7\x3d\x7c\x4d\xf8\xdc\xb6\xc2\xdf\x07\x17\x8c\x06\x3c\x82\xb8\xf5\x39\xd9\xd9\x48\x33\xa9\xd4\xae\xb0\x03\x48\x0d\x01\x6a\x98\xc3\x60\xe8\x96\x08\xa2\x3b\xcd\x6f\x98\x2d\x6b\x8b\x08\x01\xc9\xae\x43\x09\x99\xcd\x63\x55\xab\x7f\x23\x85\x5c\x24\x5b\xc0\x9b\x23\x16\xbd\x99\x4b\x51\xfe\x0d\x5b\x53\x1d\x21\x9d\xa4\x3f\xfc\xad\xab\xa5\xed\x57", 224, { 0x03, 0xe5, 0xd1, 0xd0, 0xaf, 0x2a, 0x72, 0xab, 0x3c, 0xa0, 0xdc, 0x8e, 0x46, 0xe4, 0x33, 0x69 } },
{ "\xe7\x61\x30\x9e\xca\x25\x61\xd9\x2d\x37\x7a\xf9\x74\x80\x8a\xe5\x40\xc4\xf5\x06\xca\xed\x62\xde\xe3\x80\x45\xd3\x85\x31\x08\x0a\x14\x91\xd8\x6f\x37\xe5\x0b\xd0\x4e\x13\x29\x29\x8f\xb6\x9e\xfb\xc0\x50\xfa\xd5\x93\x9f\xe2\xf4\x99\x08\x97\x06\xe4\x30\x20\x0b\x3e\xaa\x17\x54\xff\xca\x0e\xae\x8f\x98\x9c\x98\x79\xe1\x07\xaf\x27\x67\x8f\x2b\x40\x38\x55\x82\x9b\x1c\xe7\x2a\xde\x27\x69\x7c\xb7\x6f\x61\xb4\xb7\xd8\x1e\x15\xc6\x0b\xc6\x29\x47\x85\xfb\x26\xc9\xcb\x29\x6c\x66\xbc\x6f\x2b\xbc\xff\x28\x22\x85\x9a\x15\x82\xd6\xa4\x29\xf1\xf1\x13\xf0\xaa\xc6\xaf\x9c\xc1\x0d\x28\xad\x84\xf9\x42\x3c\xfe\x99\x2e\xe5\x95\xd0\xf5\x4f\xd3\xef\x0d\x5a\x41\xec\x0e\xe3\x6f\x42\x7e\x36\x11\x64\xc8\x11\x44\xdf\x4c\x51\xee\x0b\x90\x3c\x42\x6d\x93\x6a\x63\xac\x42\x50\x20\x2e\x04\x6a\xde\x4f\x70\x05\x93\x3e\x1e\xa6\xee\x73\x9b\x1a\x95\xd0\x76\x1b\x86\x54\x77\x9c\x9e\x76\xf0\xb2\x38\x24\x2b\x1e\xee\x57\x02\x7b\x7b\x52", 225, { 0x1e, 0x37, 0x6b, 0x9f, 0xa2, 0xf6, 0xbd, 0xe8, 0x1e, 0x4e, 0xdc, 0xe4, 0x20, 0xaf, 0x0b, 0x93 } },
{ "\x8e\x01\xc1\x78\xf7\xf3\xe5\xc8\x6b\xab\x98\xf6\x2a\x40\x71\x27\xbd\xd1\x76\x48\x46\x71\xba\x0b\xf3\xaf\x20\x7f\x8d\xc0\x3d\x4a\x2d\x4b\x5c\x86\x0d\xd1\x9b\x36\x7c\xb7\x32\x64\xeb\xf2\xd4\xd8\x25\x4e\x2e\x76\x9c\x5b\x8c\x35\xde\xf4\x9b\xb8\x27\x6d\x49\x8a\x0f\x58\xc8\xfb\x64\xf4\xb2\x91\x12\x34\x47\x2d\x3f\x67\xd1\xbc\x74\x88\x28\x96\xf5\x24\x52\x76\x31\xe4\x42\x16\x54\xb6\xc1\x67\xfa\x9c\x6a\x6a\xff\x11\xcf\xae\x72\x13\xba\x66\xa8\xd2\x8e\x26\x6c\xf3\xcb\x3a\x54\x81\xb0\xa3\x2f\x71\xfa\xaf\x9a\xd0\xcb\x34\xb2\x8b\xa6\x69\xe3\xdb\x97\x60\xdf\x4b\x6f\x24\xab\x67\x2d\x6b\xd3\x03\x79\xf8\xbe\x25\x49\x90\x1c\x90\xa6\x96\x7b\xed\x89\x45\xf9\x98\xdf\x8a\x14\x05\xac\x7c\x9d\xce\x4c\x79\xcc\x5a\xd4\xde\x6c\x2b\x96\x62\x37\xc3\xc3\x10\x3c\x34\x2b\xdf\x7c\x43\x21\xef\x95\x38\x7a\x62\x96\x45\xb4\xd5\xf1\x90\x32\x7a\x8e\xdf\xa5\xd3\xfc\xdf\x87\x0d\xc2\x11\xc1\xf7\xd4\x52\x6d\x9d\x21\x05\xb5\x85\x49\xdf", 226, { 0xfc, 0xeb, 0xea, 0x4e, 0x08, 0xfa, 0x14, 0xd6, 0xcc, 0x61, 0x90, 0xfa, 0x48, 0x06, 0x9e, 0x7a } },
{ "\xda\x85\x6c\x4e\x51\x3f\x1e\x87\x08\x3b\x76\x2b\xc6\x9f\x46\x95\xb0\x8d\x65\xc2\xe7\x19\x27\xde\x4f\xe6\xee\x67\x6b\x5c\xda\x00\x64\x88\x9c\xd4\x29\x8f\x68\xa5\xaf\xd0\x08\x70\x59\x96\x0e\xbf\x74\x76\x22\x8b\xd9\x4b\x79\xcb\xb5\xcc\x66\x9d\x66\xc7\x0a\x10\x7b\xfc\x28\x8c\xde\x99\x13\xdf\x0e\x4d\xc8\xe4\x84\x0a\x18\xd2\x37\x5c\xfe\x1e\x3b\x61\x0f\x0e\x85\x64\x0f\xdc\xc9\x1a\x9f\x83\x84\x78\x14\x28\x16\x2f\x64\x28\x07\xc6\x3a\xba\x6a\x52\x29\x41\x5e\xd3\xfc\x31\xb1\x2e\x2c\x9b\xa0\xc3\x6b\x56\xba\xaa\x5d\xcc\xe2\x8f\x66\x5e\x73\x27\x9e\x75\x7e\x4e\xe1\x26\x2e\xbf\x04\x81\x4d\x6f\x22\x77\xdf\x86\x14\x65\xec\xfb\xfe\x41\x5c\xca\x06\x60\xfa\xd5\x20\xe1\x9b\x55\x05\xba\x58\xfb\x43\x5d\xf6\xa7\x6d\x06\x67\xc4\x72\xa4\x6a\x3e\x0b\x61\x4d\x21\x4e\xd0\xd2\xcf\x60\xb2\x30\xdd\x47\x65\x30\x8c\xde\xea\x78\xdf\xe1\x91\xe5\x5d\x28\x43\x11\x65\xf1\x6c\xf4\x29\x56\x83\xa6\x49\xdd\x37\x42\x0c\x2e\x37\xe7\x3d\xdf", 227, { 0xe2, 0x2e, 0xd4, 0x1d, 0xb5, 0x4e, 0xf8, 0x9c, 0x90, 0x06, 0xb3, 0xdc, 0x67, 0x84, 0x36, 0xff } },
{ "\x9e\xd8\xa0\x6c\xfa\xc3\x10\x42\xf8\x1f\x36\xfa\xad\xae\xe8\xa3\x83\x22\x1b\x38\xbe\xdc\x86\x31\xaa\xcf\xd6\x35\x63\x83\x87\xec\x40\x36\x66\xf3\xdd\x1d\xa0\xe9\xc4\xc8\x85\xa6\xb8\xa3\xdf\x8c\x9d\x98\xe9\xf5\x07\xd2\xee\xcb\x5d\x9d\x80\x37\xaa\x69\x51\x72\x00\xee\xb1\xb7\x89\x69\xa4\x59\x2b\x04\x9f\xf0\xd7\x53\xdf\xaf\x6f\xfa\x66\xd1\x51\x6d\x94\x32\x85\xd0\x5e\xfa\x7f\xfd\x2d\x74\x91\xa3\x55\xfd\xf0\x8b\x30\x17\x60\xf6\xd7\x9d\x7f\x23\x7c\x11\xac\xed\x67\x11\x92\x90\x8b\xe9\x54\x8c\xc4\x41\x57\xa2\xb0\xf7\xa6\xfb\x27\x08\x06\x68\x58\xa7\x97\xc4\x81\x70\x24\x76\x4a\xb6\x07\x13\xbf\xf8\xbc\x20\xe9\x73\x00\xf5\x08\xd1\xa5\xbc\x6e\xff\xf5\x99\xfa\x1c\xc1\xe4\x30\x8d\x05\x91\xf2\x2d\x39\xb1\xdd\xa2\x36\x11\x43\xcb\xc3\x00\xc0\x55\xbc\x2f\x7b\x6d\xde\xb1\xfb\x8f\x31\x21\xa3\x7d\x12\xe2\xcc\x4c\xdc\x81\x7d\x99\x33\x38\xc9\xd8\xed\x8c\xc6\xcc\x9c\x15\x25\x13\xbc\x5e\x73\xd9\x76\xe2\xeb\xad\x0f\x37\xf5\x70", 228, { 0x55, 0xd9, 0x74, 0x7a, 0xdd, 0xdb, 0x7e, 0x9a, 0x38, 0xc0, 0x19, 0xe1, 0xa7, 0xca, 0x59, 0x91 } },
{ "\xf0\x47\x3f\x69\x52\xb8\x77\x76\x86\x6d\xa2\x5e\xde\x26\x1c\x41\x3b\x43\x2c\x30\x34\xa6\x43\xd9\xe0\xb5\xd2\x5b\x41\x7f\xc5\x38\x4d\xf6\x9f\x68\x9a\x5a\x33\x8a\xa5\xb7\xfc\x36\xbe\x85\x1a\x6d\x94\x6d\xe9\x32\x59\x2b\x40\x32\x91\x8e\x43\x9e\x7d\x9d\xe6\x1a\x1e\xd4\xfe\xcd\xe8\x8c\x05\x99\x05\x78\x6a\x65\x42\x4b\x93\x95\x9a\x77\x70\x9b\x5d\x11\xbd\xa7\xfd\xd0\xd4\x7a\x75\x35\x2c\x58\x57\xc4\x72\x1c\x70\x70\x42\x65\x19\x14\x82\xee\x1d\x1e\x5b\xfc\x42\x46\xd5\xf0\x76\x7c\x0f\xfc\x98\xe0\x17\xf2\xdf\xeb\x6c\x38\xeb\xab\x0d\xd8\x66\x2b\x3d\xb4\x56\xf1\xd6\xd7\x03\xa0\x47\xe6\x7f\xbe\x2c\xb5\xd7\x90\x88\xf6\xd5\x22\xa2\x0c\x6e\x63\x79\xfd\xcf\x6c\xe4\x86\xff\xe1\x4b\x49\x30\x70\xfc\x22\xa8\x77\x2b\x97\x47\xc2\x89\x6d\xb4\x71\x7f\x57\x28\xf8\x07\xca\xd6\x41\x27\xec\xf6\xec\x0c\xaa\x6d\xb6\xbf\xe9\x1b\xf7\x86\xd4\xc0\x45\xd9\x54\x8b\x37\xff\x9e\x41\x44\x46\xe5\x07\xc9\xdb\x31\xcc\x29\x4b\x48\x55\x0e\x97\xc8\xe6", 229, { 0xd9, 0xf5, 0xd6, 0x7b, 0x77, 0x59, 0xa0, 0xb5, 0x8b, 0xaf, 0xaa, 0xc9, 0xea, 0x60, 0xdf, 0xe2 } },
{ "\xce\xed\x56\x8a\xea\x09\xf1\x00\xb0\x28\x60\x42\x00\xa4\xca\xb2\x2b\x31\x64\xe4\xf7\xb2\x44\x02\xbf\xb9\x55\xe0\xb7\x9a\x63\x9d\x4e\x2a\x56\xf2\xac\x19\x7c\x1a\x5b\xe9\xff\x5e\x02\x8a\x75\x3c\x4b\x94\x96\x33\xe6\x20\x09\x2e\xa7\x44\x57\x8b\xcb\x28\x01\xc6\x58\xc9\xac\xc6\xa2\x4a\x7a\xc4\xe9\xf2\x75\xd4\x20\xc7\x25\x12\xc9\xc4\x41\x6a\xfb\xb9\x82\x03\x85\xc0\x92\x49\x2d\x57\x24\x95\x11\x1f\x1d\x7c\x2f\x30\xef\xb1\x00\x17\x07\x97\x3f\x6d\x5d\x67\xc7\x15\x28\xfb\x9a\x11\xb4\x35\xfe\x01\x4b\x0e\xc1\x3a\x50\x3f\x9e\x7f\x6c\x85\xb8\x5f\xdf\x95\x43\x2a\xf8\x9c\x42\x91\xa0\x71\x6a\xd9\x10\x5a\x26\xbc\xa3\x78\xb4\xdc\xc5\xd0\x6c\xe4\x0b\xf6\x21\x3e\x5d\x58\xa0\x6a\x4e\x24\x56\x02\x8c\xd7\x0e\x47\x73\x64\xd7\x66\xbc\xfd\x03\x4d\x52\x80\x4c\x25\x45\x81\x12\x6b\xd7\xf0\xc0\xae\xb4\x67\x4c\xe7\x3f\x13\xdc\x70\x08\x3c\x86\xa3\x5a\x72\x30\x10\xf0\x56\x97\x5a\xc7\x02\x6b\xae\x0e\x16\x96\xe1\x3a\x60\x9d\x26\x36\x27\x1f\x69", 230, { 0xc7, 0xb4, 0xa5, 0x2d, 0xd4, 0x3e, 0xbe, 0xcb, 0x90, 0xc5, 0x10, 0x56, 0x22, 0x68, 0xc4, 0x02 } },
{ "\xb8\x14\x7b\x4a\xaf\xbe\x8f\xb9\xd8\xd0\x9b\x2a\x70\xd9\x43\x75\x04\xb0\xb3\x4e\x64\x82\xdd\x5c\xb6\x7a\xde\xf7\xa6\x0b\x7e\x83\xbb\xdd\xd6\x64\xc5\x51\x0c\x9c\x72\xb3\x3a\x08\x01\xfb\x6d\x34\x0e\x40\xc9\xd6\x8b\xba\xca\xee\xcf\x72\xa5\x78\xc8\x88\xcd\xca\x4d\x21\x91\x90\x8e\xeb\xe2\x62\xad\x4e\x33\xff\x65\x30\x79\x29\xe8\x65\x52\x1c\xc7\xb2\x42\xac\x0b\x7c\x18\xfa\x61\x12\x6f\xd2\x71\x94\x50\xa5\x4f\x7e\xf5\x1e\x0a\xd5\xa8\x62\x63\xec\xca\xe9\x98\xd3\xf0\xf4\x5d\x5d\x28\xaa\xec\xcd\x59\xd3\x33\x1c\xc8\x30\x2c\xea\xb7\x74\x4b\xff\x28\xe3\x10\x7c\xbf\x86\xbc\xa2\xc5\x08\x20\x3e\x49\x31\x90\xb0\x61\xfc\xf7\x97\x8e\x56\x05\x1c\x3d\x76\x83\xd7\x6a\xd3\xc6\x61\x5f\xc7\x42\x77\x9b\xbe\x36\xc7\xcd\x1a\x85\x5b\xff\xa7\xa5\x9f\xd0\xb6\xb0\x10\xda\xd8\x98\x83\x32\x38\x78\x0f\x0d\x96\x05\x99\x7f\xdc\x23\xfa\x5f\x5e\x94\xcf\x47\xb3\xcb\xdc\xb8\x2a\xdf\x1d\x5a\xeb\x9b\x6b\x60\xac\xb2\xc8\x0a\x0c\x0f\x47\x44\x90\x4b\x9b\x6c", 231, { 0x40, 0xc0, 0x57, 0x95, 0x6a, 0x81, 0x82, 0x29, 0x3b, 0xd0, 0x98, 0x14, 0x84, 0xa7, 0xa4, 0xd1 } },
{ "\x37\x38\x3a\x31\xbb\x9a\x2d\x97\x67\xf5\x77\x16\x90\x82\x1f\xe2\xb1\x3b\xdc\x46\x27\x15\x5d\x2a\x85\x4e\x32\xb3\x95\xdc\x5a\x09\xec\x05\x69\x34\x29\x0c\x56\x1f\xca\x09\xdf\xbf\xaa\xd2\x99\x4d\x1b\x15\x98\xaf\x9c\x88\xb6\xf5\x37\x37\x84\xfe\x7a\xfc\xcb\x3b\x0f\x0d\xbd\x8b\xfa\xaf\xbd\x46\x6f\x09\xc8\x56\x4e\x13\x7e\x7f\x3c\xad\xd1\xbe\x5f\xcc\x49\xcf\xb5\xcf\xf1\xc9\x12\xf0\xe1\xe5\xb4\xd6\x69\x5c\x84\x46\xd7\x6e\xec\xfa\xe6\x7e\x4f\x8a\xc3\x5a\x29\x87\xbd\x99\xc5\xa7\x88\x1e\x95\x1a\x2d\xb9\x31\xfb\x92\xec\xef\xe2\xa1\xca\x1b\x96\x18\xfb\xd3\xe0\xed\xdd\x82\x7a\x5c\xc5\xf7\x26\x8e\x63\x21\xdc\xe7\x43\x69\x1b\xed\x70\xac\x61\xd0\x33\xd4\xb6\x9a\xf9\x12\x62\xf4\x52\xb9\xbe\x92\x16\xba\x28\x3c\xa2\xb8\x10\x7a\x40\xc7\x2f\xdc\xa5\xc6\xd8\xe3\x93\x56\x66\x8f\x9f\x76\xd5\x86\x0d\xbd\x6d\xde\xd7\x33\x99\x87\xcd\xcb\xd6\x58\xd6\x81\xc7\xb4\x54\x0c\x65\xd9\xa5\x41\x53\xc5\xc6\x04\x4f\xc5\x13\xeb\xc5\x9b\x2a\x70\x7e\x4b\xed", 232, { 0x0f, 0x8c, 0xf7, 0x8f, 0xe2, 0x33, 0xb8, 0xdb, 0x7b, 0x4b, 0x15, 0x6c, 0x88, 0x92, 0xce, 0x22 } },
{ "\xd3\x75\x82\xa5\x8a\xca\xa4\x44\x66\xd0\x70\xc3\x44\x41\x52\xaa\x6c\x91\x46\xae\x89\x5f\x64\x74\x45\x08\x0c\x74\x81\x56\xae\xf9\x2e\x56\x36\x44\xcb\x47\x13\xd0\x7b\xae\xe3\xb1\xc2\x87\xbd\x16\xdc\x96\x1a\xed\xba\xdb\x60\xa5\x99\x23\x0d\x0f\x41\xbb\x7c\x5e\xd8\x40\x57\x4d\x60\x92\x9a\x5f\xd4\xe7\x3f\x42\xda\xfb\x8c\x4d\x24\x65\xf5\x28\x69\x05\xae\x8b\xfc\x9a\xd2\x1f\x06\x70\x29\x80\x65\x36\x99\x64\x1f\xee\x2c\xd5\x84\xfd\xba\x9a\xe0\x62\x33\xb4\xda\x03\x8b\x04\x6d\x23\x42\x0a\x80\xf1\x8f\xc8\x23\x3a\x28\xc5\x68\x3d\xb1\x2d\xdc\x9f\xbf\x15\xa1\x75\x87\xdd\x29\x7f\x27\xae\x91\x75\x91\x23\x98\x78\x10\x05\x3a\xab\x78\x2e\xed\xdb\xee\x8e\x77\x59\x51\x4c\x6a\xe9\x44\x04\x3d\xd3\xda\x2f\x09\x16\xdc\xa0\xdd\xbc\xb9\x2b\xbe\x49\x0b\x60\x3e\x4d\xc2\x75\xb7\x19\xef\x42\x25\x8e\x2f\x65\x9d\x11\xb2\x85\x6e\x9a\xe7\xb4\xd3\xec\xc6\xee\x51\xdf\xb9\xbe\xb3\xd9\x28\x00\xa0\x5b\xa0\xc1\xd6\xb7\x9f\x42\x05\xe0\xfe\x1c\x4a\x5a\xfb\x7d\x46", 233, { 0xe5, 0x61, 0x01, 0x70, 0x0e, 0x76, 0x73, 0x8d, 0xa8, 0x90, 0x1c, 0xda, 0x14, 0x27, 0xde, 0xf4 } },
{ "\x50\xe2\x90\xdb\xc4\x58\xfb\x83\xe0\x44\x82\x4c\x6c\x5a\xc9\x74\x59\x45\x36\x9c\x7a\x71\xf5\xac\x52\x72\x15\x44\x08\x14\xcf\xda\x77\x00\xe7\x75\x62\x07\x2c\x05\xc5\x0e\x19\x5c\x46\x96\x9e\xcd\xca\xe7\xf8\x60\x25\xd9\xbd\xaf\xe9\x3f\xf4\x60\x5f\xf0\x60\x3f\x94\x73\xde\xf6\x8a\x46\xe4\x6c\x90\xcb\x29\xb8\xac\xd0\x63\xc1\x34\xba\x2c\x74\x7c\x4d\xfe\xa0\xa9\x1a\x5d\x71\xa4\x85\x14\x87\x2a\x71\x97\xb2\x01\x8b\x87\x4c\x45\x30\x55\x33\xe1\xfc\xfe\x62\x19\xf0\xf4\x2c\x43\x3f\x1d\x14\x96\xb5\xf4\x4b\x1a\xc4\xce\xc7\xbf\x2d\x37\xfc\x8a\x48\x7b\x39\xea\xef\x40\xa2\x29\x0d\x50\xc6\xfe\xbe\x75\xdc\x3f\x23\x7d\x9f\xb3\xc6\x5d\xc3\x05\xa4\x72\x12\xd5\xdb\xe2\x28\xe9\xf1\x21\xc7\x81\xbe\x90\xd8\xc8\xf8\x40\xff\x66\x59\xd4\xd9\x32\x6f\x83\xd5\x05\x00\x3d\xac\xaa\x17\xe5\x7e\xf1\xaf\xbd\x8b\xe3\xfb\xe0\x8a\x0f\x50\xe8\xa9\x03\xb0\xaf\x22\xd7\xf4\x33\x77\xe3\x95\x93\x4a\x90\x17\x36\xdb\x4c\x12\x0b\x1e\x97\xde\xea\x78\x3b\xda\x19\x16\x98\x59", 234, { 0x2d, 0x01, 0x5b, 0x45, 0xca, 0x44, 0x57, 0xf8, 0xea, 0xbb, 0x14, 0x25, 0xd1, 0x7b, 0x62, 0xec } },
{ "\xeb\xea\x1d\x2a\x84\x43\xd3\xc3\xb7\x08\x13\x09\x10\x91\x58\xbf\xed\x23\x2f\x88\xc7\x05\x4b\x9a\x8f\x43\xb5\x01\x33\xff\x20\x8d\x3f\x6e\x5a\x5a\xa0\x76\xdf\xfa\x85\xe2\x88\x41\x5e\x40\x61\xac\x06\x58\x97\x6e\xfd\x49\x90\x19\xce\x41\x15\xe6\x90\xd8\xaa\xa1\x87\x0a\xff\x33\xea\xcf\x7f\xcd\xbf\x59\x05\xaf\xe3\xea\xae\x92\x26\x4f\xc9\xb8\x92\xfc\xeb\x8e\xcc\xc5\x20\xfa\x94\x37\x3c\x47\x67\x91\x4f\xab\x44\x62\x36\x71\x8b\xc0\x4e\xc7\x00\x22\x44\x26\xef\xdb\x08\x59\x6a\x34\xe0\x2d\xae\x24\x99\xb4\xa4\xae\xd8\x35\x83\xd7\x8e\xb3\x92\x43\x8a\x18\x0b\x6b\x28\xff\x1b\x7d\x27\x1b\x07\xd1\x98\x46\x68\x03\xf3\x2a\x97\xb1\x44\x86\x23\xd2\x82\x1e\x7f\xb1\x00\x42\xb9\x86\xfd\xf8\x65\xaf\x56\xc8\x98\x90\x5b\x25\x10\x04\xbe\x73\x71\x7e\xa7\xb9\xaa\xc1\xe5\xe5\x76\x38\x40\xb6\xff\xf2\xea\x4a\x9d\x3e\x14\x44\xbb\xdf\x9c\x99\xda\xed\xe3\xf8\xaf\x48\xbd\xd4\x68\xb9\x82\x0f\x0d\xa6\x41\x44\x01\x72\x14\xb1\xa7\x6f\x8f\xbf\x21\x81\x52\x30\x33\x50\xbe", 235, { 0x92, 0x09, 0x96, 0x7f, 0x5b, 0x1d, 0x4f, 0x40, 0xe8, 0xc7, 0x0d, 0x8f, 0x7e, 0xd7, 0x63, 0x43 } },
{ "\xe4\x46\x29\xc4\x48\x89\x72\xd9\x5c\x32\xc8\x06\x5e\xe2\xb7\x1b\x18\x27\x15\x03\xc3\x1b\xfb\x33\x97\x29\x61\x3d\xf0\xef\x55\x81\x1e\x3f\xd8\x02\xc9\x40\x55\x56\xff\xb2\xbf\xb8\xdc\x4f\x45\x38\xd5\x4c\xb5\x11\xa1\xff\x6b\x1b\xc4\x9a\xf3\x57\xb9\x15\x43\xa8\xbb\x2a\x8e\xa1\x30\x7a\xc6\x79\xb3\xcd\xb1\x1b\xbc\x77\xa7\x5a\xee\xd5\xe5\x42\xfd\xf7\x91\x8a\x3a\x58\x4b\x25\xbd\xc8\x6c\xf7\x2e\x6b\xde\xa5\x30\xda\x98\x85\x6a\x67\xb4\xb5\x32\x7d\x2e\x47\xd8\x26\x3b\x9a\x8d\xa7\x44\xc4\xef\x46\x7e\x2b\x32\x2c\x27\x33\xec\x64\x5e\x11\x7c\x03\x9f\xbe\x18\x62\xdb\x08\x73\x87\x30\xc2\x07\xa2\x4a\x1c\x04\xb3\x55\x0d\xd5\x49\x9e\xec\x4b\x64\xc5\x1f\x68\x7c\xa1\xea\xca\x9b\xb0\x69\xb3\xa5\x39\x99\xb1\xb8\x00\x90\xee\xae\xa5\xcd\x16\xbf\x9a\xaa\xe0\x44\xbf\xee\x3c\x96\x9c\x7f\x17\x15\x3a\x34\xce\x44\x9e\xc9\x2d\x12\xe3\xec\x22\x34\xf3\x74\x02\xad\xe2\xb1\x77\x6f\xe7\xde\x06\x1b\xf7\xb3\x39\xe5\x00\x17\x6d\x93\xbf\xf3\x3a\xa4\x3e\x22\x7a\x8d\xee\x84", 236, { 0x53, 0x0e, 0x63, 0xd7, 0x2d, 0xd0, 0xbc, 0x79, 0xa2, 0x08, 0xa2, 0x58, 0x61, 0x64, 0x23, 0x51 } },
{ "\xf8\x1f\x5c\x32\xb7\x04\x93\xcb\xdc\x68\x0f\xed\x39\xb4\x59\xc0\x76\x75\x44\xac\xde\x5b\xc2\x2a\xc3\x5e\x63\xb8\x8f\xfb\x6c\xe6\x69\x9c\x90\x8e\x80\x16\x4e\x21\xf7\x4c\xe6\x1b\x6d\xf1\xf9\x98\x28\x6a\xbc\x01\xdd\xd1\xd8\xdf\x1e\x16\xe2\xd0\x60\x40\x72\x96\xa8\xd1\xe2\x4d\xd2\x48\xa5\xb5\x7e\xc0\x41\xad\x97\xb6\xea\xc1\x81\xe8\xbd\xda\xdf\x58\x95\x14\xfe\x70\x8e\xad\xe1\x3f\x14\x18\xe9\xe1\xa6\x31\x21\xfd\x2d\x8c\x24\x68\xf3\xe6\xab\xe8\x42\xbd\xb7\x13\x9a\xfc\x57\x55\x8d\xce\x17\x0f\x3b\x93\x05\xdd\x66\xf0\x61\xf0\x31\x01\xe0\x9a\x7a\xaa\x9d\xe9\xd0\x0b\x9d\x6a\x13\x11\xfd\x0f\xa7\x29\xba\x2b\x54\x10\x1d\x99\xbe\xc6\xc1\xfd\x7b\xa1\x42\x22\xd6\x7e\x84\x83\x20\x12\x9d\xe5\xad\x5e\x60\x21\x72\x41\x87\x00\x39\x27\x7c\x3e\x7e\xe0\xc4\xb1\xea\x8b\x09\x83\x69\xb1\xc2\x9b\xea\x6e\x81\x1b\xb2\xc9\xd8\x02\x5e\x25\xe9\xf0\x73\xd1\x89\x0a\xa3\xba\x11\xf4\x9f\x40\xc1\xfb\x93\x25\xd0\x55\x43\xa2\x14\x7f\xc0\x94\x4a\xc6\xc6\xd3\x03\xe2\xb5\xa4\x2c", 237, { 0x78, 0xa2, 0x83, 0x25, 0x5d, 0x8e, 0x55, 0xc1, 0x14, 0xb5, 0xee, 0x4c, 0xc1, 0x74, 0x53, 0x04 } },
{ "\xbc\x2a\x44\x38\x51\x3a\xa4\xec\xae\x4b\x35\xc6\x1b\x8e\xd9\x0b\x54\x1c\xf8\x6c\xf2\xac\xb4\x54\xe9\xef\x34\xd1\x2a\x88\x1d\x1c\x69\xab\x1f\xc6\xf9\x51\xab\x81\xd3\x15\xc3\x89\xb5\xaf\xe9\xad\x67\x0a\x39\xfe\x19\x03\x93\x12\xe8\xc0\xf0\xf5\x7f\xab\xd6\xae\xda\x0a\xe6\x69\x26\x3d\x93\x46\xc4\x93\xed\xbd\x6d\xc8\x9b\xab\x6f\x1f\xe7\xfe\x16\x18\xf4\xfa\x26\xcf\x0a\x25\x84\xf1\x12\xd5\xf4\x5b\x1d\x54\xfc\x51\x1e\xad\xe8\x57\xb8\x16\xe7\xaf\x32\xf9\x53\x70\x88\xa1\x0e\x40\x9b\x7e\x07\xae\x88\x14\xdc\x2e\x15\x86\x9a\xb2\x47\xbb\x9f\xe1\x12\x2a\xa1\xf6\x28\x48\xc7\x38\xf3\x8b\xf5\x11\x9d\x19\x25\xce\x4c\x12\xf0\xf2\x6c\x77\x2d\x37\x24\xb5\xb0\x22\xea\xd2\x34\x42\x32\x33\x53\x05\x41\x07\xb1\x36\x21\x54\x39\x16\xad\x9c\x7f\x16\xcc\x2b\x45\x2f\xba\x00\x19\xdf\x82\x56\x1b\xc1\x88\xcd\xdd\xc7\x42\x3a\x63\x16\x07\x08\x49\x7d\x00\x49\x04\x93\x3b\x5d\x41\x6d\xde\x3d\x69\xf9\x78\x65\x46\xfb\x73\x5c\xbf\xa1\xa6\xe1\xbf\xc4\x07\xb4\x34\xbe\x7d\xfd\x34\xe2", 238, { 0xb6, 0xbc, 0xec, 0x14, 0x2d, 0x49, 0xb6, 0x90, 0x18, 0xf9, 0x7b, 0xa5, 0x1b, 0xe9, 0x0c, 0x12 } },
{ "\xd4\x5d\x39\x9c\xca\x90\x8d\x26\x46\xbc\xc1\xe4\xa8\x58\x57\x5e\x3b\xbc\x7f\xd7\xc7\x41\xfe\x8e\x44\x14\x2b\x91\xa9\x9c\x14\x38\xe1\x85\xbd\x45\xdf\x69\x88\x96\xc9\x1b\xb0\x84\x4f\x8f\xef\xdc\x1f\x69\x40\x78\x71\x20\xbf\x79\xbd\xcf\xac\x22\x8d\x98\x8e\x54\x6c\xb5\x74\xa2\xfe\x1d\x57\x10\x29\xcf\x9b\x6d\x71\xbd\xb4\x4a\x62\x58\xe5\x96\x26\xb4\x24\xd7\x36\x58\x1a\x07\x2d\xa5\x46\x09\xb8\xe1\x41\xc6\xaa\xde\x1c\xe9\x2c\x4b\xe5\x33\x12\x97\x49\x7b\x48\x7d\x53\x46\x6b\x31\x53\xff\x74\x25\xda\xd3\x8f\x78\xe1\x2b\x0a\xfc\x09\xc7\x69\xe2\xfc\x74\x96\x04\xf3\x69\x35\xcf\x52\x44\x16\xcb\x6e\x9c\xc4\xc9\x6e\x42\x3a\xa8\x4f\x19\xb5\xc3\x01\x8f\xa5\xfa\xaf\xb7\xbd\x5c\x1c\x05\x29\x6e\x29\xa5\xdf\x1b\x73\x78\x0f\x37\x19\xac\xb4\xb1\x9b\xf6\x4c\x55\xdd\x6f\xa4\x3c\x4b\x08\xcd\xd1\x17\xab\x2b\x80\x9e\xf0\xab\xfc\xe9\x79\x14\x2d\x50\xeb\x77\xb5\x38\x89\xc1\x1e\xfc\x6e\x6f\xa2\xe9\x67\x60\x95\x64\x6b\xc6\x73\x27\xb8\x36\x82\xa8\x8b\xe0\x24\x9a\x7b\xd8\xbb\x8c", 239, { 0x9e, 0x1e, 0x8a, 0x93, 0x65, 0x23, 0xbe, 0x28, 0x19, 0x89, 0x4c, 0xa5, 0x58, 0xc6, 0x31, 0x08 } },
{ "\x72\x92\x38\xb0\x49\x6d\x43\xb7\xff\x66\x01\xd7\x96\xed\x84\xee\x8b\xd4\xd5\xc0\xf0\x64\x96\x5d\x27\x8a\x57\x9e\x3d\x2f\x78\xcd\xe0\xa5\xb6\x64\xff\x3d\x53\xee\xfc\xf5\xe6\x0a\x90\x4e\xbc\x8f\x3c\x3c\xea\xc9\x68\x37\xf1\xe0\x1a\x6f\x0c\x59\x54\x1c\x18\xb6\x0a\xf3\x20\x39\xbe\xb4\x85\xc7\xba\xe0\xc6\xe7\xea\x89\xf2\xe9\x53\x41\xa7\x23\x34\x34\xc5\x57\xb7\x52\xb5\x30\x54\xa4\x4f\xeb\xc3\xc0\x6d\x13\x9b\x58\x0a\x64\x8c\xec\x15\xd1\x35\xa0\xd8\xa2\xa3\x28\x00\xb5\x68\xdf\x48\xe4\x53\xf7\xc6\x87\xd1\xcb\xd2\x10\xdf\x51\x8f\xd5\xab\xab\x17\xeb\xc7\xdc\x47\x2d\x08\x98\x24\x5c\x01\x34\xe8\x60\x17\xbc\xad\xad\x41\x23\xb5\xc1\x5f\x95\x54\xc9\x33\xe9\x7a\x64\x00\x32\xe1\x7f\xbd\x74\xcf\x5f\xf6\x74\x88\xbd\x40\xa9\x54\x0b\x57\x4e\x28\xd5\xd6\x99\xf4\x43\x91\x05\x88\xbb\x92\xcc\x24\xa3\xaf\x71\x9a\x44\xc5\x79\x22\xca\x93\x39\xba\x67\x35\xcb\x38\x98\x3a\x1a\xee\x80\x65\x1d\xf8\x70\xfd\x21\x24\x88\xd1\x3e\x7f\x76\xcc\xeb\x78\x5d\x30\xae\xb3\xd2\x72\xec\x6d\x00", 240, { 0x32, 0x30, 0x23, 0x6a, 0x09, 0x78, 0x4b, 0x95, 0x15, 0x31, 0x10, 0x58, 0xba, 0xcc, 0x32, 0x4e } },
{ "\xb2\xde\x87\xeb\xd6\xa4\x31\xd1\x42\x74\x34\xad\x36\xeb\xdb\xd5\xc3\x84\x7f\xc3\x6b\x26\xae\xf0\x54\xd7\xf8\xdc\x29\x8f\x55\x2b\x8e\x27\x36\xe9\x3d\x70\x26\xee\xc2\x60\x1d\x5d\xcf\x68\x62\x46\x3d\xe1\x19\x6b\xa0\xa4\x70\x20\x85\xb8\x62\x4b\x4a\x26\x27\x8b\x9a\xe9\x39\x76\x85\x02\x02\xfa\x38\xa7\x27\xe4\x5d\x9b\x6b\x7f\x12\x99\x41\x55\x7e\xea\xf3\x11\x16\x16\x68\x84\x6b\xb7\x95\xc6\xac\x69\x83\x75\xc0\xdd\xf8\x19\xf8\x0d\xc5\xa8\x75\x8a\xac\x25\x16\xf1\xeb\x62\x1b\x7c\x69\xe7\x5b\xb4\x7c\xeb\x1e\x44\x55\x7f\x98\xe9\x09\xca\x03\x86\x3c\x6f\x57\x54\x6c\x0b\xa9\x37\xd7\xda\x1e\x2b\x0a\x79\x8a\xdd\x08\xc6\xa9\x56\x13\xe3\xf8\xd2\x1a\x5a\x31\xaf\xbe\x5a\x62\x81\x02\x20\xa9\x42\x8f\x71\x8e\xa7\xa2\x43\xfd\x8d\x93\x7c\xde\x92\x03\xd2\x53\xc1\xa0\xd1\x8d\x65\x97\xfb\x4b\xfe\x98\x09\xf1\x52\x7f\x50\x41\x9a\xa4\x3f\xb8\xdd\xc0\x04\x87\x5b\x7a\x4f\x2c\x1f\x8d\x2f\xad\xb8\x98\x18\x71\x01\x83\x05\xbb\x1b\x88\xba\xc3\x7c\xe5\x23\x73\x21\x1d\xd8\xbb\xdf\xe5\xc2\x91", 241, { 0x93, 0x4c, 0xcc, 0x99, 0x4e, 0xac, 0x76, 0x01, 0xbd, 0x95, 0x4b, 0x71, 0x97, 0xc2, 0xb6, 0x4b } },
{ "\xd7\xb2\xd7\x89\x08\xdd\x01\x0c\xff\x6f\x1c\x38\xa9\x8f\x1e\x54\x49\x85\x52\xee\x84\x6a\xbd\x93\x9a\x6e\xa1\x2b\xaf\xc6\x1f\xee\x47\x30\xf7\x07\xd1\x24\x6c\xc3\x5a\x99\x43\x76\x62\x70\xe9\xeb\xcc\x81\xb4\x85\xee\x41\x42\xf6\xc9\x0d\xfe\x9b\x52\x15\xc1\x73\xef\xe7\x94\xbb\xfd\x97\x94\x27\x8e\x89\xee\xbe\x30\xdb\x0a\x52\xe8\x71\xc5\x9b\x3e\x9e\xd6\xf0\x72\x6b\x52\xa1\xcc\x88\x4a\xf3\x11\xcd\x92\xb9\x11\x6b\x9d\x8b\x5e\xb3\x84\xa6\x17\x83\x25\x60\xe2\x49\x68\x46\xf8\xb5\x9d\xd4\x59\xff\x01\xcf\x21\xd2\x60\x43\xf3\xd4\xd4\x15\x91\xd2\xab\x44\x8e\x8d\x67\xc0\x1a\x1b\xde\xe7\xfd\xfc\x82\x98\x9f\xba\xbb\xf6\x43\x3b\x70\xbb\x54\xa7\xa5\x36\xd8\xf0\x3e\xe2\x01\x02\xe2\xa5\xe2\x89\xfb\xa2\x3f\x59\xd9\xbb\x7d\x7f\xf6\xa6\xb8\xe2\x54\xaa\xf3\x94\x03\xf7\x6a\xbb\xbf\xa0\x04\x16\xb5\x36\xe5\x2e\x66\x02\x1f\x1c\xa5\xde\x88\xf1\xba\xb0\xa6\xc5\xa9\x84\xc7\x5f\x8d\x45\x2e\x7e\x1d\x18\x67\xc2\x50\x56\xbc\x3a\x1d\x24\xc0\x8b\x5c\xb0\x08\xb9\xc8\x09\xfa\x95\x25\x9b\xbd\xc3", 242, { 0x02, 0x2b, 0x4e, 0xda, 0x1a, 0x44, 0x77, 0x00, 0x7e, 0xbc, 0x69, 0xdc, 0x8d, 0x36, 0x54, 0xec } },
{ "\x7e\x46\x50\x67\x88\x1b\xb7\x6c\x23\xb3\x4f\x70\xfe\x2b\x43\x4f\x59\xbf\x17\x4b\xe6\x02\x61\xd5\xc9\xb7\x98\xfb\xbf\x50\x05\x6d\x5a\x00\xd6\x2d\x6a\x7f\x51\xd3\x78\x5a\x26\x7a\x6c\xf4\xdd\x4b\x4e\x1d\x6e\xa3\x29\x4c\xef\xe4\x0b\x7c\x68\xd5\x2a\xa1\xc2\xb7\x21\xc6\xde\xe5\x57\xc5\xc3\x26\x81\xa2\xef\x93\x3d\x84\xce\x1f\xdf\x50\x49\xc8\x49\xe3\x75\x59\xf3\xec\x6c\xd9\x0b\x65\x39\x94\xb6\xac\xed\xc3\x74\x42\xce\xda\xa1\x1e\xaf\x6f\x17\xaf\x5b\xc2\xf1\x6d\x2b\xed\x6b\x1b\xb7\xa9\xe5\x9b\xa9\xba\x06\x6d\xad\xf8\xfd\xc6\x84\xfc\xe3\x49\x38\x63\x3d\x64\x6a\xc2\x9d\x4a\xc7\x26\x67\x88\x99\x46\xb1\x46\x7a\x48\x44\x1d\x23\x2c\xc0\x8f\x62\xd9\xdb\x27\x2a\xc2\xc9\x2e\xc4\x35\xb8\x07\x24\x40\x73\x28\x56\x40\x26\xb5\x17\x07\x41\xbb\x80\xa9\x75\x05\xdd\xe3\xdb\x9f\x9c\x29\x34\xe5\x61\x4b\x4b\x46\x37\xc3\x77\x9b\xe0\x9d\x3c\x1e\x4d\x03\x11\x08\x29\x64\x3d\xcb\x8f\x41\xdb\xe9\xdd\x94\xfc\x6f\xa0\xdd\xeb\x12\xae\xca\x8b\xe4\x53\x82\xdd\xb3\xa3\x8e\x9e\xff\xef\x64\x0d\x95\x52", 243, { 0x9b, 0x3c, 0x03, 0x49, 0xe3, 0x82, 0x8c, 0x8e, 0xfb, 0x2f, 0xc9, 0x7b, 0xee, 0x4c, 0x16, 0x17 } },
{ "\x92\xcb\xcc\x6b\x83\xda\x5b\x25\xf1\xc8\xd6\xb1\xe8\xe5\xc3\x95\x73\xaf\x5d\xde\xe5\x4f\xe4\x71\xc5\x3c\x9f\x80\x57\xfe\x70\x18\xc3\x0d\x12\xd6\xe5\xd8\xf1\xba\xb0\xe1\xa5\x13\x3f\x05\x0d\x9a\x7a\xd9\x04\x9b\x61\x30\xc3\x4e\xf8\xba\xd3\x44\xcf\xc7\xac\xfd\x2d\x29\xef\x96\xd9\x36\x3d\x9f\x84\xec\xb2\x0b\xd6\x30\x02\x41\x13\x2f\x2e\x4f\x6a\xe5\xe2\x3e\xda\xbc\x6e\x80\xc1\x4c\x5f\x86\x03\x41\xba\x6e\xd3\x5a\xd4\xda\x21\x8e\xd1\xdc\xa0\x49\xb7\x0d\x73\xd4\x2e\xbd\x73\xd2\xd6\x44\x1f\xe6\x45\x77\x21\x72\x9b\x36\x79\x7b\xc4\x23\x48\xa8\x4a\x6d\x3b\x69\xd4\xca\x92\x35\x40\x83\xcc\xeb\x58\xa9\xf1\x5a\x33\x65\x7c\xdc\x2b\x6d\xe2\x1d\x76\x93\xc3\xf9\x63\x77\xac\x84\x33\x5d\x87\x23\x92\x19\xa0\xd7\xb0\x27\x54\x9a\x01\xd7\x58\xe2\x8d\xa5\xa3\x42\xf4\xa7\xf9\x30\x02\x1f\x16\xe1\xeb\x30\x73\x50\x23\xae\xb7\x5e\xdc\x0e\xbd\x14\x1d\x7c\x3e\x04\x7c\x0c\x1b\xcd\x78\x08\x4a\xbc\x75\x68\x5a\x8f\x54\x5f\xa4\x56\xae\x12\x10\x73\xae\x64\x81\xc0\x88\xec\xde\xcf\x9a\x08\xbe\x4c\x1d\x0b", 244, { 0x74, 0x8d, 0xdc, 0x08, 0x81, 0xb7, 0x48, 0x31, 0x0b, 0xac, 0x8b, 0x95, 0x9a, 0xee, 0x5a, 0xfc } },
{ "\xaf\x7f\x88\x91\x24\xee\x81\xf4\xf8\x20\x80\xd7\xa3\x7b\x03\xdf\xf8\x4f\x68\x82\x98\xec\x6a\xf7\xf7\xed\x3a\x4d\x08\x98\x39\x98\x88\x5d\x50\x46\xe4\x7c\xed\x8f\xc8\xc4\x9a\x0b\x46\x76\x3b\x5d\x9f\x48\xe4\x0d\xb0\x85\x55\x74\xfb\x51\x13\xd0\x51\x0b\x24\x77\x1a\xcb\x66\x29\x41\x0b\x8c\x7e\xbe\x61\xb6\x7e\xc1\x6a\xac\x4f\x78\xc3\xb8\x09\x7d\x31\x1d\xa6\xdf\xe0\x37\x15\xcb\xc9\x30\x6d\xd8\x2c\x5c\x3e\xec\x3d\x32\x04\xcd\xdb\xe8\xb5\x48\x7b\xaa\x7a\xf8\x23\x76\x7a\xb3\x93\x97\xd1\x97\x7e\xbb\x9f\xac\xf5\xb3\x3d\x36\xe5\xc8\x8b\x9a\xb7\xb4\x65\xea\x15\x44\x34\x0f\xcd\x88\xa0\x92\xce\xb3\x63\x07\x4e\x96\x39\x16\x0e\xb4\xf4\x27\xb5\x01\xab\xa9\x59\x3c\x12\x00\x1d\xe6\xe6\x09\xf4\xdd\x7f\x4b\x84\x9a\x87\xbb\x25\x04\xc9\x2b\x08\xee\x23\x51\x75\x34\x96\x70\x2c\x6d\x7c\xa5\xed\x4d\xd9\xd0\x13\x9a\xc9\x1d\x5c\xc9\x19\x2e\xc4\x35\xf2\xe7\x8e\xfb\xb1\xd5\x64\x74\xd2\x3c\x96\x50\x0a\xbb\x7e\x4b\x73\x9e\x04\x8f\xe2\xc0\x3e\xa6\x54\x1b\x2f\x1a\x87\xee\xb0\xac\xa6\x89\x6d\x2d\x1c\xb8", 245, { 0x64, 0x0a, 0x21, 0xd7, 0x1f, 0x9e, 0xd7, 0xcd, 0x82, 0xd1, 0xb8, 0xb3, 0x7e, 0xb4, 0xa8, 0x66 } },
{ "\x06\xfb\x8a\xca\x55\x1c\xd3\x3d\xcf\xf0\x54\x07\x03\x96\x31\x83\x40\xde\xcb\xf7\x54\xe6\x4e\xbe\x6e\x53\x66\x17\x25\x2e\x11\x88\x92\x58\x8f\xf0\x97\xab\x77\x28\x43\xaf\xe4\x55\x4e\xf6\xcc\xce\xbf\x15\x70\xa4\xad\x3f\xef\xd2\x21\x7f\xf6\x02\x1b\x92\x92\xfa\xac\x5e\x26\xa1\x40\x13\x78\xb2\xfe\xdd\xe5\xfc\x48\x43\xb5\x53\x5d\x1f\x89\x17\x1e\x3a\xf1\x5e\xee\x83\x1a\xc1\xb2\xec\xa5\xc0\xf7\xe2\x92\xd3\x33\x67\x5b\x0e\x24\xcd\x1d\x6f\x55\x10\xf1\xc7\xbf\xd1\x5a\x43\x8c\xeb\xd6\x97\xf7\xb4\x97\xc6\x4f\xd2\x4c\x90\x19\xb7\x18\x77\x55\xba\xa4\x70\xd9\xd3\x50\x23\xda\xf3\x84\xdf\x8a\xfe\x25\x1e\xdb\x66\x24\xaf\x61\x65\x30\x86\x55\xd7\x8b\x1c\xb5\xb1\xfa\x84\x89\x22\xd6\x0c\x41\x44\x40\x8c\x3b\x7f\x72\x4e\x60\x7b\x30\x99\xee\xbf\x5c\xdc\x50\xeb\xa9\x74\x29\x8e\x68\x1a\x6f\xa5\x7e\xec\xb4\xb1\x77\x16\x81\x73\xb3\x1d\xdb\x47\xbe\xc8\xe7\x1a\xbe\xab\xa9\x0a\x05\x51\xe8\x99\xc7\x05\x2e\x8c\xe5\x3d\xeb\x66\xe7\xa4\xb9\x7c\x09\xc3\xbb\xb5\x6c\x4b\x1e\xe0\x6d\x01\xc1\xb2\x13\x46\xf1\x5a", 246, { 0xb9, 0x5e, 0x62, 0x77, 0x29, 0xc0, 0xa2, 0xbd, 0x30, 0xef, 0x76, 0xa4, 0x2d, 0x67, 0xa4, 0xb2 } },
{ "\xa2\x42\x4d\xc3\x4c\xad\xc9\x66\x07\x39\xce\xc9\x7a\x9f\x7d\x97\x11\x45\x14\x5d\x30\x89\x6a\xdf\x83\xad\x94\x15\x74\x5f\xaa\xc5\xb6\xe3\xa3\xbe\xfe\xdf\x5d\xae\xd2\xc3\xba\xa1\x7a\xd3\xe4\x16\x12\xd2\xb0\xbf\xc1\x4c\x20\xd6\x04\x81\x03\x17\x24\xe9\xb7\x5e\xc6\x68\x0f\xdd\xa1\x10\x4f\xf9\x4a\x8d\x54\xc2\x2b\x31\xd1\x0d\x92\x9d\xb3\x30\xe5\x08\xa6\x5a\xf4\x2f\xb1\x8c\x67\xd9\xfd\x38\x56\x06\xb3\x74\xf7\xb4\x03\xdb\x72\x4d\x40\x01\xd1\xb0\x28\x90\x13\xda\x42\x04\x60\x31\x60\xff\x56\x6d\x44\x49\x81\x23\x5f\x68\xea\xf0\xb4\xd8\xc6\x3e\xdc\xe8\x4f\xb6\x22\x31\xb0\x42\xce\xb3\x1a\xbd\x7f\x8d\xf4\x3a\xb1\x59\x2f\xee\x5f\x22\xb7\xbb\xc2\x02\x05\x59\x37\x5d\xd1\x23\x3e\xb4\xe5\x7c\x9e\x26\x0d\xdc\xa7\x8a\x2b\x7b\x90\x21\x67\x98\xfe\xfb\x83\x66\xa6\xe9\x4c\x94\x09\x1b\x2c\x77\x5e\x55\xdd\xd7\x8e\xd2\x38\x53\x59\xb5\x2c\x71\x96\x28\xca\x46\x97\x14\x7c\xbe\xaa\x7b\x56\x89\xbc\x75\x84\xa3\x19\xc5\xe3\x7d\x4f\x17\xad\xcd\x30\xd8\x4c\xef\xf5\xb2\x4f\xf6\x7f\xa3\x7a\x54\xb9\xb9\xf7\x21\x1a", 247, { 0x98, 0x8d, 0x87, 0xaa, 0xc7, 0xf9, 0xad, 0x6e, 0x99, 0x00, 0xf9, 0x61, 0x74, 0x29, 0xad, 0xca } },
{ "\x6c\xab\x0b\x47\x97\xa4\xdb\xd5\x15\xae\xa0\x2c\xf4\x05\x7a\x87\x59\x24\x05\x17\xf0\xbc\x5f\x47\x0d\xc0\xd8\x1b\x64\x9d\x35\xb2\x61\x87\xa1\xea\x25\xef\x31\x22\x1e\x11\x12\x1a\x42\xa9\x52\xf7\xe8\xc5\x47\x64\x43\xb6\x9f\xd2\x7b\x20\x06\xdf\x6a\x31\x6e\xd5\xf0\xee\xfe\x49\xf3\xa9\x99\xe4\xf6\x8f\x09\x3e\x55\x5e\xc8\xe6\x1a\x33\x6b\x7e\x7f\x81\xac\x03\x01\x1e\x12\x2b\x1e\x77\x3f\xe7\xab\xe4\xd5\x08\xd4\x16\x06\xfe\xb0\xad\xb8\xbb\x7f\xe6\x51\xb5\x84\x72\x24\x0b\x79\x62\x77\xbf\xb4\x3d\x30\x21\xd4\x34\x1c\x4d\x27\x6d\xdc\xcb\x9c\x7b\x6d\x54\x5f\xef\x52\xb4\x17\x08\x60\xcb\xb8\x85\x26\xad\x05\x9b\xf7\xe9\xa6\x03\x95\xe7\xe1\x2a\x7b\x6a\xf8\x8c\xc7\x36\x1f\x1b\xc2\xcb\x19\xd9\x0d\x4f\x6e\x85\x6b\x89\x4b\x71\x25\x09\xf6\x72\x1e\x66\xec\xf2\x73\xa0\x98\x20\xce\xa4\xb2\x46\x48\xed\x32\x3a\xf8\x47\xf0\xee\x1d\xae\xda\x23\xe3\x56\xd1\x3a\xd6\xc4\x20\x2b\xe0\x19\x99\x8e\x00\x6f\x4e\xd7\x8a\x5c\xe9\x9f\x14\x94\xa9\x1d\x04\xab\xf9\xb3\xb4\xf7\xaf\xa5\x3f\x93\xde\xe4\xeb\x81\x58\x09\x33\xe7", 248, { 0xaf, 0x11, 0x9d, 0x89, 0x3d, 0x36, 0x81, 0xb4, 0x1a, 0x91, 0x0b, 0x44, 0x0c, 0xe4, 0x73, 0xf2 } },
{ "\xc3\x5d\x6f\x7a\x56\x15\x22\xf8\x31\x9b\xe0\xcf\x57\x07\xda\xdb\x49\xac\x08\x4d\x3f\xcf\xf1\xa7\x05\x73\x1a\xe3\x71\x50\x09\xb3\x7d\xe1\xf4\xe4\x05\x9c\x0b\xdc\x1e\x3d\x5f\x42\x10\x3c\x6d\xbc\xf2\x5d\x4b\xd3\xe1\x66\x6e\xf4\xdc\xea\x16\x90\x3f\x44\x56\x62\xda\xa0\xc3\xd0\xae\x33\xb9\x6b\x43\x8a\x45\x91\xa9\x00\xb2\x32\x09\x4a\xb3\xaf\xe6\x2c\x2a\xde\xf6\x4e\x93\x2a\x97\x29\x10\xd8\xf0\x1c\x11\x64\xa5\x9b\x9f\x0a\x36\x87\x46\x60\xf5\x98\x9d\x20\xa2\xf6\x73\x04\xa4\xe7\x98\xca\xe6\xa3\x45\x57\x4c\x44\x29\xf8\xd1\xd9\x10\xc3\x3f\x9a\x32\x1c\x89\x35\x16\x53\xc8\x47\x21\x6b\xd0\xe6\xbf\xf6\x6f\x5b\x2d\x1c\x42\xed\xe0\xba\x33\xd8\x95\xa6\x92\x5d\xf6\x11\xcf\xf3\xe6\x06\xd2\x9b\x69\x0f\xf7\x51\x33\xf6\xa9\x9e\xcc\xa9\x9a\x4b\x5c\x37\x9c\x30\x19\xf7\x1f\x2a\x49\xc7\x48\x2a\xf6\x72\xaa\x6a\x2e\x2b\xa3\xbb\xf4\x36\x55\xfb\xc7\xa6\x40\xa2\xcc\x41\x79\x7b\x9a\x7f\x89\x6f\xa2\xb1\xe5\x7c\x39\x3f\x05\xc5\x44\x0a\x22\xc4\x7f\xf0\x91\x9b\x6a\x6d\xb7\x87\xd0\x5e\xa8\x75\xf5\xe1\x61\xaf\x5b\x59\x9d", 249, { 0x49, 0x19, 0x39, 0x2e, 0xe6, 0x98, 0xbd, 0xe8, 0x33, 0xe7, 0x7f, 0x85, 0xcb, 0x16, 0x46, 0xeb } },
{ "\xaf\x31\x8e\x57\x14\x59\xf1\xde\xb2\x14\xfd\x8e\xc4\x4d\xb8\x30\x3c\x7f\x59\xf0\x3b\x43\x03\xf7\x9d\x79\xaf\xa5\xab\x13\x29\x6c\xf4\x79\x31\x4c\x35\x9c\xc2\xe6\x75\x9b\x6f\x40\x2e\x0b\xe8\x14\xa5\xe7\x9c\xd5\x5b\x14\x79\x3f\x9c\x8e\xce\x99\x34\x35\x52\x8a\x41\x2e\x3e\x95\x24\xf7\x95\x33\x91\x0b\x84\x8c\xc6\x2e\xe3\xd1\xd9\x56\xdb\x39\x29\x36\xa2\x95\xf6\x68\x62\x92\x0d\x35\x39\x8b\x9c\x04\x59\x09\x24\x5e\x4e\xd8\x8c\x9a\x60\xc6\x51\x2a\x0e\xfb\xdb\x80\xbb\xf0\xeb\x9e\x65\x0e\x31\x39\x8f\xe3\xfb\x89\x41\x03\x07\xb0\x26\x79\x79\xc4\xd3\xe9\xe8\x7b\x27\x43\x92\x72\xcd\x26\xb0\x1a\xde\xcf\xe5\x3f\xa4\xbc\xcf\x36\x7a\xe1\xc0\xa3\xcf\x86\x87\xe4\x49\xbb\x67\x1e\x05\x79\x29\xe2\xfd\x57\x4d\x7b\x83\xe5\x5c\xd6\xea\xa9\x59\x0e\x43\xb4\x56\x94\x45\xdf\x22\xf8\x46\xa7\x20\x56\x66\xa2\x33\x5f\xcb\x9d\xd5\x03\x06\x55\x47\xb8\x94\xce\xe3\x6a\x81\x52\x8d\xff\x27\x09\x48\x85\x15\x32\xe4\xfb\x0b\xfc\xd5\xb9\x21\x03\x20\x7d\x06\x6a\x6e\x12\x66\x91\x43\x9e\x65\x73\x48\x89\x49\x9f\xc4\x06\x34\xd1\x29\x3f", 250, { 0x7c, 0x09, 0xc7, 0x86, 0x67, 0x7f, 0xd7, 0x70, 0x09, 0x34, 0xc4, 0x7d, 0xa5, 0x07, 0x09, 0x7f } },
{ "\x0e\x2d\xcb\x21\x81\x17\xab\xc1\x1e\xb1\x72\x69\x9d\xf2\x79\x44\x41\x60\x05\xa1\x5a\x6a\x90\xe7\xe4\x64\x42\x16\x4d\x1f\x7f\xf5\x54\x24\x9a\xde\x0d\x8d\xa7\x22\x01\x81\x6d\x1a\x72\x4a\x7a\xcb\xbb\x15\x51\x35\xd6\x45\xbf\x38\xf8\x73\x4c\x24\x57\x06\xcc\xdc\x0b\x6c\x15\xa5\x12\xf2\xca\x90\x6e\x46\x56\x82\x69\x86\xf5\xdd\xf9\x04\xec\xcd\x3e\x99\xd9\x31\x27\xa3\x25\x23\x35\x9c\x95\x26\x58\x58\x00\xeb\xf5\xdb\x1b\xc0\x09\xd4\x70\x96\x67\xba\x6d\xad\x1d\x82\x99\xde\xf5\xfa\xe1\x84\x17\xc5\x11\x08\xcc\xf3\x5e\x08\x5d\x3c\x20\x24\x1a\xda\x9d\x65\x76\x00\xff\x49\x4f\xfa\x68\x6f\x4c\xe2\x1c\xdb\x60\xfc\xdd\xe7\x6b\xaf\x54\xc7\xff\x21\xab\xb7\x3f\x6d\x37\xc3\xe4\x84\x53\x32\x59\x9d\x48\x90\x06\x5a\x68\x57\xab\x79\x3a\x3a\xe2\x33\xcf\x0d\xc6\x34\x33\x54\xb3\x38\xff\x66\x23\x3f\x0c\x3d\xb7\x6c\x42\xdd\x57\x80\x8e\x5f\x70\xed\xf2\x9a\x5c\x9a\xb6\x6c\xe0\x33\xbc\xaa\xce\x29\xf1\xa2\xcb\x4d\xdf\x49\x2b\x04\x60\x06\xf8\x28\x6e\x1a\x12\x7c\x15\xaa\x70\xc9\x89\x6a\x84\x99\x54\xe8\xbd\x8f\xa7\x72\x26\x61\xd2", 251, { 0xb3, 0x4a, 0x6d, 0xad, 0x44, 0xc4, 0x04, 0xa4, 0x65, 0x17, 0xe7, 0x33, 0x5a, 0xd6, 0x98, 0x59 } },
{ "\xa5\x2c\x74\xcf\x94\x7c\x13\xf9\x91\x0b\x4b\xda\x9b\x2f\x65\x21\x64\xeb\x01\xb3\xfd\x48\xcb\xd8\x20\xde\xdd\x96\x1a\x72\xb1\x1b\x53\xb9\xc1\x53\x7b\x3b\xbd\x9a\x53\x53\x68\x8b\x15\x53\x10\xf7\x81\xc4\xa8\xf2\x86\xca\x83\x07\x89\xa6\xaf\x8b\x54\x56\xec\x0f\x9e\x57\x48\xef\x33\x8a\x58\x07\xc0\x34\x15\x86\x3d\x20\x50\xda\xf7\xdf\xd3\xcb\x39\x30\x16\xa4\x96\x7a\x9b\x8b\xd6\x76\xe7\xf2\x7b\xe9\x1d\x26\xee\x8f\x38\x05\x4b\x14\xe4\xcc\xc6\x3b\xfa\x0e\xb8\x22\x96\xc1\x4a\x9c\xd7\x73\xbc\xbe\x33\x9a\x53\x76\x74\x08\xdd\x54\x53\x7d\xe2\x6c\xaf\x57\x69\x54\x6a\x64\x64\x49\xe1\xd8\xb9\x6e\x06\x5a\xed\x34\x1b\x38\x6f\xd5\x0c\xbc\x7f\xf9\x6a\x96\xb9\x7c\x00\x78\x42\x47\x14\xc1\x8d\x5b\x3b\x51\xcb\xec\xd9\x7b\xed\xaa\x35\x18\x57\x1a\x35\xb8\x22\x23\xba\xf4\x0e\xa5\x9a\xdf\x03\x44\x36\x9b\x42\x43\xb8\x07\x2d\x8a\xeb\x96\xaf\xca\xb7\x3b\x49\xb7\x37\x80\xba\x74\x79\xb6\x4b\x0d\xd1\x47\xb4\x1d\xda\x27\xae\x90\x0b\x69\x16\x83\xf1\xee\xbb\x48\x0e\x38\xc4\x85\x4e\x5c\xc1\x7c\x22\x16\x4c\x65\x3c\xf7\x5b\xf7\xe5\xb9", 252, { 0xdc, 0x2a, 0xf1, 0x01, 0x65, 0x86, 0xb7, 0x25, 0x94, 0xcb, 0x82, 0x3e, 0x4d, 0x4f, 0xbe, 0x81 } },
{ "\x26\x05\xfe\xb3\xaf\x45\x91\x67\xf3\x2d\x13\x39\xab\xf7\x38\x3b\xbf\xc3\x73\x23\x48\xda\x09\x5e\x40\x10\xd1\x3d\xc9\x44\x8a\x4e\x16\x02\xd9\xc6\xfa\x47\xdd\x19\x0b\x64\x70\xac\x72\xfb\xfd\xa2\x52\x26\xf9\xd3\xd3\xb8\x00\xdb\xca\x9b\x8c\x4e\x07\x58\x54\x09\x3a\xb6\x3f\xa4\x82\x79\x03\x03\x94\x4b\x5f\x0c\x84\xb9\xf1\x73\x33\x54\xb4\xb0\x56\xf8\x1a\x12\x1e\x29\xc2\xed\x89\x99\xd7\xef\x45\xc6\x04\x91\x3c\xc0\x17\xa9\xc1\x08\x31\x1c\x55\x94\xa7\xb0\x15\xf0\x79\xff\xc4\x7e\x6d\x87\x71\xde\xc7\xdf\xc5\x68\xa6\x04\xeb\xd6\xfe\xd2\x1c\xff\x2d\x8e\xc6\xbe\x3c\xa0\x58\xf1\xb5\x5e\xac\x9d\x1c\x03\x12\x2f\x0b\xbe\xf5\xdd\xed\xe7\x2d\x2b\x57\x4c\xe0\x8a\xfe\xaa\x15\x1b\x59\x37\xd7\x91\xd4\x5c\x02\x34\xad\x80\xad\x01\x6f\x00\x34\xef\x09\x3b\xe0\x4c\x8b\xc9\x35\x46\x7c\xcb\xd9\x86\xda\x5d\x1c\xf7\xaf\x28\x28\xa5\x4c\x15\xc6\xc0\x25\x1c\xca\xbf\x48\x3a\x1d\xa1\x7b\x81\x65\x4e\xf2\x49\x53\x1c\xaa\x84\x88\x6f\x65\x30\x25\x78\x42\xe5\xee\x1e\xf8\x8e\x19\xf9\xaf\x34\xb9\x7a\x7b\xf6\xc2\x29\x75\x15\xb8\x07\x78\x2c\xf9", 253, { 0x84, 0x6b, 0xe9, 0x6b, 0xfc, 0xff, 0x73, 0x49, 0xce, 0xf5, 0x59, 0xf8, 0x85, 0x75, 0x4b, 0x6d } },
{ "\x12\x75\x7c\xa3\xe7\x74\x65\x23\x81\x28\xf1\x5b\x1f\x2d\x8b\x6f\x44\xe0\xcd\x2d\xd8\xdb\x77\x16\x6c\x0b\x7b\x0d\x9b\x70\x34\x9b\x8c\x71\xb7\xdd\x93\xda\x42\x0b\xf7\x73\xd2\xa5\xce\x3e\xd1\x3c\x05\x4c\xeb\xda\x7c\x3c\x01\x0f\x4e\x51\x37\x99\x2e\x2f\x28\xaf\xed\x32\x39\xea\x18\x6b\x0b\xd0\xbd\x39\x0a\xff\x4e\x7f\x22\xf3\x9f\x87\x92\x74\x0a\x73\xd8\x9f\xb2\x5b\xcc\x8e\xe4\x08\xc9\xa7\x99\x4c\x06\x7e\x18\xfc\x02\x68\xb8\x8c\x1e\x9d\xc3\x45\x44\x08\x77\x25\xc5\xaf\x26\x53\x41\xba\x7d\x3d\xbf\x22\xe1\x50\xdd\xf7\xf5\x53\x21\x4d\x38\x61\x6d\xc4\xcc\x81\x91\xb3\x51\xe3\xfb\xf1\xf0\xba\x89\x3f\x74\xb0\x7f\x55\x92\x0a\x94\x88\x49\x5a\x27\x14\x64\xdb\x8f\x0c\x1d\x6c\x90\xdb\xdc\x2c\xe9\x76\x1d\xae\x09\x20\x6f\xd9\xe2\xd9\x98\x5f\xd7\x64\xd6\xd8\xcf\xf4\x40\x7a\x6b\x72\x4b\x77\x54\x6d\x69\xf4\xad\x9f\xcc\xa1\xa8\x18\x49\xf9\x34\x0a\x57\x18\xd4\x30\x36\x34\x8b\xdb\x2c\xb9\xf4\x9a\xea\x05\x6e\x85\x0e\xbd\x73\x26\xc2\xca\x0a\x05\x81\xf4\x53\xcf\xfa\x19\x40\x22\x0d\x09\x63\xf8\xf2\x01\xe1\xad\x79\xc3\x86\xae\x6b\x4e", 254, { 0x23, 0xe9, 0x88, 0x6a, 0xc2, 0x8c, 0x64, 0x59, 0xdb, 0xa2, 0xe7, 0x84, 0x10, 0x37, 0x53, 0xd0 } },
{ "\xf3\xc0\x10\x3f\xf2\xea\xca\xc4\xea\x01\xdf\x39\x6d\xce\x54\x61\xef\xdd\xf4\x42\x92\xe5\x70\x9d\x1c\xcf\xa0\x08\x0a\x65\xe8\xaa\xbe\x98\xb6\x93\xd3\x6d\x27\xb5\x91\x86\xc9\x83\x7c\x49\x7b\x25\x07\xaf\x71\x55\x66\xab\x54\xd9\x81\x34\x86\x9d\x04\xf1\x83\x6c\x93\x80\x63\x4b\x1b\x64\x7b\x72\x44\x89\x24\xe8\x02\x74\x93\xba\x4b\x0b\xe7\xd7\xe3\xfe\x42\x8b\x53\xd1\x0e\x96\xf8\x88\x61\xe9\x37\xee\x7b\xfc\xce\x81\x6c\xce\x46\xfd\xd3\x7a\x84\x83\xc1\x73\x7f\x66\xbb\x5c\x0c\x93\xde\x86\xd6\x9a\x1d\x07\x69\x5d\xa6\x73\x6d\x54\x64\x3a\xef\x7a\x9d\x9e\xdb\xd7\xba\x4f\x86\xab\x27\xa4\x68\x34\x51\x78\xe7\x1c\xcc\x9f\x4e\x83\x97\x04\xdc\xa4\x77\x61\xf9\x26\x7f\x99\x84\x01\xb1\xb5\x47\x0b\xbf\x79\x8c\x1f\xea\xa2\xc9\xe8\x0c\xbf\x76\x4f\xb1\xa9\xff\x7f\x5f\xa1\xd5\x91\xf6\x04\xa0\xd9\x32\xad\x8f\xcc\x4e\xe7\xcf\x8c\xc3\x0d\x19\x12\x2f\xc1\x66\xf7\x50\xc5\xbe\xdf\x2f\x79\x2e\x83\x59\xf1\xd8\x59\x48\xb2\x24\xe1\xe1\x0a\x15\x8e\x17\x09\xb6\x50\xad\x1f\xb3\xba\x18\x54\x03\xd5\x82\x1e\xc3\x80\xeb\xe2\x1f\x82\x6a\x0a\x69\x2e\x92", 255, { 0xe8, 0x99, 0x39, 0xbb, 0x59, 0x6c, 0x74, 0x74, 0x67, 0x04, 0x26, 0x4c, 0xd3, 0xaf, 0x38, 0x31 } },
{ "\xbf\x4b\xb1\xf0\x43\x19\xfc\xb0\xee\x40\x48\x5f\xc3\xdc\x4a\xca\xaf\x65\xf5\x06\x5d\x88\xe7\x89\xb8\x14\x71\x76\xfe\x0b\x46\xf6\x7e\xd9\xbf\xc1\xee\xa1\xc8\xbd\x6b\xb2\x6b\xbd\x0d\x18\xf7\x6a\x26\x4f\xcc\x3f\x18\x13\xc6\xae\xd0\x53\x44\x60\xe3\x43\xd4\x9a\x43\x91\x7c\xbb\x9d\xaf\xa7\xe1\x53\x4f\xab\xac\x11\xed\xf3\x1a\x0e\x85\xce\x92\xe1\x66\xd3\xfc\xfd\x1f\xee\x0d\xcb\x95\x0c\xa0\x63\x36\x5f\x40\xe6\x48\x4e\xc2\x7a\x5b\xfd\x0f\xe3\xdd\x74\x00\xbb\xcc\x6e\x62\x4e\x86\xc0\x18\x14\xbc\x64\x60\xcb\x85\x22\x2e\x31\x8f\xda\xb4\x5b\x70\x70\x03\xb5\x1a\x54\xcb\x97\x6d\xac\x3e\x7f\xe7\x21\x13\xf1\x77\x43\xa7\xe8\x6f\x9a\x3e\xf7\x97\x4a\x66\x01\x5d\x62\x7c\x91\x2a\xc1\x48\xd7\xd1\xa5\xc4\x40\x21\xd1\xfa\xb1\x9a\x5b\x0b\x5f\x3c\x0f\x4b\x4d\x7a\x83\x8a\x63\x4e\xb9\x6e\x28\x66\x6a\xfc\x1d\x7c\xf5\x35\xb5\xc3\xe4\xdd\xf4\x7d\x16\x57\xa2\xa9\x8f\xab\x2c\xda\xd9\xaa\x18\x23\x14\x29\x23\x2f\xa1\x69\xf9\x6d\x67\x97\x91\x68\xc0\x6e\x22\x34\x04\xfa\xc5\x04\xb0\x78\xa8\xd9\x32\x5a\xec\x55\x53\x66\x1d\xae\x41\xfe\x4b\xbe\x38\x7a", 256, { 0xf9, 0xd0, 0x4d, 0xbb, 0xc9, 0x3f, 0xc3, 0xa4, 0x73, 0xd0, 0xd2, 0x2a, 0xb4, 0x79, 0x0a, 0xcb } },
snapraid-12.1/cmdline/state.c 0000664 0000000 0000000 00000377267 14166610522 0016166 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "elem.h"
#include "import.h"
#include "search.h"
#include "state.h"
#include "support.h"
#include "parity.h"
#include "stream.h"
#include "handle.h"
#include "io.h"
#include "raid/raid.h"
#include "raid/cpu.h"
/**
* Configure the multithread support.
*
* Multi thread for write could be either faster or slower, depending
* on the specific conditions. With multithreads it's likely faster
* writing to disk, but you'll need to access multiple times the same data,
* being potentially slower.
*
* Multi thread for verify is instead always generally faster,
* so we enable it if possible.
*/
#if HAVE_THREAD
/* #define HAVE_MT_WRITE 1 */
#define HAVE_MT_VERIFY 1
#endif
const char* lev_name(unsigned l)
{
switch (l) {
case 0 : return "Parity";
case 1 : return "2-Parity";
case 2 : return "3-Parity";
case 3 : return "4-Parity";
case 4 : return "5-Parity";
case 5 : return "6-Parity";
}
return 0;
}
const char* lev_config_name(unsigned l)
{
switch (l) {
case 0 : return "parity";
case 1 : return "2-parity";
case 2 : return "3-parity";
case 3 : return "4-parity";
case 4 : return "5-parity";
case 5 : return "6-parity";
}
return 0;
}
static int lev_config_scan(const char* s, unsigned* level, unsigned* mode)
{
if (strcmp(s, "parity") == 0 || strcmp(s, "1-parity") == 0) {
*level = 0;
return 0;
}
if (strcmp(s, "q-parity") == 0 || strcmp(s, "2-parity") == 0) {
*level = 1;
return 0;
}
if (strcmp(s, "r-parity") == 0 || strcmp(s, "3-parity") == 0) {
*level = 2;
return 0;
}
if (strcmp(s, "4-parity") == 0) {
*level = 3;
return 0;
}
if (strcmp(s, "5-parity") == 0) {
*level = 4;
return 0;
}
if (strcmp(s, "6-parity") == 0) {
*level = 5;
return 0;
}
if (strcmp(s, "z-parity") == 0) {
*level = 2;
if (mode)
*mode = RAID_MODE_VANDERMONDE;
return 0;
}
return -1;
}
const char* lev_raid_name(unsigned mode, unsigned n)
{
switch (n) {
case 1 : return "par1";
case 2 : return "par2";
case 3 : if (mode == RAID_MODE_CAUCHY)
return "par3";
else
return "parz";
case 4 : return "par4";
case 5 : return "par5";
case 6 : return "par6";
}
return 0;
}
void state_init(struct snapraid_state* state)
{
unsigned l, s;
memset(&state->opt, 0, sizeof(state->opt));
state->filter_hidden = 0;
state->autosave = 0;
state->need_write = 0;
state->checked_read = 0;
state->block_size = 256 * KIBI; /* default 256 KiB */
state->raid_mode = RAID_MODE_CAUCHY;
state->file_mode = ADVISE_DEFAULT;
for (l = 0; l < LEV_MAX; ++l) {
state->parity[l].split_mac = 0;
for (s = 0; s < SPLIT_MAX; ++s) {
state->parity[l].split_map[s].path[0] = 0;
state->parity[l].split_map[s].uuid[0] = 0;
state->parity[l].split_map[s].size = PARITY_SIZE_INVALID;
state->parity[l].split_map[s].device = 0;
}
state->parity[l].smartctl[0] = 0;
state->parity[l].total_blocks = 0;
state->parity[l].free_blocks = 0;
state->parity[l].skip_access = 0;
state->parity[l].tick = 0;
state->parity[l].cached_blocks = 0;
state->parity[l].is_excluded_by_filter = 0;
}
state->tick_io = 0;
state->tick_misc = 0;
state->tick_sched = 0;
state->tick_raid = 0;
state->tick_hash = 0;
state->tick_last = tick();
state->share[0] = 0;
state->pool[0] = 0;
state->pool_device = 0;
state->lockfile[0] = 0;
state->level = 1; /* default is the lowest protection */
state->clear_past_hash = 0;
state->no_conf = 0;
tommy_list_init(&state->disklist);
tommy_list_init(&state->maplist);
tommy_list_init(&state->contentlist);
tommy_list_init(&state->filterlist);
tommy_list_init(&state->importlist);
tommy_hashdyn_init(&state->importset);
tommy_hashdyn_init(&state->previmportset);
tommy_hashdyn_init(&state->searchset);
tommy_arrayblkof_init(&state->infoarr, sizeof(snapraid_info));
}
void state_done(struct snapraid_state* state)
{
tommy_list_foreach(&state->disklist, (tommy_foreach_func*)disk_free);
tommy_list_foreach(&state->maplist, (tommy_foreach_func*)map_free);
tommy_list_foreach(&state->contentlist, (tommy_foreach_func*)content_free);
tommy_list_foreach(&state->filterlist, (tommy_foreach_func*)filter_free);
tommy_list_foreach(&state->importlist, (tommy_foreach_func*)import_file_free);
tommy_hashdyn_foreach(&state->searchset, (tommy_foreach_func*)search_file_free);
tommy_hashdyn_done(&state->importset);
tommy_hashdyn_done(&state->previmportset);
tommy_hashdyn_done(&state->searchset);
tommy_arrayblkof_done(&state->infoarr);
}
/**
* Check the configuration.
*/
static void state_config_check(struct snapraid_state* state, const char* path, tommy_list* filterlist_disk)
{
tommy_node* i;
unsigned l, s;
/* check for parity level */
if (state->raid_mode == RAID_MODE_VANDERMONDE) {
if (state->level > 3) {
/* LCOV_EXCL_START */
log_fatal("If you use the z-parity you cannot have more than 3 parities.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STIO */
}
}
for (l = 0; l < state->level; ++l) {
if (state->parity[l].split_mac == 0) {
/* LCOV_EXCL_START */
log_fatal("No '%s' specification in '%s'\n", lev_config_name(l), path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
if (tommy_list_empty(&state->contentlist)) {
/* LCOV_EXCL_START */
log_fatal("No 'content' specification in '%s'\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* check for equal paths */
for (i = state->contentlist; i != 0; i = i->next) {
struct snapraid_content* content = i->data;
for (l = 0; l < state->level; ++l) {
for (s = 0; s < state->parity[l].split_mac; ++s) {
if (pathcmp(state->parity[l].split_map[s].path, content->content) == 0) {
/* LCOV_EXCL_START */
log_fatal("Same path used for '%s' and 'content' as '%s'\n", lev_config_name(l), content->content);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
}
/* check device of data disks */
if (!state->opt.skip_device && !state->opt.skip_disk_access) {
for (i = state->disklist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_disk* disk = i->data;
/* skip data disks that are not accessible */
if (disk->skip_access)
continue;
#ifdef _WIN32
if (disk->device == 0) {
/* LCOV_EXCL_START */
log_fatal("Disk '%s' has a zero serial number.\n", disk->dir);
log_fatal("This is not necessarily wrong, but for using SnapRAID\n");
log_fatal("it's better to change the serial number of the disk.\n");
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
for (j = i->next; j != 0; j = j->next) {
struct snapraid_disk* other = j->data;
if (disk->device == other->device) {
if (state->opt.force_device) {
/* note that we just ignore the issue */
/* and we DON'T mark the disk to be skipped */
/* because we want to use these disks */
if (!state->opt.no_warnings)
log_fatal("DANGER! Ignoring that disks '%s' and '%s' are on the same device\n", disk->name, other->name);
} else {
/* LCOV_EXCL_START */
log_fatal("Disks '%s' and '%s' are on the same device.\n", disk->dir, other->dir);
#ifdef _WIN32
log_fatal("Both have the serial number '%" PRIx64 "'.\n", disk->device);
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
log_fatal("to change one of the disk serial.\n");
#endif
/* in "fix" we allow to continue anyway */
if (strcmp(state->command, "fix") == 0) {
log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
/* skip data disks that are not accessible */
if (disk->skip_access)
continue;
if (!state->opt.skip_parity_access) {
for (l = 0; l < state->level; ++l) {
for (s = 0; s < state->parity[l].split_mac; ++s) {
if (disk->device == state->parity[l].split_map[s].device) {
if (state->opt.force_device) {
/* note that we just ignore the issue */
/* and we DON'T mark the disk to be skipped */
/* because we want to use these disks */
if (!state->opt.no_warnings)
log_fatal("DANGER! Ignoring that disks '%s' and %s '%s' are on the same device\n", disk->dir, lev_name(l), state->parity[l].split_map[s].path);
} else {
/* LCOV_EXCL_START */
log_fatal("Disk '%s' and %s '%s' are on the same device.\n", disk->dir, lev_name(l), state->parity[l].split_map[s].path);
#ifdef _WIN32
log_fatal("Both have the serial number '%" PRIx64 "'.\n", disk->device);
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
log_fatal("to change one of the disk serial.\n");
#endif
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
}
}
if (state->pool[0] != 0 && disk->device == state->pool_device) {
/* LCOV_EXCL_START */
log_fatal("Disk '%s' and pool '%s' are on the same device.\n", disk->dir, state->pool);
#ifdef _WIN32
log_fatal("Both have the serial number '%" PRIx64 "'.\n", disk->device);
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
log_fatal("to change one of the disk serial.\n");
#endif
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
/* check device of parity disks */
if (!state->opt.skip_device && !state->opt.skip_parity_access) {
for (l = 0; l < state->level; ++l) {
for (s = 0; s < state->parity[l].split_mac; ++s) {
unsigned j, t;
/* skip parity disks that are not accessible */
if (state->parity[l].skip_access)
continue;
#ifdef _WIN32
if (state->parity[l].split_map[s].device == 0) {
/* LCOV_EXCL_START */
log_fatal("Disk '%s' has a zero serial number.\n", state->parity[l].split_map[s].path);
log_fatal("This is not necessarily wrong, but for using SnapRAID\n");
log_fatal("it's better to change the serial number of the disk.\n");
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
for (j = l + 1; j < state->level; ++j) {
for (t = 0; t < state->parity[j].split_mac; ++t) {
if (state->parity[l].split_map[s].device == state->parity[j].split_map[t].device) {
if (state->opt.force_device) {
/* note that we just ignore the issue */
/* and we DON'T mark the disk to be skipped */
/* because we want to use these disks */
if (!state->opt.no_warnings)
log_fatal("DANGER! Skipping parities '%s' and '%s' on the same device\n", lev_config_name(l), lev_config_name(j));
} else {
/* LCOV_EXCL_START */
log_fatal("Parity '%s' and '%s' are on the same device.\n", state->parity[l].split_map[s].path, state->parity[j].split_map[t].path);
#ifdef _WIN32
log_fatal("Both have the serial number '%" PRIx64 "'.\n", state->parity[l].split_map[s].device);
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
log_fatal("to change one of the disk serial.\n");
#endif
/* in "fix" we allow to continue anyway */
if (strcmp(state->command, "fix") == 0) {
log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
}
if (state->pool[0] != 0 && state->pool_device == state->parity[l].split_map[s].device) {
/* LCOV_EXCL_START */
log_fatal("Pool '%s' and parity '%s' are on the same device.\n", state->pool, state->parity[l].split_map[s].path);
#ifdef _WIN32
log_fatal("Both have the serial number '%" PRIx64 "'.\n", state->pool_device);
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
log_fatal("to change one of the disk serial.\n");
#endif
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
}
/* check device of pool disk */
#ifdef _WIN32
if (!state->opt.skip_device) {
if (state->pool[0] != 0 && state->pool_device == 0) {
/* LCOV_EXCL_START */
log_fatal("Disk '%s' has a zero serial number.\n", state->pool);
log_fatal("This is not necessarily wrong, but for using SnapRAID\n");
log_fatal("it's better to change the serial number of the disk.\n");
log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#endif
/* count the content files */
if (!state->opt.skip_device && !state->opt.skip_content_access) {
unsigned content_count;
content_count = 0;
for (i = state->contentlist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_content* content = i->data;
/* check if there are others in the same disk */
for (j = i->next; j != 0; j = j->next) {
struct snapraid_content* other = j->data;
if (content->device == other->device) {
log_fatal("WARNING! Content files on the same disk: '%s' and '%s'.\n", content->content, other->content);
break;
}
}
if (j != 0) {
/* skip it */
continue;
}
++content_count;
}
if (content_count < state->level + 1) {
/* LCOV_EXCL_START */
log_fatal("You must have at least %d 'content' files in different disks.\n", state->level + 1);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* check for speed */
#ifdef CONFIG_X86
if (!raid_cpu_has_ssse3())
#endif
if (state->raid_mode == RAID_MODE_CAUCHY && !state->opt.no_warnings) {
if (state->level == 3) {
log_fatal("WARNING! Your CPU doesn't have a fast implementation for triple parity.\n");
log_fatal("WARNING! It's recommended to switch to 'z-parity' instead than '3-parity'.\n");
} else if (state->level > 3) {
log_fatal("WARNING! Your CPU doesn't have a fast implementation beyond triple parity.\n");
log_fatal("WARNING! It's recommended to reduce the parity levels to triple parity.\n");
}
}
/* ensure that specified filter disks are valid ones */
for (i = tommy_list_head(filterlist_disk); i != 0; i = i->next) {
tommy_node* j;
struct snapraid_filter* filter = i->data;
for (j = state->disklist; j != 0; j = j->next) {
struct snapraid_disk* disk = j->data;
if (fnmatch(filter->pattern, disk->name, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
break;
}
if (j == 0) {
/* check matching with parity disks */
for (l = 0; l < state->level; ++l)
if (fnmatch(filter->pattern, lev_config_name(l), FNM_CASEINSENSITIVE_FOR_WIN) == 0)
break;
if (l == state->level) {
/* LCOV_EXCL_START */
log_fatal("Option -d, --filter-disk %s doesn't match any data or parity disk.\n", filter->pattern);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
}
/**
* Validate the smartctl command.
*
* It must contains only one %s string, and not other % chars.
*/
static int validate_smartctl(const char* custom)
{
const char* s = custom;
int arg = 0;
while (*s) {
if (s[0] == '%' && s[1] == 's') {
if (arg) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
arg = 1;
} else if (s[0] == '%') {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
++s;
}
return 0;
}
void state_config(struct snapraid_state* state, const char* path, const char* command, struct snapraid_option* opt, tommy_list* filterlist_disk)
{
STREAM* f;
unsigned line;
tommy_node* i;
unsigned l, s;
/* copy the options */
state->opt = *opt;
/* if unset, sort by physical order */
if (!state->opt.force_order)
state->opt.force_order = SORT_PHYSICAL;
/* adjust file mode */
if (state->opt.file_mode != ADVISE_DEFAULT) {
state->file_mode = state->opt.file_mode;
} else {
/* default mode, if nothing is specified */
state->file_mode = ADVISE_DISCARD;
}
/* store current command */
state->command = command;
log_tag("conf:file:%s\n", path);
f = sopen_read(path);
if (!f) {
/* LCOV_EXCL_START */
if (errno == ENOENT) {
if (state->opt.auto_conf) {
log_tag("conf:missing:\n");
/* mark that we are without a configuration file */
state->no_conf = 1;
state->level = 0;
return;
}
log_fatal("No configuration file found at '%s'\n", path);
} else if (errno == EACCES) {
log_fatal("You do not have rights to access the configuration file '%s'\n", path);
} else {
log_fatal("Error opening the configuration file '%s'. %s.\n", path, strerror(errno));
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
line = 1;
while (1) {
char tag[PATH_MAX];
char buffer[PATH_MAX];
int ret;
int c;
unsigned level;
/* skip initial spaces */
sgetspace(f);
/* read the command */
ret = sgettok(f, tag, sizeof(tag));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Error reading the configuration file '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* skip spaces after the command */
sgetspace(f);
if (strcmp(tag, "blocksize") == 0
/* block_size is the old format of the option */
|| strcmp(tag, "block_size") == 0) {
ret = sgetu32(f, &state->block_size);
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'blocksize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (state->block_size < 1) {
/* LCOV_EXCL_START */
log_fatal("Too small 'blocksize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (state->block_size > 16 * KIBI) {
/* LCOV_EXCL_START */
log_fatal("Too big 'blocksize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* check if it's a power of 2 */
if ((state->block_size & (state->block_size - 1)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Not power of 2 'blocksize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
state->block_size *= KIBI;
} else if (strcmp(tag, "hashsize") == 0
|| strcmp(tag, "hash_size") == 0 /* v11.0 used incorrectly this one, kept now for backward compatibility */
) {
uint32_t hash_size;
ret = sgetu32(f, &hash_size);
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'hashsize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (hash_size < 2) {
/* LCOV_EXCL_START */
log_fatal("Too small 'hashsize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (hash_size > HASH_MAX) {
/* LCOV_EXCL_START */
log_fatal("Too big 'hashsize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* check if it's a power of 2 */
if ((hash_size & (hash_size - 1)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Not power of 2 'hashsize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
BLOCK_HASH_SIZE = hash_size;
} else if (lev_config_scan(tag, &level, &state->raid_mode) == 0) {
char device[PATH_MAX];
char* split_map[SPLIT_MAX + 1];
unsigned split_mac;
char* slash;
uint64_t dev;
int skip_access;
if (state->parity[level].split_mac != 0) {
/* LCOV_EXCL_START */
log_fatal("Multiple '%s' specification in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = sgetlasttok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid '%s' specification in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty '%s' specification in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
split_mac = strsplit(split_map, SPLIT_MAX + 1, buffer, ",");
if (split_mac > SPLIT_MAX) {
/* LCOV_EXCL_START */
log_fatal("Too many files in '%s' specification in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
skip_access = 0;
state->parity[level].split_mac = split_mac;
for (s = 0; s < split_mac; ++s) {
pathimport(state->parity[level].split_map[s].path, sizeof(state->parity[level].split_map[s].path), split_map[s]);
if (!state->opt.skip_parity_access) {
struct stat st;
/* get the device of the directory containing the parity file */
pathimport(device, sizeof(device), split_map[s]);
slash = strrchr(device, '/');
if (slash)
*slash = 0;
else
pathcpy(device, sizeof(device), ".");
if (stat(device, &st) == 0) {
dev = st.st_dev;
} else {
/* if the disk can be skipped */
if (state->opt.force_device) {
/* use a fake device, and mark the disk to be skipped */
dev = 0;
skip_access = 1;
log_fatal("DANGER! Skipping inaccessible parity disk '%s'...\n", tag);
} else {
/* LCOV_EXCL_START */
log_fatal("Error accessing 'parity' dir '%s' specification in '%s' at line %u\n", device, path, line);
/* in "fix" we allow to continue anyway */
if (strcmp(state->command, "fix") == 0) {
log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
} else {
/* use a fake device */
dev = 0;
}
state->parity[level].split_map[s].device = dev;
}
/* store the global parity skip_access */
state->parity[level].skip_access = skip_access;
/* adjust the level */
if (state->level < level + 1)
state->level = level + 1;
} else if (strcmp(tag, "share") == 0) {
if (*state->share) {
/* LCOV_EXCL_START */
log_fatal("Multiple 'share' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = sgetlasttok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'share' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'share' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
pathimport(state->share, sizeof(state->share), buffer);
} else if (strcmp(tag, "pool") == 0) {
struct stat st;
if (*state->pool) {
/* LCOV_EXCL_START */
log_fatal("Multiple 'pool' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
ret = sgetlasttok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'pool' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'pool' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
pathimport(state->pool, sizeof(state->pool), buffer);
/* get the device of the directory containing the pool tree */
if (stat(buffer, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing 'pool' dir '%s' specification in '%s' at line %u\n", buffer, path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
state->pool_device = st.st_dev;
} else if (strcmp(tag, "content") == 0) {
struct snapraid_content* content;
char device[PATH_MAX];
char* slash;
struct stat st;
uint64_t dev;
ret = sgetlasttok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'content' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (pathcmp(buffer, "/dev/null") == 0 || pathcmp(buffer, "NUL") == 0) {
/* LCOV_EXCL_START */
log_fatal("You cannot use the null device as 'content' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'content' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* check if the content file is already specified */
for (i = state->contentlist; i != 0; i = i->next) {
content = i->data;
if (pathcmp(content->content, buffer) == 0)
break;
}
if (i) {
/* LCOV_EXCL_START */
log_fatal("Duplicate 'content' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* get the device of the directory containing the content file */
pathimport(device, sizeof(device), buffer);
slash = strrchr(device, '/');
if (slash)
*slash = 0;
else
pathcpy(device, sizeof(device), ".");
if (stat(device, &st) == 0) {
dev = st.st_dev;
} else {
if (state->opt.skip_content_access) {
/* use a fake device */
dev = 0;
log_fatal("WARNING! Skipping inaccessible content file '%s'...\n", buffer);
} else {
/* LCOV_EXCL_START */
log_fatal("Error accessing 'content' dir '%s' specification in '%s' at line %u\n", device, path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* set the lock file at the first accessible content file */
if (state->lockfile[0] == 0 && dev != 0) {
pathcpy(state->lockfile, sizeof(state->lockfile), buffer);
pathcat(state->lockfile, sizeof(state->lockfile), ".lock");
}
content = content_alloc(buffer, dev);
tommy_list_insert_tail(&state->contentlist, &content->node, content);
} else if (strcmp(tag, "data") == 0 || strcmp(tag, "disk") == 0) {
/* "disk" is the deprecated name up to SnapRAID 9.x */
char dir[PATH_MAX];
char device[PATH_MAX];
char uuid[UUID_MAX];
struct snapraid_disk* disk;
uint64_t dev;
int skip_access;
ret = sgettok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'data' name specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'data' name specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
sgetspace(f);
ret = sgetlasttok(f, dir, sizeof(dir));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'data' dir specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*dir) {
/* LCOV_EXCL_START */
log_fatal("Empty 'data' dir specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* get the device of the dir */
pathimport(device, sizeof(device), dir);
/* check if the disk name already exists */
for (i = state->disklist; i != 0; i = i->next) {
disk = i->data;
if (strcmp(disk->name, buffer) == 0)
break;
}
if (i) {
/* LCOV_EXCL_START */
log_fatal("Duplicate 'data' name '%s' at line %u\n", buffer, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* if the disk has to be present */
skip_access = 0;
if (!state->opt.skip_disk_access) {
struct stat st;
if (stat(device, &st) == 0) {
dev = st.st_dev;
/* read the uuid, if unsupported use an empty one */
if (devuuid(dev, uuid, sizeof(uuid)) != 0) {
*uuid = 0;
}
/* fake a different UUID when testing */
if (state->opt.fake_uuid) {
snprintf(uuid, sizeof(uuid), "fake-uuid-%d", state->opt.fake_uuid);
--state->opt.fake_uuid;
}
} else {
/* if the disk can be skipped */
if (state->opt.force_device) {
/* use a fake device, and mark the disk to be skipped */
dev = 0;
*uuid = 0;
skip_access = 1;
log_fatal("DANGER! Skipping inaccessible data disk '%s'...\n", buffer);
} else {
/* LCOV_EXCL_START */
log_fatal("Error accessing 'disk' '%s' specification in '%s' at line %u\n", dir, device, line);
/* in "fix" we allow to continue anyway */
if (strcmp(state->command, "fix") == 0) {
log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
} else {
/* use a fake device */
dev = 0;
*uuid = 0;
}
disk = disk_alloc(buffer, dir, dev, uuid, skip_access);
tommy_list_insert_tail(&state->disklist, &disk->node, disk);
} else if (strcmp(tag, "smartctl") == 0) {
char custom[PATH_MAX];
ret = sgettok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'smartctl' name specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'smartctl' name specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
sgetspace(f);
ret = sgetlasttok(f, custom, sizeof(custom));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'smartctl' option specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*custom) {
/* LCOV_EXCL_START */
log_fatal("Empty 'smartctl' option specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (validate_smartctl(custom) != 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'smartctl' option specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* search for parity */
if (lev_config_scan(buffer, &level, 0) == 0) {
if (state->parity[level].smartctl[0] != 0) {
/* LCOV_EXCL_START */
log_fatal("Duplicate parity smartctl '%s' at line %u\n", buffer, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
pathcpy(state->parity[level].smartctl, sizeof(state->parity[level].smartctl), custom);
} else {
struct snapraid_disk* disk;
/* search the disk */
disk = 0;
for (i = state->disklist; i != 0; i = i->next) {
disk = i->data;
if (strcmp(disk->name, buffer) == 0)
break;
}
if (!disk) {
/* LCOV_EXCL_START */
log_fatal("Missing disk smartctl '%s' at line %u\n", buffer, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (disk->smartctl[0] != 0) {
/* LCOV_EXCL_START */
log_fatal("Duplicate disk name '%s' at line %u\n", buffer, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
pathcpy(disk->smartctl, sizeof(disk->smartctl), custom);
}
} else if (strcmp(tag, "nohidden") == 0) {
state->filter_hidden = 1;
} else if (strcmp(tag, "exclude") == 0) {
struct snapraid_filter* filter;
ret = sgetlasttok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'exclude' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'exclude' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
filter = filter_alloc_file(-1, buffer);
if (!filter) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'exclude' specification '%s' in '%s' at line %u\n", buffer, path, line);
log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
} else if (strcmp(tag, "include") == 0) {
struct snapraid_filter* filter;
ret = sgetlasttok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'include' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'include' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
filter = filter_alloc_file(1, buffer);
if (!filter) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'include' specification '%s' in '%s' at line %u\n", buffer, path, line);
log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
} else if (strcmp(tag, "autosave") == 0) {
char* e;
ret = sgetlasttok(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'autosave' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!*buffer) {
/* LCOV_EXCL_START */
log_fatal("Empty 'autosave' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
state->autosave = strtoul(buffer, &e, 0);
if (!e || *e) {
/* LCOV_EXCL_START */
log_fatal("Invalid 'autosave' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* convert to GB */
state->autosave *= GIGA;
} else if (tag[0] == 0) {
/* allow empty lines */
} else if (tag[0] == '#') {
ret = sgetline(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
log_fatal("Invalid comment in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else {
/* LCOV_EXCL_START */
log_fatal("Invalid command '%s' in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* skip final spaces */
sgetspace(f);
/* next line */
c = sgeteol(f);
if (c == EOF) {
break;
}
if (c != '\n') {
/* LCOV_EXCL_START */
log_fatal("Extra data in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
++line;
}
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error reading the configuration file '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
sclose(f);
state_config_check(state, path, filterlist_disk);
/* select the default hash */
if (state->opt.force_murmur3) {
state->besthash = HASH_MURMUR3;
} else if (state->opt.force_spooky2) {
state->besthash = HASH_SPOOKY2;
} else {
#ifdef CONFIG_X86
if (sizeof(void*) == 4 && !raid_cpu_has_slowmult())
state->besthash = HASH_MURMUR3;
else
state->besthash = HASH_SPOOKY2;
#else
if (sizeof(void*) == 4)
state->besthash = HASH_MURMUR3;
else
state->besthash = HASH_SPOOKY2;
#endif
}
/* by default use the best hash */
state->hash = state->besthash;
/* by default use a random hash seed */
if (randomize(state->hashseed, HASH_MAX) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to get random values.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* no previous hash by default */
state->prevhash = HASH_UNDEFINED;
/* intentionally not set the prevhashseed, if used valgrind will warn about it */
log_tag("blocksize:%u\n", state->block_size);
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
log_tag("data:%s:%s\n", disk->name, disk->dir);
}
log_tag("mode:%s\n", lev_raid_name(state->raid_mode, state->level));
for (l = 0; l < state->level; ++l)
for (s = 0; s < state->parity[l].split_mac; ++s)
log_tag("%s:%u:%s\n", lev_config_name(l), s, state->parity[l].split_map[s].path);
if (state->pool[0] != 0)
log_tag("pool:%s\n", state->pool);
if (state->share[0] != 0)
log_tag("share:%s\n", state->share);
if (state->autosave != 0)
log_tag("autosave:%" PRIu64 "\n", state->autosave);
for (i = tommy_list_head(&state->filterlist); i != 0; i = i->next) {
char out[PATH_MAX];
struct snapraid_filter* filter = i->data;
log_tag("filter:%s\n", filter_type(filter, out, sizeof(out)));
}
if (state->filter_hidden)
log_tag("filter:nohidden:\n");
log_flush();
}
/**
* Find a disk by name.
*/
static struct snapraid_disk* find_disk_by_name(struct snapraid_state* state, const char* name)
{
tommy_node* i;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
if (strcmp(disk->name, name) == 0)
return disk;
}
if (state->no_conf) {
/* without a configuration file, add disks automatically */
struct snapraid_disk* disk;
disk = disk_alloc(name, "DUMMY/", -1, "", 0);
tommy_list_insert_tail(&state->disklist, &disk->node, disk);
return disk;
}
return 0;
}
/**
* Find a disk by UUID.
*/
static struct snapraid_disk* find_disk_by_uuid(struct snapraid_state* state, const char* uuid)
{
tommy_node* i;
struct snapraid_disk* found = 0;
/* special test case to find the first matching UUID */
/* when testing UUID are all equal or not supported */
/* and we should handle this case specifically */
if (state->opt.match_first_uuid)
return state->disklist->data;
/* LCOV_EXCL_START */
/* never find an empty uuid */
if (!*uuid)
return 0;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
if (strcmp(disk->uuid, uuid) == 0) {
/* never match duplicate UUID */
if (found)
return 0;
found = disk;
}
}
return found;
/* LCOV_EXCL_STOP */
}
/**
* Update the disk mapping if required.
*/
static void state_map(struct snapraid_state* state)
{
unsigned hole;
tommy_node* i;
unsigned uuid_mismatch;
unsigned diskcount;
unsigned l, s;
/* remove all the mapping without a disk */
/* this happens when a disk is removed from the configuration file */
/* From SnapRAID 4.0 mappings are automatically removed if a disk is not used */
/* when saving the content file, but we keep this code to import older content files. */
for (i = state->maplist; i != 0; ) {
struct snapraid_map* map = i->data;
struct snapraid_disk* disk;
disk = find_disk_by_name(state, map->name);
/* go to the next mapping before removing */
i = i->next;
if (disk == 0) {
/* disk not found, remove the mapping */
tommy_list_remove_existing(&state->maplist, &map->node);
map_free(map);
}
}
/* maps each unmapped disk present in the configuration file in the first available hole */
/* this happens when you add disks for the first time in the configuration file */
hole = 0; /* first position to try */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
struct snapraid_map* map;
tommy_node* j;
/* check if the disk is already mapped */
for (j = state->maplist; j != 0; j = j->next) {
map = j->data;
if (strcmp(disk->name, map->name) == 0) {
/* mapping found */
break;
}
}
if (j != 0) {
/* mapping is present, then copy the free blocks into to disk */
disk->total_blocks = map->total_blocks;
disk->free_blocks = map->free_blocks;
continue;
}
/* mapping not found, search for an hole */
while (1) {
for (j = state->maplist; j != 0; j = j->next) {
map = j->data;
if (map->position == hole) {
/* position already used */
break;
}
}
if (j == 0) {
/* hole found */
break;
}
/* try with the next one */
++hole;
}
/* insert the new mapping */
map = map_alloc(disk->name, hole, 0, 0, "");
tommy_list_insert_tail(&state->maplist, &map->node, map);
}
/* without configuration don't check for number of data disks or uuid changes */
if (state->no_conf)
return;
/* counter for the number of UUID mismatches */
uuid_mismatch = 0;
/* check if mapping match the disk uuid */
if (!state->opt.skip_disk_access) {
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
struct snapraid_disk* disk;
disk = find_disk_by_name(state, map->name);
if (disk == 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for mapping '%s'\n", map->name);
os_abort();
/* LCOV_EXCL_STOP */
}
if (disk->has_unsupported_uuid) {
/* if uuid is not available, skip this one */
continue;
}
/* if the uuid is changed */
if (strcmp(disk->uuid, map->uuid) != 0) {
/* mark the disk as with an UUID change */
disk->has_different_uuid = 1;
/* if the previous uuid is available */
if (map->uuid[0] != 0) {
/* count the number of uuid change */
++uuid_mismatch;
log_fatal("UUID change for disk '%s' from '%s' to '%s'\n", disk->name, map->uuid, disk->uuid);
} else {
/* no message here, because having a disk without */
/* UUID is the normal state of an empty disk */
disk->had_empty_uuid = 1;
}
/* update the uuid in the mapping, */
pathcpy(map->uuid, sizeof(map->uuid), disk->uuid);
/* write the new state with the new uuid */
state->need_write = 1;
}
}
}
/* check the parity uuid */
if (!state->opt.skip_parity_access) {
for (l = 0; l < state->level; ++l) {
for (s = 0; s < state->parity[l].split_mac; ++s) {
char uuid[UUID_MAX];
int ret;
ret = devuuid(state->parity[l].split_map[s].device, uuid, sizeof(uuid));
if (ret != 0) {
/* uuid not available, just ignore */
continue;
}
/* if the uuid is changed */
if (strcmp(uuid, state->parity[l].split_map[s].uuid) != 0) {
/* if the previous uuid is available */
if (state->parity[l].split_map[s].uuid[0] != 0) {
/* count the number of uuid change */
++uuid_mismatch;
log_fatal("UUID change for parity '%s[%u]' from '%s' to '%s'\n", lev_config_name(l), s, state->parity[l].split_map[s].uuid, uuid);
}
/* update the uuid */
pathcpy(state->parity[l].split_map[s].uuid, sizeof(state->parity[l].split_map[s].uuid), uuid);
/* write the new state with the new uuid */
state->need_write = 1;
}
}
}
}
if (!state->opt.force_uuid && uuid_mismatch > state->level) {
/* LCOV_EXCL_START */
log_fatal("Too many disks have UUID changed from the latest 'sync'.\n");
log_fatal("If this happens because you really replaced them,\n");
log_fatal("you can '%s' anyway, using 'snapraid --force-uuid %s'.\n", state->command, state->command);
log_fatal("Instead, it's possible that you messed up the disk mount points,\n");
log_fatal("and you have to restore the mount points at the state of the latest sync.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* count the number of data disks, including holes left after removing some */
diskcount = 0;
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
if (map->position + 1 > diskcount)
diskcount = map->position + 1;
}
/* ensure to don't go over the limit of the RAID engine */
if (diskcount > RAID_DATA_MAX) {
/* LCOV_EXCL_START */
log_fatal("Too many data disks. No more than %u.\n", RAID_DATA_MAX);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* now count the real number of data disks, excluding holes left after removing some */
diskcount = tommy_list_count(&state->maplist);
/* recommend number of parities */
if (!state->opt.no_warnings) {
/* intentionally use log_fatal() instead of log_error() to give more visibility at the warning */
if (diskcount >= 36 && state->level < 6) {
log_fatal("WARNING! With %u disks it's recommended to use six parity levels.\n", diskcount);
} else if (diskcount >= 29 && state->level < 5) {
log_fatal("WARNING! With %u disks it's recommended to use five parity levels.\n", diskcount);
} else if (diskcount >= 22 && state->level < 4) {
log_fatal("WARNING! With %u disks it's recommended to use four parity levels.\n", diskcount);
} else if (diskcount >= 15 && state->level < 3) {
log_fatal("WARNING! With %u disks it's recommended to use three parity levels.\n", diskcount);
} else if (diskcount >= 5 && state->level < 2) {
log_fatal("WARNING! With %u disks it's recommended to use two parity levels.\n", diskcount);
}
}
}
void state_refresh(struct snapraid_state* state)
{
tommy_node* i;
unsigned l, s;
/* for all disks */
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
struct snapraid_disk* disk;
uint64_t total_space;
uint64_t free_space;
int ret;
disk = find_disk_by_name(state, map->name);
if (disk == 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for mapping '%s'\n", map->name);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = fsinfo(disk->dir, 0, 0, &total_space, &free_space);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing disk '%s' to get file-system info. %s.\n", disk->dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* set the new free blocks */
map->total_blocks = total_space / state->block_size;
map->free_blocks = free_space / state->block_size;
/* also update the disk info */
disk->total_blocks = map->total_blocks;
disk->free_blocks = map->free_blocks;
}
/* for all parities */
for (l = 0; l < state->level; ++l) {
/* set the new free blocks */
state->parity[l].total_blocks = 0;
state->parity[l].free_blocks = 0;
for (s = 0; s < state->parity[l].split_mac; ++s) {
uint64_t total_space;
uint64_t free_space;
int ret;
ret = fsinfo(state->parity[l].split_map[s].path, 0, 0, &total_space, &free_space);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error accessing file '%s' to get file-system info. %s.\n", state->parity[l].split_map[s].path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* add the new free blocks */
state->parity[l].total_blocks += total_space / state->block_size;
state->parity[l].free_blocks += free_space / state->block_size;
}
}
/* note what we don't set need_write = 1, because we don't want */
/* to update the content file only for the free space info. */
}
/**
* Check the content.
*/
static void state_content_check(struct snapraid_state* state, const char* path)
{
tommy_node* i;
/* check that any map has different name and position */
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
tommy_node* j;
for (j = i->next; j != 0; j = j->next) {
struct snapraid_map* other = j->data;
if (strcmp(map->name, other->name) == 0) {
/* LCOV_EXCL_START */
log_fatal("Colliding 'map' disk specification in '%s'\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (map->position == other->position) {
/* LCOV_EXCL_START */
log_fatal("Colliding 'map' index specification in '%s'\n", path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
}
/**
* Check if the position is REQUIRED, or we can completely clear it from the state.
*
* Note that position with only DELETED blocks are discharged.
*/
static int fs_position_is_required(struct snapraid_state* state, block_off_t pos)
{
tommy_node* i;
/* check for each disk */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
struct snapraid_block* block = fs_par2block_find(disk, pos);
/* if we have at least one file, the position is needed */
if (block_has_file(block))
return 1;
}
return 0;
}
/**
* Check if the info block is REQUIREQ.
*
* This is used to ensure that we keep the last check used for scrubbing.
* and that we add it when importing old context files.
*
* Note that you can have position without info blocks, for example
* if all the blocks are not synced.
*
* Note also that not requiring an info block, doesn't mean that if present it
* can be discarded.
*/
static int fs_info_is_required(struct snapraid_state* state, block_off_t pos)
{
tommy_node* i;
/* check for each disk */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
struct snapraid_block* block = fs_par2block_find(disk, pos);
/* if we have at least one synced file, the info is required */
if (block_state_get(block) == BLOCK_STATE_BLK)
return 1;
}
return 0;
}
static void fs_position_clear_deleted(struct snapraid_state* state, block_off_t pos)
{
tommy_node* i;
/* check for each disk if block is really used */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
struct snapraid_block* block = fs_par2block_find(disk, pos);
/* if the block is deleted */
if (block_state_get(block) == BLOCK_STATE_DELETED) {
/* set it to empty */
fs_deallocate(disk, pos);
}
}
}
/**
* Check if a block position in a disk is deleted.
*/
static int fs_is_block_deleted(struct snapraid_disk* disk, block_off_t pos)
{
struct snapraid_block* block = fs_par2block_find(disk, pos);
return block_state_get(block) == BLOCK_STATE_DELETED;
}
/**
* Flush the file checking the final CRC.
* We exploit the fact that the CRC is always stored in the last 4 bytes.
*/
static void decoding_error(const char* path, STREAM* f)
{
unsigned char buf[4];
uint32_t crc_stored;
uint32_t crc_computed;
if (seof(f)) {
/* LCOV_EXCL_START */
log_fatal("Unexpected end of content file '%s' at offset %" PRIi64 "\n", path, stell(f));
log_fatal("This content file is truncated. Use an alternate copy.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error reading the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
log_fatal("Decoding error in '%s' at offset %" PRIi64 "\n", path, stell(f));
if (sdeplete(f, buf) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error flushing the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* get the stored crc from the last four bytes */
crc_stored = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
/* get the computed crc */
crc_computed = scrc(f);
/* adjust the stored crc to include itself */
crc_stored = crc32c(crc_stored, buf, 4);
if (crc_computed != crc_stored) {
log_fatal("Mismatching CRC in '%s'\n", path);
log_fatal("This content file is damaged! Use an alternate copy.\n");
exit(EXIT_FAILURE);
} else {
log_fatal("The file CRC is correct!\n");
}
}
static void state_read_content(struct snapraid_state* state, const char* path, STREAM* f)
{
block_off_t blockmax;
unsigned count_file;
unsigned count_hardlink;
unsigned count_symlink;
unsigned count_dir;
int crc_checked;
char buffer[PATH_MAX];
int ret;
tommy_array disk_mapping;
uint32_t mapping_max;
blockmax = 0;
count_file = 0;
count_hardlink = 0;
count_symlink = 0;
count_dir = 0;
crc_checked = 0;
mapping_max = 0;
tommy_array_init(&disk_mapping);
ret = sread(f, buffer, 12);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid header!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
/*
* File format versions:
* - SNAPCNT1/SnapRAID 4.0 First version.
* - SNAPCNT2/SnapRAID 7.0 Adds entries 'M' and 'P', to add free_blocks support.
* The previous 'm' entry is now deprecated, but supported for importing.
* Similarly for text file, we add 'mapping' and 'parity' deprecating 'map'.
* - SNAPCNT3/SnapRAID 11.0 Adds entry 'y' for hash size.
* - SNAPCNT3/SnapRAID 11.0 Adds entry 'Q' for multi parity file.
* The previous 'P' entry is now deprecated, but supported for importing.
*/
if (memcmp(buffer, "SNAPCNT1\n\3\0\0", 12) != 0
&& memcmp(buffer, "SNAPCNT2\n\3\0\0", 12) != 0
&& memcmp(buffer, "SNAPCNT3\n\3\0\0", 12) != 0
) {
/* LCOV_EXCL_START */
if (memcmp(buffer, "SNAPCNT", 7) != 0) {
decoding_error(path, f);
log_fatal("Invalid header!\n");
os_abort();
} else {
log_fatal("The content file '%s' was generated with a newer version of SnapRAID!\n", path);
exit(EXIT_FAILURE);
}
/* LCOV_EXCL_STOP */
}
while (1) {
int c;
/* read the command */
c = sgetc(f);
if (c == EOF) {
break;
}
if (c == 'f') {
/* file */
char sub[PATH_MAX];
uint64_t v_size;
uint64_t v_mtime_sec;
uint32_t v_mtime_nsec;
uint64_t v_inode;
uint32_t v_idx;
struct snapraid_file* file;
struct snapraid_disk* disk;
uint32_t mapping;
ret = sgetb32(f, &mapping);
if (ret < 0 || mapping >= mapping_max) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in mapping index!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
disk = tommy_array_get(&disk_mapping, mapping);
ret = sgetb64(f, &v_size);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (state->block_size == 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency due zero blocksize!\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* check for impossible file size to avoid to crash for a too big allocation */
if (v_size / state->block_size > blockmax) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in file size too big!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb64(f, &v_mtime_sec);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_mtime_nsec);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* STAT_NSEC_INVALID is encoded as 0 */
if (v_mtime_nsec == 0)
v_mtime_nsec = STAT_NSEC_INVALID;
else
--v_mtime_nsec;
ret = sgetb64(f, &v_inode);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetbs(f, sub, sizeof(sub));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (!*sub) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency for null file!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
/* allocate the file */
file = file_alloc(state->block_size, sub, v_size, v_mtime_sec, v_mtime_nsec, v_inode, 0);
/* insert the file in the file containers */
tommy_hashdyn_insert(&disk->inodeset, &file->nodeset, file, file_inode_hash(file->inode));
tommy_hashdyn_insert(&disk->pathset, &file->pathset, file, file_path_hash(file->sub));
tommy_hashdyn_insert(&disk->stampset, &file->stampset, file, file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec));
tommy_list_insert_tail(&disk->filelist, &file->nodelist, file);
/* read all the blocks */
v_idx = 0;
while (v_idx < file->blockmax) {
block_off_t v_pos;
uint32_t v_count;
/* get the "subcommand */
c = sgetc(f);
ret = sgetb32(f, &v_pos);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_count);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (v_idx + v_count > file->blockmax) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in block number!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
if (v_pos + v_count > blockmax) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in block size %u/%u!\n", blockmax, v_pos + v_count);
os_abort();
/* LCOV_EXCL_START */
}
/* fill the blocks in the run */
while (v_count) {
struct snapraid_block* block = fs_file2block_get(file, v_idx);
switch (c) {
case 'b' :
block_state_set(block, BLOCK_STATE_BLK);
break;
case 'n' :
/* deprecated NEW blocks are converted to CHG ones */
block_state_set(block, BLOCK_STATE_CHG);
break;
case 'g' :
block_state_set(block, BLOCK_STATE_CHG);
break;
case 'p' :
block_state_set(block, BLOCK_STATE_REP);
break;
default :
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid block type!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
/* read the hash only for 'blk/chg/rep', and not for 'new' */
if (c != 'n') {
ret = sread(f, block->hash, BLOCK_HASH_SIZE);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
} else {
/* set the ZERO hash for deprecated NEW blocks */
hash_zero_set(block->hash);
}
/* if the block contains a hash of past data */
/* and we are clearing such indeterminate hashes */
if (state->clear_past_hash
&& block_has_past_hash(block)
) {
/* set the hash value to INVALID */
hash_invalid_set(block->hash);
}
/* if we are disabling the copy optimization */
/* we want also to clear any already previously stored information */
/* in other sync commands */
/* note that this is required only in sync, and we detect */
/* this using the clear_past_hash flag */
if (state->clear_past_hash
&& state->opt.force_nocopy
&& block_state_get(block) == BLOCK_STATE_REP
) {
/* set the hash value to INVALID */
hash_invalid_set(block->hash);
/* convert from REP to CHG block */
block_state_set(block, BLOCK_STATE_CHG);
}
/* if we want a full reallocation, marks block as invalid parity */
/* note that we do this after the force_nocopy option */
/* to avoid to mixup the two things */
if (state->opt.force_realloc
&& block_state_get(block) == BLOCK_STATE_BLK) {
/* convert from BLK to REP */
block_state_set(block, BLOCK_STATE_REP);
}
/* set the parity association */
fs_allocate(disk, v_pos, file, v_idx);
/* go to the next block */
++v_idx;
++v_pos;
--v_count;
}
}
/* stat */
++count_file;
} else if (c == 'i') {
/* "inf" command */
snapraid_info info;
uint32_t v_pos;
uint32_t v_oldest;
ret = sgetb32(f, &v_oldest);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
v_pos = 0;
while (v_pos < blockmax) {
int bad;
int rehash;
int justsynced;
uint32_t t;
uint32_t flag;
uint32_t v_count;
ret = sgetb32(f, &v_count);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (v_pos + v_count > blockmax) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in info size %u/%u!\n", blockmax, v_pos + v_count);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &flag);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* if there is an info */
if ((flag & 1) != 0) {
/* read the time */
ret = sgetb32(f, &t);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* analyze the flags */
bad = (flag & 2) != 0;
rehash = (flag & 4) != 0;
justsynced = (flag & 8) != 0;
if (rehash && state->prevhash == HASH_UNDEFINED) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency for missing previous checksum!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
info = info_make(t + v_oldest, bad, rehash, justsynced);
} else {
info = 0;
}
while (v_count) {
/* insert the info in the array */
info_set(&state->infoarr, v_pos, info);
/* ensure that an info is present only for used positions */
if (fs_info_is_required(state, v_pos)) {
if (!info) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency for missing info!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
} else {
/* extra info are accepted for backward compatibility */
/* they are discarded at the first write */
}
/* go to next block */
++v_pos;
--v_count;
}
}
} else if (c == 'h') {
/* hole */
uint32_t v_pos;
struct snapraid_disk* disk;
uint32_t mapping;
ret = sgetb32(f, &mapping);
if (ret < 0 || mapping >= mapping_max) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in mapping index!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
disk = tommy_array_get(&disk_mapping, mapping);
v_pos = 0;
while (v_pos < blockmax) {
uint32_t v_idx;
uint32_t v_count;
struct snapraid_file* deleted;
ret = sgetb32(f, &v_count);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (v_pos + v_count > blockmax) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in hole size %u/%u!\n", blockmax, v_pos + v_count);
os_abort();
/* LCOV_EXCL_STOP */
}
/* get the sub-command */
c = sgetc(f);
switch (c) {
case 'o' :
/* if it's a run of deleted blocks */
/* allocate a fake deleted file */
deleted = file_alloc(state->block_size, "", v_count * (data_off_t)state->block_size, 0, 0, 0, 0);
/* mark the file as deleted */
file_flag_set(deleted, FILE_IS_DELETED);
/* insert it in the list of deleted files */
tommy_list_insert_tail(&disk->deletedlist, &deleted->nodelist, deleted);
/* process all blocks */
v_idx = 0;
while (v_count) {
struct snapraid_block* block = fs_file2block_get(deleted, v_idx);
/* set the block as deleted */
block_state_set(block, BLOCK_STATE_DELETED);
/* read the hash */
ret = sread(f, block->hash, BLOCK_HASH_SIZE);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* if we are clearing indeterminate hashes */
if (state->clear_past_hash) {
/* set the hash value to INVALID */
hash_invalid_set(block->hash);
}
/* insert the block in the block array */
fs_allocate(disk, v_pos, deleted, v_idx);
/* go to next block */
++v_pos;
++v_idx;
--v_count;
}
break;
case 'O' :
/* go to the next run */
v_pos += v_count;
break;
default :
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid hole type!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
} else if (c == 's') {
/* symlink */
char sub[PATH_MAX];
char linkto[PATH_MAX];
struct snapraid_link* slink;
struct snapraid_disk* disk;
uint32_t mapping;
ret = sgetb32(f, &mapping);
if (ret < 0 || mapping >= mapping_max) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in mapping index!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
disk = tommy_array_get(&disk_mapping, mapping);
ret = sgetbs(f, sub, sizeof(sub));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (!*sub) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency for null symlink!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetbs(f, linkto, sizeof(linkto));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* allocate the link as symbolic link */
slink = link_alloc(sub, linkto, FILE_IS_SYMLINK);
/* insert the link in the link containers */
tommy_hashdyn_insert(&disk->linkset, &slink->nodeset, slink, link_name_hash(slink->sub));
tommy_list_insert_tail(&disk->linklist, &slink->nodelist, slink);
/* stat */
++count_symlink;
} else if (c == 'a') {
/* hardlink */
char sub[PATH_MAX];
char linkto[PATH_MAX];
struct snapraid_link* slink;
struct snapraid_disk* disk;
uint32_t mapping;
ret = sgetb32(f, &mapping);
if (ret < 0 || mapping >= mapping_max) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in mapping index!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
disk = tommy_array_get(&disk_mapping, mapping);
ret = sgetbs(f, sub, sizeof(sub));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (!*sub) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency for null hardlink!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetbs(f, linkto, sizeof(linkto));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (!*linkto) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency for empty hardlink '%s'!\n", sub);
os_abort();
/* LCOV_EXCL_STOP */
}
/* allocate the link as hard link */
slink = link_alloc(sub, linkto, FILE_IS_HARDLINK);
/* insert the link in the link containers */
tommy_hashdyn_insert(&disk->linkset, &slink->nodeset, slink, link_name_hash(slink->sub));
tommy_list_insert_tail(&disk->linklist, &slink->nodelist, slink);
/* stat */
++count_hardlink;
} else if (c == 'r') {
/* dir */
char sub[PATH_MAX];
struct snapraid_dir* dir;
struct snapraid_disk* disk;
uint32_t mapping;
ret = sgetb32(f, &mapping);
if (ret < 0 || mapping >= mapping_max) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency in mapping index!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
disk = tommy_array_get(&disk_mapping, mapping);
ret = sgetbs(f, sub, sizeof(sub));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (!*sub) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Internal inconsistency for null dir!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
/* allocate the dir */
dir = dir_alloc(sub);
/* insert the dir in the dir containers */
tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub));
tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir);
/* stat */
++count_dir;
} else if (c == 'c') {
/* get the subcommand */
c = sgetc(f);
switch (c) {
case 'u' :
state->hash = HASH_MURMUR3;
break;
case 'k' :
state->hash = HASH_SPOOKY2;
break;
case 'm' :
state->hash = HASH_METRO;
break;
default :
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid checksum!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
/* read the seed */
ret = sread(f, state->hashseed, HASH_MAX);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
} else if (c == 'C') {
/* get the sub-command */
c = sgetc(f);
switch (c) {
case 'u' :
state->prevhash = HASH_MURMUR3;
break;
case 'k' :
state->prevhash = HASH_SPOOKY2;
break;
case 'm' :
state->prevhash = HASH_METRO;
break;
default :
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid checksum!\n");
os_abort();
/* LCOV_EXCL_STOP */
}
/* read the seed */
ret = sread(f, state->prevhashseed, HASH_MAX);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
} else if (c == 'z') {
uint32_t block_size;
ret = sgetb32(f, &block_size);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (block_size == 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Zero 'blocksize' specification in the content file!\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* without configuration, auto assign the block size */
if (state->no_conf) {
state->block_size = block_size;
}
if (block_size != state->block_size) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Mismatching 'blocksize' specification in the content file!\n");
log_fatal("Please restore the 'blocksize' value in the configuration file to '%u'\n", block_size / KIBI);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else if (c == 'y') {
uint32_t hash_size;
ret = sgetb32(f, &hash_size);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (hash_size < 2 || hash_size > HASH_MAX) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid 'hashsize' specification in the content file!\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* without configuration, auto assign the block size */
if (state->no_conf) {
BLOCK_HASH_SIZE = hash_size;
}
if ((int)hash_size != BLOCK_HASH_SIZE) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Mismatching 'hashsize' specification in the content file!\n");
log_fatal("Please restore the 'hashsize' value in the configuration file to '%u'\n", hash_size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else if (c == 'x') {
ret = sgetb32(f, &blockmax);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
} else if (c == 'm' || c == 'M') {
struct snapraid_map* map;
char uuid[UUID_MAX];
uint32_t v_pos;
uint32_t v_total_blocks;
uint32_t v_free_blocks;
struct snapraid_disk* disk;
ret = sgetbs(f, buffer, sizeof(buffer));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_pos);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* from SnapRAID 7.0 the 'M' command includes the free space */
if (c == 'M') {
ret = sgetb32(f, &v_total_blocks);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_free_blocks);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
} else {
v_total_blocks = 0;
v_free_blocks = 0;
}
/* read the uuid */
ret = sgetbs(f, uuid, sizeof(uuid));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* find the disk */
disk = find_disk_by_name(state, buffer);
if (!disk) {
/* search by UUID if renamed */
disk = find_disk_by_uuid(state, uuid);
if (disk) {
log_fatal("WARNING! Renaming disk '%s' to '%s'\n", buffer, disk->name);
/* write the new state with the new name */
state->need_write = 1;
}
}
if (!disk) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Disk '%s' with uuid '%s' not present in the configuration file!\n", buffer, uuid);
log_fatal("If you have removed it from the configuration file, please restore it\n");
/* if it's a command without UUID, it cannot autorename using UUID */
if (state->opt.skip_disk_access)
log_fatal("If you have renamed it, run 'sync' to update the new name\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
map = map_alloc(disk->name, v_pos, v_total_blocks, v_free_blocks, uuid);
tommy_list_insert_tail(&state->maplist, &map->node, map);
/* insert in the mapping vector */
tommy_array_grow(&disk_mapping, mapping_max + 1);
tommy_array_set(&disk_mapping, mapping_max, disk);
++mapping_max;
} else if (c == 'P') {
/* from SnapRAID 7.0 the 'P' command includes the free space */
/* from SnapRAID 11.0 the 'P' command is deprecated by 'Q' */
char v_uuid[UUID_MAX];
uint32_t v_level;
uint32_t v_total_blocks;
uint32_t v_free_blocks;
ret = sgetb32(f, &v_level);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_total_blocks);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_free_blocks);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetbs(f, v_uuid, sizeof(v_uuid));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (v_level >= LEV_MAX) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid parity level '%u' in the configuration file!\n", v_level);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* auto configure if configuration is missing */
if (state->no_conf) {
if (v_level >= state->level)
state->level = v_level + 1;
}
/* if we use this parity entry */
if (v_level < state->level) {
/* if the configuration has more splits, keep them */
if (state->parity[v_level].split_mac < 1)
state->parity[v_level].split_mac = 1;
/* set the parity info */
pathcpy(state->parity[v_level].split_map[0].uuid, sizeof(state->parity[v_level].split_map[0].uuid), v_uuid);
state->parity[v_level].total_blocks = v_total_blocks;
state->parity[v_level].free_blocks = v_free_blocks;
}
} else if (c == 'Q') {
/* from SnapRAID 11.0 the 'Q' command include size info and multi file support */
uint32_t v_level;
uint32_t v_total_blocks;
uint32_t v_free_blocks;
uint32_t v_split_mac;
unsigned s;
ret = sgetb32(f, &v_level);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_total_blocks);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_free_blocks);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb32(f, &v_split_mac);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
if (v_level >= LEV_MAX) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid parity level '%u' in the configuration file!\n", v_level);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* auto configure if configuration is missing */
if (state->no_conf) {
if (v_level >= state->level)
state->level = v_level + 1;
if (state->parity[v_level].split_mac < v_split_mac)
state->parity[v_level].split_mac = v_split_mac;
}
/* if we use this parity entry */
if (v_level < state->level) {
/* set the parity info */
state->parity[v_level].total_blocks = v_total_blocks;
state->parity[v_level].free_blocks = v_free_blocks;
}
for (s = 0; s < v_split_mac; ++s) {
char v_path[PATH_MAX];
char v_uuid[UUID_MAX];
uint64_t v_size;
ret = sgetbs(f, v_path, sizeof(v_path));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetbs(f, v_uuid, sizeof(v_uuid));
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
ret = sgetb64(f, &v_size);
if (ret < 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
os_abort();
/* LCOV_EXCL_STOP */
}
/* if we use this parity entry */
if (v_level < state->level) {
/* if this split was removed from the configuration */
if (s >= state->parity[v_level].split_mac) {
/* if the file is used, we really need it */
if (v_size != 0) {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Parity '%s' misses used file '%u'!\n", lev_config_name(v_level), s);
log_fatal("If you have removed it from the configuration file, please restore it\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* otherwise we can drop it */
log_fatal("WARNING! Dropping from '%s' unused split '%u'\n", lev_config_name(v_level), s);
} else {
/* we copy the path only if without configuration file */
if (state->no_conf)
pathcpy(state->parity[v_level].split_map[s].path, sizeof(state->parity[v_level].split_map[s].path), v_path);
/* set the split info */
pathcpy(state->parity[v_level].split_map[s].uuid, sizeof(state->parity[v_level].split_map[s].uuid), v_uuid);
state->parity[v_level].split_map[s].size = v_size;
/* log the info read from the content file */
log_tag("content:%s:%u:%s:%s:%" PRIi64 "\n", lev_config_name(v_level), s,
state->parity[v_level].split_map[s].path,
state->parity[v_level].split_map[s].uuid,
state->parity[v_level].split_map[s].size);
}
}
}
} else if (c == 'N') {
uint32_t crc_stored;
uint32_t crc_computed;
/* get the crc before reading it from the file */
crc_computed = scrc(f);
ret = sgetble32(f, &crc_stored);
if (ret < 0) {
/* LCOV_EXCL_START */
/* here don't call decoding_error() because it's too late to get the crc */
log_fatal("Error reading the CRC in '%s' at offset %" PRIi64 "\n", path, stell(f));
log_fatal("This content file is damaged! Use an alternate copy.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (crc_stored != crc_computed) {
/* LCOV_EXCL_START */
/* here don't call decoding_error() because it's too late to get the crc */
log_fatal("Mismatching CRC in '%s'\n", path);
log_fatal("This content file is damaged! Use an alternate copy.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
crc_checked = 1;
} else {
/* LCOV_EXCL_START */
decoding_error(path, f);
log_fatal("Invalid command '%c'!\n", (char)c);
os_abort();
/* LCOV_EXCL_STOP */
}
}
tommy_array_done(&disk_mapping);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error reading the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (!crc_checked) {
/* LCOV_EXCL_START */
log_fatal("Finished reading '%s' without finding the CRC\n", path);
log_fatal("This content file is truncated or damaged! Use an alternate copy.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* check the file-system on all disks */
state_fscheck(state, "after read");
/* check that the stored parity size matches the loaded state */
if (blockmax != parity_allocated_size(state)) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in parity size %u/%u in '%s' at offset %" PRIi64 "\n", blockmax, parity_allocated_size(state), path, stell(f));
if (state->opt.skip_content_check) {
log_fatal("Overriding.\n");
blockmax = parity_allocated_size(state);
} else {
exit(EXIT_FAILURE);
}
/* LCOV_EXCL_STOP */
}
msg_verbose("%8u files\n", count_file);
msg_verbose("%8u hardlinks\n", count_hardlink);
msg_verbose("%8u symlinks\n", count_symlink);
msg_verbose("%8u empty dirs\n", count_dir);
}
struct state_write_thread_context {
struct snapraid_state* state;
#if HAVE_MT_WRITE
thread_id_t thread;
#endif
/* input */
block_off_t blockmax;
time_t info_oldest;
time_t info_now;
int info_has_rehash;
STREAM* f;
/* output */
uint32_t crc;
unsigned count_file;
unsigned count_hardlink;
unsigned count_symlink;
unsigned count_dir;
};
static void* state_write_thread(void* arg)
{
struct state_write_thread_context* context = arg;
struct snapraid_state* state = context->state;
block_off_t blockmax = context->blockmax;
time_t info_oldest = context->info_oldest;
time_t info_now = context->info_now;
int info_has_rehash = context->info_has_rehash;
STREAM* f = context->f;
uint32_t crc;
unsigned count_file;
unsigned count_hardlink;
unsigned count_symlink;
unsigned count_dir;
tommy_node* i;
block_off_t idx;
block_off_t begin;
unsigned l, s;
int version;
count_file = 0;
count_hardlink = 0;
count_symlink = 0;
count_dir = 0;
/* check what version to use */
version = 2;
for (l = 0; l < state->level; ++l) {
if (state->parity[l].split_mac > 1)
version = 3;
}
if (BLOCK_HASH_SIZE != 16)
version = 3;
/* write header */
if (version == 3)
swrite("SNAPCNT3\n\3\0\0", 12, f);
else
swrite("SNAPCNT2\n\3\0\0", 12, f);
/* write block size and block max */
sputc('z', f);
sputb32(state->block_size, f);
sputc('x', f);
sputb32(blockmax, f);
/* hash size */
if (version == 3) {
sputc('y', f);
sputb32(BLOCK_HASH_SIZE, f);
}
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
sputc('c', f);
if (state->hash == HASH_MURMUR3) {
sputc('u', f);
} else if (state->hash == HASH_SPOOKY2) {
sputc('k', f);
} else if (state->hash == HASH_METRO) {
sputc('m', f);
} else {
/* LCOV_EXCL_START */
log_fatal("Unexpected hash when writing the content file '%s'.\n", serrorfile(f));
return context;
/* LCOV_EXCL_STOP */
}
swrite(state->hashseed, HASH_MAX, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
/* previous hash only present */
if (state->prevhash != HASH_UNDEFINED) {
/* if at least one rehash tag found, we have to save the previous hash */
if (info_has_rehash) {
sputc('C', f);
if (state->prevhash == HASH_MURMUR3) {
sputc('u', f);
} else if (state->prevhash == HASH_SPOOKY2) {
sputc('k', f);
} else if (state->prevhash == HASH_METRO) {
sputc('m', f);
} else {
/* LCOV_EXCL_START */
log_fatal("Unexpected prevhash when writing the content file '%s'.\n", serrorfile(f));
return context;
/* LCOV_EXCL_STOP */
}
swrite(state->prevhashseed, HASH_MAX, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
}
}
/* for each map */
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
struct snapraid_disk* disk;
/* find the disk for this mapping */
disk = find_disk_by_name(state, map->name);
if (!disk) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for unmapped disk '%s'\n", map->name);
return context;
/* LCOV_EXCL_STOP */
}
/* save the mapping only if disk is mapped */
if (disk->mapping_idx != -1) {
sputc('M', f);
sputbs(map->name, f);
sputb32(map->position, f);
sputb32(map->total_blocks, f);
sputb32(map->free_blocks, f);
sputbs(map->uuid, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
}
}
/* for each parity */
for (l = 0; l < state->level; ++l) {
if (version == 3) {
sputc('Q', f);
sputb32(l, f);
sputb32(state->parity[l].total_blocks, f);
sputb32(state->parity[l].free_blocks, f);
sputb32(state->parity[l].split_mac, f);
for (s = 0; s < state->parity[l].split_mac; ++s) {
sputbs(state->parity[l].split_map[s].path, f);
sputbs(state->parity[l].split_map[s].uuid, f);
sputb64(state->parity[l].split_map[s].size, f);
}
} else {
sputc('P', f);
sputb32(l, f);
sputb32(state->parity[l].total_blocks, f);
sputb32(state->parity[l].free_blocks, f);
sputbs(state->parity[l].split_map[0].uuid, f);
}
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
}
/* for each disk */
for (i = state->disklist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_disk* disk = i->data;
/* if the disk is not mapped, skip it */
if (disk->mapping_idx < 0)
continue;
/* for each file */
for (j = disk->filelist; j != 0; j = j->next) {
struct snapraid_file* file = j->data;
uint64_t size;
uint64_t mtime_sec;
int32_t mtime_nsec;
uint64_t inode;
size = file->size;
mtime_sec = file->mtime_sec;
mtime_nsec = file->mtime_nsec;
inode = file->inode;
sputc('f', f);
sputb32(disk->mapping_idx, f);
sputb64(size, f);
sputb64(mtime_sec, f);
/* encode STAT_NSEC_INVALID as 0 */
if (mtime_nsec == STAT_NSEC_INVALID)
sputb32(0, f);
else
sputb32(mtime_nsec + 1, f);
sputb64(inode, f);
sputbs(file->sub, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
/* for all the blocks of the file */
begin = 0;
while (begin < file->blockmax) {
unsigned v_state = block_state_get(fs_file2block_get(file, begin));
block_off_t v_pos = fs_file2par_get(disk, file, begin);
uint32_t v_count;
block_off_t end;
/* find the end of run of blocks */
end = begin + 1;
while (end < file->blockmax) {
if (v_state != block_state_get(fs_file2block_get(file, end)))
break;
if (v_pos + (end - begin) != fs_file2par_get(disk, file, end))
break;
++end;
}
switch (v_state) {
case BLOCK_STATE_BLK :
sputc('b', f);
break;
case BLOCK_STATE_CHG :
sputc('g', f);
break;
case BLOCK_STATE_REP :
sputc('p', f);
break;
default :
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in state for block %u state %u\n", v_pos, v_state);
return context;
/* LCOV_EXCL_STOP */
}
sputb32(v_pos, f);
v_count = end - begin;
sputb32(v_count, f);
/* write hashes */
for (idx = begin; idx < end; ++idx) {
struct snapraid_block* block = fs_file2block_get(file, idx);
swrite(block->hash, BLOCK_HASH_SIZE, f);
}
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
/* next begin position */
begin = end;
}
++count_file;
}
/* for each link */
for (j = disk->linklist; j != 0; j = j->next) {
struct snapraid_link* slink = j->data;
switch (link_flag_get(slink, FILE_IS_LINK_MASK)) {
case FILE_IS_HARDLINK :
sputc('a', f);
++count_hardlink;
break;
case FILE_IS_SYMLINK :
sputc('s', f);
++count_symlink;
break;
}
sputb32(disk->mapping_idx, f);
sputbs(slink->sub, f);
sputbs(slink->linkto, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
}
/* for each dir */
for (j = disk->dirlist; j != 0; j = j->next) {
struct snapraid_dir* dir = j->data;
sputc('r', f);
sputb32(disk->mapping_idx, f);
sputbs(dir->sub, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
++count_dir;
}
/* deleted blocks of the disk */
sputc('h', f);
sputb32(disk->mapping_idx, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
begin = 0;
while (begin < blockmax) {
int is_deleted;
block_off_t end;
is_deleted = fs_is_block_deleted(disk, begin);
/* find the end of run of blocks */
end = begin + 1;
while (end < blockmax
&& is_deleted == fs_is_block_deleted(disk, end)
) {
++end;
}
sputb32(end - begin, f);
if (is_deleted) {
/* write the run of deleted blocks with hash */
sputc('o', f);
/* write all the hash */
while (begin < end) {
struct snapraid_block* block = fs_par2block_get(disk, begin);
swrite(block->hash, BLOCK_HASH_SIZE, f);
++begin;
}
} else {
/* write the run of blocks without hash */
/* they can be either used or empty blocks */
sputc('O', f);
/* next begin position */
begin = end;
}
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
}
}
/* write the info for each block */
sputc('i', f);
sputb32(info_oldest, f);
begin = 0;
while (begin < blockmax) {
snapraid_info info;
block_off_t end;
time_t t;
unsigned flag;
info = info_get(&state->infoarr, begin);
/* find the end of run of blocks */
end = begin + 1;
while (end < blockmax
&& info == info_get(&state->infoarr, end)
) {
++end;
}
sputb32(end - begin, f);
/* if there is info */
if (info) {
/* other flags */
flag = 1; /* info is present */
if (info_get_bad(info))
flag |= 2;
if (info_get_rehash(info))
flag |= 4;
if (info_get_justsynced(info))
flag |= 8;
sputb32(flag, f);
t = info_get_time(info);
/* truncate any time that is in the future */
if (t > info_now)
t = info_now;
/* the oldest info is computed only on required blocks, so it may not be the absolute oldest */
if (t < info_oldest)
t = 0;
else
t -= info_oldest;
sputb32(t, f);
} else {
/* write a special 0 flag to mark missing info */
sputb32(0, f);
}
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
/* next begin position */
begin = end;
}
sputc('N', f);
/* flush data written to the disk */
if (sflush(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s' (in flush before crc). %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
/* get the file crc */
crc = scrc(f);
/* compare the crc of the data written to file */
/* with the one of the data written to the stream */
if (crc != scrc_stream(f)) {
/* LCOV_EXCL_START */
log_fatal("CRC mismatch writing the content stream.\n");
log_fatal("DANGER! Your RAM memory is broken! DO NOT PROCEED UNTIL FIXED!\n");
log_fatal("Try running a memory test like http://www.memtest86.com/\n");
return context;
/* LCOV_EXCL_STOP */
}
sputble32(crc, f);
if (serror(f)) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
/* set output variables */
context->crc = crc;
context->count_file = count_file;
context->count_hardlink = count_hardlink;
context->count_symlink = count_symlink;
context->count_dir = count_dir;
return 0;
}
static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
{
#if HAVE_MT_WRITE
int fail;
int first;
#else
STREAM* f;
unsigned count_content;
unsigned k;
struct state_write_thread_context* context;
void* retval;
#endif
tommy_node* i;
block_off_t blockmax;
time_t info_oldest;
time_t info_now;
int info_has_rehash;
int mapping_idx;
block_off_t idx;
uint32_t crc;
unsigned count_file;
unsigned count_hardlink;
unsigned count_symlink;
unsigned count_dir;
/* blocks of all array */
blockmax = parity_allocated_size(state);
/* check the file-system on all disks */
state_fscheck(state, "before write");
/* clear the info for unused blocks */
/* and get some other info */
info_oldest = 0; /* oldest time in info */
info_now = time(0); /* get the present time */
info_has_rehash = 0; /* if there is a rehash info */
for (idx = 0; idx < blockmax; ++idx) {
/* if the position is used */
if (fs_position_is_required(state, idx)) {
snapraid_info info = info_get(&state->infoarr, idx);
/* only if there is some info to store */
if (info) {
time_t info_time = info_get_time(info);
if (!info_oldest || info_time < info_oldest)
info_oldest = info_time;
if (info_get_rehash(info))
info_has_rehash = 1;
}
} else {
/* clear any previous info */
info_set(&state->infoarr, idx, 0);
/* and clear any deleted blocks */
fs_position_clear_deleted(state, idx);
}
}
/* map disks */
mapping_idx = 0;
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;
struct snapraid_disk* disk;
/* find the disk for this mapping */
disk = find_disk_by_name(state, map->name);
if (!disk) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency for unmapped disk '%s'\n", map->name);
os_abort();
/* LCOV_EXCL_STOP */
}
/* save the mapping only for not empty disks */
if (!fs_is_empty(disk, blockmax)) {
/* assign the mapping index used to identify disks */
disk->mapping_idx = mapping_idx;
++mapping_idx;
} else {
/* mark the disk as without mapping */
disk->mapping_idx = -1;
}
}
#if HAVE_MT_WRITE
/* start all writing threads */
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
struct state_write_thread_context* context;
char tmp[PATH_MAX];
STREAM* f;
msg_progress("Saving state to %s...\n", content->content);
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
/* ensure to delete a previous stale file */
if (remove(tmp) != 0) {
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error removing the stale content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
f = sopen_write(tmp);
if (f == 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* allocate the thread context */
context = malloc_nofail(sizeof(struct state_write_thread_context));
content->context = context;
/* initialize */
context->state = state;
context->blockmax = blockmax;
context->info_oldest = info_oldest;
context->info_now = info_now;
context->info_has_rehash = info_has_rehash;
context->f = f;
thread_create(&context->thread, state_write_thread, context);
i = i->next;
}
/* join all thread */
fail = 0;
first = 1;
crc = 0;
count_file = 0;
count_hardlink = 0;
count_symlink = 0;
count_dir = 0;
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
struct state_write_thread_context* context = content->context;
void* retval;
thread_join(context->thread, &retval);
if (retval) {
/* LCOV_EXCL_START */
fail = 1;
/* LCOV_EXCL_STOP */
} else {
STREAM* f = context->f;
/* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure */
/* than even in a system crash event we have one valid copy of the file. */
if (sflush(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#if HAVE_FSYNC
if (ssync(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s' in sync(). %s.\n", serrorfile(f), strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
if (sclose(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing the content file. %s.\n", strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (first) {
first = 0;
crc = context->crc;
count_file = context->count_file;
count_hardlink = context->count_hardlink;
count_symlink = context->count_symlink;
count_dir = context->count_dir;
} else {
if (crc != context->crc) {
/* LCOV_EXCL_START */
log_fatal("Different CRCs writing content streams.\n");
log_fatal("DANGER! Your RAM memory is broken! DO NOT PROCEED UNTIL FIXED!\n");
log_fatal("Try running a memory test like http://www.memtest86.com/\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
}
free(context);
i = i->next;
}
/* abort on failure */
if (fail) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#else
/* count the content files */
count_content = 0;
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
msg_progress("Saving state to %s...\n", content->content);
++count_content;
i = i->next;
}
/* open all the content files */
f = sopen_multi_write(count_content);
if (!f) {
/* LCOV_EXCL_START */
log_fatal("Error opening the content files.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
k = 0;
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
char tmp[PATH_MAX];
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
/* ensure to delete a previous stale file */
if (remove(tmp) != 0) {
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error removing the stale content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
if (sopen_multi_file(f, k, tmp) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
++k;
i = i->next;
}
/* allocate the thread context */
context = malloc_nofail(sizeof(struct state_write_thread_context));
/* initialize */
context->state = state;
context->blockmax = blockmax;
context->info_oldest = info_oldest;
context->info_now = info_now;
context->info_has_rehash = info_has_rehash;
context->f = f;
retval = state_write_thread(context);
/* abort on failure */
if (retval) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure */
/* than even in a system crash event we have one valid copy of the file. */
if (sflush(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#if HAVE_FSYNC
if (ssync(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error writing the content file '%s' in sync(). %s.\n", serrorfile(f), strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
if (sclose(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing the content file. %s.\n", strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
crc = context->crc;
count_file = context->count_file;
count_hardlink = context->count_hardlink;
count_symlink = context->count_symlink;
count_dir = context->count_dir;
free(context);
#endif
msg_verbose("%8u files\n", count_file);
msg_verbose("%8u hardlinks\n", count_hardlink);
msg_verbose("%8u symlinks\n", count_symlink);
msg_verbose("%8u empty dirs\n", count_dir);
*out_crc = crc;
}
void state_read(struct snapraid_state* state)
{
STREAM* f;
char path[PATH_MAX];
struct stat st;
tommy_node* node;
int ret;
int c;
/* iterate over all the available content files and load the first one present */
f = 0;
node = tommy_list_head(&state->contentlist);
while (node) {
struct snapraid_content* content = node->data;
pathcpy(path, sizeof(path), content->content);
if (!state->no_conf) {
log_tag("content:%s\n", path);
log_flush();
}
msg_progress("Loading state from %s...\n", path);
f = sopen_read(path);
if (f != 0) {
/* if opened stop the search */
break;
} else {
/* if it's real error of an existing file, abort */
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error opening the content file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* otherwise continue */
if (node->next) {
log_fatal("WARNING! Content file '%s' not found, trying with another copy...\n", path);
/* ensure to rewrite all the content files */
state->need_write = 1;
}
}
/* next content file */
node = node->next;
}
/* if not found, assume empty */
if (!f) {
log_fatal("No content file found. Assuming empty.\n");
/* create the initial mapping */
state_map(state);
return;
}
/* get the stat of the content file */
ret = fstat(shandle(f), &st);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error stating the content file '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* go further to check other content files */
while (node) {
char other_path[PATH_MAX];
struct stat other_st;
struct snapraid_content* content = node->data;
pathcpy(other_path, sizeof(other_path), content->content);
ret = stat(other_path, &other_st);
if (ret != 0) {
/* allow missing content files, but not any other kind of error */
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error stating the content file '%s'. %s.\n", other_path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* ensure to rewrite all the content files */
state->need_write = 1;
} else {
/* if the size is different */
if (other_st.st_size != st.st_size) {
log_fatal("WARNING! Content files '%s' and '%s' have a different size!\n", path, other_path);
log_fatal("Likely one of the two is broken!\n");
/* ensure to rewrite all the content files */
state->need_write = 1;
}
}
/* next content file */
node = node->next;
}
/* start with a undefined default. */
/* it's for compatibility with version 1.0 where MD5 was implicit. */
state->hash = HASH_UNDEFINED;
/* start with a zero seed, it was the default in old versions */
memset(state->hashseed, 0, HASH_MAX);
/* previous hash, start with an undefined value */
state->prevhash = HASH_UNDEFINED;
/* intentionally not set the prevhashseed, if used valgrind will warn about it */
/* get the first char to detect the file type */
c = sgetc(f);
sungetc(c, f);
/* guess the file type from the first char */
if (c == 'S') {
state_read_content(state, path, f);
} else {
/* LCOV_EXCL_START */
log_fatal("From SnapRAID v9.0 the text content file is not supported anymore.\n");
log_fatal("You have first to upgrade to SnapRAID v8.1 to convert it to binary format.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
sclose(f);
if (state->hash == HASH_UNDEFINED) {
/* LCOV_EXCL_START */
log_fatal("The checksum to use is not specified.\n");
log_fatal("This happens because you are likely upgrading from SnapRAID 1.0.\n");
log_fatal("To use a new SnapRAID you must restart from scratch,\n");
log_fatal("deleting all the content and parity files.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* update the mapping */
state_map(state);
state_content_check(state, path);
/* mark that we read the content file, and it passed all the checks */
state->checked_read = 1;
}
struct state_verify_thread_context {
struct snapraid_state* state;
struct snapraid_content* content;
#if HAVE_MT_VERIFY
thread_id_t thread;
#else
void* retval;
#endif
/* input */
uint32_t crc;
STREAM* f;
};
static void* state_verify_thread(void* arg)
{
struct state_verify_thread_context* context = arg;
struct snapraid_content* content = context->content;
STREAM* f = context->f;
unsigned char buf[4];
uint32_t crc_stored;
uint32_t crc_computed;
uint64_t start;
start = tick_ms();
if (sdeplete(f, buf) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error flushing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
return context;
/* LCOV_EXCL_STOP */
}
/* get the stored crc from the last four bytes */
crc_stored = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
if (crc_stored != context->crc) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Wrong stored CRC in '%s'\n", serrorfile(f));
return context;
/* LCOV_EXCL_STOP */
}
/* get the computed crc */
crc_computed = scrc(f);
/* adjust the stored crc to include itself */
crc_stored = crc32c(crc_stored, buf, 4);
if (crc_computed != crc_stored) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Wrong file CRC in '%s'\n", serrorfile(f));
return context;
/* LCOV_EXCL_STOP */
}
msg_progress("Verified %s in %" PRIu64 " seconds\n", content->content, (tick_ms() - start) / 1000);
return 0;
}
static void state_verify_content(struct snapraid_state* state, uint32_t crc)
{
tommy_node* i;
int fail;
msg_progress("Verifying...\n");
/* start all reading threads */
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
struct state_verify_thread_context* context;
char tmp[PATH_MAX];
STREAM* f;
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
f = sopen_read(tmp);
if (f == 0) {
/* LCOV_EXCL_START */
log_fatal("Error reopening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* allocate the thread context */
context = malloc_nofail(sizeof(struct state_verify_thread_context));
content->context = context;
/* initialize */
context->state = state;
context->content = content;
context->crc = crc;
context->f = f;
#if HAVE_MT_VERIFY
thread_create(&context->thread, state_verify_thread, context);
#else
context->retval = state_verify_thread(context);
#endif
i = i->next;
}
/* join all thread */
fail = 0;
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
struct state_verify_thread_context* context = content->context;
void* retval;
#if HAVE_MT_VERIFY
thread_join(context->thread, &retval);
#else
retval = context->retval;
#endif
if (retval) {
/* LCOV_EXCL_START */
fail = 1;
/* LCOV_EXCL_STOP */
} else {
STREAM* f = context->f;
if (sclose(f) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing the content file. %s.\n", strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
free(context);
i = i->next;
}
/* abort on failure */
if (fail) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
static void state_rename_content(struct snapraid_state* state)
{
tommy_node* i;
#if defined(_linux) /* this sequence is linux specific */
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
char tmp[PATH_MAX];
char dir[PATH_MAX];
char* slash;
int handle;
pathcpy(dir, sizeof(dir), content->content);
slash = strrchr(tmp, '/');
if (slash)
*slash = 0;
else
pathcpy(dir, sizeof(dir), ".");
/* open the directory to get the handle */
handle = open(dir, O_RDONLY | O_DIRECTORY);
if (handle < 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening the directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* now rename the just written copy with the correct name */
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
if (rename(tmp, content->content) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error renaming the content file '%s' to '%s'. %s.\n", tmp, content->content, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* sync the directory */
if (fsync(handle) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error syncing the directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (close(handle) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing the directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
i = i->next;
}
#else
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
char tmp[PATH_MAX];
/* now renames the just written copy with the correct name */
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
if (rename(tmp, content->content) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error renaming the content file '%s' to '%s' in rename(). %s.\n", tmp, content->content, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
i = i->next;
}
#endif
}
void state_write(struct snapraid_state* state)
{
uint32_t crc;
/* write all the content files */
state_write_content(state, &crc);
/* verify the just written files */
state_verify_content(state, crc);
/* rename the new files, over the old ones */
state_rename_content(state);
state->need_write = 0; /* no write needed anymore */
state->checked_read = 0; /* what we wrote is not checked in read */
}
void state_skip(struct snapraid_state* state)
{
tommy_node* i;
/* for each disk */
for (i = state->disklist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_disk* disk = i->data;
if (!disk->skip_access)
continue;
/* for each file */
for (j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
struct snapraid_file* file = j->data;
file_flag_set(file, FILE_IS_EXCLUDED);
}
/* for each link */
for (j = tommy_list_head(&disk->linklist); j != 0; j = j->next) {
struct snapraid_link* slink = j->data;
link_flag_set(slink, FILE_IS_EXCLUDED);
}
/* for each dir */
for (j = tommy_list_head(&disk->dirlist); j != 0; j = j->next) {
struct snapraid_dir* dir = j->data;
dir_flag_set(dir, FILE_IS_EXCLUDED);
}
}
}
void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk, int filter_missing, int filter_error)
{
tommy_node* i;
unsigned l;
/* if no filter, include all */
if (!filter_missing && !filter_error && tommy_list_empty(filterlist_file) && tommy_list_empty(filterlist_disk))
return;
msg_progress("Selecting...\n");
for (i = tommy_list_head(filterlist_disk); i != 0; i = i->next) {
struct snapraid_filter* filter = i->data;
msg_verbose("\t%s%s\n", filter->pattern, filter->is_disk ? "//" : "");
}
for (i = tommy_list_head(filterlist_file); i != 0; i = i->next) {
struct snapraid_filter* filter = i->data;
msg_verbose("\t%s%s\n", filter->pattern, filter->is_dir ? "/" : "");
}
if (filter_missing)
msg_verbose("\t\n");
if (filter_error)
msg_verbose("\t\n");
/* for each disk */
for (i = state->disklist; i != 0; i = i->next) {
tommy_node* j;
struct snapraid_disk* disk = i->data;
/* if we filter for presence, we have to access the disk, so better to print something */
if (filter_missing)
msg_progress("Scanning disk %s...\n", disk->name);
/* for each file */
for (j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
struct snapraid_file* file = j->data;
if (filter_path(filterlist_disk, 0, disk->name, file->sub) != 0
|| filter_path(filterlist_file, 0, disk->name, file->sub) != 0
|| filter_existence(filter_missing, disk->dir, file->sub) != 0
|| filter_correctness(filter_error, &state->infoarr, disk, file) != 0
) {
file_flag_set(file, FILE_IS_EXCLUDED);
}
}
/* for each link */
for (j = tommy_list_head(&disk->linklist); j != 0; j = j->next) {
struct snapraid_link* slink = j->data;
if (filter_path(filterlist_disk, 0, disk->name, slink->sub) != 0
|| filter_path(filterlist_file, 0, disk->name, slink->sub) != 0
|| filter_existence(filter_missing, disk->dir, slink->sub) != 0
) {
link_flag_set(slink, FILE_IS_EXCLUDED);
}
}
/* for each empty dir */
for (j = tommy_list_head(&disk->dirlist); j != 0; j = j->next) {
struct snapraid_dir* dir = j->data;
if (filter_emptydir(filterlist_disk, 0, disk->name, dir->sub) != 0
|| filter_emptydir(filterlist_file, 0, disk->name, dir->sub) != 0
|| filter_existence(filter_missing, disk->dir, dir->sub) != 0
) {
dir_flag_set(dir, FILE_IS_EXCLUDED);
}
}
}
/* if we are filtering by disk, exclude any parity not explicitly included */
if (!tommy_list_empty(filterlist_disk)) {
/* for each parity disk */
for (l = 0; l < state->level; ++l) {
/* check if the parity is excluded by name */
if (filter_path(filterlist_disk, 0, lev_config_name(l), 0) != 0) {
/* excluded the parity from further operation */
state->parity[l].is_excluded_by_filter = 1;
}
}
} else {
/* if we are filtering by file, exclude all parity */
if (filter_missing || !tommy_list_empty(filterlist_file)) {
/* for each parity disk */
for (l = 0; l < state->level; ++l) {
state->parity[l].is_excluded_by_filter = 1;
}
}
}
}
int state_progress_begin(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax, block_off_t countmax)
{
time_t now;
if (state->opt.gui) {
log_tag("run:begin:%u:%u:%u\n", blockstart, blockmax, countmax);
log_flush();
}
now = time(0);
state->progress_whole_start = now;
state->progress_tick = 0;
state->progress_ptr = 0;
state->progress_wasted = 0;
/* stop if requested */
if (global_interrupt) {
/* LCOV_EXCL_START */
if (!state->opt.gui) {
msg_status("Not starting for interruption\n");
}
log_tag("sigint:0: SIGINT received\n");
log_flush();
return 0;
/* LCOV_EXCL_STOP */
}
return 1;
}
void state_progress_end(struct snapraid_state* state, block_off_t countpos, block_off_t countmax, data_off_t countsize)
{
if (state->opt.gui) {
log_tag("run:end\n");
log_flush();
} else if (countmax == 0) {
msg_status("Nothing to do\n");
} else {
time_t now;
time_t elapsed;
unsigned countsize_MB = (countsize + MEGA - 1) / MEGA;
now = time(0);
elapsed = now - state->progress_whole_start - state->progress_wasted;
msg_bar("%u%% completed, %u MB accessed", countpos * 100 / countmax, countsize_MB);
msg_bar(" in %u:%02u", (unsigned)(elapsed / 3600), (unsigned)((elapsed % 3600) / 60));
/* some extra spaces to ensure to overwrite the latest status line */
msg_bar(" ");
msg_bar("\n");
msg_flush();
}
}
void state_progress_stop(struct snapraid_state* state)
{
time_t now;
now = time(0);
if (!state->opt.gui) {
msg_bar("\n");
msg_flush();
}
state->progress_interruption = now;
}
void state_progress_restart(struct snapraid_state* state)
{
time_t now;
now = time(0);
/* reset the progress counter */
state->progress_tick = 0;
state->progress_ptr = 0;
if (now >= state->progress_interruption) /* avoid degenerated cases when the clock is manually adjusted */
state->progress_wasted += now - state->progress_interruption;
}
#define PROGRESS_CLEAR " "
/**
* Set the latest tick data in the progress vector.
*/
static void state_progress_latest(struct snapraid_state* state)
{
tommy_node* i;
unsigned l;
state->progress_tick_misc[state->progress_ptr] = state->tick_misc;
state->progress_tick_sched[state->progress_ptr] = state->tick_sched;
state->progress_tick_raid[state->progress_ptr] = state->tick_raid;
state->progress_tick_hash[state->progress_ptr] = state->tick_hash;
state->progress_tick_io[state->progress_ptr] = state->tick_io;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
disk->progress_tick[state->progress_ptr] = disk->tick;
}
for (l = 0; l < state->level; ++l)
state->parity[l].progress_tick[state->progress_ptr] = state->parity[l].tick;
}
/**
* Number of columns
*/
#define GRAPH_COLUMN 68
/**
* Get the reference value, 0 if index is PROGRESS_MAX
*/
#define ref(map, index) (index < PROGRESS_MAX ? map[index] : 0)
static void state_progress_graph(struct snapraid_state* state, struct snapraid_io* io, unsigned current, unsigned oldest)
{
uint64_t v;
uint64_t tick_total;
unsigned bar;
tommy_node* i;
unsigned l;
size_t pad;
tick_total = 0;
tick_total += state->progress_tick_misc[current] - ref(state->progress_tick_misc, oldest);
tick_total += state->progress_tick_sched[current] - ref(state->progress_tick_sched, oldest);
tick_total += state->progress_tick_raid[current] - ref(state->progress_tick_raid, oldest);
tick_total += state->progress_tick_hash[current] - ref(state->progress_tick_hash, oldest);
tick_total += state->progress_tick_io[current] - ref(state->progress_tick_io, oldest);
if (!tick_total)
return;
pad = 4;
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
size_t len = strlen(disk->name);
if (pad < len)
pad = len;
}
for (l = 0; l < state->level; ++l) {
size_t len = strlen(lev_config_name(l));
if (pad < len)
pad = len;
}
/* extra space */
pad += 1;
if (pad + 30 < GRAPH_COLUMN)
bar = GRAPH_COLUMN - pad;
else
bar = 30;
if (io) {
const char* legend = "cached blocks (instant, more is better)";
/* refresh the cached blocks info */
io_refresh(io);
printf("\n");
/* search for the slowest */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
v = disk->cached_blocks;
printr(disk->name, pad);
printf("%4" PRIu64 " | ", v);
if (disk->progress_file && disk->progress_file->sub)
printf("%s", disk->progress_file->sub);
else
printf("-");
printf("\n");
}
for (l = 0; l < state->level; ++l) {
v = state->parity[l].cached_blocks;
printr(lev_config_name(l), pad);
printf("%4" PRIu64 " | ", v);
printc('o', v * bar / io->io_max);
printf("\n");
}
printc(' ', pad);
printf(" |_");
printc('_', bar);
printf("\n");
printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend) / 2);
printf("%s", legend);
printf("\n");
}
printf("\n");
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
v = disk->progress_tick[current] - ref(disk->progress_tick, oldest);
printr(disk->name, pad);
printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
printc('*', v * bar / tick_total);
printf("\n");
/* clear the file in progress */
disk->progress_file = 0;
}
for (l = 0; l < state->level; ++l) {
v = state->parity[l].progress_tick[current] - ref(state->parity[l].progress_tick, oldest);
printr(lev_config_name(l), pad);
printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
printc('*', v * bar / tick_total);
printf("\n");
}
v = state->progress_tick_raid[current] - ref(state->progress_tick_raid, oldest);
printr("raid", pad);
printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
printc('*', v * bar / tick_total);
printf("\n");
v = state->progress_tick_hash[current] - ref(state->progress_tick_hash, oldest);
printr("hash", pad);
printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
printc('*', v * bar / tick_total);
printf("\n");
v = state->progress_tick_sched[current] - ref(state->progress_tick_sched, oldest);
printr("sched", pad);
printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
printc('*', v * bar / tick_total);
printf("\n");
v = state->progress_tick_misc[current] - ref(state->progress_tick_misc, oldest);
printr("misc", pad);
printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
printc('*', v * bar / tick_total);
printf("\n");
printc(' ', pad);
printf(" |_");
printc('_', bar);
printf("\n");
if (oldest == PROGRESS_MAX) {
const char* legend = "wait time (total, less is better)";
printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend) / 2);
printf("%s", legend);
printf("\n");
} else {
const char* legend_d = "wait time (last %d secs, less is better)";
printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend_d) / 2);
printf(legend_d, PROGRESS_MAX);
printf("\n");
}
printf("\n");
}
int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_off_t blockpos, block_off_t countpos, block_off_t countmax, data_off_t countsize)
{
time_t now;
int pred;
now = time(0);
/* previous position */
pred = state->progress_ptr + PROGRESS_MAX - 1;
if (pred >= PROGRESS_MAX)
pred -= PROGRESS_MAX;
/* if the previous measure is different */
if (state->progress_tick == 0
|| state->progress_time[pred] != now
) {
time_t elapsed;
unsigned out_perc = 0;
unsigned out_size_speed = 0;
unsigned out_block_speed = 0;
unsigned out_cpu = 0;
unsigned out_eta = 0;
int out_computed = 0;
/* store the new measure */
state->progress_time[state->progress_ptr] = now;
state->progress_pos[state->progress_ptr] = countpos;
state->progress_size[state->progress_ptr] = countsize;
state_progress_latest(state);
elapsed = now - state->progress_whole_start - state->progress_wasted;
/* completion percentage */
if (countmax)
out_perc = countpos * 100 / countmax;
/* if we have at least 5 measures */
if (state->progress_tick >= 5
/* or if we are running in test mode, with at least one measure */
|| (state->opt.force_progress && state->progress_tick >= 1)
) {
int oldest;
int past;
time_t delta_time;
block_off_t delta_pos;
data_off_t delta_size;
uint64_t tick_cpu;
uint64_t tick_total;
uint64_t delta_tick_cpu;
uint64_t delta_tick_total;
uint64_t oldest_tick_cpu;
uint64_t oldest_tick_total;
/* number of past measures */
past = state->progress_tick;
/* drop the oldest ones, to promptly */
/* skip the startup phase */
past -= past / 5;
/* check how much we can go in the past */
if (past >= PROGRESS_MAX - 1) {
/* the vector is filled, so we are already in position */
/* to get the possible oldest one */
oldest = state->progress_ptr + 1;
} else {
/* go backward the number of positions selected */
oldest = state->progress_ptr + PROGRESS_MAX - past;
}
if (oldest >= PROGRESS_MAX)
oldest -= PROGRESS_MAX;
tick_cpu = state->progress_tick_misc[state->progress_ptr]
+ state->progress_tick_sched[state->progress_ptr]
+ state->progress_tick_raid[state->progress_ptr]
+ state->progress_tick_hash[state->progress_ptr];
tick_total = tick_cpu + state->progress_tick_io[state->progress_ptr];
oldest_tick_cpu = state->progress_tick_misc[oldest]
+ state->progress_tick_sched[oldest]
+ state->progress_tick_raid[oldest]
+ state->progress_tick_hash[oldest];
oldest_tick_total = oldest_tick_cpu + state->progress_tick_io[oldest];
delta_time = now - state->progress_time[oldest];
delta_pos = countpos - state->progress_pos[oldest];
delta_size = countsize - state->progress_size[oldest];
delta_tick_cpu = tick_cpu - oldest_tick_cpu;
delta_tick_total = tick_total - oldest_tick_total;
/* estimate the speed in MB/s */
if (delta_time != 0)
out_size_speed = (unsigned)(delta_size / MEGA / delta_time);
/* estimate the speed in block/s */
if (delta_time != 0)
out_block_speed = (unsigned)(delta_pos / delta_time);
/* estimate the cpu usage percentage */
if (delta_tick_total != 0)
out_cpu = (unsigned)(delta_tick_cpu * 100U / delta_tick_total);
/* estimate the remaining time in minutes */
if (delta_pos != 0)
out_eta = (countmax - countpos) * delta_time / (60 * delta_pos);
if (state->opt.force_stats) {
os_clear();
state_progress_graph(state, io, state->progress_ptr, oldest);
}
/* we have the output value */
out_computed = 1;
}
if (state->opt.gui) {
log_tag("run:pos:%u:%u:%" PRIu64 ":%u:%u:%u:%u:%" PRIu64 "\n", blockpos, countpos, countsize, out_perc, out_eta, out_size_speed, out_cpu, (uint64_t)elapsed);
log_flush();
} else {
msg_bar("%u%%, %u MB", out_perc, (unsigned)(countsize / MEGA));
if (out_computed) {
msg_bar(", %u MB/s", out_size_speed);
msg_bar(", %u stripe/s", out_block_speed);
msg_bar(", CPU %u%%", out_cpu);
msg_bar(", %u:%02u ETA", out_eta / 60, out_eta % 60);
}
msg_bar("%s\r", PROGRESS_CLEAR);
msg_flush();
}
/* next position to fill */
++state->progress_ptr;
if (state->progress_ptr >= PROGRESS_MAX)
state->progress_ptr -= PROGRESS_MAX;
/* one more measure */
++state->progress_tick;
}
/* stop if requested */
if (global_interrupt) {
/* LCOV_EXCL_START */
if (!state->opt.gui) {
log_fatal("\n");
log_fatal("Stopping for interruption at block %u\n", blockpos);
}
log_tag("sigint:%u: SIGINT received\n", blockpos);
log_flush();
return 1;
/* LCOV_EXCL_STOP */
}
return 0;
}
void state_usage_waste(struct snapraid_state* state)
{
uint64_t now = tick();
state->tick_last = now;
}
void state_usage_misc(struct snapraid_state* state)
{
uint64_t now = tick();
uint64_t delta = now - state->tick_last;
/* increment the time spent in computations */
state->tick_misc += delta;
state->tick_last = now;
}
void state_usage_sched(struct snapraid_state* state)
{
uint64_t now = tick();
uint64_t delta = now - state->tick_last;
/* increment the time spent in computations */
state->tick_sched += delta;
state->tick_last = now;
}
void state_usage_raid(struct snapraid_state* state)
{
uint64_t now = tick();
uint64_t delta = now - state->tick_last;
/* increment the time spent in computations */
state->tick_raid += delta;
state->tick_last = now;
}
void state_usage_hash(struct snapraid_state* state)
{
uint64_t now = tick();
uint64_t delta = now - state->tick_last;
/* increment the time spent in computations */
state->tick_hash += delta;
state->tick_last = now;
}
void state_usage_file(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
{
(void)state;
disk->progress_file = file;
}
void state_usage_disk(struct snapraid_state* state, struct snapraid_handle* handle_map, unsigned* waiting_map, unsigned waiting_mac)
{
uint64_t now = tick();
uint64_t delta = now - state->tick_last;
unsigned i;
/* increment the time spent in the data disks */
for (i = 0; i < waiting_mac; ++i) {
struct snapraid_disk* disk = handle_map[waiting_map[i]].disk;
if (!disk)
continue;
disk->tick += delta;
}
state->tick_io += delta;
state->tick_last = now;
}
void state_usage_parity(struct snapraid_state* state, unsigned* waiting_map, unsigned waiting_mac)
{
uint64_t now = tick();
uint64_t delta = now - state->tick_last;
unsigned i;
/* increment the time spent in the parity disk */
for (i = 0; i < waiting_mac; ++i)
state->parity[waiting_map[i]].tick += delta;
state->tick_io += delta;
state->tick_last = now;
}
void state_usage_print(struct snapraid_state* state)
{
/* set the latest data */
state_progress_latest(state);
if (msg_level < MSG_PROGRESS)
return;
/* print a graph for it */
state_progress_graph(state, 0, state->progress_ptr, PROGRESS_MAX);
}
void state_fscheck(struct snapraid_state* state, const char* ope)
{
tommy_node* i;
/* check the file-system on all disks */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
if (fs_check(disk) != 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in file-system for disk '%s' %s\n", disk->name, ope);
os_abort();
/* LCOV_EXCL_STOP */
}
}
}
void generate_configuration(const char* path)
{
struct snapraid_state state;
struct snapraid_content* content;
char esc_buffer[ESC_MAX];
unsigned l, s;
tommy_node* j;
state_init(&state);
/* mark that we are without a configuration file */
state.no_conf = 1;
/* create the dummy content entry */
content = content_alloc(path, -1);
/* adds the content entry */
tommy_list_insert_tail(&state.contentlist, &content->node, content);
/* read the content file */
state_read(&state);
/* output a dummy configuration file */
printf("# Configuration file generated from %s\n", path);
printf("\n");
printf("# Use this blocksize\n");
printf("blocksize %u\n", state.block_size / KIBI);
printf("\n");
printf("# Use this hashsize\n");
printf("hashsize %u\n", BLOCK_HASH_SIZE);
printf("\n");
for (l = 0; l < state.level; ++l) {
printf("# Set the correct path for the %s files\n", lev_name(l));
printf("# You had %u of them:\n", state.parity[l].split_mac);
for (s = 0; s < state.parity[l].split_mac; ++s) {
printf("# %u:\n", s);
printf("# PATH:");
if (state.parity[l].split_map[s].path[0])
printf("%s", state.parity[l].split_map[s].path);
else
printf("?");
printf("\n");
printf("# SIZE:");
if (state.parity[l].split_map[s].size != PARITY_SIZE_INVALID)
printf("%" PRIu64, state.parity[l].split_map[s].size);
else
printf("?");
printf("\n");
printf("# UUID:");
if (state.parity[l].split_map[s].uuid[0])
printf("%s", state.parity[l].split_map[s].uuid);
else
printf("?");
printf("\n");
printf("#\n");
}
printf("%s ENTER_HERE_THE_PARITY_FILES_COMMA_SEPARATED\n", lev_config_name(l));
printf("\n");
}
printf("# Add any other content file\n");
printf("content %s\n", path);
printf("\n");
for (j = state.maplist; j; j = j->next) {
struct snapraid_map* map = j->data;
struct snapraid_disk* disk;
printf("# Set the correct dir for disk '%s'\n", map->name);
if (map->uuid[0])
printf("# Disk '%s' is the one with id '%s'\n", map->name, map->uuid);
disk = find_disk_by_name(&state, map->name);
if (disk && disk->filelist) {
struct snapraid_file* file = disk->filelist->data;
if (file) {
printf("# and containing: %s\n", fmt_poll(disk, file->sub, esc_buffer));
}
}
printf("data %s ENTER_HERE_THE_DIR\n", map->name);
printf("\n");
}
state_done(&state);
}
snapraid-12.1/cmdline/state.h 0000664 0000000 0000000 00000032656 14166610522 0016161 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __STATE_H
#define __STATE_H
#include "elem.h"
struct snapraid_handle;
struct snapraid_io;
/****************************************************************************/
/* parity level */
/**
* Max level of parity supported.
*/
#define LEV_MAX 6
/**
* Return the parity name: Parity, 2-Parity, 3-Parity, 4-Parity, 5-Parity, 6-Parity.
*/
const char* lev_name(unsigned level);
/**
* Return the parity name used in the config file: parity, 2-parity, 3-parity, 4-parity, 5-parity, 6-parity.
*/
const char* lev_config_name(unsigned level);
/****************************************************************************/
/* state */
/**
* Units for disk space.
*/
#define KILO (1000)
#define MEGA (1000 * 1000)
#define GIGA (1000 * 1000 * 1000)
#define TERA (1000 * 1000 * 1000 * 1000LL)
#define KIBI (1024)
#define MEBI (1024 * 1024)
#define GIBI (1024 * 1024 * 1024)
#define TEBI (1024 * 1024 * 1024 * 1024LL)
/**
* Global variable to identify if Ctrl+C is pressed.
*/
extern volatile int global_interrupt;
#define SORT_PHYSICAL 1 /**< Sort by physical order. */
#define SORT_INODE 2 /**< Sort by inode. */
#define SORT_ALPHA 3 /**< Sort by alphabetic order. */
#define SORT_DIR 4 /**< Sort by directory order. */
/**
* Options set only at startup.
* For all these options a value of 0 means nothing set, and to use the default.
*/
struct snapraid_option {
int gui; /**< Gui output. */
int auditonly; /**< In check, checks only the hash and not the parity. */
int badfileonly; /**< In fix, fixes only files marked as bad. */
int badblockonly; /**< In fix, fixes only the blocks marked as bad. */
int syncedonly; /**< In fix, fixes only files that are synced. */
int prehash; /**< Enables the prehash mode for sync. */
unsigned io_error_limit; /**< Max number of input/output errors before aborting. */
int force_zero; /**< Forced dangerous operations of syncing files now with zero size. */
int force_empty; /**< Forced dangerous operations of syncing disks now empty. */
int force_uuid; /**< Forced dangerous operations of syncing disks with uuid changed. */
int force_device; /**< Forced dangerous operations of using disks with save device id. */
int force_nocopy; /**< Force dangerous operations of syncing files without using copy detection. */
int force_full; /**< Force a full parity update. */
int force_realloc; /**< Force a full reallocation and parity update. */
int expect_unrecoverable; /**< Expect presence of unrecoverable error in checking or fixing. */
int expect_recoverable; /**< Expect presence of recoverable error in checking. */
int skip_device; /**< Skip devices matching checks. */
int skip_sign; /**< Skip the sign check for content files. */
int skip_fallocate; /**< Skip the use of fallocate(). */
int skip_space_holder; /**< Skip the use of spaceholder file. */
int file_mode; /**< File mode. Mask of ADVISE_* flags. */
int skip_lock; /**< Skip the lock file protection. */
int skip_self; /**< Skip the self-test. */
int skip_content_check; /**< Relax some content file checks. */
int skip_parity_access; /**< Skip the parity access for commands that don't need it. */
int skip_disk_access; /**< Skip the data disk access for commands that don't need it. */
int skip_content_access; /**< Skip the content access for commands that don't need it. */
int kill_after_sync; /**< Kill the process after sync without saving the final state. */
int force_murmur3; /**< Force Murmur3 choice. */
int force_spooky2; /**< Force Spooky2 choice. */
int force_order; /**< Force sorting order. One of the SORT_* defines. */
unsigned force_scrub_at; /**< Force scrub for the specified number of blocks. */
int force_scrub_even; /**< Force scrub of all the even blocks. */
int force_content_write; /**< Force the update of the content file. */
int skip_content_write; /**< Skip the update of the content file. */
int force_scan_winfind; /**< Force the use of FindFirst/Next in Windows to list directories. */
int force_progress; /**< Force the use of the progress status. */
unsigned force_autosave_at; /**< Force autosave at the specified block. */
int fake_device; /**< Fake device data. */
int no_warnings; /**< Remove some warning messages. */
int expected_missing; /**< If missing files are expected and should not be reported. */
int fake_uuid; /**< Set fakes UUID for testing. */
int match_first_uuid; /**< Force the matching of the first UUID. */
int force_parity_update; /**< Force parity update even if data is not changed. */
unsigned io_cache; /**< Number of IO buffers to use. 0 for default. */
int auto_conf; /**< Allow to run without configuration file. */
int force_stats; /**< Force stats print during process. */
uint64_t parity_limit_size; /**< Test limit for parity files. */
int skip_multi_scan; /**< Don't use threads in scan. */
};
struct snapraid_state {
struct snapraid_option opt; /**< Setup options. */
int filter_hidden; /**< Filter out hidden files. */
uint64_t autosave; /**< Autosave after the specified amount of data. 0 to disable. */
int need_write; /**< If the state is changed. */
int checked_read; /**< If the state was read and checked. */
uint32_t block_size; /**< Block size in bytes. */
unsigned raid_mode; /**< Raid mode to use. RAID_MODE_DEFAULT or RAID_MODE_ALTERNATE. */
int file_mode; /**< File access mode. Combination of ADVISE_* flags. */
struct snapraid_parity parity[LEV_MAX]; /**< Parity vector. */
char share[PATH_MAX]; /**< Path of the share tree. If !=0 pool links are created in a different way. */
char pool[PATH_MAX]; /**< Path of the pool tree. */
uint64_t pool_device; /**< Device identifier of the pool. */
unsigned char hashseed[HASH_MAX]; /**< Hash seed. Just after a uint64 to provide a minimal alignment. */
unsigned char prevhashseed[HASH_MAX]; /**< Previous hash seed. In case of rehash. */
char lockfile[PATH_MAX]; /**< Path of the lock file to use. */
unsigned level; /**< Number of parity levels. 1 for PAR1, 2 for PAR2. */
unsigned hash; /**< Hash kind used. */
unsigned prevhash; /**< Previous hash kind used. In case of rehash. */
unsigned besthash; /**< Best hash suggested. */
const char* command; /**< Command running. */
tommy_list contentlist; /**< List of content files. */
tommy_list disklist; /**< List of all the disks. */
tommy_list maplist; /**< List of all the disk mappings. */
tommy_list filterlist; /**< List of inclusion/exclusion. */
tommy_list importlist; /**< List of import file. */
tommy_hashdyn importset; /**< Hashtable by hash of all the import blocks. */
tommy_hashdyn previmportset; /**< Hashtable by prevhash of all the import blocks. Valid only if we are in a rehash state. */
tommy_hashdyn searchset; /**< Hashtable by timestamp of all the search files. */
tommy_arrayblkof infoarr; /**< Block information array. */
/**
* Cumulative time used for computations.
*/
uint64_t tick_misc;
uint64_t tick_sched;
uint64_t tick_raid;
uint64_t tick_hash;
/**
* Cumulative time used for all io operations of disks.
*/
uint64_t tick_io;
/**
* Last time used for time measure.
*/
uint64_t tick_last;
int clear_past_hash; /**< Clear all the hash from CHG and DELETED blocks when reading the state from an incomplete sync. */
time_t progress_whole_start; /**< Initial start of the whole process. */
time_t progress_interruption; /**< Time of the start of the progress interruption. */
time_t progress_wasted; /**< Time wasted in interruptions. */
time_t progress_time[PROGRESS_MAX]; /**< Last times of progress. */
block_off_t progress_pos[PROGRESS_MAX]; /**< Last positions of progress. */
data_off_t progress_size[PROGRESS_MAX]; /**< Last sizes of progress. */
uint64_t progress_tick_misc[PROGRESS_MAX]; /**< Last cpu ticks of progress. */
uint64_t progress_tick_sched[PROGRESS_MAX]; /**< Last scheduling ticks of progress. */
uint64_t progress_tick_raid[PROGRESS_MAX]; /**< Last raid ticks of progress. */
uint64_t progress_tick_hash[PROGRESS_MAX]; /**< Last hash ticks of progress. */
uint64_t progress_tick_io[PROGRESS_MAX]; /**< Last io ticks of progress. */
int progress_ptr; /**< Pointer to the next position to fill. Rolling over. */
int progress_tick; /**< Number of measures done. */
int no_conf; /**< Automatically add missing info. Used to load content without a configuration file. */
};
/**
* Initialize the state.
*/
void state_init(struct snapraid_state* state);
/**
* Deinitialize the state.
*/
void state_done(struct snapraid_state* state);
/**
* Read the configuration file.
*/
void state_config(struct snapraid_state* state, const char* path, const char* command, struct snapraid_option* opt, tommy_list* filterlist_disk);
/**
* Read the state.
*/
void state_read(struct snapraid_state* state);
/**
* Write the new state.
*/
void state_write(struct snapraid_state* state);
/**
* Diff all the disks.
*/
int state_diff(struct snapraid_state* state);
/**
* Scan all the disks to update the state.
*/
void state_scan(struct snapraid_state* state);
/**
* Set the nanosecond timestamp of all files that have a zero value.
*/
void state_touch(struct snapraid_state* state);
/**
* Devices operations.
*/
void state_device(struct snapraid_state* state, int operation, tommy_list* filterlist_disk);
/**
* Sync the parity data.
*/
int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount);
/**
* Check (and fixes) all the files and parity data.
* \param fix If we have to fix, after checking.
*/
int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, block_off_t blockcount);
/**
* Dry the files.
*/
void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount);
/**
* Rehash the files.
*/
void state_rehash(struct snapraid_state* state);
/**
* Scrub levels.
*/
#define SCRUB_AUTO -1 /**< Automatic selection. */
#define SCRUB_BAD -2 /**< Scrub only the bad blocks. */
#define SCRUB_NEW -3 /**< Scrub the new blocks. */
#define SCRUB_FULL -4 /**< Scrub everything. */
#define SCRUB_EVEN -5 /**< Even blocks. */
/**
* Scrub the files.
*/
int state_scrub(struct snapraid_state* state, int plan, int olderthan);
/**
* Print the status.
*/
int state_status(struct snapraid_state* state);
/**
* Find duplicates.
*/
void state_dup(struct snapraid_state* state);
/**
* List content.
*/
void state_list(struct snapraid_state* state);
/**
* Create pool tree.
*/
void state_pool(struct snapraid_state* state);
/**
* Refresh the free space info.
*
* Note that it requires disks access.
*/
void state_refresh(struct snapraid_state* state);
/**
* Skip files, symlinks and dirs.
* Apply any skip access disk.
*/
void state_skip(struct snapraid_state* state);
/**
* Filter files, symlinks and dirs.
* Apply an additional filter to the list currently loaded.
*/
void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk, int filter_missing, int filter_error);
/**
* Begin the progress visualization.
*/
int state_progress_begin(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax, block_off_t countmax);
/**
* End the progress visualization.
*/
void state_progress_end(struct snapraid_state* state, block_off_t countpos, block_off_t countmax, data_off_t countsize);
/**
* Write the progress.
*/
int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_off_t blockpos, block_off_t countpos, block_off_t countmax, data_off_t countsize);
/**
* Stop temporarily the progress.
*/
void state_progress_stop(struct snapraid_state* state);
/**
* Restart the progress.
*/
void state_progress_restart(struct snapraid_state* state);
/**
* Set the usage time as wasted one not counted.
*/
void state_usage_waste(struct snapraid_state* state);
/**
* Set the usage time for CPU.
*/
void state_usage_misc(struct snapraid_state* state);
void state_usage_sched(struct snapraid_state* state);
void state_usage_raid(struct snapraid_state* state);
void state_usage_hash(struct snapraid_state* state);
/**
* Set the last file used
*/
void state_usage_file(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file);
/**
* Set the usage time for a set of data disks.
*/
void state_usage_disk(struct snapraid_state* state, struct snapraid_handle* handle_map, unsigned* waiting_map, unsigned waiting_mac);
/**
* Set the usage time for a set of parity disk.
*/
void state_usage_parity(struct snapraid_state* state, unsigned* waiting_map, unsigned waiting_mac);
/**
* Print the stats of the usage time.
*/
void state_usage_print(struct snapraid_state* state);
/**
* Check the file-system on all disks.
* On error it aborts.
*/
void state_fscheck(struct snapraid_state* state, const char* ope);
/****************************************************************************/
/* misc */
/**
* Generate a dummy configuration file from a content file.
*/
void generate_configuration(const char* content);
#endif
snapraid-12.1/cmdline/status.c 0000664 0000000 0000000 00000036635 14166610522 0016360 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
#include "raid/raid.h"
/****************************************************************************/
/* status */
unsigned day_ago(time_t ref, time_t now)
{
/* in case some dates is in the future */
if (now < ref)
return 0;
return (now - ref) / (24 * 3600);
}
#define GRAPH_COLUMN 70
#define GRAPH_ROW 15
static unsigned perc(uint64_t part, uint64_t total)
{
if (!total)
return 0;
return (unsigned)(part * 100 / total);
}
/**
* Bit used to mark unscrubbed time info.
*/
#define TIME_NEW 1
int state_status(struct snapraid_state* state)
{
block_off_t blockmax;
block_off_t i;
time_t* timemap;
time_t now;
block_off_t bad;
block_off_t bad_first;
block_off_t bad_last;
block_off_t rehash;
block_off_t count;
unsigned l;
unsigned dayoldest, daymedian, daynewest;
unsigned bar_scrubbed[GRAPH_COLUMN];
unsigned bar_new[GRAPH_COLUMN];
unsigned barpos;
unsigned barmax;
time_t oldest, newest, median;
unsigned x, y;
tommy_node* node_disk;
unsigned file_count;
unsigned file_fragmented;
unsigned extra_fragment;
unsigned file_zerosubsecond;
uint64_t file_size;
uint64_t file_block_count;
uint64_t file_block_free;
block_off_t parity_block_free;
unsigned unsynced_blocks;
unsigned unscrubbed_blocks;
uint64_t all_wasted;
int free_not_zero;
/* get the present time */
now = time(0);
/* keep track if at least a free info is available */
free_not_zero = 0;
blockmax = parity_allocated_size(state);
log_tag("summary:block_size:%u\n", state->block_size);
log_tag("summary:parity_block_count:%u\n", blockmax);
/* get the minimum parity free space */
parity_block_free = state->parity[0].free_blocks;
for (l = 0; l < state->level; ++l) {
log_tag("summary:parity_block_total:%s:%u\n", lev_config_name(l), state->parity[l].total_blocks);
log_tag("summary:parity_block_free:%s:%u\n", lev_config_name(l), state->parity[l].free_blocks);
if (state->parity[l].free_blocks < parity_block_free)
parity_block_free = state->parity[l].free_blocks;
if (state->parity[l].free_blocks != 0)
free_not_zero = 1;
}
log_tag("summary:parity_block_free_min:%u\n", parity_block_free);
printf("SnapRAID status report:\n");
printf("\n");
printf(" Files Fragmented Excess Wasted Used Free Use Name\n");
printf(" Files Fragments GB GB GB\n");
/* count fragments */
file_count = 0;
file_size = 0;
file_block_count = 0;
file_block_free = 0;
file_fragmented = 0;
extra_fragment = 0;
file_zerosubsecond = 0;
all_wasted = 0;
for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) {
struct snapraid_disk* disk = node_disk->data;
tommy_node* node;
block_off_t j;
unsigned disk_file_count = 0;
unsigned disk_file_fragmented = 0;
unsigned disk_extra_fragment = 0;
unsigned disk_file_zerosubsecond = 0;
block_off_t disk_block_count = 0;
uint64_t disk_file_size = 0;
block_off_t disk_block_latest_used = 0;
block_off_t disk_block_max_by_space;
block_off_t disk_block_max_by_parity;
block_off_t disk_block_max;
int64_t wasted;
/* for each file in the disk */
node = disk->filelist;
while (node) {
struct snapraid_file* file;
file = node->data;
node = node->next; /* next node */
if (file->mtime_nsec == STAT_NSEC_INVALID
|| file->mtime_nsec == 0
) {
++file_zerosubsecond;
++disk_file_zerosubsecond;
if (disk_file_zerosubsecond < 50)
log_tag("zerosubsecond:%s:%s: \n", disk->name, file->sub);
if (disk_file_zerosubsecond == 50)
log_tag("zerosubsecond:%s:%s: (more follow)\n", disk->name, file->sub);
}
/* check fragmentation */
if (file->blockmax != 0) {
block_off_t prev_pos;
block_off_t last_pos;
int fragmented;
fragmented = 0;
prev_pos = fs_file2par_get(disk, file, 0);
for (j = 1; j < file->blockmax; ++j) {
block_off_t parity_pos = fs_file2par_get(disk, file, j);
if (prev_pos + 1 != parity_pos) {
fragmented = 1;
++extra_fragment;
++disk_extra_fragment;
}
prev_pos = parity_pos;
}
/* keep track of latest block used */
last_pos = fs_file2par_get(disk, file, file->blockmax - 1);
if (last_pos > disk_block_latest_used) {
disk_block_latest_used = last_pos;
}
if (fragmented) {
++file_fragmented;
++disk_file_fragmented;
}
disk_block_count += file->blockmax;
}
/* count files */
++file_count;
++disk_file_count;
file_size += file->size;
file_block_count += file->blockmax;
disk_file_size += file->size;
}
if (disk->free_blocks != 0)
free_not_zero = 1;
/* get the free block info */
disk_block_max_by_space = disk_block_count + disk->free_blocks;
disk_block_max_by_parity = blockmax + parity_block_free;
/* the maximum usable space in a disk is limited by the smallest */
/* of the disk size and the parity size */
/* the wasted space is the space that we have to leave */
/* free on the data disk, when the parity is filled up */
if (disk_block_max_by_space < disk_block_max_by_parity) {
disk_block_max = disk_block_max_by_space;
} else {
disk_block_max = disk_block_max_by_parity;
}
/* wasted space is the difference of the two maximum size */
/* if negative, it's extra space available in parity */
wasted = (int64_t)disk_block_max_by_space - (int64_t)disk_block_max_by_parity;
wasted *= state->block_size;
if (wasted > 0)
all_wasted += wasted;
file_block_free += disk_block_max - disk_block_count;
printf("%8u", disk_file_count);
printf("%8u", disk_file_fragmented);
printf("%8u", disk_extra_fragment);
if (wasted < -100LL * GIGA) {
printf(" -");
} else {
printf("%8.1f", (double)wasted / GIGA);
}
printf("%8" PRIu64, disk_file_size / GIGA);
if (disk_block_max == 0 && disk_block_count == 0) {
/* if the disk is empty and we don't have the free space info */
printf(" -");
printf(" - ");
} else {
printf("%8" PRIu64, (disk_block_max - disk_block_count) * (uint64_t)state->block_size / GIGA);
printf(" %3u%%", perc(disk_block_count, disk_block_max));
}
printf(" %s\n", disk->name);
log_tag("summary:disk_file_count:%s:%u\n", disk->name, disk_file_count);
log_tag("summary:disk_block_count:%s:%u\n", disk->name, disk_block_count);
log_tag("summary:disk_fragmented_file_count:%s:%u\n", disk->name, disk_file_fragmented);
log_tag("summary:disk_excess_fragment_count:%s:%u\n", disk->name, disk_extra_fragment);
log_tag("summary:disk_zerosubsecond_file_count:%s:%u\n", disk->name, disk_file_zerosubsecond);
log_tag("summary:disk_file_size:%s:%" PRIu64 "\n", disk->name, disk_file_size);
log_tag("summary:disk_block_allocated:%s:%u\n", disk->name, disk_block_latest_used + 1);
log_tag("summary:disk_block_total:%s:%u\n", disk->name, disk->total_blocks);
log_tag("summary:disk_block_free:%s:%u\n", disk->name, disk->free_blocks);
log_tag("summary:disk_block_max_by_space:%s:%u\n", disk->name, disk_block_max_by_space);
log_tag("summary:disk_block_max_by_parity:%s:%u\n", disk->name, disk_block_max_by_parity);
log_tag("summary:disk_block_max:%s:%u\n", disk->name, disk_block_max);
log_tag("summary:disk_space_wasted:%s:%" PRId64 "\n", disk->name, wasted);
}
/* totals */
printf(" --------------------------------------------------------------------------\n");
printf("%8u", file_count);
printf("%8u", file_fragmented);
printf("%8u", extra_fragment);
printf("%8.1f", (double)all_wasted / GIGA);
printf("%8" PRIu64, file_size / GIGA);
printf("%8" PRIu64, file_block_free * state->block_size / GIGA);
printf(" %3u%%", perc(file_block_count, file_block_count + file_block_free));
printf("\n");
/* warn about invalid data free info */
if (!free_not_zero)
printf("\nWARNING! Free space info will be valid after the first sync.\n");
log_tag("summary:file_count:%u\n", file_count);
log_tag("summary:file_block_count:%" PRIu64 "\n", file_block_count);
log_tag("summary:fragmented_file_count:%u\n", file_fragmented);
log_tag("summary:excess_fragment_count:%u\n", extra_fragment);
log_tag("summary:zerosubsecond_file_count:%u\n", file_zerosubsecond);
log_tag("summary:file_size:%" PRIu64 "\n", file_size);
log_tag("summary:parity_size:%" PRIu64 "\n", blockmax * (uint64_t)state->block_size);
log_tag("summary:parity_size_max:%" PRIu64 "\n", (blockmax + parity_block_free) * (uint64_t)state->block_size);
log_tag("summary:hash:%s\n", hash_config_name(state->hash));
log_tag("summary:prev_hash:%s\n", hash_config_name(state->prevhash));
log_tag("summary:best_hash:%s\n", hash_config_name(state->besthash));
log_flush();
/* copy the info a temp vector, and count bad/rehash/unsynced blocks */
timemap = malloc_nofail(blockmax * sizeof(time_t));
bad = 0;
bad_first = 0;
bad_last = 0;
count = 0;
rehash = 0;
unsynced_blocks = 0;
unscrubbed_blocks = 0;
log_tag("block_count:%u\n", blockmax);
for (i = 0; i < blockmax; ++i) {
int one_invalid;
int one_valid;
snapraid_info info = info_get(&state->infoarr, i);
/* for each disk */
one_invalid = 0;
one_valid = 0;
for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) {
struct snapraid_disk* disk = node_disk->data;
struct snapraid_block* block = fs_par2block_find(disk, i);
if (block_has_file(block))
one_valid = 1;
if (block_has_invalid_parity(block))
one_invalid = 1;
}
/* if both valid and invalid, we need to update */
if (one_invalid && one_valid) {
++unsynced_blocks;
}
/* skip unused blocks */
if (info != 0) {
time_t scrub_time;
if (info_get_bad(info)) {
if (bad == 0)
bad_first = i;
bad_last = i;
++bad;
}
if (info_get_rehash(info))
++rehash;
scrub_time = info_get_time(info);
if (info_get_justsynced(info)) {
++unscrubbed_blocks;
/* mark the time as not scrubbed */
scrub_time |= TIME_NEW;
}
timemap[count++] = scrub_time;
}
if (state->opt.gui) {
if (info != 0)
log_tag("block:%u:%" PRIu64 ":%s:%s:%s:%s\n", i, (uint64_t)info_get_time(info), one_valid ? "used" : "", one_invalid ? "unsynced" : "", info_get_bad(info) ? "bad" : "", info_get_rehash(info) ? "rehash" : "");
else
log_tag("block_noinfo:%u:%s:%s\n", i, one_valid ? "used" : "", one_invalid ? "unsynced" : "");
}
}
log_tag("summary:has_unsynced:%u\n", unsynced_blocks);
log_tag("summary:has_unscrubbed:%u\n", unscrubbed_blocks);
log_tag("summary:has_rehash:%u\n", rehash);
log_tag("summary:has_bad:%u:%u:%u\n", bad, bad_first, bad_last);
log_flush();
if (!count) {
log_fatal("The array is empty.\n");
free(timemap);
return 0;
}
/* sort the info to get the time info */
qsort(timemap, count, sizeof(time_t), time_compare);
/* output the info map */
i = 0;
log_tag("info_count:%u\n", count);
while (i < count) {
unsigned j = i + 1;
while (j < count && timemap[i] == timemap[j])
++j;
if ((timemap[i] & TIME_NEW) == 0) {
log_tag("info_time:%" PRIu64 ":%u:scrubbed\n", (uint64_t)timemap[i], j - i);
} else {
log_tag("info_time:%" PRIu64 ":%u:new\n", (uint64_t)(timemap[i] & ~TIME_NEW), j - i);
}
i = j;
}
oldest = timemap[0];
median = timemap[count / 2];
newest = timemap[count - 1];
dayoldest = day_ago(oldest, now);
daymedian = day_ago(median, now);
daynewest = day_ago(newest, now);
/* compute graph limits */
barpos = 0;
barmax = 0;
for (i = 0; i < GRAPH_COLUMN; ++i) {
time_t limit;
unsigned step_scrubbed, step_new;
limit = oldest + (newest - oldest) * (i + 1) / GRAPH_COLUMN;
step_scrubbed = 0;
step_new = 0;
while (barpos < count && timemap[barpos] <= limit) {
if ((timemap[barpos] & TIME_NEW) != 0)
++step_new;
else
++step_scrubbed;
++barpos;
}
if (step_new + step_scrubbed > barmax)
barmax = step_new + step_scrubbed;
bar_scrubbed[i] = step_scrubbed;
bar_new[i] = step_new;
}
printf("\n\n");
/* print the graph */
for (y = 0; y < GRAPH_ROW; ++y) {
if (y == 0)
printf("%3u%%|", barmax * 100 / count);
else if (y == GRAPH_ROW - 1)
printf(" 0%%|");
else if (y == GRAPH_ROW / 2)
printf("%3u%%|", barmax * 50 / count);
else
printf(" |");
for (x = 0; x < GRAPH_COLUMN; ++x) {
unsigned pivot_upper = barmax * (GRAPH_ROW - y) / GRAPH_ROW;
unsigned pivot_lower = barmax * (GRAPH_ROW - 1 - y) / GRAPH_ROW;
unsigned both = bar_scrubbed[x] + bar_new[x];
unsigned scrubbed = bar_scrubbed[x];
if (both > pivot_upper) {
if (scrubbed > pivot_lower)
printf("*");
else
printf("o");
} else if (both > pivot_lower) {
if (scrubbed == both)
printf("*");
else
printf("o");
} else {
if (y == GRAPH_ROW - 1)
printf("_");
else
printf(" ");
}
}
printf("\n");
}
printf(" %3u days ago of the last scrub/sync %3u\n", dayoldest, daynewest);
printf("\n");
printf("The oldest block was scrubbed %u days ago, the median %u, the newest %u.\n", dayoldest, daymedian, daynewest);
printf("\n");
if (newest > now) {
printf("WARNING! You have scrub dates in the future! The next sync/scrub will truncate them!\n");
}
if (unsynced_blocks) {
printf("WARNING! The array is NOT fully synced.\n");
printf("You have a sync in progress at %u%%.\n", (blockmax - unsynced_blocks) * 100 / blockmax);
} else {
printf("No sync is in progress.\n");
}
if (unscrubbed_blocks) {
printf("The %u%% of the array is not scrubbed.\n", (unscrubbed_blocks * 100 + blockmax - 1) / blockmax);
} else {
printf("The full array was scrubbed at least one time.\n");
}
if (file_zerosubsecond) {
printf("You have %u files with zero sub-second timestamp.\n", file_zerosubsecond);
printf("Run the 'touch' command to set it to a not zero value.\n");
} else {
printf("No file has a zero sub-second timestamp.\n");
}
if (rehash) {
printf("You have a rehash in progress at %u%%.\n", (count - rehash) * 100 / count);
} else {
if (state->besthash != state->hash) {
printf("No rehash is in progress, but for optimal performance one is recommended.\n");
} else {
printf("No rehash is in progress or needed.\n");
}
}
if (bad) {
block_off_t bad_print;
printf("DANGER! In the array there are %u errors!\n\n", bad);
printf("They are from block %u to %u, specifically at blocks:", bad_first, bad_last);
/* print some of the errors */
bad_print = 0;
for (i = 0; i < blockmax; ++i) {
snapraid_info info = info_get(&state->infoarr, i);
/* skip unused blocks */
if (info == 0)
continue;
if (info_get_bad(info)) {
printf(" %u", i);
++bad_print;
}
if (bad_print > 100) {
printf(" and %u more...", bad - bad_print);
break;
}
}
printf("\n\n");
printf("To fix them use the command 'snapraid -e fix'.\n");
printf("The errors will disappear from the 'status' at the next 'scrub' command.\n");
} else {
printf("No error detected.\n");
}
/* free the temp vector */
free(timemap);
return 0;
}
snapraid-12.1/cmdline/stream.c 0000664 0000000 0000000 00000032161 14166610522 0016316 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "util.h"
#include "stream.h"
/****************************************************************************/
/* stream */
unsigned STREAM_SIZE = 64 * 1024;
STREAM* sopen_read(const char* file)
{
#if HAVE_POSIX_FADVISE
int ret;
#endif
STREAM* s = malloc_nofail(sizeof(STREAM));
s->handle_size = 1;
s->handle = malloc_nofail(sizeof(struct stream_handle));
pathcpy(s->handle[0].path, sizeof(s->handle[0].path), file);
s->handle[0].f = open(file, O_RDONLY | O_BINARY | O_SEQUENTIAL);
if (s->handle[0].f == -1) {
free(s->handle);
free(s);
return 0;
}
#if HAVE_POSIX_FADVISE
/* advise sequential access */
ret = posix_fadvise(s->handle[0].f, 0, 0, POSIX_FADV_SEQUENTIAL);
if (ret == ENOSYS) {
log_fatal("WARNING! fadvise() is not supported in this platform. Performance may not be optimal!\n");
/* call is not supported, like in armhf, see posix_fadvise manpage */
ret = 0;
}
if (ret != 0) {
/* LCOV_EXCL_START */
close(s->handle[0].f);
free(s->handle);
free(s);
errno = ret; /* posix_fadvise return the error code */
return 0;
/* LCOV_EXCL_STOP */
}
#endif
s->buffer = malloc_nofail_test(STREAM_SIZE);
s->pos = s->buffer;
s->end = s->buffer;
s->state = STREAM_STATE_READ;
s->state_index = 0;
s->offset = 0;
s->offset_uncached = 0;
s->crc = 0;
s->crc_uncached = 0;
s->crc_stream = CRC_IV;
return s;
}
STREAM* sopen_multi_write(unsigned count)
{
unsigned i;
STREAM* s = malloc_nofail(sizeof(STREAM));
s->handle_size = count;
s->handle = malloc_nofail(count * sizeof(struct stream_handle));
for (i = 0; i < count; ++i)
s->handle[i].f = -1;
s->buffer = malloc_nofail_test(STREAM_SIZE);
s->pos = s->buffer;
s->end = s->buffer + STREAM_SIZE;
s->state = STREAM_STATE_WRITE;
s->state_index = 0;
s->offset = 0;
s->offset_uncached = 0;
s->crc = 0;
s->crc_uncached = 0;
s->crc_stream = CRC_IV;
return s;
}
int sopen_multi_file(STREAM* s, unsigned i, const char* file)
{
#if HAVE_POSIX_FADVISE
int ret;
#endif
int f;
pathcpy(s->handle[i].path, sizeof(s->handle[i].path), file);
/* O_EXCL to be resilient ensure to always create a new file and not use a stale link to the original file */
f = open(file, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_SEQUENTIAL, 0600);
if (f == -1) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
#if HAVE_POSIX_FADVISE
/* advise sequential access */
ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
if (ret == ENOSYS) {
/* call is not supported, like in armhf, see posix_fadvise manpage */
ret = 0;
}
if (ret != 0) {
/* LCOV_EXCL_START */
close(f);
errno = ret; /* posix_fadvise return the error code */
return -1;
/* LCOV_EXCL_STOP */
}
#endif
s->handle[i].f = f;
return 0;
}
STREAM* sopen_write(const char* file)
{
STREAM* s = sopen_multi_write(1);
if (sopen_multi_file(s, 0, file) != 0) {
sclose(s);
return 0;
}
return s;
}
int sclose(STREAM* s)
{
int fail = 0;
unsigned i;
if (s->state == STREAM_STATE_WRITE) {
if (sflush(s) != 0) {
/* LCOV_EXCL_START */
fail = 1;
/* LCOV_EXCL_STOP */
}
}
for (i = 0; i < s->handle_size; ++i) {
if (close(s->handle[i].f) != 0) {
/* LCOV_EXCL_START */
fail = 1;
/* LCOV_EXCL_STOP */
}
}
free(s->handle);
free(s->buffer);
free(s);
if (fail) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int shandle(STREAM* s)
{
if (!s->handle_size) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
return s->handle[0].f;
}
/**
* Fill the read stream buffer.
* \return 0 if at least on char is read, or EOF on error.
*/
static int sfill(STREAM* s)
{
ssize_t ret;
if (s->state != STREAM_STATE_READ) {
/* LCOV_EXCL_START */
return EOF;
/* LCOV_EXCL_STOP */
}
ret = read(s->handle[0].f, s->buffer, STREAM_SIZE);
if (ret < 0) {
/* LCOV_EXCL_START */
s->state = STREAM_STATE_ERROR;
return EOF;
/* LCOV_EXCL_STOP */
}
if (ret == 0) {
s->state = STREAM_STATE_EOF;
return EOF;
}
/* update the crc */
s->crc_uncached = s->crc;
s->crc = crc32c(s->crc, s->buffer, ret);
/* update the offset */
s->offset_uncached = s->offset;
s->offset += ret;
s->pos = s->buffer;
s->end = s->buffer + ret;
return 0;
}
int sdeplete(STREAM* s, unsigned char* last)
{
/* last four bytes */
last[0] = 0;
last[1] = 0;
last[2] = 0;
last[3] = 0;
while (1) {
/* increase the position up to 4 bytes before the end */
if (s->pos + 4 <= s->end)
s->pos = s->end - 4;
/* insert the last 4 bytes */
while (s->pos < s->end) {
last[0] = last[1];
last[1] = last[2];
last[2] = last[3];
last[3] = *s->pos++;
}
/* fill again the buffer until the end of the file */
if (sfill(s) != 0) {
/* on error fail */
if (serror(s)) {
/* LCOV_EXCL_START */
return EOF;
/* LCOV_EXCL_STOP */
}
/* on EOF terminate */
break;
}
}
return 0;
}
int sflush(STREAM* s)
{
ssize_t ret;
ssize_t size;
unsigned i;
if (s->state != STREAM_STATE_WRITE) {
/* LCOV_EXCL_START */
return EOF;
/* LCOV_EXCL_STOP */
}
size = s->pos - s->buffer;
if (!size)
return 0;
for (i = 0; i < s->handle_size; ++i) {
ret = write(s->handle[i].f, s->buffer, size);
if (ret != size) {
/* LCOV_EXCL_START */
s->state = STREAM_STATE_ERROR;
s->state_index = i;
return EOF;
/* LCOV_EXCL_STOP */
}
}
/*
* Update the crc *after* writing the data.
*
* This must be done after the file write,
* to be able to detect memory errors on the buffer,
* happening during the write.
*/
s->crc = crc32c(s->crc, s->buffer, size);
s->crc_uncached = s->crc;
/* update the offset */
s->offset += size;
s->offset_uncached = s->offset;
s->pos = s->buffer;
return 0;
}
int64_t stell(STREAM* s)
{
return s->offset_uncached + (s->pos - s->buffer);
}
uint32_t scrc(STREAM*s)
{
return crc32c(s->crc_uncached, s->buffer, s->pos - s->buffer);
}
uint32_t scrc_stream(STREAM*s)
{
return s->crc_stream ^ CRC_IV;
}
int sgetc_uncached(STREAM* s)
{
/* if at the end of the buffer, fill it */
if (s->pos == s->end && sfill(s) != 0)
return EOF;
return *s->pos++;
}
int sgettok(STREAM* f, char* str, int size)
{
char* i = str;
char* send = str + size;
int c;
while (1) {
c = sgetc(f);
if (c == EOF) {
break;
}
if (c == ' ' || c == '\t') {
sungetc(c, f);
break;
}
if (c == '\n') {
/* remove ending carriage return to support the Windows CR+LF format */
if (i != str && i[-1] == '\r')
--i;
sungetc(c, f);
break;
}
*i++ = c;
if (i == send) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
*i = 0;
return i - str;
}
int sread(STREAM* f, void* void_data, unsigned size)
{
unsigned char* data = void_data;
/* if there is enough space in memory */
if (sptrlookup(f, size)) {
/* optimized version with all the data in memory */
unsigned char* pos = sptrget(f);
/* copy it */
while (size--)
*data++ = *pos++;
sptrset(f, pos);
} else {
/* standard version using sgetc() */
while (size--) {
int c = sgetc(f);
if (c == EOF) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
*data++ = c;
}
}
return 0;
}
int sgetline(STREAM* f, char* str, int size)
{
char* i = str;
char* send = str + size;
int c;
/* if there is enough data in memory */
if (sptrlookup(f, size)) {
/* optimized version with all the data in memory */
unsigned char* pos = sptrget(f);
while (1) {
c = *pos++;
if (c == '\n') {
/* remove ending carriage return to support the Windows CR+LF format */
if (i != str && i[-1] == '\r')
--i;
--pos;
break;
}
*i++ = c;
if (i == send) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
sptrset(f, pos);
} else {
while (1) {
c = sgetc(f);
if (c == EOF) {
/* LCOV_EXCL_START */
break;
/* LCOV_EXCL_STOP */
}
if (c == '\n') {
/* remove ending carriage return to support the Windows CR+LF format */
if (i != str && i[-1] == '\r')
--i;
sungetc(c, f);
break;
}
*i++ = c;
if (i == send) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
}
*i = 0;
return i - str;
}
int sgetlasttok(STREAM* f, char* str, int size)
{
int ret;
ret = sgetline(f, str, size);
if (ret < 0) {
/* LCOV_EXCL_START */
return ret;
/* LCOV_EXCL_STOP */
}
while (ret > 0 && (str[ret - 1] == ' ' || str[ret - 1] == '\t'))
--ret;
str[ret] = 0;
return ret;
}
int sgetu32(STREAM* f, uint32_t* value)
{
int c;
c = sgetc(f);
if (c == '0') {
*value = 0;
return 0;
} else if (c >= '1' && c <= '9') {
uint32_t v;
v = c - '0';
c = sgetc(f);
while (c >= '0' && c <= '9') {
uint32_t digit;
if (v > 0xFFFFFFFFU / 10) {
/* LCOV_EXCL_START */
/* overflow */
return -1;
/* LCOV_EXCL_STOP */
}
v *= 10;
digit = c - '0';
if (v > 0xFFFFFFFFU - digit) {
/* LCOV_EXCL_START */
/* overflow */
return -1;
/* LCOV_EXCL_STOP */
}
v += digit;
c = sgetc(f);
}
*value = v;
sungetc(c, f);
return 0;
} else {
/* LCOV_EXCL_START */
/* nothing read */
return -1;
/* LCOV_EXCL_STOP */
}
}
int sgetb32(STREAM* f, uint32_t* value)
{
uint32_t v;
unsigned char b;
unsigned char s;
int c;
v = 0;
s = 0;
loop:
c = sgetc(f);
if (c == EOF) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
b = (unsigned char)c;
if ((b & 0x80) == 0) {
v |= (uint32_t)b << s;
s += 7;
if (s >= 32) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
goto loop;
}
v |= (uint32_t)(b & 0x7f) << s;
*value = v;
return 0;
}
int sgetb64(STREAM* f, uint64_t* value)
{
uint64_t v;
unsigned char b;
unsigned char s;
int c;
v = 0;
s = 0;
loop:
c = sgetc(f);
if (c == EOF) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
b = (unsigned char)c;
if ((b & 0x80) == 0) {
v |= (uint64_t)b << s;
s += 7;
if (s >= 64) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
goto loop;
}
v |= (uint64_t)(b & 0x7f) << s;
*value = v;
return 0;
}
int sgetble32(STREAM* f, uint32_t* value)
{
unsigned char buf[4];
if (sread(f, buf, 4) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
*value = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
return 0;
}
int sgetbs(STREAM* f, char* str, int size)
{
uint32_t len;
if (sgetb32(f, &len) < 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
if (len + 1 > (uint32_t)size) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
str[len] = 0;
return sread(f, str, (int)len);
}
int swrite(const void* void_data, unsigned size, STREAM* f)
{
const unsigned char* data = void_data;
/* if there is enough space in memory */
if (sptrlookup(f, size)) {
/* optimized version with all the data in memory */
unsigned char* pos = sptrget(f);
/**
* Update the crc *before* writing the data in the buffer
*
* This must be done before the memory write,
* to be able to detect memory errors on the buffer,
* happening before we write it on the file.
*/
f->crc_stream = crc32c_plain(f->crc_stream, data, size);
/* copy it */
while (size--)
*pos++ = *data++;
sptrset(f, pos);
} else {
/* standard version using sputc() */
while (size--) {
if (sputc(*data++, f) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
}
return 0;
}
int sputb32(uint32_t value, STREAM* s)
{
unsigned char b;
unsigned char buf[16];
unsigned i;
i = 0;
loop:
b = value & 0x7f;
value >>= 7;
if (value) {
buf[i++] = b;
goto loop;
}
buf[i++] = b | 0x80;
return swrite(buf, i, s);
}
int sputb64(uint64_t value, STREAM* s)
{
unsigned char b;
unsigned char buf[16];
unsigned i;
i = 0;
loop:
b = value & 0x7f;
value >>= 7;
if (value) {
buf[i++] = b;
goto loop;
}
buf[i++] = b | 0x80;
return swrite(buf, i, s);
}
int sputble32(uint32_t value, STREAM* s)
{
unsigned char buf[4];
buf[0] = value & 0xFF;
buf[1] = (value >> 8) & 0xFF;
buf[2] = (value >> 16) & 0xFF;
buf[3] = (value >> 24) & 0xFF;
return swrite(buf, 4, s);
}
int sputbs(const char* str, STREAM* f)
{
size_t len = strlen(str);
if (sputb32(len, f) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
return swrite(str, len, f);
}
#if HAVE_FSYNC
int ssync(STREAM* s)
{
unsigned i;
for (i = 0; i < s->handle_size; ++i) {
if (fsync(s->handle[i].f) != 0) {
/* LCOV_EXCL_START */
s->state = STREAM_STATE_ERROR;
s->state_index = i;
return -1;
/* LCOV_EXCL_STOP */
}
}
return 0;
}
#endif
snapraid-12.1/cmdline/stream.h 0000664 0000000 0000000 00000022246 14166610522 0016326 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __STREAM_H
#define __STREAM_H
#include "util.h"
/****************************************************************************/
/* stream */
/**
* Size of the buffer of the stream.
*
* It's not a constant for testing purpose.
*/
extern unsigned STREAM_SIZE;
#define STREAM_STATE_READ 0 /**< The stream is in a normal state of read. */
#define STREAM_STATE_WRITE 1 /**< The stream is in a normal state of write. */
#define STREAM_STATE_ERROR -1 /**< An error was encountered. */
#define STREAM_STATE_EOF 2 /**< The end of file was encountered. */
struct stream_handle {
int f; /**< Handle of the file. */
char path[PATH_MAX]; /**< Path of the file. */
};
struct stream {
unsigned char* buffer; /**< Buffer of the stream. */
unsigned char* pos; /**< Current position in the buffer. */
unsigned char* end; /**< End position of the buffer. */
int state; /**< State of the stream. One of STREAM_STATE. */
int state_index; /**< Index of the handle causing a state change. */
unsigned handle_size; /**< Number of handles. */
struct stream_handle* handle; /**< Set of handles. */
off_t offset; /**< Offset into the file. */
off_t offset_uncached; /**< Offset into the file excluding the cached data. */
/**
* CRC of the data read or written in the file.
*
* If reading, it's the CRC of all data read from the file,
* including the one in the buffer.
* If writing it's all the data wrote to the file,
* excluding the one still in buffer yet to be written.
*/
uint32_t crc;
/**
* CRC of the file excluding the cached data in the buffer.
*
* If reading, it's the CRC of the data read from the file,
* excluding the one in the buffer.
* If writing it's all the data wrote to the file,
* excluding the one still in buffer yet to be written.
*/
uint32_t crc_uncached;
/**
* CRC of the data written to the stream.
*
* This is an extra check of the data that is written to
* file to ensure that it's consistent even in case
* of memory errors.
*
* This extra check takes about 2 seconds for each GB of
* content file with the Intel CRC instruction,
* and about 4 seconds without it.
* But usually this doesn't slow down the write process,
* as the disk is the bottle-neck.
*
* Note that this CRC doesn't have the IV processing.
*
* Not used in reading.
* In writing, it's all the data wrote calling sput() functions.
*/
uint32_t crc_stream;
};
/**
* Opaque STREAM type. Like ::FILE.
*/
typedef struct stream STREAM;
/**
* Open a stream for reading. Like fopen("r").
*/
STREAM* sopen_read(const char* file);
/**
* Open a stream for writing. Like fopen("w").
*/
STREAM* sopen_write(const char* file);
/**
* Open a set of streams for writing. Like fopen("w").
*/
STREAM* sopen_multi_write(unsigned count);
/**
* Specify the file to open.
*/
int sopen_multi_file(STREAM* s, unsigned i, const char* file);
/**
* Close a stream. Like fclose().
*/
int sclose(STREAM* s);
/**
* Return the handle of the file.
* In case of multi file, the first one is returned.
*/
int shandle(STREAM* s);
/**
* Read the stream until the end, and return the latest 4 chars.
* The CRC of the file is also computed, and you can get it using scrc().
* \return 0 on success, or EOF on error.
*/
int sdeplete(STREAM* s, unsigned char* last);
/**
* Flush the write stream buffer.
* \return 0 on success, or EOF on error.
*/
int sflush(STREAM* s);
/**
* Get the file pointer.
*/
int64_t stell(STREAM* s);
/**
* Get the CRC of the processed data.
*/
uint32_t scrc(STREAM* s);
/**
* Get the CRC of the processed data in put.
*/
uint32_t scrc_stream(STREAM* s);
/**
* Check if the buffer has enough data loaded.
*/
static inline int sptrlookup(STREAM* s, int size)
{
return s->pos + size <= s->end;
}
/**
* Get the current stream ptr.
*/
static inline unsigned char* sptrget(STREAM* s)
{
return s->pos;
}
/**
* Set the current stream ptr.
*/
static inline void sptrset(STREAM* s, unsigned char* ptr)
{
s->pos = ptr;
}
/**
* Check the error status. Like ferror().
*/
static inline int serror(STREAM* s)
{
return s->state == STREAM_STATE_ERROR;
}
/**
* Check the eof status. Like feof().
*/
static inline int seof(STREAM* s)
{
return s->state == STREAM_STATE_EOF;
}
/**
* Get the index of the handle that caused the error.
*/
static inline int serrorindex(STREAM* s)
{
return s->state_index;
}
/**
* Get the path of the handle that caused the error.
*/
static inline const char* serrorfile(STREAM* s)
{
return s->handle[s->state_index].path;
}
/**
* Sync the stream. Like fsync().
*/
int ssync(STREAM* s);
/****************************************************************************/
/* get */
/**
* \internal Used by sgetc().
* \note Don't call this directly, but use sgetc().
*/
int sgetc_uncached(STREAM* s);
/**
* Read a char. Like fgetc().
*/
static inline int sgetc(STREAM* s)
{
if (tommy_unlikely(s->pos == s->end))
return sgetc_uncached(s);
return *s->pos++;
}
/**
* Unread a char.
* Like ungetc() but you have to unget the same char read.
*/
static inline void sungetc(int c, STREAM* s)
{
if (c != EOF)
--s->pos;
}
/**
* Read a fixed amount of chars.
* Return 0 on success, or -1 on error.
*/
int sread(STREAM* f, void* void_data, unsigned size);
/**
* Get a char from a stream, ignoring one '\r'.
*/
static inline int sgeteol(STREAM* f)
{
int c;
c = sgetc(f);
if (c == '\r')
c = sgetc(f);
return c;
}
/**
* Read all the spaces and tabs.
* Return the number of spaces and tabs read.
*/
static inline int sgetspace(STREAM* f)
{
int count = 0;
int c;
c = sgetc(f);
while (c == ' ' || c == '\t') {
++count;
c = sgetc(f);
}
sungetc(c, f);
return count;
}
/**
* Read until the first space or tab.
* Stop at the first ' ', '\t', '\n' or EOF.
* Return <0 if the buffer is too small, or the number of chars read.
*/
int sgettok(STREAM* f, char* str, int size);
/**
* Read until the end of line.
* Stop at the first '\n' or EOF. Note that '\n' is left in the stream.
* Return <0 if the buffer is too small, or the number of chars read.
*/
int sgetline(STREAM* f, char* str, int size);
/**
* Like sgetline() but remove ' ' and '\t' at the end.
*/
int sgetlasttok(STREAM* f, char* str, int size);
/**
* Read a 32 bit number.
* Stop at the first not digit char or EOF.
* Return <0 if there isn't enough to read.
*/
int sgetu32(STREAM* f, uint32_t* value);
/****************************************************************************/
/* binary get */
/**
* Read a binary 32 bit number in packet format.
* Return <0 if there isn't enough to read.
*/
int sgetb32(STREAM* f, uint32_t* value);
/**
* Read a binary 64 bit number in packet format.
* Return <0 if there isn't enough to read.
*/
int sgetb64(STREAM* f, uint64_t* value);
/**
* Read a binary 32 bit number in little endian format.
* Return <0 if there isn't enough to read.
*/
int sgetble32(STREAM* f, uint32_t* value);
/**
* Read a binary string.
* Return -1 on error or if the buffer is too small, or the number of chars read.
*/
int sgetbs(STREAM* f, char* str, int size);
/****************************************************************************/
/* put */
/**
* Write a char. Like fputc().
* Return 0 on success or -1 on error.
*/
static inline int sputc(int c, STREAM* s)
{
if (s->pos == s->end) {
if (sflush(s) != 0)
return -1;
}
/**
* Update the crc *before* writing the data in the buffer
*
* This must be done before the memory write,
* to be able to detect memory errors on the buffer,
* happening before we write it on the file.
*/
s->crc_stream = crc32c_plain_char(s->crc_stream, c);
*s->pos++ = c;
return 0;
}
/**
* Write a end of line.
* Return 0 on success or -1 on error.
*/
static inline int sputeol(STREAM* s)
{
#ifdef _WIN32
if (sputc('\r', s) != 0)
return -1;
#endif
return sputc('\n', s);
}
/**
* Write a sized string.
* Return 0 on success or -1 on error.
*/
int swrite(const void* data, unsigned size, STREAM* f);
/****************************************************************************/
/* binary put */
/**
* Write a binary 32 bit number in packed format.
* Return 0 on success or -1 on error.
*/
int sputb32(uint32_t value, STREAM* s);
/**
* Write a binary 64 bit number in packed format.
* Return 0 on success or -1 on error.
*/
int sputb64(uint64_t value, STREAM* s);
/**
* Write a binary 32 bit number in little endian format.
* Return 0 on success or -1 on error.
*/
int sputble32(uint32_t value, STREAM* s);
/**
* Write a binary string.
* Return 0 on success or -1 on error.
*/
int sputbs(const char* str, STREAM* s);
#endif
snapraid-12.1/cmdline/support.c 0000664 0000000 0000000 00000112330 14166610522 0016534 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
/****************************************************************************/
/* lock */
/**
* Locks used externally.
*/
#if HAVE_THREAD
static thread_mutex_t msg_lock;
static thread_mutex_t memory_lock;
#endif
void lock_msg(void)
{
#if HAVE_THREAD
thread_mutex_lock(&msg_lock);
#endif
}
void unlock_msg(void)
{
#if HAVE_THREAD
thread_mutex_unlock(&msg_lock);
#endif
}
void lock_memory(void)
{
#if HAVE_THREAD
thread_mutex_lock(&memory_lock);
#endif
}
void unlock_memory(void)
{
#if HAVE_THREAD
thread_mutex_unlock(&memory_lock);
#endif
}
void lock_init(void)
{
#if HAVE_THREAD
/* initialize the locks as first operation as log_fatal depends on them */
thread_mutex_init(&msg_lock);
thread_mutex_init(&memory_lock);
#endif
}
void lock_done(void)
{
#if HAVE_THREAD
thread_mutex_destroy(&msg_lock);
thread_mutex_destroy(&memory_lock);
#endif
}
/****************************************************************************/
/* print */
int msg_level = 0;
FILE* stdlog = 0;
/*
* Note that in the following functions we always flush both
* stdout and stderr, because we want to ensure that they mixes
* well when redirected to files
*
* The buffering is similar at the "line buffered" one, that
* is not available on Windows, so we emulate it in this way.
*
* For stdlog flushing is limited. To ensure flushing the
* caller should use log_flush().
*/
void log_fatal(const char* format, ...)
{
va_list ap;
lock_msg();
if (stdlog) {
va_start(ap, format);
fprintf(stdlog, "msg:fatal: ");
vfprintf(stdlog, format, ap);
fflush(stdlog);
va_end(ap);
}
va_start(ap, format);
vfprintf(stderr, format, ap);
fflush(stderr);
va_end(ap);
unlock_msg();
}
void log_error(const char* format, ...)
{
va_list ap;
lock_msg();
if (stdlog) {
va_start(ap, format);
fprintf(stdlog, "msg:error: ");
vfprintf(stdlog, format, ap);
fflush(stdlog);
va_end(ap);
} else {
va_start(ap, format);
vfprintf(stderr, format, ap);
fflush(stderr);
va_end(ap);
}
unlock_msg();
}
void log_expected(const char* format, ...)
{
va_list ap;
lock_msg();
if (stdlog) {
va_start(ap, format);
fprintf(stdlog, "msg:expected: ");
vfprintf(stdlog, format, ap);
fflush(stdlog);
va_end(ap);
}
unlock_msg();
}
void log_tag(const char* format, ...)
{
va_list ap;
lock_msg();
if (stdlog) {
va_start(ap, format);
vfprintf(stdlog, format, ap);
/* here we intentionally don't flush */
/* to make the output faster */
va_end(ap);
}
unlock_msg();
}
void log_flush(void)
{
lock_msg();
if (stdlog)
fflush(stdlog);
fflush(stdout);
fflush(stderr);
unlock_msg();
}
void msg_status(const char* format, ...)
{
va_list ap;
lock_msg();
if (stdlog) {
va_start(ap, format);
fprintf(stdlog, "msg:status: ");
vfprintf(stdlog, format, ap);
fflush(stdlog);
va_end(ap);
}
if (msg_level >= MSG_STATUS) {
va_start(ap, format);
vfprintf(stdout, format, ap);
fflush(stdout);
va_end(ap);
}
unlock_msg();
}
void msg_info(const char* format, ...)
{
va_list ap;
lock_msg();
/* don't output in stdlog as these messages */
/* are always paired with a msg_tag() call */
if (msg_level >= MSG_INFO) {
va_start(ap, format);
vfprintf(stdout, format, ap);
fflush(stdout);
va_end(ap);
}
unlock_msg();
}
void msg_progress(const char* format, ...)
{
va_list ap;
lock_msg();
if (stdlog) {
va_start(ap, format);
fprintf(stdlog, "msg:progress: ");
vfprintf(stdlog, format, ap);
fflush(stdlog);
va_end(ap);
}
if (msg_level >= MSG_PROGRESS) {
va_start(ap, format);
vfprintf(stdout, format, ap);
fflush(stdout);
va_end(ap);
}
unlock_msg();
}
void msg_bar(const char* format, ...)
{
va_list ap;
lock_msg();
/* don't output in stdlog as these messages */
/* are intended for screen only */
/* also don't flush stdout as they are intended to be partial messages */
if (msg_level >= MSG_BAR) {
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
}
unlock_msg();
}
void msg_verbose(const char* format, ...)
{
va_list ap;
lock_msg();
if (stdlog) {
va_start(ap, format);
fprintf(stdlog, "msg:verbose: ");
vfprintf(stdlog, format, ap);
fflush(stdlog);
va_end(ap);
}
if (msg_level >= MSG_VERBOSE) {
va_start(ap, format);
vfprintf(stdout, format, ap);
fflush(stdout);
va_end(ap);
}
unlock_msg();
}
void msg_flush(void)
{
lock_msg();
fflush(stdout);
fflush(stderr);
unlock_msg();
}
void printc(char c, size_t pad)
{
while (pad) {
/* group writes in long pieces */
char buf[128];
size_t len = pad;
if (len >= sizeof(buf))
len = sizeof(buf) - 1;
memset(buf, c, len);
buf[len] = 0;
fputs(buf, stdout);
pad -= len;
}
}
void printr(const char* str, size_t pad)
{
size_t len;
len = strlen(str);
if (len < pad)
printc(' ', pad - len);
fputs(str, stdout);
}
void printl(const char* str, size_t pad)
{
size_t len;
fputs(str, stdout);
len = strlen(str);
if (len < pad)
printc(' ', pad - len);
}
void printp(double v, size_t pad)
{
char buf[64];
const char* s = "%";
if (v > 0.1)
snprintf(buf, sizeof(buf), "%5.2f%s", v, s);
else if (v > 0.01)
snprintf(buf, sizeof(buf), "%6.3f%s", v, s);
else if (v > 0.001)
snprintf(buf, sizeof(buf), "%7.4f%s", v, s);
else if (v > 0.0001)
snprintf(buf, sizeof(buf), "%8.5f%s", v, s);
else if (v > 0.00001)
snprintf(buf, sizeof(buf), "%9.6f%s", v, s);
else if (v > 0.000001)
snprintf(buf, sizeof(buf), "%10.7f%s", v, s);
else if (v > 0.0000001)
snprintf(buf, sizeof(buf), "%11.8f%s", v, s);
else if (v > 0.00000001)
snprintf(buf, sizeof(buf), "%12.9f%s", v, s);
else if (v > 0.000000001)
snprintf(buf, sizeof(buf), "%13.10f%s", v, s);
else if (v > 0.0000000001)
snprintf(buf, sizeof(buf), "%14.11f%s", v, s);
else if (v > 0.00000000001)
snprintf(buf, sizeof(buf), "%15.12f%s", v, s);
else if (v > 0.000000000001)
snprintf(buf, sizeof(buf), "%16.13f%s", v, s);
else
snprintf(buf, sizeof(buf), "%17.14f%s", v, s);
printl(buf, pad);
}
#define ESCAPE(from,escape,to) \
case from : \
if (p == end) \
goto bail; \
*p++ = escape; \
if (p == end) \
goto bail; \
*p++ = to; \
break
const char* esc_tag(const char* str, char* buffer)
{
char* begin = buffer;
char* end = begin + ESC_MAX;
char* p = begin;
/* copy string with escaping */
while (*str) {
char c = *str;
switch (c) {
ESCAPE('\n', '\\', 'n');
ESCAPE('\r', '\\', 'r');
ESCAPE(':', '\\', 'd');
ESCAPE('\\', '\\', '\\');
default:
if (p == end)
goto bail;
*p++ = c;
break;
}
++str;
}
/* put final 0 */
if (p == end)
goto bail;
*p = 0;
return begin;
bail:
/* LCOV_EXCL_START */
log_fatal("Escape for log too long\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer)
{
char* begin = buffer;
char* end = begin + ESC_MAX;
char* p = begin;
unsigned str_mac;
const char* str;
#ifdef _WIN32
int has_quote = 0;
for (str_mac = 0; str_mac < str_max; ++str_mac) {
str = str_map[str_mac];
if (strchr(str, ' ') != 0)
has_quote = 1;
}
if (has_quote) {
if (p == end)
goto bail;
*p++ = '"';
}
#endif
/* copy string with escaping */
str_mac = 0;
str = str_map[str_mac];
while (1) {
/* get the next char */
char c = *str;
/* if one string is finished, go to the next */
while (c == 0 && str_mac + 1 < str_max) {
++str_mac;
str = str_map[str_mac];
c = *str;
}
/* if we read all the strings, stop */
if (!c)
break;
switch (c) {
#ifdef _WIN32
/*
* Windows shell escape
*
* The Windows NT Command Shell
* https://technet.microsoft.com/en-us/library/cc723564.aspx
*/
case '"' :
/* double quote, it needs to be quoted with \ */
if (has_quote) {
/* " -> "\"" -> (close quote)(quoted with \ ")(reopen quote) */
if (p == end)
goto bail;
*p++ = '"';
if (p == end)
goto bail;
*p++ = '\\';
if (p == end)
goto bail;
*p++ = '"';
if (p == end)
goto bail;
*p++ = '"';
} else {
/* " -> \" */
if (p == end)
goto bail;
*p++ = '\\';
if (p == end)
goto bail;
*p++ = '"';
}
break;
case '&' :
case '|' :
case '(' :
case ')' :
case '<' :
case '>' :
case '^' :
/* reserved chars, they need to be quoted with ^ */
if (has_quote) {
if (p == end)
goto bail;
*p++ = c;
} else {
if (p == end)
goto bail;
*p++ = '^';
if (p == end)
goto bail;
*p++ = c;
}
break;
#else
/* special chars that need to be quoted */
case ' ' : /* space */
case '~' : /* home */
case '`' : /* command */
case '#' : /* comment */
case '$' : /* variable */
case '&' : /* background job */
case '*' : /* wildcard */
case '(' : /* shell */
case ')' : /* shell */
case '\\': /* quote */
case '|' : /* pipe */
case '[' : /* wildcard */
case ']' : /* wildcard */
case '{' : /* code */
case '}' : /* code */
case ';' : /* separator */
case '\'': /* quote */
case '"' : /* quote */
case '<' : /* redirect */
case '>' : /* redirect */
case '?' : /* wildcard */
if (p == end)
goto bail;
*p++ = '\\';
if (p == end)
goto bail;
*p++ = c;
break;
#endif
default :
/* unquoted */
if (p == end)
goto bail;
*p++ = c;
break;
}
++str;
}
#ifdef _WIN32
if (has_quote) {
if (p == end)
goto bail;
*p++ = '"';
}
#endif
/* put final 0 */
if (p == end)
goto bail;
*p = 0;
return begin;
bail:
/* LCOV_EXCL_START */
log_fatal("Escape for shell too long\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
char* strpolish(char* s)
{
char* i = s;
while (*i) {
if (isspace(*i) || !isprint(*i))
*i = ' ';
++i;
}
return s;
}
unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters)
{
unsigned mac = 0;
/* skip initial delimiters */
str += strspn(str, delimiters);
while (*str != 0 || mac == split_max) {
/* start of the token */
split_map[mac] = str;
++mac;
/* find the first delimiter or the end of the string */
str += strcspn(str, delimiters);
/* put the final terminator if missing */
if (*str != 0)
*str++ = 0;
/* skip trailing delimiters */
str += strspn(str, delimiters);
}
return mac;
}
/****************************************************************************/
/* path */
void pathcpy(char* dst, size_t size, const char* src)
{
size_t len = strlen(src);
if (len + 1 > size) {
/* LCOV_EXCL_START */
log_fatal("Path too long '%s'\n", src);
os_abort();
/* LCOV_EXCL_STOP */
}
memcpy(dst, src, len + 1);
}
void pathcat(char* dst, size_t size, const char* src)
{
size_t dst_len = strlen(dst);
size_t src_len = strlen(src);
if (dst_len + src_len + 1 > size) {
/* LCOV_EXCL_START */
log_fatal("Path too long '%s%s'\n", dst, src);
os_abort();
/* LCOV_EXCL_STOP */
}
memcpy(dst + dst_len, src, src_len + 1);
}
void pathcatl(char* dst, size_t dst_len, size_t size, const char* src)
{
size_t src_len = strlen(src);
if (dst_len + src_len + 1 > size) {
/* LCOV_EXCL_START */
log_fatal("Path too long '%s%s'\n", dst, src);
os_abort();
/* LCOV_EXCL_STOP */
}
memcpy(dst + dst_len, src, src_len + 1);
}
void pathcatc(char* dst, size_t size, char c)
{
size_t dst_len = strlen(dst);
if (dst_len + 2 > size) {
/* LCOV_EXCL_START */
log_fatal("Path too long '%s%c'\n", dst, c);
os_abort();
/* LCOV_EXCL_STOP */
}
dst[dst_len] = c;
dst[dst_len + 1] = 0;
}
void pathimport(char* dst, size_t size, const char* src)
{
pathcpy(dst, size, src);
#ifdef _WIN32
/* convert the Windows dir separator '\' to C '/', */
/* and the Windows escaping char '^' to the fnmatch '\' */
while (*dst) {
switch (*dst) {
case '\\' :
*dst = '/';
break;
case '^' :
*dst = '\\';
break;
}
++dst;
}
#endif
}
void pathexport(char* dst, size_t size, const char* src)
{
pathcpy(dst, size, src);
#ifdef _WIN32
/* invert the import */
while (*dst) {
switch (*dst) {
case '/' :
*dst = '\\';
break;
case '\\' :
*dst = '^';
break;
}
++dst;
}
#endif
}
void pathprint(char* dst, size_t size, const char* format, ...)
{
size_t len;
va_list ap;
va_start(ap, format);
len = vsnprintf(dst, size, format, ap);
va_end(ap);
if (len >= size) {
/* LCOV_EXCL_START */
if (size > 0) {
dst[size - 1] = 0;
log_fatal("Path too long '%s...'\n", dst);
} else {
log_fatal("Path too long for empty size'\n");
}
os_abort();
/* LCOV_EXCL_STOP */
}
}
void pathslash(char* dst, size_t size)
{
size_t len = strlen(dst);
if (len > 0 && dst[len - 1] != '/') {
if (len + 2 >= size) {
/* LCOV_EXCL_START */
log_fatal("Path too long '%s/'\n", dst);
os_abort();
/* LCOV_EXCL_STOP */
}
dst[len] = '/';
dst[len + 1] = 0;
}
}
void pathcut(char* dst)
{
char* slash = strrchr(dst, '/');
if (slash)
slash[1] = 0;
else
dst[0] = 0;
}
int pathcmp(const char* a, const char* b)
{
#ifdef _WIN32
char ai[PATH_MAX];
char bi[PATH_MAX];
/* import to convert \ to / */
pathimport(ai, sizeof(ai), a);
pathimport(bi, sizeof(bi), b);
/* case insensitive compare in Windows */
return stricmp(ai, bi);
#else
return strcmp(a, b);
#endif
}
/****************************************************************************/
/* file-system */
int mkancestor(const char* file)
{
char dir[PATH_MAX];
struct stat st;
char* c;
pathcpy(dir, sizeof(dir), file);
c = strrchr(dir, '/');
if (!c) {
/* no ancestor */
return 0;
}
/* clear the file */
*c = 0;
/* if it's the root dir */
if (*dir == 0) {
/* nothing more to do */
return 0;
}
#ifdef _WIN32
/* if it's a drive specification like "C:" */
if (isalpha(dir[0]) && dir[1] == ':' && dir[2] == 0) {
/* nothing more to do */
return 0;
}
#endif
/*
* Check if the dir already exists using lstat().
*
* Note that in Windows when dealing with read-only media
* you cannot try to create the directory, and expecting
* the EEXIST error because the call will fail with ERROR_WRITE_PROTECTED.
*
* Also in Windows it's better to use lstat() than stat() because it
* doesn't need to open the dir with CreateFile().
*/
if (lstat(dir, &st) == 0) {
/* it already exists */
return 0;
}
/* recursively create them all */
if (mkancestor(dir) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
/* create it */
if (mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error creating directory '%s'. %s.\n", dir, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int fmtime(int f, int64_t mtime_sec, int mtime_nsec)
{
#if HAVE_FUTIMENS
struct timespec tv[2];
#else
struct timeval tv[2];
#endif
int ret;
#if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */
tv[0].tv_sec = mtime_sec;
if (mtime_nsec != STAT_NSEC_INVALID)
tv[0].tv_nsec = mtime_nsec;
else
tv[0].tv_nsec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_nsec = tv[0].tv_nsec;
ret = futimens(f, tv);
#elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */
tv[0].tv_sec = mtime_sec;
if (mtime_nsec != STAT_NSEC_INVALID)
tv[0].tv_usec = mtime_nsec / 1000;
else
tv[0].tv_usec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_usec = tv[0].tv_usec;
ret = futimes(f, tv);
#elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
tv[0].tv_sec = mtime_sec;
if (mtime_nsec != STAT_NSEC_INVALID)
tv[0].tv_usec = mtime_nsec / 1000;
else
tv[0].tv_usec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_usec = tv[0].tv_usec;
ret = futimesat(f, 0, tv);
#else
#error No function available to set file timestamps with sub-second precision
#endif
return ret;
}
int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec)
{
#if HAVE_UTIMENSAT
struct timespec tv[2];
#else
struct timeval tv[2];
#endif
int ret;
#if HAVE_UTIMENSAT /* utimensat() is preferred because it gives nanosecond precision */
tv[0].tv_sec = mtime_sec;
if (mtime_nsec != STAT_NSEC_INVALID)
tv[0].tv_nsec = mtime_nsec;
else
tv[0].tv_nsec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_nsec = tv[0].tv_nsec;
ret = utimensat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW);
#elif HAVE_LUTIMES /* fallback to lutimes() if nanosecond precision is not available */
tv[0].tv_sec = mtime_sec;
if (mtime_nsec != STAT_NSEC_INVALID)
tv[0].tv_usec = mtime_nsec / 1000;
else
tv[0].tv_usec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_usec = tv[0].tv_usec;
ret = lutimes(path, tv);
#elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
tv[0].tv_sec = mtime_sec;
if (mtime_nsec != STAT_NSEC_INVALID)
tv[0].tv_usec = mtime_nsec / 1000;
else
tv[0].tv_usec = 0;
tv[1].tv_sec = tv[0].tv_sec;
tv[1].tv_usec = tv[0].tv_usec;
ret = futimesat(AT_FDCWD, path, tv);
#else
#error No function available to set file timestamps with sub-second precision
#endif
return ret;
}
/****************************************************************************/
/* advise */
void advise_init(struct advise_struct* advise, int mode)
{
advise->mode = mode;
advise->dirty_begin = 0;
advise->dirty_end = 0;
}
int advise_flags(struct advise_struct* advise)
{
int flags = 0;
if (advise->mode == ADVISE_SEQUENTIAL
|| advise->mode == ADVISE_FLUSH
|| advise->mode == ADVISE_FLUSH_WINDOW
|| advise->mode == ADVISE_DISCARD
|| advise->mode == ADVISE_DISCARD_WINDOW
)
flags |= O_SEQUENTIAL;
#if HAVE_DIRECT_IO
if (advise->mode == ADVISE_DIRECT)
flags |= O_DIRECT;
#endif
return flags;
}
int advise_open(struct advise_struct* advise, int f)
{
(void)advise;
(void)f;
#if HAVE_POSIX_FADVISE
if (advise->mode == ADVISE_SEQUENTIAL
|| advise->mode == ADVISE_FLUSH
|| advise->mode == ADVISE_FLUSH_WINDOW
|| advise->mode == ADVISE_DISCARD
|| advise->mode == ADVISE_DISCARD_WINDOW
) {
int ret;
/* advise sequential access */
ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
if (ret == ENOSYS) {
/* call is not supported, like in armhf, see posix_fadvise manpage */
ret = 0;
}
if (ret != 0) {
/* LCOV_EXCL_START */
errno = ret; /* posix_fadvise return the error code */
return -1;
/* LCOV_EXCL_STOP */
}
}
#endif
return 0;
}
int advise_write(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
{
data_off_t flush_offset;
data_off_t flush_size;
data_off_t discard_offset;
data_off_t discard_size;
(void)f;
(void)flush_offset;
(void)flush_size;
(void)discard_offset;
(void)discard_size;
flush_offset = 0;
flush_size = 0;
discard_offset = 0;
discard_size = 0;
/*
* Follow Linus recommendations about fast writes.
*
* Linus "Unexpected splice "always copy" behavior observed"
* http://thread.gmane.org/gmane.linux.kernel/987247/focus=988070
* ---
* I have had _very_ good experiences with even a rather trivial
* file writer that basically used (iirc) 8MB windows, and the logic was very
* trivial:
*
* - before writing a new 8M window, do "start writeback"
* (SYNC_FILE_RANGE_WRITE) on the previous window, and do
* a wait (SYNC_FILE_RANGE_WAIT_AFTER) on the window before that.
*
* in fact, in its simplest form, you can do it like this (this is from my
* "overwrite disk images" program that I use on old disks):
*
* for (index = 0; index < max_index ;index++) {
* if (write(fd, buffer, BUFSIZE) != BUFSIZE)
* break;
* // This won't block, but will start writeout asynchronously
* sync_file_range(fd, index*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WRITE);
* // This does a blocking write-and-wait on any old ranges
* if (index)
* sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
* }
*
* and even if you don't actually do a discard (maybe we should add a
* SYNC_FILE_RANGE_DISCARD bit, right now you'd need to do a separate
* fadvise(FADV_DONTNEED) to throw it out) the system behavior is pretty
* nice, because the heavy writer gets good IO performance _and_ leaves only
* easy-to-free pages around after itself.
* ---
*
* Linus "Unexpected splice "always copy" behavior observed"
* http://thread.gmane.org/gmane.linux.kernel/987247/focus=988176
* ---
* The behavior for dirty page writeback is _not_ well defined, and
* if you do POSIX_FADV_DONTNEED, I would suggest you do it as part of that
* writeback logic, ie you do it only on ranges that you have just waited on.
*
* IOW, in my example, you'd couple the
*
* sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
*
* with a
*
* posix_fadvise(fd, (index-1)*BUFSIZE, BUFSIZE, POSIX_FADV_DONTNEED);
*
* afterwards to throw out the pages that you just waited for.
* ---
*/
switch (advise->mode) {
case ADVISE_FLUSH :
flush_offset = offset;
flush_size = size;
break;
case ADVISE_DISCARD :
discard_offset = offset;
discard_size = size;
break;
case ADVISE_FLUSH_WINDOW :
/* if the dirty range can be extended */
if (advise->dirty_end == offset) {
/* extent the dirty range */
advise->dirty_end += size;
/* if we reached the window size */
if (advise->dirty_end - advise->dirty_begin >= ADVISE_WINDOW_SIZE) {
/* flush the window */
flush_offset = advise->dirty_begin;
flush_size = ADVISE_WINDOW_SIZE;
/* remove it from the dirty range */
advise->dirty_begin += ADVISE_WINDOW_SIZE;
}
} else {
/* otherwise flush the existing dirty */
flush_offset = advise->dirty_begin;
flush_size = advise->dirty_end - advise->dirty_begin;
/* and set the new range as dirty */
advise->dirty_begin = offset;
advise->dirty_end = offset + size;
}
break;
case ADVISE_DISCARD_WINDOW :
/* if the dirty range can be extended */
if (advise->dirty_end == offset) {
/* extent the dirty range */
advise->dirty_end += size;
/* if we reached the double window size */
if (advise->dirty_end - advise->dirty_begin >= 2 * ADVISE_WINDOW_SIZE) {
/* discard the first window */
discard_offset = advise->dirty_begin;
discard_size = ADVISE_WINDOW_SIZE;
/* remove it from the dirty range */
advise->dirty_begin += ADVISE_WINDOW_SIZE;
/* flush the second window */
flush_offset = advise->dirty_begin;
flush_size = ADVISE_WINDOW_SIZE;
}
} else {
/* otherwise discard the existing dirty */
discard_offset = advise->dirty_begin;
discard_size = advise->dirty_end - advise->dirty_begin;
/* and set the new range as dirty */
advise->dirty_begin = offset;
advise->dirty_end = offset + size;
}
break;
}
#if HAVE_SYNC_FILE_RANGE
if (flush_size != 0) {
int ret;
/* start writing immediately */
ret = sync_file_range(f, flush_offset, flush_size, SYNC_FILE_RANGE_WRITE);
if (ret != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
#endif
#if HAVE_SYNC_FILE_RANGE && HAVE_POSIX_FADVISE
if (discard_size != 0) {
int ret;
/* send the data to the disk and wait until it's written */
ret = sync_file_range(f, discard_offset, discard_size, SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER);
if (ret != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
/* flush the data from the cache */
ret = posix_fadvise(f, discard_offset, discard_size, POSIX_FADV_DONTNEED);
/* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */
if (ret != 0) {
/* LCOV_EXCL_START */
errno = ret; /* posix_fadvise return the error code */
return -1;
/* LCOV_EXCL_STOP */
}
}
#endif
return 0;
}
int advise_read(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
{
(void)advise;
(void)f;
(void)offset;
(void)size;
#if HAVE_POSIX_FADVISE
if (advise->mode == ADVISE_DISCARD
|| advise->mode == ADVISE_DISCARD_WINDOW
) {
int ret;
/* flush the data from the cache */
ret = posix_fadvise(f, offset, size, POSIX_FADV_DONTNEED);
/* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */
if (ret != 0) {
/* LCOV_EXCL_START */
errno = ret; /* posix_fadvise return the error code */
return -1;
/* LCOV_EXCL_STOP */
}
}
#endif
/*
* Here we cannot call posix_fadvise(..., POSIX_FADV_WILLNEED) for the next block
* because it may be blocking.
*
* Ted Ts'o "posix_fadvise(POSIX_FADV_WILLNEED) waits before returning?"
* https://lkml.org/lkml/2010/12/6/122
* ---
* readahead and posix_fadvise(POSIX_FADV_WILLNEED) work exactly the same
* way, and in fact share mostly the same code path (see
* force_page_cache_readahead() in mm/readahead.c).
*
* They are asynchronous in that there is no guarantee the pages will be
* in the page cache by the time they return. But at the same time, they
* are not guaranteed to be non-blocking. That is, the work of doing the
* readahead does not take place in a kernel thread. So if you try to
* request I/O than will fit in the request queue, the system call will
* block until some I/O is completed so that more I/O requested cam be
* loaded onto the request queue.
*
* The only way to fix this would be to either put the work on a kernel
* thread (i.e., some kind of workqueue) or in a userspace thread. For
* ion programmer wondering what to do today, I'd suggest the
* latter since it will be more portable across various kernel versions.
*
* This does leave the question about whether we should change the kernel
* to allow readahead() and posix_fadvise(POSIX_FADV_WILLNEED) to be
* non-blocking and do this work in a workqueue (or via some kind of
* callback/continuation scheme). My worry is just doing this if a user
* application does something crazy, like request gigabytes and gigabytes
* of readahead, and then repented of their craziness, there should be a
* way of cancelling the readahead request. Today, the user can just
* kill the application. But if we simply shove the work to a kernel
* thread, it becomes a lot harder to cancel the readahead request. We'd
* have to invent a new API, and then have a way to know whether the user
* has access to kill a particular readahead request, etc.
* ---
*/
return 0;
}
/****************************************************************************/
/* memory */
/**
* Total amount of memory allocated.
*/
static size_t mcounter;
size_t malloc_counter_get(void)
{
size_t ret;
lock_memory();
ret = mcounter;
unlock_memory();
return ret;
}
void malloc_counter_inc(size_t inc)
{
lock_memory();
mcounter += inc;
unlock_memory();
}
/* LCOV_EXCL_START */
static ssize_t malloc_print(int f, const char* str)
{
ssize_t len = 0;
while (str[len])
++len;
return write(f, str, len);
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START */
static ssize_t malloc_printn(int f, size_t value)
{
char buf[32];
int i;
if (!value)
return write(f, "0", 1);
i = sizeof(buf);
while (value) {
buf[--i] = (value % 10) + '0';
value /= 10;
}
return write(f, buf + i, sizeof(buf) - i);
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START */
void malloc_fail(size_t size)
{
/* don't use printf to avoid any possible extra allocation */
int f = 2; /* stderr */
malloc_print(f, "Failed for Low Memory!\n");
malloc_print(f, "Allocating ");
malloc_printn(f, size);
malloc_print(f, " bytes.\n");
malloc_print(f, "Already allocated ");
malloc_printn(f, malloc_counter_get());
malloc_print(f, " bytes.\n");
if (sizeof(void*) == 4) {
malloc_print(f, "You are currently using a 32 bits executable.\n");
malloc_print(f, "If you have more than 4GB of memory, please upgrade to a 64 bits one.\n");
}
}
/* LCOV_EXCL_STOP */
void* malloc_nofail(size_t size)
{
void* ptr = malloc(size);
if (!ptr) {
/* LCOV_EXCL_START */
malloc_fail(size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#ifndef CHECKER /* Don't preinitialize when running for valgrind */
/* Here we preinitialize the memory to ensure that the OS is really allocating it */
/* and not only reserving the addressable space. */
/* Otherwise we are risking that the OOM (Out Of Memory) killer in Linux will kill the process. */
/* Filling the memory doesn't ensure to disable OOM, but it increase a lot the chances to */
/* get a real error from malloc() instead than a process killed. */
/* Note that calloc() doesn't have the same effect. */
memset(ptr, 0xA5, size);
#endif
malloc_counter_inc(size);
return ptr;
}
void* calloc_nofail(size_t count, size_t size)
{
void* ptr;
size *= count;
/* see the note in malloc_nofail() of why we don't use calloc() */
ptr = malloc(size);
if (!ptr) {
/* LCOV_EXCL_START */
malloc_fail(size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
memset(ptr, 0, size);
malloc_counter_inc(size);
return ptr;
}
char* strdup_nofail(const char* str)
{
size_t size;
char* ptr;
size = strlen(str) + 1;
ptr = malloc(size);
if (!ptr) {
/* LCOV_EXCL_START */
malloc_fail(size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
memcpy(ptr, str, size);
malloc_counter_inc(size);
return ptr;
}
/****************************************************************************/
/* smartctl */
/**
* Match a string with the specified pattern.
* Like sscanf() a space match any sequence of spaces.
* Return 0 if it matches.
*/
static int smatch(const char* str, const char* pattern)
{
while (*pattern) {
if (isspace(*pattern)) {
++pattern;
while (isspace(*str))
++str;
} else if (*pattern == *str) {
++pattern;
++str;
} else
return -1;
}
return 0;
}
int smartctl_attribute(FILE* f, const char* file, const char* name, uint64_t* smart, char* serial, char* vendor, char* model)
{
unsigned i;
int inside;
/* preclear attribute */
*serial = 0;
for (i = 0; i < SMART_COUNT; ++i)
smart[i] = SMART_UNASSIGNED;
/* read the file */
inside = 0;
while (1) {
char buf[256];
unsigned id;
uint64_t raw;
char* s;
s = fgets(buf, sizeof(buf), f);
if (s == 0)
break;
/* remove extraneous chars */
s = strpolish(buf);
log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
/* skip initial spaces */
while (isspace(*s))
++s;
if (*s == 0) {
inside = 0;
/* common */
} else if (smatch(s, "Rotation Rate: Solid State") == 0) {
smart[SMART_ROTATION_RATE] = 0;
} else if (sscanf(s, "Rotation Rate: %" SCNu64, &smart[SMART_ROTATION_RATE]) == 1) {
} else if (smatch(s, "User Capacity:") == 0) {
char* begin = strchr(s, ':');
char* end = strstr(s, "bytes");
if (begin != 0 && end != 0 && begin < end) {
char* p;
smart[SMART_SIZE] = 0;
for (p = begin; p != end; ++p) {
if (isdigit(*p)) {
smart[SMART_SIZE] *= 10;
smart[SMART_SIZE] += *p - '0';
}
}
}
} else if (sscanf(s, "Device Model: %63s %63s", vendor, model) == 2) {
} else if (sscanf(s, "Device Model: %63s", model) == 1) {
/* SCSI */
} else if (sscanf(s, "Serial number: %63s", serial) == 1) { /* note "n" of "number" lower case */
} else if (sscanf(s, "Elements in grown defect list: %" SCNu64, &smart[SMART_REALLOCATED_SECTOR_COUNT]) == 1) {
} else if (sscanf(s, "Current Drive Temperature: %" SCNu64, &smart[SMART_TEMPERATURE_CELSIUS]) == 1) {
} else if (sscanf(s, "Drive Trip Temperature: %" SCNu64, &smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS]) == 1) {
} else if (sscanf(s, "Accumulated start-stop cycles: %" SCNu64, &smart[SMART_START_STOP_COUNT]) == 1) {
} else if (sscanf(s, "Accumulated load-unload cycles: %" SCNu64, &smart[SMART_LOAD_CYCLE_COUNT]) == 1) {
} else if (sscanf(s, " number of hours powered up = %" SCNu64, &smart[SMART_POWER_ON_HOURS]) == 1) {
/* ATA */
} else if (sscanf(s, "Serial Number: %63s", serial) == 1) {
} else if (smatch(s, "ID#") == 0) {
inside = 1;
} else if (smatch(s, "No Errors Logged") == 0) {
smart[SMART_ERROR] = 0;
} else if (sscanf(s, "ATA Error Count: %" SCNu64, &raw) == 1) {
smart[SMART_ERROR] = raw;
} else if (inside) {
if (sscanf(s, "%u %*s %*s %*s %*s %*s %*s %*s %*s %" SCNu64, &id, &raw) != 2) {
/* LCOV_EXCL_START */
log_fatal("Invalid smartctl line '%s'.\n", s);
return -1;
/* LCOV_EXCL_STOP */
}
if (id >= 256) {
/* LCOV_EXCL_START */
log_fatal("Invalid SMART id '%u'.\n", id);
return -1;
/* LCOV_EXCL_STOP */
}
smart[id] = raw;
}
}
return 0;
}
int smartctl_flush(FILE* f, const char* file, const char* name)
{
/* read the file */
while (1) {
char buf[256];
char* s;
s = fgets(buf, sizeof(buf), f);
if (s == 0)
break;
/* remove extraneous chars */
s = strpolish(buf);
log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
}
return 0;
}
/****************************************************************************/
/* thread */
#if HAVE_THREAD
void thread_mutex_init(thread_mutex_t* mutex)
{
if (pthread_mutex_init(mutex, 0) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_mutex_init().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_mutex_destroy(thread_mutex_t* mutex)
{
if (pthread_mutex_destroy(mutex) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_mutex_destroy().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_mutex_lock(thread_mutex_t* mutex)
{
if (pthread_mutex_lock(mutex) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_mutex_lock().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_mutex_unlock(thread_mutex_t* mutex)
{
if (pthread_mutex_unlock(mutex) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_mutex_unlock().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_cond_init(thread_cond_t* cond)
{
if (pthread_cond_init(cond, 0) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_cond_init().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_cond_destroy(thread_cond_t* cond)
{
if (pthread_cond_destroy(cond) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_cond_destroy().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_cond_signal(thread_cond_t* cond)
{
if (pthread_cond_signal(cond) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_cond_signal().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_cond_broadcast(thread_cond_t* cond)
{
if (pthread_cond_broadcast(cond) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_cond_broadcast().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_cond_wait(thread_cond_t* cond, thread_mutex_t* mutex)
{
if (pthread_cond_wait(cond, mutex) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_cond_wait().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
/**
* Implementation note about conditional variables.
*
* The conditional variables can be signaled inside or outside the mutex,
* what is better it's debatable but in general doing that outside the mutex,
* reduces the number of context switches.
*
* But when testing with helgrind and drd, this disallows such tools to
* to see the dependency between the signal and the wait.
*
* To avoid it we signal everything inside the mutex. And we do this in both
* test mode (with CHECKER defined) and release mode (CHECKER not defined),
* to be on the safe side and avoid any difference in behaviour between test and
* release.
*
* Here some interesting discussion:
*
* Condvars: signal with mutex locked or not?
* http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/
*
* Calling pthread_cond_signal without locking mutex
* http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex/4544494#4544494
*/
/**
* Control when to signal the condition variables.
*/
int thread_cond_signal_outside = 0;
void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
{
if (thread_cond_signal_outside) {
/* without the thread checker unlock before signaling, */
/* this reduces the number of context switches */
thread_mutex_unlock(mutex);
}
thread_cond_signal(cond);
if (!thread_cond_signal_outside) {
/* with the thread checker unlock after signaling */
/* to make explicit the condition and mutex relation */
thread_mutex_unlock(mutex);
}
}
void thread_cond_broadcast_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
{
if (thread_cond_signal_outside) {
/* without the thread checker unlock before signaling, */
/* this reduces the number of context switches */
thread_mutex_unlock(mutex);
}
thread_cond_broadcast(cond);
if (!thread_cond_signal_outside) {
/* with the thread checker unlock after signaling */
/* to make explicit the condition and mutex relation */
thread_mutex_unlock(mutex);
}
}
void thread_create(thread_id_t* thread, void* (* func)(void *), void *arg)
{
if (pthread_create(thread, 0, func, arg) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_create().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
void thread_join(thread_id_t thread, void** retval)
{
if (pthread_join(thread, retval) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed call to pthread_join().\n");
os_abort();
/* LCOV_EXCL_STOP */
}
}
#endif
snapraid-12.1/cmdline/support.h 0000664 0000000 0000000 00000027340 14166610522 0016547 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __SUPPORT_H
#define __SUPPORT_H
/****************************************************************************/
/* lock */
/**
* Initialize and destroy the locks.
*/
void lock_init(void);
void lock_done(void);
/**
* Lock used for printf.
*
* In Windows printf() is not atomic, and multiple threads
* will have output interleaved.
*
* Note that even defining __USE_MINGW_ANSI_STDIO the problem persists.
*
* See for example:
*
* Weird output when I use pthread and printf.
* http://stackoverflow.com/questions/13190254/weird-output-when-i-use-pthread-and-printf
*
* This is also required in other OS because we split output in stdlog in
* two fprintf calls.
*/
void lock_msg(void);
void unlock_msg(void);
/**
* Lock used for memory counter.
*/
void lock_memory(void);
void unlock_memory(void);
/****************************************************************************/
/* log */
/**
* Fatal error messages.
*
* Messages printed before an early termination.
*
* These messages go in the log file and in stderr unconditionally.
*/
void log_fatal(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Unexpected error messages.
*
* Messages reporting error conditions that don't prevent the program to run.
*
* Some of them could be also serious errors, like "silent errors".
* In such case, the summary result is always printed as error,
* and we are sure to notify the user in some way.
*
* These messages go in the log file if specified, otherwise they go in stderr.
*/
void log_error(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Expected error messages, without fallback to stderr.
*
* These errors are "someway" expected, and then they never go to screen.
* For example, when undeleting missing files, the messages for missing files
* are not shown.
*
* These messages go in the log file if specified, otherwise they are lost.
*/
void log_expected(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Tag messages.
*
* Messages are in tag format, like "tag:entry:...".
*
* These messages never go on the screen, but only in the log file if specified.
*
* Note that this function, allows not \n terminated strings.
*
* These messages are buffered. Use msg_flush() to flush them.
*/
void log_tag(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Flush the log.
*/
void log_flush(void);
/**
* Pointer to log function.
*/
typedef void fptr(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/****************************************************************************/
/* message */
/**
* Message levels.
*
* The levels control the amount of information printed on the screen.
* Note that log_fatal(), log_error(), log_expected() and log_tag() are not affected by this option.
*
* From the most quiet to the most verbose.
*/
#define MSG_STATUS -3
#define MSG_INFO -2
#define MSG_PROGRESS -1
#define MSG_BAR 0
#define MSG_VERBOSE 1
/**
* Selected message level.
*/
extern int msg_level;
/**
* State messages.
*
* Messages that tell what the program is doing or did, but limited to few lines.
* They are status information, and summary results.
*/
void msg_status(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Info messages.
*
* Messages that tell what was done.
* Potentially a lot of messages are possible. They can still be on the screen,
* as losing them we don't lose information.
*
* These messages never go in the log file, because there is always a corresponding log_tag().
*/
void msg_info(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Progress messages.
*
* Message that tell the progress of program.
*
* These messages also go in the log file.
*/
void msg_progress(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Progress bar messages.
*
* Message that show the percentage of the progress.
*
* These messages never go in the log file.
*
* These messages are buffered. Use msg_flush() to flush them.
*/
void msg_bar(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Verbose messages.
*
* Message that tell what is already expected.
*
* These messages also go in the log file.
*/
void msg_verbose(const char* format, ...) __attribute__((format(attribute_printf, 1, 2)));
/**
* Flush the output.
*/
void msg_flush(void);
/****************************************************************************/
/* print */
/**
* Print a repeated char.
*/
void printc(char c, size_t pad);
/**
* Print a string with right space padding.
*/
void printr(const char* str, size_t pad);
/**
* Print a string with left space padding.
*/
void printl(const char* str, size_t pad);
/**
* Print a probability with space padding.
*/
void printp(double v, size_t pad);
/****************************************************************************/
/* string */
#define ESC_MAX (PATH_MAX*2 + 1)
/**
* Escape a string for the log.
*
* \param buffer Preallocated buffer of ESC_MAX size.
*
* Chars ':', '\n', '\r' and '\' are escaped to '\d', '\\n', '\\r' and '\\'.
*/
const char* esc_tag(const char* str, char* buffer);
/**
* Escape a string for the shell.
*
* \param buffer Preallocated buffer of ESC_MAX size.
*/
const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer);
static inline const char* esc_shell(const char* str, char* buffer)
{
return esc_shell_multi(&str, 1, buffer);
}
/**
* Polish a string.
*
* Not printable chars are replaced by spaces.
*
* Note that the passed string is modified.
*/
char* strpolish(char* s);
/**
* Split a string in multiple tokens separated by delimiters.
*
* Multiple delimiters are grouped together.
*/
unsigned strsplit(char** split_map, unsigned split_max, char* line, const char* delimiters);
/****************************************************************************/
/* path */
/**
* Copy a path limiting the size.
* Abort if too long.
*/
void pathcpy(char* dst, size_t size, const char* src);
/**
* Concatenate a path limiting the size.
* Abort if too long.
*/
void pathcat(char* dst, size_t size, const char* src);
/**
* Concatenate a path limiting the size knowing the length.
* Abort if too long.
*/
void pathcatl(char* dst, size_t dst_len, size_t size, const char* src);
/**
* Concatenate a path limiting the size.
* Abort if too long.
*/
void pathcatc(char* dst, size_t size, char c);
/**
* Import a path limiting the size.
* In Windows all the backslash are converted to the C standard of forward slash.
* Abort if too long.
*/
void pathimport(char* dst, size_t size, const char* src);
/**
* Export a path limiting the size.
* In Windows all the C slashes are converted to the Windows backslash.
* Abort if too long.
*/
void pathexport(char* dst, size_t size, const char* src);
/**
* Print a path.
* Abort if too long.
*/
void pathprint(char* dst, size_t size, const char* format, ...) __attribute__((format(attribute_printf, 3, 4)));
/**
* Ensure the presence of a terminating slash, if it isn't empty.
* Abort if too long.
*/
void pathslash(char* dst, size_t size);
/**
* Cut everything after the latest slash.
*
* If the string doesn't contain any slash, it returns the empty string.
*/
void pathcut(char* dst);
/**
* Compare two paths.
* In Windows it's case insensitive and assumes \ equal at /.
*/
int pathcmp(const char* a, const char* b);
/****************************************************************************/
/* file-system */
/**
* Create all the ancestor directories if missing.
* The file name, after the last /, is ignored.
*/
int mkancestor(const char* file);
/**
* Change the modification time of an open file.
*/
int fmtime(int f, int64_t mtime_sec, int mtime_nsec);
/**
* Change the modification time of a file or link.
* Note that links are NOT deferenced.
*/
int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec);
/****************************************************************************/
/* advise */
/**
* Advise modes.
*/
#define ADVISE_DEFAULT 0 /**< Default mode. */
#define ADVISE_NONE 1 /**< Bare read/write mode. */
#define ADVISE_SEQUENTIAL 2 /**< Sequential mode. */
#define ADVISE_FLUSH 3 /**< Flush mode. */
#define ADVISE_FLUSH_WINDOW 4 /**< Flush mode with a window of 8MB. */
#define ADVISE_DISCARD 5 /**< Discard the cache after every operation. */
#define ADVISE_DISCARD_WINDOW 6 /**< Discard the cache with a window of 8MB. */
#define ADVISE_DIRECT 7 /**< Direct mode. */
#define ADVISE_WINDOW_SIZE (8 * 1024 * 1024) /**< Window size. */
struct advise_struct {
int mode;
data_off_t dirty_begin;
data_off_t dirty_end;
};
void advise_init(struct advise_struct* advise, int mode);
int advise_flags(struct advise_struct* advise);
int advise_open(struct advise_struct* advise, int f);
int advise_write(struct advise_struct* advise, int f, data_off_t offset, data_off_t size);
int advise_read(struct advise_struct* advise, int f, data_off_t offset, data_off_t size);
/****************************************************************************/
/* memory */
/**
* Return the size of the allocated memory.
*/
size_t malloc_counter_get(void);
/**
* Safe malloc.
* If no memory is available, it aborts.
*/
void* malloc_nofail(size_t size);
/**
* Safe cmalloc.
* If no memory is available, it aborts.
*/
void* calloc_nofail(size_t count, size_t size);
/**
* Safe strdup.
* If no memory is available, it aborts.
*/
char* strdup_nofail(const char* str);
/**
* Helper for printing an error about a failed allocation.
*/
void malloc_fail(size_t size);
/****************************************************************************/
/* smartctl */
/**
* Read smartctl attributes from a stream.
* Return 0 on success.
*/
int smartctl_attribute(FILE* f, const char* file, const char* name, uint64_t* smart, char* serial, char* vendor, char* model);
/**
* Flush smartctl output from a stream.
*/
int smartctl_flush(FILE* f, const char* file, const char* name);
/****************************************************************************/
/* thread */
#if HAVE_THREAD
/**
* Control when to signal the condition variables.
*
* Default is inside the mutex.
*
* Ensure to change that before starting any thread.
*/
extern int thread_cond_signal_outside;
/**
* Thread wrappers to handle error conditions.
*/
void thread_mutex_init(thread_mutex_t* mutex);
void thread_mutex_destroy(thread_mutex_t* mutex);
void thread_mutex_lock(thread_mutex_t* mutex);
void thread_mutex_unlock(thread_mutex_t* mutex);
void thread_cond_init(thread_cond_t* cond);
void thread_cond_destroy(thread_cond_t* cond);
void thread_cond_signal(thread_cond_t* cond);
void thread_cond_broadcast(thread_cond_t* cond);
void thread_cond_wait(thread_cond_t* cond, thread_mutex_t* mutex);
void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex);
void thread_cond_broadcast_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex);
void thread_create(thread_id_t* thread, void* (* func)(void *), void *arg);
void thread_join(thread_id_t thread, void** retval);
#endif
#endif
snapraid-12.1/cmdline/sync.c 0000664 0000000 0000000 00000153231 14166610522 0016001 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "elem.h"
#include "state.h"
#include "parity.h"
#include "handle.h"
#include "io.h"
#include "raid/raid.h"
/****************************************************************************/
/* hash */
static int state_hash_process(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax, int* skip_sync)
{
struct snapraid_handle* handle;
unsigned diskmax;
block_off_t i;
unsigned j;
void* buffer;
void* buffer_alloc;
data_off_t countsize;
block_off_t countpos;
block_off_t countmax;
int ret;
unsigned error;
unsigned silent_error;
unsigned io_error;
char esc_buffer[ESC_MAX];
/* maps the disks to handles */
handle = handle_mapping(state, &diskmax);
/* buffer for reading */
buffer = malloc_nofail_direct(state->block_size, &buffer_alloc);
if (!state->opt.skip_self)
mtest_vector(1, state->block_size, &buffer);
error = 0;
silent_error = 0;
io_error = 0;
/* first count the number of blocks to process */
countmax = 0;
for (j = 0; j < diskmax; ++j) {
struct snapraid_disk* disk = handle[j].disk;
/* if no disk, nothing to check */
if (!disk)
continue;
for (i = blockstart; i < blockmax; ++i) {
struct snapraid_block* block;
unsigned block_state;
block = fs_par2block_find(disk, i);
/* get the state of the block */
block_state = block_state_get(block);
/* process REP and CHG blocks */
if (block_state != BLOCK_STATE_REP && block_state != BLOCK_STATE_CHG)
continue;
++countmax;
}
}
/* drop until now */
state_usage_waste(state);
countsize = 0;
countpos = 0;
if (!state_progress_begin(state, blockstart, blockmax, countmax))
goto end;
for (j = 0; j < diskmax; ++j) {
struct snapraid_disk* disk = handle[j].disk;
/* if no disk, nothing to check */
if (!disk)
continue;
for (i = blockstart; i < blockmax; ++i) {
snapraid_info info;
int rehash;
struct snapraid_block* block;
int read_size;
unsigned char hash[HASH_MAX];
unsigned block_state;
struct snapraid_file* file;
block_off_t file_pos;
block = fs_par2block_find(disk, i);
/* get the state of the block */
block_state = block_state_get(block);
/* process REP and CHG blocks */
if (block_state != BLOCK_STATE_REP && block_state != BLOCK_STATE_CHG)
continue;
/* get the file of this block */
file = fs_par2file_get(disk, i, &file_pos);
/* get block specific info */
info = info_get(&state->infoarr, i);
/* if we have to use the old hash */
rehash = info_get_rehash(info);
/* until now is misc */
state_usage_misc(state);
/* if the file is different than the current one, close it */
if (handle[j].file != 0 && handle[j].file != file) {
/* keep a pointer at the file we are going to close for error reporting */
struct snapraid_file* report = handle[j].file;
ret = handle_close(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
if (errno == EIO) {
log_tag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path);
log_fatal("Stopping at block %u\n", i);
++io_error;
goto bail;
}
log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path);
log_fatal("Stopping at block %u\n", i);
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
}
ret = handle_open(&handle[j], file, state->file_mode, log_error, 0);
if (ret == -1) {
if (errno == EIO) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path);
log_fatal("Stopping at block %u\n", i);
++io_error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (errno == ENOENT) {
log_tag("error:%u:%s:%s: Open ENOENT error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_error("Missing file '%s'.\n", handle[j].path);
log_error("WARNING! You cannot modify data disk during a sync.\n");
log_error("Rerun the sync command when finished.\n");
++error;
/* if the file is missing, it means that it was removed during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
continue;
}
if (errno == EACCES) {
log_tag("error:%u:%s:%s: Open EACCES error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_error("No access at file '%s'.\n", handle[j].path);
log_error("WARNING! Please fix the access permission in the data disk.\n");
log_error("Rerun the sync command when finished.\n");
++error;
/* this isn't a serious error, so we skip this block, and continue with others */
continue;
}
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Open error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Unexpected open error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path);
log_fatal("Stopping to allow recovery. Try with 'snapraid check -f /%s'\n", fmt_poll(disk, file->sub, esc_buffer));
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* check if the file is changed */
if (handle[j].st.st_size != file->size
|| handle[j].st.st_mtime != file->mtime_sec
|| STAT_NSEC(&handle[j].st) != file->mtime_nsec
|| handle[j].st.st_ino != file->inode
) {
log_tag("error:%u:%s:%s: Unexpected attribute change\n", i, disk->name, esc_tag(file->sub, esc_buffer));
if (handle[j].st.st_size != file->size) {
log_error("Unexpected size change at file '%s' from %" PRIu64 " to %" PRIu64 ".\n", handle[j].path, file->size, (uint64_t)handle[j].st.st_size);
} else if (handle[j].st.st_mtime != file->mtime_sec
|| STAT_NSEC(&handle[j].st) != file->mtime_nsec) {
log_error("Unexpected time change at file '%s' from %" PRIu64 ".%d to %" PRIu64 ".%d.\n", handle[j].path, file->mtime_sec, file->mtime_nsec, (uint64_t)handle[j].st.st_mtime, STAT_NSEC(&handle[j].st));
} else {
log_error("Unexpected inode change from %" PRIu64 " to %" PRIu64 " at file '%s'.\n", file->inode, (uint64_t)handle[j].st.st_ino, handle[j].path);
}
log_error("WARNING! You cannot modify files during a sync.\n");
log_error("Rerun the sync command when finished.\n");
++error;
/* if the file is changed, it means that it was modified during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
continue;
}
read_size = handle_read(&handle[j], file_pos, buffer, state->block_size, log_fatal, 0);
if (read_size == -1) {
/* LCOV_EXCL_START */
if (errno == EIO) {
log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, strerror(errno));
log_fatal("DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be read.\n", disk->dir, handle[j].path);
log_fatal("Stopping at block %u\n", i);
++io_error;
goto bail;
}
log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, strerror(errno));
log_fatal("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that file '%s' can be read.\n", handle[j].path);
log_fatal("Stopping to allow recovery. Try with 'snapraid check -f /%s'\n", fmt_poll(disk, file->sub, esc_buffer));
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* until now is disk */
state_usage_disk(state, handle, &j, 1);
state_usage_file(state, disk, file);
countsize += read_size;
/* now compute the hash */
if (rehash) {
memhash(state->prevhash, state->prevhashseed, hash, buffer, read_size);
} else {
memhash(state->hash, state->hashseed, hash, buffer, read_size);
}
/* until now is hash */
state_usage_hash(state);
if (block_state == BLOCK_STATE_REP) {
/* compare the hash */
if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
log_tag("error:%u:%s:%s: Unexpected data change\n", i, disk->name, esc_tag(file->sub, esc_buffer));
log_error("Data change at file '%s' at position '%u'\n", handle[j].path, file_pos);
log_error("WARNING! Unexpected data modification of a file without parity!\n");
if (file_flag_has(file, FILE_IS_COPY)) {
log_error("This file was detected as a copy of another file with the same name, size,\n");
log_error("and timestamp, but the file data isn't matching the assumed copy.\n");
log_error("If this is a false positive, and the files are expected to be different,\n");
log_error("you can 'sync' anyway using 'snapraid --force-nocopy sync'\n");
} else {
log_error("Try removing the file from the array and rerun the 'sync' command!\n");
}
/* block sync to allow a recovery before overwriting */
/* the parity needed to make such recovery */
*skip_sync = 1; /* avoid to run the next sync */
++silent_error;
continue;
}
} else {
/* the only other case is BLOCK_STATE_CHG */
assert(block_state == BLOCK_STATE_CHG);
/* copy the hash in the block */
memcpy(block->hash, hash, BLOCK_HASH_SIZE);
/* and mark the block as hashed */
block_state_set(block, BLOCK_STATE_REP);
/* mark the state as needing write */
state->need_write = 1;
}
/* count the number of processed block */
++countpos;
/* progress */
if (state_progress(state, 0, i, countpos, countmax, countsize)) {
/* LCOV_EXCL_START */
*skip_sync = 1; /* avoid to run the next sync */
break;
/* LCOV_EXCL_STOP */
}
}
/* close the last file in the disk */
if (handle[j].file != 0) {
/* keep a pointer at the file we are going to close for error reporting */
struct snapraid_file* report = handle[j].file;
ret = handle_close(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
if (errno == EIO) {
log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockmax, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path);
log_fatal("Stopping at block %u\n", blockmax);
++io_error;
goto bail;
}
log_tag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path);
log_fatal("Stopping at block %u\n", blockmax);
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
}
}
end:
state_progress_end(state, countpos, countmax, countsize);
/* note that at this point no io_error is possible */
/* because at the first one we bail out */
assert(io_error == 0);
if (error || io_error || silent_error) {
msg_status("\n");
msg_status("%8u file errors\n", error);
msg_status("%8u io errors\n", io_error);
msg_status("%8u data errors\n", silent_error);
} else {
/* print the result only if processed something */
if (countpos != 0)
msg_status("Everything OK\n");
}
if (error)
log_fatal("WARNING! Unexpected file errors!\n");
log_tag("hash_summary:error_file:%u\n", error);
/* proceed without bailing out */
goto finish;
bail:
/* on bail, don't run the next sync */
*skip_sync = 1;
/* close files left open */
for (j = 0; j < diskmax; ++j) {
struct snapraid_file* file = handle[j].file;
struct snapraid_disk* disk = handle[j].disk;
ret = handle_close(&handle[j]);
if (ret == -1) {
log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
++error;
/* continue, as we are already exiting */
}
}
finish:
free(handle);
free(buffer_alloc);
if (error + io_error + silent_error != 0)
return -1;
return 0;
}
/****************************************************************************/
/* sync */
/**
* Sync plan to use.
*/
struct snapraid_plan {
unsigned handle_max;
struct snapraid_handle* handle_map;
int force_full;
};
/**
* A block that failed the hash check, or that was deleted.
*/
struct failed_struct {
unsigned index; /**< Index of the failed block. */
unsigned size; /**< Size of the block. */
struct snapraid_block* block; /**< The failed block, or BLOCK_DELETED for a deleted block */
};
/**
* Comparison function for sorting by index.
*/
int failed_compare_by_index(const void* void_a, const void* void_b)
{
const struct failed_struct* a = void_a;
const struct failed_struct* b = void_b;
if (a->index < b->index)
return -1;
if (a->index > b->index)
return 1;
return 0;
}
/**
* Buffer for storing the new hashes.
*/
struct snapraid_rehash {
unsigned char hash[HASH_MAX];
struct snapraid_block* block;
};
/**
* Check if we have to process the specified block index ::i.
*/
static int block_is_enabled(struct snapraid_plan* plan, block_off_t i)
{
unsigned j;
int one_invalid;
int one_valid;
/* for each disk */
one_invalid = 0;
one_valid = 0;
for (j = 0; j < plan->handle_max; ++j) {
struct snapraid_block* block;
struct snapraid_disk* disk = plan->handle_map[j].disk;
/* if no disk, nothing to check */
if (!disk)
continue;
block = fs_par2block_find(disk, i);
if (block_has_file(block))
one_valid = 1;
if (block_has_invalid_parity(block) || plan->force_full)
one_invalid = 1;
}
/* if none valid or none invalid, we don't need to update */
if (!one_invalid || !one_valid)
return 0;
return 1;
}
static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_task* task)
{
struct snapraid_io* io = worker->io;
struct snapraid_state* state = io->state;
struct snapraid_handle* handle = worker->handle;
struct snapraid_disk* disk = handle->disk;
block_off_t blockcur = task->position;
unsigned char* buffer = task->buffer;
int ret;
char esc_buffer[ESC_MAX];
/* if the disk position is not used */
if (!disk) {
/* use an empty block */
memset(buffer, 0, state->block_size);
task->state = TASK_STATE_DONE;
return;
}
/* get the block */
task->block = fs_par2block_find(disk, blockcur);
/* if the block has no file, meaning that it's EMPTY or DELETED, */
/* it doesn't participate in the new parity computation */
if (!block_has_file(task->block)) {
/* use an empty block */
memset(buffer, 0, state->block_size);
task->state = TASK_STATE_DONE;
return;
}
/* get the file of this block */
task->file = fs_par2file_get(disk, blockcur, &task->file_pos);
/* if the file is different than the current one, close it */
if (handle->file != 0 && handle->file != task->file) {
/* keep a pointer at the file we are going to close for error reporting */
struct snapraid_file* report = handle->file;
ret = handle_close(handle);
if (ret == -1) {
/* LCOV_EXCL_START */
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
if (errno == EIO) {
log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_IOERROR;
return;
}
log_tag("error:%u:%s:%s: Close error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that file '%s' can be accessed.\n", handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_ERROR;
return;
/* LCOV_EXCL_STOP */
}
}
ret = handle_open(handle, task->file, state->file_mode, log_error, 0);
if (ret == -1) {
if (errno == EIO) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Open EIO error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_IOERROR;
return;
/* LCOV_EXCL_STOP */
}
if (errno == ENOENT) {
log_tag("error:%u:%s:%s: Open ENOENT error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
log_error("Missing file '%s'.\n", handle->path);
log_error("WARNING! You cannot modify data disk during a sync.\n");
log_error("Rerun the sync command when finished.\n");
/* if the file is missing, it means that it was removed during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
if (errno == EACCES) {
log_tag("error:%u:%s:%s: Open EACCES error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
log_error("No access at file '%s'.\n", handle->path);
log_error("WARNING! Please fix the access permission in the data disk.\n");
log_error("Rerun the sync command when finished.\n");
/* this isn't a serious error, so we skip this block, and continue with others */
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Open error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
log_fatal("WARNING! Unexpected open error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that file '%s' can be accessed.\n", handle->path);
log_fatal("Stopping to allow recovery. Try with 'snapraid check -f /%s'\n", fmt_poll(disk, task->file->sub, esc_buffer));
task->state = TASK_STATE_ERROR;
return;
/* LCOV_EXCL_STOP */
}
/* check if the file is changed */
if (handle->st.st_size != task->file->size
|| handle->st.st_mtime != task->file->mtime_sec
|| STAT_NSEC(&handle->st) != task->file->mtime_nsec
|| handle->st.st_ino != task->file->inode
) {
log_tag("error:%u:%s:%s: Unexpected attribute change\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer));
if (handle->st.st_size != task->file->size) {
log_error("Unexpected size change at file '%s' from %" PRIu64 " to %" PRIu64 ".\n", handle->path, task->file->size, (uint64_t)handle->st.st_size);
} else if (handle->st.st_mtime != task->file->mtime_sec
|| STAT_NSEC(&handle->st) != task->file->mtime_nsec) {
log_error("Unexpected time change at file '%s' from %" PRIu64 ".%d to %" PRIu64 ".%d.\n", handle->path, task->file->mtime_sec, task->file->mtime_nsec, (uint64_t)handle->st.st_mtime, STAT_NSEC(&handle->st));
} else {
log_error("Unexpected inode change from %" PRIu64 " to %" PRIu64 " at file '%s'.\n", task->file->inode, (uint64_t)handle->st.st_ino, handle->path);
}
log_error("WARNING! You cannot modify files during a sync.\n");
log_error("Rerun the sync command when finished.\n");
/* if the file is changed, it means that it was modified during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
task->state = TASK_STATE_ERROR_CONTINUE;
return;
}
task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
if (task->read_size == -1) {
/* LCOV_EXCL_START */
if (errno == EIO) {
log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
log_error("Input/Output error in file '%s' at position '%u'\n", handle->path, task->file_pos);
task->state = TASK_STATE_IOERROR_CONTINUE;
return;
}
log_tag("error:%u:%s:%s: Read error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
log_fatal("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that file '%s' can be read.\n", handle->path);
log_fatal("Stopping to allow recovery. Try with 'snapraid check -f /%s'\n", fmt_poll(disk, task->file->sub, esc_buffer));
task->state = TASK_STATE_ERROR;
return;
/* LCOV_EXCL_STOP */
}
/* store the path of the opened file */
pathcpy(task->path, sizeof(task->path), handle->path);
task->state = TASK_STATE_DONE;
}
static void sync_parity_writer(struct snapraid_worker* worker, struct snapraid_task* task)
{
struct snapraid_io* io = worker->io;
struct snapraid_state* state = io->state;
struct snapraid_parity_handle* parity_handle = worker->parity_handle;
unsigned level = parity_handle->level;
block_off_t blockcur = task->position;
unsigned char* buffer = task->buffer;
int ret;
/* write parity */
ret = parity_write(parity_handle, blockcur, buffer, state->block_size);
if (ret == -1) {
/* LCOV_EXCL_START */
if (errno == EIO) {
log_tag("parity_error:%u:%s: Write EIO error. %s\n", blockcur, lev_config_name(level), strerror(errno));
log_error("Input/Output error in parity '%s' at position '%u'\n", lev_config_name(level), blockcur);
task->state = TASK_STATE_IOERROR_CONTINUE;
return;
}
log_tag("parity_error:%u:%s: Write error. %s\n", blockcur, lev_config_name(level), strerror(errno));
log_fatal("WARNING! Unexpected write error in the %s disk, it isn't possible to sync.\n", lev_name(level));
log_fatal("Ensure that disk '%s' has some free space available.\n", lev_config_name(level));
log_fatal("Stopping at block %u\n", blockcur);
task->state = TASK_STATE_ERROR;
return;
/* LCOV_EXCL_STOP */
}
task->state = TASK_STATE_DONE;
}
static int state_sync_process(struct snapraid_state* state, struct snapraid_parity_handle* parity_handle, block_off_t blockstart, block_off_t blockmax)
{
struct snapraid_io io;
struct snapraid_plan plan;
struct snapraid_handle* handle;
void* rehandle_alloc;
struct snapraid_rehash* rehandle;
unsigned diskmax;
block_off_t blockcur;
unsigned j;
void* zero_alloc;
void** zero;
void* copy_alloc;
void** copy;
unsigned buffermax;
data_off_t countsize;
block_off_t countpos;
block_off_t countmax;
block_off_t autosavedone;
block_off_t autosavelimit;
block_off_t autosavemissing;
int ret;
unsigned error;
unsigned silent_error;
unsigned io_error;
time_t now;
struct failed_struct* failed;
int* failed_map;
unsigned l;
unsigned* waiting_map;
unsigned waiting_mac;
char esc_buffer[ESC_MAX];
bit_vect_t* block_enabled;
/* the sync process assumes that all the hashes are correct */
/* including the ones from CHG and DELETED blocks */
assert(state->clear_past_hash != 0);
/* get the present time */
now = time(0);
/* maps the disks to handles */
handle = handle_mapping(state, &diskmax);
/* rehash buffers */
rehandle = malloc_nofail_align(diskmax * sizeof(struct snapraid_rehash), &rehandle_alloc);
/* we need 1 * data + 1 * parity */
buffermax = diskmax + state->level;
/* initialize the io threads */
io_init(&io, state, state->opt.io_cache, buffermax, sync_data_reader, handle, diskmax, 0, sync_parity_writer, parity_handle, state->level);
/* allocate the copy buffer */
copy = malloc_nofail_vector_align(diskmax, diskmax, state->block_size, ©_alloc);
/* allocate and fill the zero buffer */
zero = malloc_nofail_align(state->block_size, &zero_alloc);
memset(zero, 0, state->block_size);
raid_zero(zero);
failed = malloc_nofail(diskmax * sizeof(struct failed_struct));
failed_map = malloc_nofail(diskmax * sizeof(unsigned));
/* possibly waiting disks */
waiting_mac = diskmax > RAID_PARITY_MAX ? diskmax : RAID_PARITY_MAX;
waiting_map = malloc_nofail(waiting_mac * sizeof(unsigned));
error = 0;
silent_error = 0;
io_error = 0;
msg_progress("Selecting...\n");
/* first count the number of blocks to process */
countmax = 0;
plan.handle_max = diskmax;
plan.handle_map = handle;
plan.force_full = state->opt.force_full;
block_enabled = calloc_nofail(1, bit_vect_size(blockmax)); /* preinitialize to 0 */
for (blockcur = blockstart; blockcur < blockmax; ++blockcur) {
if (!block_is_enabled(&plan, blockcur))
continue;
bit_vect_set(block_enabled, blockcur);
++countmax;
}
/* compute the autosave size for all disk, even if not read */
/* this makes sense because the speed should be almost the same */
/* if the disks are read in parallel */
autosavelimit = state->autosave / (diskmax * state->block_size);
autosavemissing = countmax; /* blocks to do */
autosavedone = 0; /* blocks done */
/* drop until now */
state_usage_waste(state);
countsize = 0;
countpos = 0;
msg_progress("Syncing...\n");
/* start all the worker threads */
io_start(&io, blockstart, blockmax, block_enabled);
if (!state_progress_begin(state, blockstart, blockmax, countmax))
goto end;
while (1) {
unsigned failed_count;
int error_on_this_block;
int silent_error_on_this_block;
int io_error_on_this_block;
int fixed_error_on_this_block;
int parity_needs_to_be_updated;
int parity_going_to_be_updated;
snapraid_info info;
int rehash;
void** buffer;
int writer_error[IO_WRITER_ERROR_MAX];
/* go to the next block */
blockcur = io_read_next(&io, &buffer);
if (blockcur >= blockmax)
break;
/* until now is scheduling */
state_usage_sched(state);
/* one more block processed for autosave */
++autosavedone;
--autosavemissing;
/* by default process the block, and skip it if something goes wrong */
error_on_this_block = 0;
silent_error_on_this_block = 0;
io_error_on_this_block = 0;
fixed_error_on_this_block = 0;
/* keep track of the number of failed blocks */
failed_count = 0;
/* get block specific info */
info = info_get(&state->infoarr, blockcur);
/* if we have to use the old hash */
rehash = info_get_rehash(info);
/* if the parity requires to be updated */
/* It could happens that all the blocks are EMPTY/BLK and CHG but with the hash */
/* still matching because the specific CHG block was not modified. */
/* In such case, we can avoid to update parity, because it would be the same as before */
/* Note that CHG/DELETED blocks already present in the content file loaded */
/* have the hash cleared (::clear_past_hash flag), and then they won't never match the hash. */
/* We are treating only CHG blocks created at runtime. */
parity_needs_to_be_updated = state->opt.force_full || state->opt.force_parity_update;
/* if the parity is going to be updated */
parity_going_to_be_updated = 0;
/* if the block is marked as bad, we force the parity update */
/* because the bad block may be the result of a wrong parity */
if (info_get_bad(info))
parity_needs_to_be_updated = 1;
/* for each disk, process the block */
for (j = 0; j < diskmax; ++j) {
struct snapraid_task* task;
int read_size;
unsigned char hash[HASH_MAX];
struct snapraid_block* block;
unsigned block_state;
struct snapraid_disk* disk;
struct snapraid_file* file;
block_off_t file_pos;
unsigned diskcur;
/* until now is misc */
state_usage_misc(state);
task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac);
/* until now is disk */
state_usage_disk(state, handle, waiting_map, waiting_mac);
/* get the results */
disk = task->disk;
block = task->block;
file = task->file;
file_pos = task->file_pos;
read_size = task->read_size;
/* by default no rehash in case of "continue" */
rehandle[diskcur].block = 0;
/* if the disk position is not used */
if (!disk)
continue;
state_usage_file(state, disk, file);
/* get the state of the block */
block_state = block_state_get(block);
/* if the block has invalid parity, */
/* we have to take care of it in case of recover */
if (block_has_invalid_parity(block)) {
/* store it in the failed set, because */
/* the parity may be still computed with the previous content */
failed[failed_count].index = diskcur;
failed[failed_count].size = state->block_size;
failed[failed_count].block = block;
++failed_count;
/* if the block has invalid parity, we have to update the parity */
/* to include this block change */
/* This also apply to CHG blocks, but we are going to handle */
/* later this case to do the updates only if really needed */
if (block_state != BLOCK_STATE_CHG)
parity_needs_to_be_updated = 1;
/* note that DELETE blocks are skipped in the next check */
/* and we have to store them in the failed blocks */
/* before skipping */
/* follow */
}
/* if the block is not used */
if (!block_has_file(block))
continue;
/* handle error conditions */
if (task->state == TASK_STATE_IOERROR) {
/* LCOV_EXCL_START */
++io_error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR) {
/* LCOV_EXCL_START */
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
if (task->state == TASK_STATE_ERROR_CONTINUE) {
++error;
error_on_this_block = 1;
continue;
}
if (task->state == TASK_STATE_IOERROR_CONTINUE) {
++io_error;
if (io_error >= state->opt.io_error_limit) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync.\n");
log_fatal("Ensure that disk '%s' is sane and that file '%s' can be read.\n", disk->dir, task->path);
log_fatal("Stopping at block %u\n", blockcur);
goto bail;
/* LCOV_EXCL_STOP */
}
/* otherwise continue */
io_error_on_this_block = 1;
continue;
}
if (task->state != TASK_STATE_DONE) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in task state\n");
os_abort();
/* LCOV_EXCL_STOP */
}
countsize += read_size;
/* now compute the hash */
if (rehash) {
memhash(state->prevhash, state->prevhashseed, hash, buffer[diskcur], read_size);
/* compute the new hash, and store it */
rehandle[diskcur].block = block;
memhash(state->hash, state->hashseed, rehandle[diskcur].hash, buffer[diskcur], read_size);
} else {
memhash(state->hash, state->hashseed, hash, buffer[diskcur], read_size);
}
/* until now is hash */
state_usage_hash(state);
if (block_has_updated_hash(block)) {
/* compare the hash */
if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
/* if the file has invalid parity, it's a REP changed during the sync */
if (block_has_invalid_parity(block)) {
log_tag("error:%u:%s:%s: Unexpected data change\n", blockcur, disk->name, esc_tag(file->sub, esc_buffer));
log_error("Data change at file '%s' at position '%u'\n", task->path, file_pos);
log_error("WARNING! Unexpected data modification of a file without parity!\n");
if (file_flag_has(file, FILE_IS_COPY)) {
log_error("This file was detected as a copy of another file with the same name, size,\n");
log_error("and timestamp, but the file data isn't matching the assumed copy.\n");
log_error("If this is a false positive, and the files are expected to be different,\n");
log_error("you can 'sync' anyway using 'snapraid --force-nocopy sync'\n");
} else {
log_error("Try removing the file from the array and rerun the 'sync' command!\n");
}
++error;
/* if the file is changed, it means that it was modified during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
error_on_this_block = 1;
continue;
} else { /* otherwise it's a BLK with silent error */
unsigned diff = memdiff(hash, block->hash, BLOCK_HASH_SIZE);
log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u/%u\n", blockcur, disk->name, esc_tag(file->sub, esc_buffer), file_pos, diff, BLOCK_HASH_SIZE * 8);
log_error("Data error in file '%s' at position '%u', diff bits %u/%u\n", task->path, file_pos, diff, BLOCK_HASH_SIZE * 8);
/* save the failed block for the fix */
failed[failed_count].index = diskcur;
failed[failed_count].size = read_size;
failed[failed_count].block = block;
++failed_count;
/* silent errors are very rare, and are not a signal that a disk */
/* is going to fail. So, we just continue marking the block as bad */
/* just like in scrub */
++silent_error;
silent_error_on_this_block = 1;
continue;
}
}
} else {
/* if until now the parity doesn't need to be updated */
if (!parity_needs_to_be_updated) {
/* for sure it's a CHG block, because EMPTY are processed before with "continue" */
/* and BLK and REP have "block_has_updated_hash()" as 1, and all the others */
/* have "parity_needs_to_be_updated" already at 1 */
assert(block_state_get(block) == BLOCK_STATE_CHG);
/* if the hash represents the data unequivocally */
if (hash_is_unique(block->hash)) {
/* check if the hash is changed */
if (memcmp(hash, block->hash, BLOCK_HASH_SIZE) != 0) {
/* the block is different, and we must update parity */
parity_needs_to_be_updated = 1;
}
} else {
/* if the hash is already invalid, we update parity */
parity_needs_to_be_updated = 1;
}
}
/* copy the hash in the block, but doesn't mark the block as hashed */
/* this allow in case of skipped block to do not save the failed computation */
memcpy(block->hash, hash, BLOCK_HASH_SIZE);
/* note that in case of rehash, this is the wrong hash, */
/* but it will be overwritten later */
}
}
/* if we have only silent errors we can try to fix them on-the-fly */
/* note the fix is not written to disk, but used only to */
/* compute the new parity */
if (!error_on_this_block && !io_error_on_this_block && silent_error_on_this_block) {
unsigned failed_mac;
int something_to_recover = 0;
/* sort the failed vector */
/* because with threads it may be in any order */
/* but RAID requires the indexes to be sorted */
qsort(failed, failed_count, sizeof(failed[0]), failed_compare_by_index);
/* setup the blocks to recover */
failed_mac = 0;
for (j = 0; j < failed_count; ++j) {
unsigned char* block_buffer = buffer[failed[j].index];
unsigned char* block_copy = copy[failed[j].index];
unsigned block_state = block_state_get(failed[j].block);
/* we try to recover only if at least one BLK is present */
if (block_state == BLOCK_STATE_BLK)
something_to_recover = 1;
/* save a copy of the content just read */
/* that it's going to be overwritten by the recovering function */
memcpy(block_copy, block_buffer, state->block_size);
if (block_state == BLOCK_STATE_CHG
&& hash_is_zero(failed[j].block->hash)
) {
/* if the block was filled with 0, restore this state */
/* and avoid to recover it */
memset(block_buffer, 0, state->block_size);
} else {
/* if we have too many failures, we cannot recover */
if (failed_mac >= state->level)
break;
/* otherwise it has to be recovered */
failed_map[failed_mac++] = failed[j].index;
}
}
/* if we have something to recover and enough parity */
if (something_to_recover && j == failed_count) {
/* until now is misc */
state_usage_misc(state);
/* read the parity */
/* we are sure that parity exists because */
/* we have at least one BLK block */
for (l = 0; l < state->level; ++l) {
ret = parity_read(&parity_handle[l], blockcur, buffer[diskmax + l], state->block_size, log_error);
if (ret == -1) {
/* LCOV_EXCL_START */
if (errno == EIO) {
log_tag("parity_error:%u:%s: Read EIO error. %s\n", blockcur, lev_config_name(l), strerror(errno));
if (io_error >= state->opt.io_error_limit) {
log_fatal("DANGER! Unexpected input/output read error in the %s disk, it isn't possible to sync.\n", lev_name(l));
log_fatal("Ensure that disk '%s' is sane and can be read.\n", lev_config_name(l));
log_fatal("Stopping at block %u\n", blockcur);
++io_error;
goto bail;
}
log_error("Input/Output error in parity '%s' at position '%u'\n", lev_config_name(l), blockcur);
++io_error;
io_error_on_this_block = 1;
continue;
}
log_tag("parity_error:%u:%s: Read error. %s\n", blockcur, lev_config_name(l), strerror(errno));
log_fatal("WARNING! Unexpected read error in the %s disk, it isn't possible to sync.\n", lev_name(l));
log_fatal("Ensure that disk '%s' can be read.\n", lev_config_name(l));
log_fatal("Stopping at block %u\n", blockcur);
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
/* until now is parity */
state_usage_parity(state, &l, 1);
}
/* if no error in parity read */
if (!io_error_on_this_block) {
/* try to fix the data */
/* note that this is a simple fix algorithm, that doesn't take into */
/* account the case of a wrong parity */
/* only 'fix' supports the most advanced fixing */
raid_rec(failed_mac, failed_map, diskmax, state->level, state->block_size, buffer);
/* until now is raid */
state_usage_raid(state);
/* check the result and prepare the data */
for (j = 0; j < failed_count; ++j) {
unsigned char hash[HASH_MAX];
unsigned char* block_buffer = buffer[failed[j].index];
unsigned char* block_copy = copy[failed[j].index];
unsigned block_state = block_state_get(failed[j].block);
if (block_state == BLOCK_STATE_BLK) {
unsigned size = failed[j].size;
/* compute the hash of the recovered block */
if (rehash) {
memhash(state->prevhash, state->prevhashseed, hash, block_buffer, size);
} else {
memhash(state->hash, state->hashseed, hash, block_buffer, size);
}
/* until now is hash */
state_usage_hash(state);
/* if the hash doesn't match */
if (memcmp(hash, failed[j].block->hash, BLOCK_HASH_SIZE) != 0) {
/* we have not recovered */
break;
}
/* pad with 0 if needed */
if (size < state->block_size)
memset(block_buffer + size, 0, state->block_size - size);
} else {
/* otherwise restore the content */
/* because we are not interested in the old state */
/* that it's recovered for CHG, REP and DELETED blocks */
memcpy(block_buffer, block_copy, state->block_size);
}
}
/* if all is processed, we have fixed it */
if (j == failed_count)
fixed_error_on_this_block = 1;
}
}
}
/* if we have read all the data required and it's correct, proceed with the parity */
if (!error_on_this_block && !io_error_on_this_block
&& (!silent_error_on_this_block || fixed_error_on_this_block)
) {
/* update the parity only if really needed */
if (parity_needs_to_be_updated) {
/* compute the parity */
raid_gen(diskmax, state->level, state->block_size, buffer);
/* until now is raid */
state_usage_raid(state);
/* mark that the parity is going to be written */
parity_going_to_be_updated = 1;
}
/* for each disk, mark the blocks as processed */
for (j = 0; j < diskmax; ++j) {
struct snapraid_block* block;
if (!handle[j].disk)
continue;
block = fs_par2block_find(handle[j].disk, blockcur);
if (block == BLOCK_NULL) {
/* nothing to do */
continue;
}
/* if it's a deleted block */
if (block_state_get(block) == BLOCK_STATE_DELETED) {
/* the parity is now updated without this block, so it's now empty */
fs_deallocate(handle[j].disk, blockcur);
continue;
}
/* now all the blocks have the hash and the parity computed */
block_state_set(block, BLOCK_STATE_BLK);
}
/* we update the info block only if we really have updated the parity */
/* because otherwise the time/justsynced info would be misleading as we didn't */
/* wrote the parity at this time */
/* we also update the info block only if no silent error was found */
/* because has no sense to refresh the time for data that we know bad */
if (parity_needs_to_be_updated
&& !silent_error_on_this_block
) {
/* if rehash is needed */
if (rehash) {
/* store all the new hash already computed */
for (j = 0; j < diskmax; ++j) {
if (rehandle[j].block)
memcpy(rehandle[j].block->hash, rehandle[j].hash, BLOCK_HASH_SIZE);
}
}
/* update the time info of the block */
/* we are also clearing any previous bad and rehash flag */
info_set(&state->infoarr, blockcur, info_make(now, 0, 0, 1));
}
}
/* if a silent (even if corrected) or input/output error was found */
/* mark the block as bad to have check/fix to handle it */
/* because our correction is in memory only and not yet written */
if (silent_error_on_this_block || io_error_on_this_block) {
/* set the error status keeping the other info */
info_set(&state->infoarr, blockcur, info_set_bad(info));
}
/* finally schedule parity write */
/* Note that the calls to io_parity_write() are mandatory */
/* even if the parity doesn't need to be updated */
/* This because we want to keep track of the time usage */
state_usage_misc(state);
/* write start */
io_write_preset(&io, blockcur, !parity_going_to_be_updated);
/* write the parity */
for (l = 0; l < state->level; ++l) {
unsigned levcur;
io_parity_write(&io, &levcur, waiting_map, &waiting_mac);
/* until now is parity */
state_usage_parity(state, waiting_map, waiting_mac);
}
/* write finished */
io_write_next(&io, blockcur, !parity_going_to_be_updated, writer_error);
/* handle errors reported */
for (j = 0; j < IO_WRITER_ERROR_MAX; ++j) {
if (writer_error[j]) {
switch (j + IO_WRITER_ERROR_BASE) {
case TASK_STATE_IOERROR_CONTINUE :
++io_error;
if (io_error >= state->opt.io_error_limit) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Unexpected input/output write error in a parity disk, it isn't possible to sync.\n");
log_fatal("Stopping at block %u\n", blockcur);
goto bail;
/* LCOV_EXCL_STOP */
}
break;
case TASK_STATE_ERROR_CONTINUE :
++error;
break;
case TASK_STATE_IOERROR :
/* LCOV_EXCL_START */
++io_error;
goto bail;
/* LCOV_EXCL_STOP */
case TASK_STATE_ERROR :
/* LCOV_EXCL_START */
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
}
}
/* mark the state as needing write */
state->need_write = 1;
/* count the number of processed block */
++countpos;
/* progress */
if (state_progress(state, &io, blockcur, countpos, countmax, countsize)) {
/* LCOV_EXCL_START */
break;
/* LCOV_EXCL_STOP */
}
/* autosave */
if ((state->autosave != 0
&& autosavedone >= autosavelimit /* if we have reached the limit */
&& autosavemissing >= autosavelimit) /* if we have at least a full step to do */
/* or if we have a forced autosave at the specified block */
|| (state->opt.force_autosave_at != 0 && state->opt.force_autosave_at == blockcur)
) {
autosavedone = 0; /* restart the counter */
/* until now is misc */
state_usage_misc(state);
state_progress_stop(state);
msg_progress("Autosaving...\n");
/* before writing the new content file we ensure that */
/* the parity is really written flushing the disk cache */
for (l = 0; l < state->level; ++l) {
ret = parity_sync(&parity_handle[l]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("parity_error:%u:%s: Sync error\n", blockcur, lev_config_name(l));
log_fatal("DANGER! Unexpected sync error in %s disk.\n", lev_name(l));
log_fatal("Ensure that disk '%s' is sane.\n", lev_config_name(l));
log_fatal("Stopping at block %u\n", blockcur);
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
}
/* now we can safely write the content file */
state_write(state);
state_progress_restart(state);
/* drop until now */
state_usage_waste(state);
}
}
end:
state_progress_end(state, countpos, countmax, countsize);
state_usage_print(state);
/* before returning we ensure that */
/* the parity is really written flushing the disk cache */
for (l = 0; l < state->level; ++l) {
ret = parity_sync(&parity_handle[l]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("parity_error:%u:%s: Sync error\n", blockcur, lev_config_name(l));
log_fatal("DANGER! Unexpected sync error in %s disk.\n", lev_name(l));
log_fatal("Ensure that disk '%s' is sane.\n", lev_config_name(l));
log_fatal("Stopping at block %u\n", blockcur);
++error;
goto bail;
/* LCOV_EXCL_STOP */
}
}
if (error || silent_error || io_error) {
msg_status("\n");
msg_status("%8u file errors\n", error);
msg_status("%8u io errors\n", io_error);
msg_status("%8u data errors\n", silent_error);
} else {
/* print the result only if processed something */
if (countpos != 0)
msg_status("Everything OK\n");
}
if (error)
log_fatal("WARNING! Unexpected file errors!\n");
if (io_error)
log_fatal("DANGER! Unexpected input/output errors! The failing blocks are now marked as bad!\n");
if (silent_error)
log_fatal("DANGER! Unexpected data errors! The failing blocks are now marked as bad!\n");
if (io_error || silent_error) {
log_fatal("Use 'snapraid status' to list the bad blocks.\n");
log_fatal("Use 'snapraid -e fix' to recover.\n");
}
log_tag("summary:error_file:%u\n", error);
log_tag("summary:error_io:%u\n", io_error);
log_tag("summary:error_data:%u\n", silent_error);
if (error + silent_error + io_error == 0)
log_tag("summary:exit:ok\n");
else
log_tag("summary:exit:error\n");
log_flush();
bail:
/* stop all the worker threads */
io_stop(&io);
for (j = 0; j < diskmax; ++j) {
struct snapraid_file* file = handle[j].file;
struct snapraid_disk* disk = handle[j].disk;
ret = handle_close(&handle[j]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("error:%u:%s:%s: Close error. %s\n", blockcur, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal("DANGER! Unexpected close error in a data disk.\n");
++error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
free(handle);
free(zero_alloc);
free(copy_alloc);
free(copy);
free(rehandle_alloc);
free(failed);
free(failed_map);
free(waiting_map);
io_done(&io);
free(block_enabled);
if (state->opt.expect_recoverable) {
if (error + silent_error + io_error == 0)
return -1;
} else {
if (error + silent_error + io_error != 0)
return -1;
}
return 0;
}
int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount)
{
block_off_t blockmax;
block_off_t used_paritymax;
block_off_t file_paritymax;
data_off_t size;
int ret;
struct snapraid_parity_handle parity_handle[LEV_MAX];
unsigned unrecoverable_error;
unsigned l;
int skip_sync = 0;
msg_progress("Initializing...\n");
blockmax = parity_allocated_size(state);
size = blockmax * (data_off_t)state->block_size;
/* minimum size of the parity files we expect */
used_paritymax = parity_used_size(state);
/* effective size of the parity files */
file_paritymax = 0;
if (blockstart > blockmax) {
/* LCOV_EXCL_START */
log_fatal("Error in the starting block %u. It's bigger than the parity size %u.\n", blockstart, blockmax);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* adjust the number of block to process */
if (blockcount != 0 && blockstart + blockcount < blockmax) {
blockmax = blockstart + blockcount;
}
for (l = 0; l < state->level; ++l) {
data_off_t out_size;
block_off_t parityblocks;
/* create the file and open for writing */
ret = parity_create(&parity_handle[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("WARNING! Without an accessible %s file, it isn't possible to sync.\n", lev_name(l));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* number of block in the parity file */
parity_size(&parity_handle[l], &out_size);
parityblocks = out_size / state->block_size;
/* if the file is too small */
if (parityblocks < used_paritymax) {
log_fatal("WARNING! The %s parity has data only %u blocks instead of %u.\n", lev_name(l), parityblocks, used_paritymax);
}
/* keep the smallest parity number of blocks */
if (l == 0 || file_paritymax > parityblocks)
file_paritymax = parityblocks;
}
/* if we do a full parity realloc or computation, having a wrong parity size is expected */
if (!state->opt.force_realloc && !state->opt.force_full) {
/* if the parities are too small */
if (file_paritymax < used_paritymax) {
/* LCOV_EXCL_START */
log_fatal("DANGER! One or more the parity files are smaller than expected!\n");
if (file_paritymax != 0) {
log_fatal("If this happens because you are using an old content file,\n");
log_fatal("you can 'sync' anyway using 'snapraid --force-full sync'\n");
log_fatal("to force a full rebuild of the parity.\n");
} else {
log_fatal("It's possible that the parity disks are not mounted.\n");
log_fatal("If instead you are adding a new parity level, you can 'sync' using\n");
log_fatal("'snapraid --force-full sync' to force a full rebuild of the parity.\n");
}
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
unrecoverable_error = 0;
if (state->opt.prehash) {
msg_progress("Hashing...\n");
ret = state_hash_process(state, blockstart, blockmax, &skip_sync);
if (ret == -1) {
/* LCOV_EXCL_START */
++unrecoverable_error;
/* continue, in case also doing the sync if ::skip_sync is not set */
/* LCOV_EXCL_STOP */
}
}
if (!skip_sync) {
msg_progress("Resizing...\n");
/* now change the size of all parities */
for (l = 0; l < state->level; ++l) {
int is_modified;
/* change the size of the parity file, truncating or extending it */
/* from this point all the DELETED blocks after the end of the parity are invalid */
/* and they are automatically removed when we save the new content file */
ret = parity_chsize(&parity_handle[l], &state->parity[l], &is_modified, size, state->block_size, state->opt.skip_fallocate, state->opt.skip_space_holder);
if (ret == -1) {
/* LCOV_EXCL_START */
data_off_t out_size;
parity_size(&parity_handle[l], &out_size);
parity_overflow(state, out_size);
log_fatal("WARNING! Without a usable %s file, it isn't possible to sync.\n", lev_name(l));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (is_modified)
state->need_write = 1;
}
/* after resizing parity files, refresh again the free info */
state_refresh(state);
/**
* Save the new state before the sync but after the hashing phase
*
* This allows to recover after an aborted sync, and at the same time
* it allows to recover broken copied/moved files identified in the
* hashing phase.
*
* For example, think at this case:
* - Add some files at the array
* - Run a sync command, it will recompute the parity adding the new files
* - Abort the sync command before it stores the new content file
* - Delete the not yet synced files from the array
* - Run a new sync command
*
* The sync command has no way to know that the parity file was modified
* because the files triggering these changes are now deleted and they aren't
* listed in the content file.
* Instead, saving the new content file in advance, keeps track of all the parity
* that may be modified.
*/
if (!state->opt.skip_content_write) {
if (state->need_write)
state_write(state);
} else {
log_fatal("WARNING! Skipped state write for --test-skip-content-write option.\n");
}
/* skip degenerated cases of empty parity, or skipping all */
if (blockstart < blockmax) {
ret = state_sync_process(state, parity_handle, blockstart, blockmax);
if (ret == -1) {
/* LCOV_EXCL_START */
++unrecoverable_error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
} else {
msg_status("Nothing to do\n");
}
}
for (l = 0; l < state->level; ++l) {
ret = parity_close(&parity_handle[l]);
if (ret == -1) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Unexpected close error in %s disk.\n", lev_name(l));
++unrecoverable_error;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
/* abort if required */
if (unrecoverable_error != 0)
return -1;
return 0;
}
snapraid-12.1/cmdline/touch.c 0000664 0000000 0000000 00000010146 14166610522 0016144 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2014 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "elem.h"
#include "state.h"
#include "handle.h"
void state_touch(struct snapraid_state* state)
{
tommy_node* i;
char esc_buffer[ESC_MAX];
msg_progress("Setting sub-second timestamps...\n");
/* for all disks */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
tommy_node* j;
/* for all files */
for (j = disk->filelist; j != 0; j = j->next) {
struct snapraid_file* file = j->data;
/* if the file has a zero nanosecond timestamp */
/* note that symbolic links are not in the file list */
/* and then are not processed */
if (file->mtime_nsec == 0) {
char path[PATH_MAX];
struct stat st;
int f;
int ret;
int nsec;
int flags;
pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
/* set a new nanosecond timestamp different than 0 */
do {
uint32_t nano;
/* get a random nanosecond value */
if (randomize(&nano, sizeof(nano)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to get random values.\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
nsec = nano % 1000000000;
} while (nsec == 0);
/* O_BINARY: open as binary file (Windows only) */
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
flags = O_BINARY | O_NOFOLLOW;
#ifdef _WIN32
/* in Windows we must have write access at the file */
flags |= O_RDWR;
#else
/* in all others platforms, read access is enough */
flags |= O_RDONLY;
#endif
/* open it */
f = open(path, flags);
if (f == -1) {
/* LCOV_EXCL_START */
log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
continue;
/* LCOV_EXCL_STOP */
}
/* get the present timestamp, that may be different than the one */
/* in the content file */
ret = fstat(f, &st);
if (ret == -1) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Error accessing file '%s'. %s.\n", path, strerror(errno));
continue;
/* LCOV_EXCL_STOP */
}
/* set the tweaked modification time, with new nano seconds */
ret = fmtime(f, st.st_mtime, nsec);
if (ret != 0) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Error timing file '%s'. %s.\n", path, strerror(errno));
continue;
/* LCOV_EXCL_STOP */
}
/* uses fstat again to get the present timestamp */
/* this is needed because the value read */
/* may be different than the written one */
ret = fstat(f, &st);
if (ret == -1) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Error accessing file '%s'. %s.\n", path, strerror(errno));
continue;
/* LCOV_EXCL_STOP */
}
/* close it */
ret = close(f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
continue;
/* LCOV_EXCL_STOP */
}
/* set the same nanosecond value in the content file */
/* note that if the seconds value is already matching */
/* the file won't be synced because the content file will */
/* contain the new updated timestamp */
file->mtime_nsec = STAT_NSEC(&st);
/* state changed, we need to update it */
state->need_write = 1;
log_tag("touch:%s:%s: %" PRIu64 ".%d\n", disk->name, esc_tag(file->sub, esc_buffer), (uint64_t)st.st_mtime, STAT_NSEC(&st));
msg_info("touch %s\n", fmt_term(disk, file->sub, esc_buffer));
}
}
}
}
snapraid-12.1/cmdline/unix.c 0000664 0000000 0000000 00000106564 14166610522 0016017 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#ifndef __MINGW32__ /* Only for Unix */
#include "support.h"
/**
* Exit codes.
*/
int exit_success = 0;
int exit_failure = 1;
int exit_sync_needed = 2;
int open_noatime(const char* file, int flags)
{
#ifdef O_NOATIME
int f = open(file, flags | O_NOATIME);
/* only root is allowed to use O_NOATIME, in case retry without it */
if (f == -1 && errno == EPERM)
f = open(file, flags);
return f;
#else
return open(file, flags);
#endif
}
int dirent_hidden(struct dirent* dd)
{
return dd->d_name[0] == '.';
}
const char* stat_desc(struct stat* st)
{
if (S_ISREG(st->st_mode))
return "regular";
if (S_ISDIR(st->st_mode))
return "directory";
if (S_ISCHR(st->st_mode))
return "character";
if (S_ISBLK(st->st_mode))
return "block-device";
if (S_ISFIFO(st->st_mode))
return "fifo";
if (S_ISLNK(st->st_mode))
return "link";
if (S_ISLNK(st->st_mode))
return "symbolic-link";
if (S_ISSOCK(st->st_mode))
return "socket";
return "unknown";
}
/**
* Get the device file from the device number.
*
* It uses /proc/self/mountinfo.
*
* Null devices (major==0) are resolved to the device indicated in mountinfo.
*/
#if HAVE_LINUX_DEVICE
static int devresolve_proc(uint64_t device, char* path, size_t path_size)
{
FILE* f;
char match[32];
/* generate the matching string */
snprintf(match, sizeof(match), "%u:%u", major(device), minor(device));
f = fopen("/proc/self/mountinfo", "r");
if (!f) {
log_tag("resolve:proc:%u:%u: failed to open /proc/self/mountinfo\n", major(device), minor(device));
return -1;
}
/*
* mountinfo format
* 0 - mount ID
* 1 - parent ID
* 2 - major:minor
* 3 - root
* 4 - mount point
* 5 - options
* 6 - "-" (separator)
* 7 - fs
* 8 - mount source - /dev/device
*/
while (1) {
char buf[256];
char* first_map[8];
unsigned first_mac;
char* second_map[8];
unsigned second_mac;
char* s;
struct stat st;
char* separator;
char* majorminor;
char* mountpoint;
char* fs;
char* mountsource;
s = fgets(buf, sizeof(buf), f);
if (s == 0)
break;
/* find the separator position */
separator = strstr(s, " - ");
if (!separator)
continue;
/* skip the separator */
*separator = 0;
separator += 3;
/* split the line */
first_mac = strsplit(first_map, 8, s, " \t\r\n");
second_mac = strsplit(second_map, 8, separator, " \t\r\n");
/* if too short, it's the wrong line */
if (first_mac < 5)
continue;
if (second_mac < 2)
continue;
majorminor = first_map[2];
mountpoint = first_map[4];
fs = second_map[0];
mountsource = second_map[1];
/* compare major:minor from mountinfo */
if (strcmp(majorminor, match) == 0) {
/*
* Accept only /dev/... mountsource
*
* This excludes ZFS that uses a bare label for mountsource, like "tank".
*
* 410 408 0:193 / /XXX rw,relatime shared:217 - zfs tank/system/data/var/lib/docker/XXX rw,xattr,noacl
*
* Also excludes AUTOFS unmounted devices that point to a fake filesystem
* used to remount them at the first use.
*
* 97 25 0:42 / /XXX rw,relatime shared:76 - autofs /etc/auto.seed rw,fd=6,pgrp=952,timeout=30,minproto=5,maxproto=5,indirect
*/
if (strncmp(mountsource, "/dev/", 5) != 0) {
log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
continue;
}
pathcpy(path, path_size, mountsource);
log_tag("resolve:proc:%u:%u: found device %s matching device %s\n", major(device), minor(device), path, match);
fclose(f);
return 0;
}
/* get the device of the mount point */
/* in Btrfs it could be different than the one in mountinfo */
if (stat(mountpoint, &st) == 0 && st.st_dev == device) {
if (strncmp(mountsource, "/dev/", 5) != 0) {
log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
continue;
}
pathcpy(path, path_size, mountsource);
log_tag("resolve:proc:%u:%u: found device %s matching mountpoint %s\n", major(device), minor(device), path, mountpoint);
fclose(f);
return 0;
}
}
log_tag("resolve:proc:%u:%u: not found\n", major(device), minor(device));
fclose(f);
return -1;
}
#endif
/**
* Get the device of a virtual superblock.
*
* This is intended to resolve the case of Btrfs filesystems that
* create a virtual superblock (major==0) not backed by any low
* level device.
*
* See:
* Bug 711881 - too funny btrfs st_dev numbers
* https://bugzilla.redhat.com/show_bug.cgi?id=711881
*/
#if HAVE_LINUX_DEVICE
static int devdereference(uint64_t device, uint64_t* new_device)
{
char path[PATH_MAX];
struct stat st;
/* use the proc interface to get the device containing the filesystem */
if (devresolve_proc(device, path, sizeof(path)) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
/* check the device */
if (stat(path, &st) != 0) {
/* LCOV_EXCL_START */
log_tag("dereference:%u:%u: failed to stat %s\n", major(device), minor(device), path);
return -1;
/* LCOV_EXCL_STOP */
}
if (major(st.st_rdev) == 0) {
/* LCOV_EXCL_START */
log_tag("dereference:%u:%u: still null device %s -> %u:%u\n", major(device), minor(device), path, major(st.st_rdev), minor(st.st_rdev));
return -1;
/* LCOV_EXCL_STOP */
}
*new_device = st.st_rdev;
log_tag("dereference:%u:%u: found %u:%u\n", major(device), minor(device), major(st.st_rdev), minor(st.st_rdev));
return 0;
}
#endif
/**
* Read a file extracting the specified tag TAG=VALUE format.
* Return !=0 on error.
*/
#if HAVE_LINUX_DEVICE
static int tagread(const char* path, const char* tag, char* value, size_t value_size)
{
int f;
int ret;
int len;
char buf[512];
size_t tag_len;
char* i;
char* e;
f = open(path, O_RDONLY);
if (f == -1) {
/* LCOV_EXCL_START */
log_fatal("Failed to open '%s'.\n", path);
return 0;
/* LCOV_EXCL_STOP */
}
len = read(f, buf, sizeof(buf));
if (len < 0) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Failed to read '%s'.\n", path);
return -1;
/* LCOV_EXCL_STOP */
}
if (len == sizeof(buf)) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Too long read '%s'.\n", path);
return -1;
/* LCOV_EXCL_STOP */
}
ret = close(f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to close '%s'.\n", path);
return -1;
/* LCOV_EXCL_STOP */
}
buf[len] = 0;
tag_len = strlen(tag);
for (i = buf; *i; ++i) {
char* p = i;
/* start with a space */
if (p != buf) {
if (!isspace(*p))
continue;
++p;
}
if (strncmp(p, tag, tag_len) != 0)
continue;
p += tag_len;
/* end with a = */
if (*p != '=')
continue;
++p;
/* found */
i = p;
break;
}
if (!*i) {
/* LCOV_EXCL_START */
log_fatal("Missing tag '%s' for '%s'.\n", tag, path);
return -1;
/* LCOV_EXCL_STOP */
}
/* terminate at the first space */
e = i;
while (*e != 0 && !isspace(*e))
++e;
*e = 0;
if (!*i) {
/* LCOV_EXCL_START */
log_fatal("Empty tag '%s' for '%s'.\n", tag, path);
return -1;
/* LCOV_EXCL_STOP */
}
pathprint(value, value_size, "%s", i);
return 0;
}
#endif
/**
* Get the device file from the device number.
*
* It uses /sys/dev/block/.../uevent.
*
* For null device (major==0) it fails.
*/
#if HAVE_LINUX_DEVICE
static int devresolve_sys(dev_t device, char* path, size_t path_size)
{
struct stat st;
char buf[PATH_MAX];
/* default device path from device number */
pathprint(path, path_size, "/sys/dev/block/%u:%u/uevent", major(device), minor(device));
if (tagread(path, "DEVNAME", buf, sizeof(buf)) != 0) {
/* LCOV_EXCL_START */
log_tag("resolve:sys:%u:%u: failed to read DEVNAME tag '%s'\n", major(device), minor(device), path);
return -1;
/* LCOV_EXCL_STOP */
}
/* set the real device path */
pathprint(path, path_size, "/dev/%s", buf);
/* check the device */
if (stat(path, &st) != 0) {
/* LCOV_EXCL_START */
log_tag("resolve:sys:%u:%u: failed to stat '%s'\n", major(device), minor(device), path);
return -1;
/* LCOV_EXCL_STOP */
}
if (st.st_rdev != device) {
/* LCOV_EXCL_START */
log_tag("resolve:sys:%u:%u: unexpected device '%u:%u' for '%s'.\n", major(device), minor(device), major(st.st_rdev), minor(st.st_rdev), path);
return -1;
/* LCOV_EXCL_STOP */
}
log_tag("resolve:sys:%u:%u:%s: found\n", major(device), minor(device), path);
return 0;
}
#endif
/**
* Get the device file from the device number.
*/
#if HAVE_LINUX_DEVICE
static int devresolve(uint64_t device, char* path, size_t path_size)
{
if (devresolve_sys(device, path, path_size) == 0)
return 0;
return -1;
}
#endif
/**
* Cache used by blkid.
*/
#if HAVE_BLKID
static blkid_cache cache = 0;
#endif
/**
* Get the UUID using the /dev/disk/by-uuid/ links.
* It doesn't require root permission, and the uuid are always updated.
* It doesn't work with Btrfs file-systems that don't export the main UUID
* in /dev/disk/by-uuid/.
*/
#if HAVE_LINUX_DEVICE
static int devuuid_dev(uint64_t device, char* uuid, size_t uuid_size)
{
int ret;
DIR* d;
struct dirent* dd;
struct stat st;
/* scan the UUID directory searching for the device */
d = opendir("/dev/disk/by-uuid");
if (!d) {
log_tag("uuid:by-uuid:%u:%u: opendir(/dev/disk/by-uuid) failed, %s\n", major(device), minor(device), strerror(errno));
/* directory missing?, likely we are not in Linux */
return -1;
}
while ((dd = readdir(d)) != 0) {
/* skip "." and ".." files, UUIDs never start with '.' */
if (dd->d_name[0] == '.')
continue;
ret = fstatat(dirfd(d), dd->d_name, &st, 0);
if (ret != 0) {
log_tag("uuid:by-uuid:%u:%u: fstatat(%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
/* generic error, ignore and continue the search */
continue;
}
/* if it matches, we have the uuid */
if (S_ISBLK(st.st_mode) && st.st_rdev == (dev_t)device) {
char buf[PATH_MAX];
char path[PATH_MAX];
/* resolve the link */
pathprint(path, sizeof(path), "/dev/disk/by-uuid/%s", dd->d_name);
ret = readlink(path, buf, sizeof(buf));
if (ret < 0 || ret >= PATH_MAX) {
log_tag("uuid:by-uuid:%u:%u: readlink(/dev/disk/by-uuid/%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
/* generic error, ignore and continue the search */
continue;
}
buf[ret] = 0;
/* found */
pathcpy(uuid, uuid_size, dd->d_name);
log_tag("uuid:by-uuid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, buf);
closedir(d);
return 0;
}
}
log_tag("uuid:by-uuid:%u:%u: /dev/disk/by-uuid doesn't contain a matching block device\n", major(device), minor(device));
/* not found */
closedir(d);
return -1;
}
#endif
/**
* Get the UUID using liblkid.
* It uses a cache to work without root permission, resulting in UUID
* not necessarily recent.
* We could call blkid_probe_all() to refresh the UUID, but it would
* require root permission to read the superblocks, and resulting in
* all the disks spinning.
*/
#if HAVE_BLKID
static int devuuid_blkid(uint64_t device, char* uuid, size_t uuid_size)
{
char* devname;
char* uuidname;
devname = blkid_devno_to_devname(device);
if (!devname) {
log_tag("uuid:blkid:%u:%u: blkid_devno_to_devname() failed, %s\n", major(device), minor(device), strerror(errno));
/* device mapping failed */
return -1;
}
uuidname = blkid_get_tag_value(cache, "UUID", devname);
if (!uuidname) {
log_tag("uuid:blkid:%u:%u: blkid_get_tag_value(UUID,%s) failed, %s\n", major(device), minor(device), devname, strerror(errno));
/* uuid mapping failed */
free(devname);
return -1;
}
pathcpy(uuid, uuid_size, uuidname);
log_tag("uuid:blkid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, devname);
free(devname);
free(uuidname);
return 0;
}
#endif
int devuuid(uint64_t device, char* uuid, size_t uuid_size)
{
#if HAVE_LINUX_DEVICE
/* if the major is the null device */
if (major(device) == 0) {
/* obtain the real device */
if (devdereference(device, &device) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
#endif
/* first try with the /dev/disk/by-uuid version */
#if HAVE_LINUX_DEVICE
if (devuuid_dev(device, uuid, uuid_size) == 0)
return 0;
#else
log_tag("uuid:by-uuid:%u:%u: by-uuid not supported\n", major(device), minor(device));
#endif
/* fall back to blkid for other cases */
#if HAVE_BLKID
if (devuuid_blkid(device, uuid, uuid_size) == 0)
return 0;
#else
log_tag("uuid:blkid:%u:%u: blkid support not compiled in\n", major(device), minor(device));
#endif
log_tag("uuid:notfound:%u:%u:\n", major(device), minor(device));
/* not supported */
(void)uuid;
(void)uuid_size;
return -1;
}
int filephy(const char* path, uint64_t size, uint64_t* physical)
{
#if HAVE_LINUX_FIEMAP_H
/* In Linux get the real physical address of the file */
/* Note that FIEMAP doesn't require root permission */
int f;
struct {
struct fiemap fiemap;
struct fiemap_extent extent;
} fm;
unsigned int blknum;
f = open(path, O_RDONLY);
if (f == -1) {
return -1;
}
/* first try with FIEMAP */
/* if works for ext2, ext3, ext4, xfs, btrfs */
memset(&fm, 0, sizeof(fm));
fm.fiemap.fm_start = 0;
fm.fiemap.fm_length = ~0ULL;
fm.fiemap.fm_flags = FIEMAP_FLAG_SYNC; /* required to ensure that just created files report a valid address and not 0 */
fm.fiemap.fm_extent_count = 1; /* we are interested only at the first block */
if (ioctl(f, FS_IOC_FIEMAP, &fm) != -1) {
uint32_t flags = fm.fiemap.fm_extents[0].fe_flags;
uint64_t offset = fm.fiemap.fm_extents[0].fe_physical;
/* check some condition for validating the offset */
if (flags & FIEMAP_EXTENT_DATA_INLINE) {
/* if the data is inline, we don't have an offset to report */
*physical = FILEPHY_WITHOUT_OFFSET;
} else if (flags & FIEMAP_EXTENT_UNKNOWN) {
/* if the offset is unknown, we don't have an offset to report */
*physical = FILEPHY_WITHOUT_OFFSET;
} else if (offset == 0) {
/* 0 is the general fallback for file-systems when */
/* they don't have an offset to report */
*physical = FILEPHY_WITHOUT_OFFSET;
} else {
/* finally report the real offset */
*physical = offset + FILEPHY_REAL_OFFSET;
}
if (close(f) == -1)
return -1;
return 0;
}
/* if the file is empty, FIBMAP doesn't work, and we don't even try to use it */
if (size == 0) {
*physical = FILEPHY_WITHOUT_OFFSET;
if (close(f) == -1)
return -1;
return 0;
}
/* then try with FIBMAP */
/* it works for jfs, reiserfs, ntfs-3g */
/* in exfat it always returns 0, that it's anyway better than the fake inodes */
blknum = 0; /* first block */
if (ioctl(f, FIBMAP, &blknum) != -1) {
*physical = blknum + FILEPHY_REAL_OFFSET;
if (close(f) == -1)
return -1;
return 0;
}
/* otherwise don't use anything, and keep the directory traversal order */
/* at now this should happen only for vfat */
/* and it's surely better than using fake inodes */
*physical = FILEPHY_UNREPORTED_OFFSET;
if (close(f) == -1)
return -1;
#else
/* In a generic Unix use a dummy value for all the files */
/* We don't want to risk to use the inode without knowing */
/* if it really improves performance. */
/* In this way we keep them in the directory traversal order */
/* that at least keeps files in the same directory together. */
/* Note also that in newer file-system with snapshot, like ZFS, */
/* the inode doesn't represent even more the disk position, because files */
/* are not overwritten in place, but rewritten in another location */
/* of the disk. */
*physical = FILEPHY_UNREPORTED_OFFSET;
(void)path; /* not used here */
(void)size;
#endif
return 0;
}
int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_hardlinks, uint64_t* total_space, uint64_t* free_space)
{
char type[64];
const char* ptype;
#if HAVE_STATFS
struct statfs st;
if (statfs(path, &st) != 0) {
char dir[PATH_MAX];
char* slash;
if (errno != ENOENT) {
return -1;
}
/* if it doesn't exist, we assume a file */
/* and we check for the containing dir */
if (strlen(path) + 1 > sizeof(dir)) {
errno = ENAMETOOLONG;
return -1;
}
strcpy(dir, path);
slash = strrchr(dir, '/');
if (!slash)
return -1;
*slash = 0;
if (statfs(dir, &st) != 0)
return -1;
}
#endif
/* to get the fs type check "man stat" or "stat -f -t FILE" */
if (has_persistent_inode) {
#if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
switch (st.f_type) {
case 0x65735546 : /* FUSE, "fuseblk" in the stat command */
case 0x4d44 : /* VFAT, "msdos" in the stat command */
*has_persistent_inode = 0;
break;
default :
/* by default assume yes */
*has_persistent_inode = 1;
break;
}
#else
/* in Unix inodes are persistent by default */
*has_persistent_inode = 1;
#endif
}
if (has_syncronized_hardlinks) {
#if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
switch (st.f_type) {
case 0x5346544E : /* NTFS */
case 0x4d44 : /* VFAT, "msdos" in the stat command */
*has_syncronized_hardlinks = 0;
break;
default :
/* by default assume yes */
*has_syncronized_hardlinks = 1;
break;
}
#else
/* in Unix hardlinks share the same metadata by default */
*has_syncronized_hardlinks = 1;
#endif
}
if (total_space) {
#if HAVE_STATFS
*total_space = st.f_bsize * (uint64_t)st.f_blocks;
#else
*total_space = 0;
#endif
}
if (free_space) {
#if HAVE_STATFS
*free_space = st.f_bsize * (uint64_t)st.f_bfree;
#else
*free_space = 0;
#endif
}
#if HAVE_STATFS && HAVE_STRUCT_STATFS_F_FSTYPENAME
/* get the filesystem type directly from the struct (Mac OS X) */
(void)type;
ptype = st.f_fstypename;
#elif HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
/* get the filesystem type from f_type (Linux) */
/* from: https://github.com/influxdata/gopsutil/blob/master/disk/disk_linux.go */
switch (st.f_type) {
case 0x65735546 : ptype = "fuseblk"; break;
case 0x4D44 : ptype = "vfat/msdos"; break;
case 0xEF53 : ptype = "ext2/3/4"; break;
case 0x6969 : ptype = "nfs"; break; /* remote */
case 0x6E667364 : ptype = "nfsd"; break; /* remote */
case 0x517B : ptype = "smb"; break; /* remote */
case 0x5346544E : ptype = "ntfs"; break;
case 0x52654973 : ptype = "reiserfs"; break;
case 0x3153464A : ptype = "jfs"; break;
case 0x58465342 : ptype = "xfs"; break;
case 0x9123683E : ptype = "btrfs"; break;
case 0x2FC12FC1 : ptype = "zfs"; break;
default :
snprintf(type, sizeof(type), "0x%X", (unsigned)st.f_type);
ptype = type;
}
#else
(void)type;
ptype = "unknown";
#endif
log_tag("statfs:%s: %s \n", ptype, path);
return 0;
}
uint64_t tick(void)
{
#if HAVE_MACH_ABSOLUTE_TIME
/* for Mac OS X */
return mach_absolute_time();
#elif HAVE_CLOCK_GETTIME && (defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW))
/* for Linux */
struct timespec tv;
/* nanosecond precision with clock_gettime() */
#if defined(CLOCK_MONOTONIC_RAW)
if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) != 0) {
#else
if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
#endif
return 0;
}
return tv.tv_sec * 1000000000ULL + tv.tv_nsec;
#else
/* other platforms */
struct timeval tv;
/* microsecond precision with gettimeofday() */
if (gettimeofday(&tv, 0) != 0) {
return 0;
}
return tv.tv_sec * 1000000ULL + tv.tv_usec;
#endif
}
uint64_t tick_ms(void)
{
struct timeval tv;
if (gettimeofday(&tv, 0) != 0)
return 0;
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
}
int randomize(void* ptr, size_t size)
{
int f;
ssize_t ret;
f = open("/dev/urandom", O_RDONLY);
if (f == -1)
return -1;
ret = read(f, ptr, size);
if (ret < 0 || (size_t)ret != size) {
close(f);
return -1;
}
if (close(f) != 0)
return -1;
return 0;
}
/**
* Read a file extracting the contained device number in %u:%u format.
* Return 0 on error.
*/
#if HAVE_LINUX_DEVICE
static dev_t devread(const char* path)
{
int f;
int ret;
int len;
char buf[64];
char* e;
unsigned ma;
unsigned mi;
f = open(path, O_RDONLY);
if (f == -1) {
/* LCOV_EXCL_START */
log_fatal("Failed to open '%s'.\n", path);
return 0;
/* LCOV_EXCL_STOP */
}
len = read(f, buf, sizeof(buf));
if (len < 0) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Failed to read '%s'.\n", path);
return 0;
/* LCOV_EXCL_STOP */
}
if (len == sizeof(buf)) {
/* LCOV_EXCL_START */
close(f);
log_fatal("Too long read '%s'.\n", path);
return 0;
/* LCOV_EXCL_STOP */
}
ret = close(f);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to close '%s'.\n", path);
return 0;
/* LCOV_EXCL_STOP */
}
buf[len] = 0;
ma = strtoul(buf, &e, 10);
if (*e != ':') {
/* LCOV_EXCL_START */
log_fatal("Invalid format in '%s' for '%s'.\n", path, buf);
return 0;
/* LCOV_EXCL_STOP */
}
mi = strtoul(e + 1, &e, 10);
if (*e != 0 && !isspace(*e)) {
/* LCOV_EXCL_START */
log_fatal("Invalid format in '%s' for '%s'.\n", path, buf);
return 0;
/* LCOV_EXCL_STOP */
}
return makedev(ma, mi);
}
#endif
/**
* Read a device tree filling the specified list of disk_t entries.
*/
#if HAVE_LINUX_DEVICE
static int devtree(const char* name, const char* custom, dev_t device, devinfo_t* parent, tommy_list* list)
{
char path[PATH_MAX];
DIR* d;
int slaves = 0;
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves", major(device), minor(device));
/* check if there is a slaves list */
d = opendir(path);
if (d != 0) {
struct dirent* dd;
while ((dd = readdir(d)) != 0) {
if (dd->d_name[0] != '.') {
dev_t subdev;
/* for each slave, expand the full potential tree */
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves/%s/dev", major(device), minor(device), dd->d_name);
subdev = devread(path);
if (!subdev) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
/* LCOV_EXCL_STOP */
}
if (devtree(name, custom, subdev, parent, list) != 0) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
/* LCOV_EXCL_STOP */
}
++slaves;
}
}
closedir(d);
}
/* if no slaves found */
if (!slaves) {
/* this is a raw device */
devinfo_t* devinfo;
/* check if it's a real device */
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device", major(device), minor(device));
if (access(path, F_OK) != 0) {
/* get the parent device */
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/../dev", major(device), minor(device));
device = devread(path);
if (!device) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
/* get the device file */
if (devresolve(device, path, sizeof(path)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
return -1;
/* LCOV_EXCL_STOP */
}
devinfo = calloc_nofail(1, sizeof(devinfo_t));
devinfo->device = device;
pathcpy(devinfo->name, sizeof(devinfo->name), name);
pathcpy(devinfo->smartctl, sizeof(devinfo->smartctl), custom);
pathcpy(devinfo->file, sizeof(devinfo->file), path);
devinfo->parent = parent;
/* insert in the list */
tommy_list_insert_tail(list, &devinfo->node, devinfo);
}
return 0;
}
#endif
/**
* Scan all the devices.
*
* If a device is already in, it's not added again.
*/
#if HAVE_LINUX_DEVICE
static int devscan(tommy_list* list)
{
char dir[PATH_MAX];
DIR* d;
struct dirent* dd;
pathprint(dir, sizeof(dir), "/sys/dev/block/");
/* check if there is a slaves list */
d = opendir(dir);
if (d == 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to open dir '%s'.\n", dir);
return -1;
/* LCOV_EXCL_STOP */
}
while ((dd = readdir(d)) != 0) {
char path[PATH_MAX];
tommy_node* i;
dev_t device;
devinfo_t* devinfo;
if (dd->d_name[0] == '.')
continue;
pathprint(path, sizeof(path), "/sys/dev/block/%s/device", dd->d_name);
/* check if it's a real device */
if (access(path, F_OK) != 0)
continue;
pathprint(path, sizeof(path), "/sys/dev/block/%s/dev", dd->d_name);
device = devread(path);
if (!device) {
/* LCOV_EXCL_START */
log_tag("scan:skip: Skipping device %s because failed to read its device number.\n", dd->d_name);
continue;
/* LCOV_EXCL_STOP */
}
/* check if already present */
for (i = tommy_list_head(list); i != 0; i = i->next) {
devinfo = i->data;
if (devinfo->device == device)
break;
}
/* if already present */
if (i != 0)
continue;
/* get the device file */
if (devresolve(device, path, sizeof(path)) != 0) {
/* LCOV_EXCL_START */
log_tag("scan:skip: Skipping device %u:%u because failed to resolve.\n", major(device), minor(device));
continue;
/* LCOV_EXCL_STOP */
}
devinfo = calloc_nofail(1, sizeof(devinfo_t));
devinfo->device = device;
pathcpy(devinfo->file, sizeof(devinfo->file), path);
/* insert in the list */
tommy_list_insert_tail(list, &devinfo->node, devinfo);
}
closedir(d);
return 0;
}
#endif
/**
* Get SMART attributes.
*/
#if HAVE_LINUX_DEVICE
static int devsmart(dev_t device, const char* name, const char* custom, uint64_t* smart, char* serial, char* vendor, char* model)
{
char cmd[PATH_MAX + 64];
char file[PATH_MAX];
FILE* f;
int ret;
if (devresolve(device, file, sizeof(file)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
return -1;
/* LCOV_EXCL_STOP */
}
/* if there is a custom command */
if (custom[0]) {
char option[PATH_MAX];
snprintf(option, sizeof(option), custom, file);
snprintf(cmd, sizeof(cmd), "smartctl -a %s", option);
} else {
snprintf(cmd, sizeof(cmd), "smartctl -a %s", file);
}
log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
f = popen(cmd, "r");
if (!f) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from popen).\n", cmd);
return -1;
/* LCOV_EXCL_STOP */
}
if (smartctl_attribute(f, file, name, smart, serial, vendor, model) != 0) {
/* LCOV_EXCL_START */
pclose(f);
return -1;
/* LCOV_EXCL_STOP */
}
ret = pclose(f);
log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
if (!WIFEXITED(ret)) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (not exited).\n", cmd);
return -1;
/* LCOV_EXCL_STOP */
}
if (WEXITSTATUS(ret) == 127) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from sh).\n", cmd);
return -1;
/* LCOV_EXCL_STOP */
}
/* store the return smartctl return value */
smart[SMART_FLAGS] = WEXITSTATUS(ret);
return 0;
}
#endif
/**
* Spin down a specific device.
*/
#if HAVE_LINUX_DEVICE
static int devdown(dev_t device, const char* name, const char* custom)
{
char cmd[PATH_MAX + 64];
char file[PATH_MAX];
FILE* f;
int ret;
if (devresolve(device, file, sizeof(file)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
return -1;
/* LCOV_EXCL_STOP */
}
/* if there is a custom command */
if (custom[0]) {
char option[PATH_MAX];
snprintf(option, sizeof(option), custom, file);
snprintf(cmd, sizeof(cmd), "smartctl -s standby,now %s", option);
} else {
snprintf(cmd, sizeof(cmd), "smartctl -s standby,now %s", file);
}
log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
f = popen(cmd, "r");
if (!f) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from popen).\n", cmd);
return -1;
/* LCOV_EXCL_STOP */
}
if (smartctl_flush(f, file, name) != 0) {
/* LCOV_EXCL_START */
pclose(f);
return -1;
/* LCOV_EXCL_STOP */
}
ret = pclose(f);
log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
if (!WIFEXITED(ret)) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (not exited).\n", cmd);
return -1;
/* LCOV_EXCL_STOP */
}
if (WEXITSTATUS(ret) == 127) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' (from sh).\n", cmd);
return -1;
/* LCOV_EXCL_STOP */
}
if (WEXITSTATUS(ret) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to run '%s' with return code %xh.\n", cmd, WEXITSTATUS(ret));
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
#endif
/**
* Spin up a device.
*
* There isn't a defined way to spin up a device,
* so we just do a generic write.
*/
static int devup(const char* mountpoint)
{
int ret;
char path[PATH_MAX];
/* add a temporary name used for writing */
pathprint(path, sizeof(path), "%s.snapraid-spinup", mountpoint);
/* do a generic write, and immediately undo it */
ret = mkdir(path, 0);
if (ret != 0 && errno != EEXIST) {
/* LCOV_EXCL_START */
log_fatal("Failed to create dir '%s'.\n", path);
return -1;
/* LCOV_EXCL_STOP */
}
/* remove the just created dir */
rmdir(path);
return 0;
}
/**
* Thread for spinning up.
*
* Note that filling up the devinfo object is done inside this thread,
* to avoid to block the main thread if the device need to be spin up
* to handle stat/resolve requests.
*/
static void* thread_spinup(void* arg)
{
devinfo_t* devinfo = arg;
struct stat st;
uint64_t start;
start = tick_ms();
/* first get the device number, this usually doesn't trigger a thread_spinup */
if (stat(devinfo->mount, &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to stat device '%s'.\n", devinfo->mount);
return (void*)-1;
/* LCOV_EXCL_STOP */
}
/* set the device number for printing */
devinfo->device = st.st_dev;
if (devup(devinfo->mount) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
msg_status("Spunup device '%u:%u' for disk '%s' in %" PRIu64 " ms.\n", major(devinfo->device), minor(devinfo->device), devinfo->name, tick_ms() - start);
return 0;
}
/**
* Thread for spinning down.
*/
static void* thread_spindown(void* arg)
{
#if HAVE_LINUX_DEVICE
devinfo_t* devinfo = arg;
uint64_t start;
start = tick_ms();
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, tick_ms() - start);
return 0;
#else
(void)arg;
return (void*)-1;
#endif
}
/**
* Thread for getting smart info.
*/
static void* thread_smart(void* arg)
{
#if HAVE_LINUX_DEVICE
devinfo_t* devinfo = arg;
if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->smart_serial, devinfo->smart_vendor, devinfo->smart_model) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
return 0;
#else
(void)arg;
return (void*)-1;
#endif
}
static int device_thread(tommy_list* list, void* (*func)(void* arg))
{
int fail = 0;
tommy_node* i;
#if HAVE_THREAD
/* start all threads */
for (i = tommy_list_head(list); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
thread_create(&devinfo->thread, func, devinfo);
}
/* join all threads */
for (i = tommy_list_head(list); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
void* retval;
thread_join(devinfo->thread, &retval);
if (retval != 0)
++fail;
}
#else
for (i = tommy_list_head(list); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
if (func(devinfo) != 0)
++fail;
}
#endif
if (fail != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
int devquery(tommy_list* high, tommy_list* low, int operation, int others)
{
tommy_node* i;
void* (*func)(void* arg) = 0;
#if HAVE_LINUX_DEVICE
if (operation != DEVICE_UP) {
struct stat st;
/* sysfs interface is required */
if (stat("/sys/dev/block", &st) != 0) {
/* LCOV_EXCL_START */
log_fatal("Missing interface /sys/dev/block.\n");
return -1;
/* LCOV_EXCL_STOP */
}
/* for each device */
for (i = tommy_list_head(high); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
uint64_t device = devinfo->device;
/* if the major is the null device, find the real one */
if (major(device) == 0) {
/* obtain the real device */
if (devdereference(device, &device) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to dereference device '%u:%u'.\n", major(device), minor(device));
return -1;
/* LCOV_EXCL_STOP */
}
}
/* get the device file */
if (devresolve(device, devinfo->file, sizeof(devinfo->file)) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
return -1;
/* LCOV_EXCL_STOP */
}
/* expand the tree of devices */
if (devtree(devinfo->name, devinfo->smartctl, device, devinfo, low) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to expand device '%u:%u'.\n", major(device), minor(device));
return -1;
/* LCOV_EXCL_STOP */
}
}
}
#endif
if (operation == DEVICE_UP) {
/* duplicate the high */
for (i = tommy_list_head(high); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
devinfo_t* entry;
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = devinfo->device;
pathcpy(entry->name, sizeof(entry->name), devinfo->name);
pathcpy(entry->mount, sizeof(entry->mount), devinfo->mount);
/* insert in the high */
tommy_list_insert_tail(low, &entry->node, entry);
}
}
#if HAVE_LINUX_DEVICE
/* add other devices */
if (others) {
if (devscan(low) != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed to list other devices.\n");
return -1;
/* LCOV_EXCL_STOP */
}
}
#else
(void)others;
#endif
switch (operation) {
case DEVICE_UP : func = thread_spinup; break;
case DEVICE_DOWN : func = thread_spindown; break;
case DEVICE_SMART : func = thread_smart; break;
}
if (!func)
return 0;
return device_thread(low, func);
}
void os_init(int opt)
{
#if HAVE_BLKID
int ret;
ret = blkid_get_cache(&cache, NULL);
if (ret != 0) {
/* LCOV_EXCL_START */
log_fatal("WARNING Failed to get blkid cache\n");
/* LCOV_EXCL_STOP */
}
#endif
(void)opt;
}
void os_done(void)
{
#if HAVE_BLKID
if (cache != 0)
blkid_put_cache(cache);
#endif
}
/* LCOV_EXCL_START */
void os_abort(void)
{
#if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
void* stack[32];
char** messages;
size_t size;
unsigned i;
#endif
printf("Stacktrace of " PACKAGE " v" VERSION);
#ifdef _linux
printf(", linux");
#endif
#ifdef __GNUC__
printf(", gcc " __VERSION__);
#endif
printf(", %d-bit", (int)sizeof(void *) * 8);
printf(", PATH_MAX=%d", PATH_MAX);
printf("\n");
#if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
size = backtrace(stack, 32);
messages = backtrace_symbols(stack, size);
for (i = 1; i < size; ++i) {
const char* msg;
if (messages)
msg = messages[i];
else
msg = "";
printf("[bt] %02u: %s\n", i, msg);
if (messages) {
int ret;
char addr2line[1024];
size_t j = 0;
while (msg[j] != '(' && msg[j] != ' ' && msg[j] != 0)
++j;
snprintf(addr2line, sizeof(addr2line), "addr2line %p -e %.*s", stack[i], (unsigned)j, msg);
ret = system(addr2line);
if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0)
printf("exit:%d\n", WEXITSTATUS(ret));
if (WIFSIGNALED(ret))
printf("signal:%d\n", WTERMSIG(ret));
}
}
#endif
printf("Please report this error to the SnapRAID Forum:\n");
printf("https://sourceforge.net/p/snapraid/discussion/1677233/\n");
abort();
}
/* LCOV_EXCL_STOP */
void os_clear(void)
{
/* ANSI codes */
printf("\033[H"); /* cursor at topleft */
printf("\033[2J"); /* clear screen */
}
size_t direct_size(void)
{
long size;
size = sysconf(_SC_PAGESIZE);
if (size == -1) {
/* LCOV_EXCL_START */
log_fatal("No page size\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return size;
}
#endif
snapraid-12.1/cmdline/unix.h 0000664 0000000 0000000 00000003703 14166610522 0016013 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __UNIX_H
#define __UNIX_H
#ifdef __linux__
#define HAVE_LINUX_DEVICE 1 /**< In Linux enables special device support. */
#define HAVE_DIRECT_IO 1 /**< Support O_DIRECT in open(). */
#endif
#define O_BINARY 0 /**< Not used in Unix. */
#define O_SEQUENTIAL 0 /**< In Unix posix_fadvise() shall be used. */
/**
* If nanoseconds are not supported, we report the special STAT_NSEC_INVALID value,
* to mark that it's undefined.
*/
#define STAT_NSEC_INVALID -1
/* Check if we have nanoseconds support */
#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
#define STAT_NSEC(st) ((int)(st)->st_mtim.tv_nsec) /* Linux */
#elif HAVE_STRUCT_STAT_ST_MTIMENSEC
#define STAT_NSEC(st) ((int)(st)->st_mtimensec) /* NetBSD */
#elif HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
#define STAT_NSEC(st) ((int)(st)->st_mtimespec.tv_nsec) /* FreeBSD, Mac OS X */
#else
#define STAT_NSEC(st) STAT_NSEC_INVALID
#endif
/**
* Open a file with the O_NOATIME flag to avoid to update the access time.
*/
int open_noatime(const char* file, int flags);
/**
* Check if the specified file is hidden.
*/
int dirent_hidden(struct dirent* dd);
/**
* Return a description of the file type.
*/
const char* stat_desc(struct stat* st);
/**
* Return the alignment requirement for direct IO.
*/
size_t direct_size(void);
#endif
snapraid-12.1/cmdline/util.c 0000664 0000000 0000000 00000050230 14166610522 0015775 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "portable.h"
#include "support.h"
#include "util.h"
#include "raid/cpu.h"
#include "raid/memory.h"
/****************************************************************************/
/* memory */
void* malloc_nofail_align(size_t size, void** freeptr)
{
void* ptr;
ptr = raid_malloc(size, freeptr);
if (!ptr) {
/* LCOV_EXCL_START */
malloc_fail(size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return ptr;
}
void* malloc_nofail_direct(size_t size, void** freeptr)
{
void* ptr;
ptr = raid_malloc_align(size, direct_size(), freeptr);
if (!ptr) {
/* LCOV_EXCL_START */
malloc_fail(size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return ptr;
}
void** malloc_nofail_vector_align(int nd, int n, size_t size, void** freeptr)
{
void* ptr;
ptr = raid_malloc_vector(nd, n, size, freeptr);
if (!ptr) {
/* LCOV_EXCL_START */
malloc_fail(n * size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return ptr;
}
void** malloc_nofail_vector_direct(int nd, int n, size_t size, void** freeptr)
{
void* ptr;
ptr = raid_malloc_vector_align(nd, n, size, direct_size(), 0, freeptr);
if (!ptr) {
/* LCOV_EXCL_START */
malloc_fail(n * size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
return ptr;
}
void* malloc_nofail_test(size_t size)
{
void* ptr;
ptr = malloc_nofail(size);
mtest_vector(1, size, &ptr);
return ptr;
}
/**
* Fast memory test.
*
* It's similar at raid_mtest_vector() but faster because
* we have a lot of buffers to verify.
*/
static int fast_mtest_vector(int n, size_t size, void** vv)
{
int i, j;
/* test with all the bits */
for (i = 0; i < 8; ++i) {
unsigned char value = 1 << i;
/* fill first */
memset(vv[0], value, size);
/* copy to the next */
for (j = 1; j < n; ++j)
memcpy(vv[j], vv[j - 1], size);
/* compare with the previous */
for (j = 1; j <= n; ++j)
if (memcmp(vv[j % n], vv[j - 1], size) != 0)
return -1;
}
return 0;
}
void mtest_vector(int n, size_t size, void** vv)
{
if (fast_mtest_vector(n, size, vv) != 0) {
/* LCOV_EXCL_START */
log_fatal("DANGER! Your RAM memory is broken! DO NOT PROCEED UNTIL FIXED!\n");
log_fatal("Try running a memory test like http://www.memtest86.com/\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/****************************************************************************/
/* crc */
uint32_t CRC32C_0[256] = {
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4,
0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b,
0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54,
0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5,
0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45,
0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48,
0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687,
0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8,
0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096,
0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9,
0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36,
0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043,
0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3,
0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652,
0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d,
0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2,
0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530,
0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f,
0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90,
0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321,
0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81,
0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
};
uint32_t CRC32C_1[256] = {
0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899,
0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945,
0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21,
0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd,
0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918,
0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4,
0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0,
0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c,
0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b,
0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47,
0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823,
0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff,
0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a,
0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6,
0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2,
0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e,
0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d,
0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41,
0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25,
0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9,
0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c,
0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0,
0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4,
0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78,
0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f,
0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43,
0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27,
0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb,
0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e,
0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2,
0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6,
0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a,
0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260,
0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc,
0x66d73941, 0x7575a136, 0x419209af, 0x523091d8,
0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004,
0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1,
0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d,
0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059,
0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185,
0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162,
0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be,
0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da,
0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306,
0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3,
0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f,
0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b,
0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287,
0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464,
0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8,
0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc,
0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600,
0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5,
0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439,
0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d,
0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781,
0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766,
0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba,
0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de,
0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502,
0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7,
0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b,
0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f,
0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483
};
uint32_t CRC32C_2[256] = {
0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073,
0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469,
0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6,
0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac,
0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9,
0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3,
0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c,
0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726,
0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67,
0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d,
0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2,
0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8,
0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed,
0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7,
0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828,
0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32,
0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa,
0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0,
0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f,
0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75,
0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20,
0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a,
0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5,
0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff,
0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe,
0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4,
0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b,
0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161,
0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634,
0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e,
0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1,
0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb,
0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730,
0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a,
0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5,
0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def,
0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba,
0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0,
0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f,
0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065,
0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24,
0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e,
0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1,
0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb,
0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae,
0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4,
0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b,
0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71,
0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9,
0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3,
0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c,
0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36,
0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63,
0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79,
0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6,
0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc,
0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd,
0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7,
0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238,
0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622,
0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177,
0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d,
0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2,
0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8
};
uint32_t CRC32C_3[256] = {
0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939,
0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca,
0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf,
0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c,
0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804,
0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7,
0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2,
0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11,
0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2,
0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41,
0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54,
0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7,
0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f,
0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c,
0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69,
0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a,
0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de,
0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d,
0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538,
0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb,
0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3,
0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610,
0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405,
0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6,
0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255,
0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6,
0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3,
0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040,
0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368,
0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b,
0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e,
0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d,
0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006,
0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5,
0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0,
0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213,
0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b,
0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8,
0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd,
0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e,
0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d,
0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e,
0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b,
0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698,
0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0,
0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443,
0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656,
0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5,
0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1,
0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12,
0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07,
0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4,
0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc,
0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f,
0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a,
0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9,
0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a,
0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99,
0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c,
0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f,
0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57,
0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4,
0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1,
0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842
};
#if HAVE_SSE42
int crc_x86;
#endif
uint32_t crc32c_gen(uint32_t crc, const unsigned char* ptr, unsigned size)
{
crc ^= CRC_IV;
crc = crc32c_gen_plain(crc, ptr, size);
crc ^= CRC_IV;
return crc;
}
#if HAVE_SSE42
uint32_t crc32c_x86(uint32_t crc, const unsigned char* ptr, unsigned size)
{
crc ^= CRC_IV;
crc = crc32c_x86_plain(crc, ptr, size);
crc ^= CRC_IV;
return crc;
}
#endif
uint32_t (*crc32c)(uint32_t crc, const unsigned char* ptr, unsigned size);
void crc32c_init(void)
{
crc32c = crc32c_gen;
#if HAVE_SSE42
if (raid_cpu_has_crc32()) {
crc_x86 = 1;
crc32c = crc32c_x86;
}
#endif
}
/****************************************************************************/
/* byte operations */
/*
* Rotate left.
* In x86/x64 they are optimized with a single assembler instruction.
*/
static inline uint32_t util_rotl32(uint32_t x, int8_t r)
{
return (x << r) | (x >> (32 - r));
}
static inline uint64_t util_rotl64(uint64_t x, int8_t r)
{
return (x << r) | (x >> (64 - r));
}
/*
* Rotate right.
* In x86/x64 they are optimized with a single assembler instruction.
*/
#if 0 /* unused */
static inline uint32_t util_rotr32(uint32_t x, int8_t r)
{
return (x >> r) | (x << (32 - r));
}
#endif
static inline uint64_t util_rotr64(uint64_t x, int8_t r)
{
return (x >> r) | (x << (64 - r));
}
/**
* Swap endianness.
* They are needed only if BigEndian.
*/
#if defined(__GNUC__)
#define util_swap32(x) __builtin_bswap32(x)
#define util_swap64(x) __builtin_bswap64(x)
#elif HAVE_BYTESWAP_H
#include
#define util_swap32(x) bswap_32(x)
#define util_swap64(x) bswap_64(x)
#else
static inline uint32_t util_swap32(uint32_t v)
{
return (util_rotl32(v, 8) & 0x00ff00ff)
| (util_rotl32(v, 24) & 0xff00ff00);
}
static inline uint64_t util_swap64(uint64_t v)
{
return (util_rotl64(v, 8) & 0x000000ff000000ffULL)
| (util_rotl64(v, 24) & 0x0000ff000000ff00ULL)
| (util_rotl64(v, 40) & 0x00ff000000ff0000ULL)
| (util_rotl64(v, 56) & 0xff000000ff000000ULL);
}
#endif
static inline uint8_t util_read8(const void* void_ptr)
{
const uint8_t* ptr = void_ptr;
return ptr[0];
}
static inline uint16_t util_read16(const void* void_ptr)
{
const uint8_t* ptr = void_ptr;
return ptr[0] + (ptr[1] << 8);
}
static inline uint32_t util_read32(const void* ptr)
{
uint32_t v;
memcpy(&v, ptr, sizeof(v));
#if WORDS_BIGENDIAN
v = util_swap32(v);
#endif
return v;
}
static inline uint64_t util_read64(const void* ptr)
{
uint64_t v;
memcpy(&v, ptr, sizeof(v));
#if WORDS_BIGENDIAN
v = util_swap64(v);
#endif
return v;
}
static inline void util_write32(void* ptr, uint32_t v)
{
#if WORDS_BIGENDIAN
v = util_swap32(v);
#endif
memcpy(ptr, &v, sizeof(v));
}
static inline void util_write64(void* ptr, uint64_t v)
{
#if WORDS_BIGENDIAN
v = util_swap64(v);
#endif
memcpy(ptr, &v, sizeof(v));
}
/****************************************************************************/
/* hash */
#include "murmur3.c"
#include "spooky2.c"
#include "metro.c"
void memhash(unsigned kind, const unsigned char* seed, void* digest, const void* src, size_t size)
{
switch (kind) {
case HASH_MURMUR3 :
MurmurHash3_x86_128(src, size, seed, digest);
break;
case HASH_SPOOKY2 :
SpookyHash128(src, size, seed, digest);
break;
case HASH_METRO :
MetroHash128(src, size, seed, digest);
break;
default :
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency in hash function %u\n", kind);
exit(EXIT_FAILURE);
break;
/* LCOV_EXCL_STOP */
}
}
const char* hash_config_name(unsigned kind)
{
switch (kind) {
case HASH_UNDEFINED : return "undefined";
case HASH_MURMUR3 : return "murmur3";
case HASH_SPOOKY2 : return "spooky2";
case HASH_METRO : return "metro";
default :
/* LCOV_EXCL_START */
return "unknown";
/* LCOV_EXCL_STOP */
}
}
unsigned memdiff(const unsigned char* data1, const unsigned char* data2, size_t size)
{
size_t i;
unsigned count;
count = 0;
for (i = 0; i < size; ++i) {
unsigned j;
unsigned char diff = data1[i] ^ data2[i];
for (j = 0; j < 8; ++j) {
if ((diff & 1) != 0)
++count;
diff >>= 1;
}
}
return count;
}
/****************************************************************************/
/* lock */
#if HAVE_LOCKFILE
int lock_lock(const char* file)
{
int f;
f = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (f == -1) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
/* exclusive lock, not blocking */
if (flock(f, LOCK_EX | LOCK_NB) == -1) {
/* LCOV_EXCL_START */
close(f);
return -1;
/* LCOV_EXCL_STOP */
}
return f;
}
#endif
#if HAVE_LOCKFILE
int lock_unlock(int f)
{
/*
* Intentionally don't remove the lock file.
* Removing it just introduces race course with other process
* that could have already opened it.
*/
if (close(f) == -1) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
return 0;
}
#endif
snapraid-12.1/cmdline/util.h 0000664 0000000 0000000 00000013577 14166610522 0016017 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __UTIL_H
#define __UTIL_H
/****************************************************************************/
/* memory */
/**
* Safe aligned malloc.
* If no memory is available, it aborts.
*/
void* malloc_nofail_align(size_t size, void** freeptr);
/**
* Safe aligned malloc. Usable for direct io.
*/
void* malloc_nofail_direct(size_t size, void** freeptr);
/**
* Safe aligned vector allocation.
* If no memory is available, it aborts.
*/
void** malloc_nofail_vector_align(int nd, int n, size_t size, void** freeptr);
/**
* Safe page vector allocation. Usable for direct io.
* If no memory is available, it aborts.
*/
void** malloc_nofail_vector_direct(int nd, int n, size_t size, void** freeptr);
/**
* Safe allocation with memory test.
*/
void* malloc_nofail_test(size_t size);
/**
* Test the memory vector for RAM problems.
* If a problem is found, it crashes.
*/
void mtest_vector(int n, size_t size, void** vv);
/****************************************************************************/
/* crc */
/**
* CRC initial value.
* Using a not zero value allows to detect a leading run of zeros.
*/
#define CRC_IV 0xffffffffU
/**
* CRC-32 (Castagnoli) table.
*/
extern uint32_t CRC32C_0[256];
extern uint32_t CRC32C_1[256];
extern uint32_t CRC32C_2[256];
extern uint32_t CRC32C_3[256];
/**
* If the CPU support the CRC instructions.
*/
#if HAVE_SSE42
extern int crc_x86;
#endif
/**
* Compute CRC-32 (Castagnoli) for a single byte without the IV.
*/
static inline uint32_t crc32c_plain_char(uint32_t crc, unsigned char c)
{
#if HAVE_SSE42
if (tommy_likely(crc_x86)) {
asm ("crc32b %1, %0\n" : "+r" (crc) : "m" (c));
return crc;
}
#endif
return CRC32C_0[(crc ^ c) & 0xff] ^ (crc >> 8);
}
/**
* Compute the CRC-32 (Castagnoli) without the IV.
*/
static inline uint32_t crc32c_gen_plain(uint32_t crc, const unsigned char* ptr, unsigned size)
{
while (size >= 4) {
crc ^= ptr[0] | (uint32_t)ptr[1] << 8 | (uint32_t)ptr[2] << 16 | (uint32_t)ptr[3] << 24;
crc = CRC32C_3[crc & 0xff] ^ CRC32C_2[(crc >> 8) & 0xff] ^ CRC32C_1[(crc >> 16) & 0xff] ^ CRC32C_0[crc >> 24];
ptr += 4;
size -= 4;
}
while (size) {
crc = CRC32C_0[(crc ^ *ptr) & 0xff] ^ (crc >> 8);
++ptr;
--size;
}
return crc;
}
/**
* Compute the CRC-32 (Castagnoli) without the IV.
*/
#if HAVE_SSE42
static inline uint32_t crc32c_x86_plain(uint32_t crc, const unsigned char* ptr, unsigned size)
{
#ifdef CONFIG_X86_64
uint64_t crc64 = crc;
while (size >= 8) {
asm ("crc32q %1, %0\n" : "+r" (crc64) : "m" (*(const uint64_t*)ptr));
ptr += 8;
size -= 8;
}
crc = crc64;
#else
while (size >= 4) {
asm ("crc32l %1, %0\n" : "+r" (crc) : "m" (*(const uint32_t*)ptr));
ptr += 4;
size -= 4;
}
#endif
while (size) {
asm ("crc32b %1, %0\n" : "+r" (crc) : "m" (*ptr));
++ptr;
--size;
}
return crc;
}
#endif
/**
* Compute CRC-32 (Castagnoli) without the IV.
*/
static inline uint32_t crc32c_plain(uint32_t crc, const unsigned char* ptr, unsigned size)
{
#if HAVE_SSE42
if (tommy_likely(crc_x86)) {
return crc32c_x86_plain(crc, ptr, size);
}
#endif
return crc32c_gen_plain(crc, ptr, size);
}
/**
* Compute the CRC-32 (Castagnoli)
*/
extern uint32_t (*crc32c)(uint32_t crc, const unsigned char* ptr, unsigned size);
/**
* Internal entry points for testing.
*/
uint32_t crc32c_gen(uint32_t crc, const unsigned char* ptr, unsigned size);
uint32_t crc32c_x86(uint32_t crc, const unsigned char* ptr, unsigned size);
/**
* Initialize the CRC-32 (Castagnoli) support.
*/
void crc32c_init(void);
/****************************************************************************/
/* hash */
/**
* Size of the hash.
*/
#define HASH_MAX 16
/**
* Hash kinds.
*/
#define HASH_UNDEFINED 0
#define HASH_MURMUR3 1
#define HASH_SPOOKY2 2
#define HASH_METRO 3
/**
* Compute the HASH of a memory block.
* Seed is a 128 bit vector.
*/
void memhash(unsigned kind, const unsigned char* seed, void* digest, const void* src, size_t size);
/**
* Return the hash name.
*/
const char* hash_config_name(unsigned kind);
/**
* Count the number of different bits in the two buffers.
*/
unsigned memdiff(const unsigned char* data1, const unsigned char* data2, size_t size);
/****************************************************************************/
/* lock */
/**
* Create and locks the lock file.
* Return -1 on error, otherwise it's the file handle to pass to lock_unlock().
*/
int lock_lock(const char* file);
/**
* Unlock the lock file.
* Return -1 on error.
*/
int lock_unlock(int f);
/****************************************************************************/
/* bitvect */
typedef unsigned char bit_vect_t;
#define BIT_VECT_SIZE (sizeof(bit_vect_t) * 8)
static inline size_t bit_vect_size(size_t max)
{
return (max + BIT_VECT_SIZE - 1) / BIT_VECT_SIZE;
}
static inline void bit_vect_set(bit_vect_t* bit_vect, size_t off)
{
bit_vect_t mask = 1 << (off % BIT_VECT_SIZE);
bit_vect[off / BIT_VECT_SIZE] |= mask;
}
static inline void bit_vect_clear(bit_vect_t* bit_vect, size_t off)
{
bit_vect_t mask = 1 << (off % BIT_VECT_SIZE);
bit_vect[off / BIT_VECT_SIZE] &= ~mask;
}
static inline int bit_vect_test(bit_vect_t* bit_vect, size_t off)
{
bit_vect_t mask = 1 << (off % BIT_VECT_SIZE);
return (bit_vect[off / BIT_VECT_SIZE] & mask) != 0;
}
#endif
snapraid-12.1/configure.ac 0000664 0000000 0000000 00000025473 14166610522 0015542 0 ustar 00root root 0000000 0000000 dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
dnl Get version number from git
m4_define([git_revision], m4_esyscmd_s([./autover.sh]))
AC_INIT([snapraid], [git_revision], [], [], [http://www.snapraid.it])
AM_INIT_AUTOMAKE([foreign no-dependencies subdir-objects])
AC_CONFIG_SRCDIR([cmdline/snapraid.c])
AC_CONFIG_HEADERS([config.h])
AC_CANONICAL_HOST
dnl Checks for programs.
AC_PROG_CC
AC_USE_SYSTEM_EXTENSIONS
AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind],[])
AC_CHECK_PROG([WINE],[wine],[wine],[])
AC_CHECK_PROG([SDE],[sde],[sde],[])
AC_CHECK_PROG([ADVD2],[advd2],[advd2],[])
AM_CONDITIONAL(HAVE_ADVD2, [test x"$ADVD2" != x])
dnl Options to improve stacktrace
AC_CHECK_CC_OPT([-fno-omit-frame-pointer], CFLAGS="$CFLAGS -fno-omit-frame-pointer", [])
AC_CHECK_CC_OPT([-fno-inline-functions-called-once], CFLAGS="$CFLAGS -fno-inline-functions-called-once", [])
AC_CHECK_CC_OPT([-fno-inline-small-functions], CFLAGS="$CFLAGS -fno-inline-small-functions", [])
AC_CHECK_CC_OPT([-rdynamic], CFLAGS="$CFLAGS -rdynamic", [])
# This the new default for gcc 10
AC_CHECK_CC_OPT([-fno-common], CFLAGS="$CFLAGS -fno-common", [])
dnl Checks for system.
AC_SYS_LARGEFILE
dnl Checks for header files.
AC_HEADER_ASSERT
AC_HEADER_DIRENT
AC_HEADER_TIME
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h limits.h])
AC_CHECK_HEADERS([unistd.h getopt.h fnmatch.h io.h inttypes.h byteswap.h])
AC_CHECK_HEADERS([pthread.h math.h])
AC_CHECK_HEADERS([sys/file.h sys/ioctl.h sys/vfs.h sys/statfs.h sys/param.h sys/mount.h sys/sysmacros.h sys/mkdev.h])
AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h mach/mach_time.h execinfo.h])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_C_RESTRICT
AC_C_VOLATILE
AC_TYPE_SIZE_T
AC_TYPE_OFF_T
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_UINT8_T
AC_TYPE_INT8_T
AC_STRUCT_DIRENT_D_INO
AC_STRUCT_DIRENT_D_TYPE
AC_CHECK_MEMBERS([struct stat.st_nlink, struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec, struct stat.st_mtimespec.tv_nsec], [], [], [[
#if HAVE_SYS_TYPES_H
#include
#endif
#if HAVE_SYS_STAT_H
#include
#endif
#if HAVE_UNISTD_H
#include
#endif
]])
AC_CHECK_MEMBERS([struct statfs.f_type], [], [], [[
#if HAVE_SYS_PARAM_H
#include
#endif
#if HAVE_SYS_MOUNT_H
#include
#endif
#if HAVE_SYS_VFS_H
#include
#endif
#if HAVE_SYS_STATFS_H
#include
#endif
]])
AC_CHECK_MEMBERS([struct statfs.f_fstypename], [], [], [[
#if HAVE_SYS_PARAM_H
#include
#endif
#if HAVE_SYS_MOUNT_H
#include
#endif
#if HAVE_SYS_VFS_H
#include
#endif
#if HAVE_SYS_STATFS_H
#include
#endif
]])
dnl Checks for library functions.
AC_CHECK_FUNCS([memset strchr strerror strrchr mkdir gettimeofday strtoul])
AC_CHECK_FUNCS([getopt getopt_long snprintf vsnprintf sigaction])
AC_CHECK_FUNCS([ftruncate fallocate access])
AC_CHECK_FUNCS([fsync posix_fadvise sync_file_range])
AC_CHECK_FUNCS([getc_unlocked ferror_unlocked fnmatch])
AC_CHECK_FUNCS([futimes futimens futimesat localtime_r lutimes utimensat])
AC_CHECK_FUNCS([fstatat flock statfs])
AC_CHECK_FUNCS([mach_absolute_time])
AC_CHECK_FUNCS([backtrace backtrace_symbols])
AC_SEARCH_LIBS([clock_gettime], [rt])
AC_CHECK_FUNCS([clock_gettime])
AC_CHECK_CC_OPT([-pthread], CFLAGS="$CFLAGS -pthread", CFLAGS="$CFLAGS -D_REENTRANT")
AC_CHECK_FUNCS([pthread_create])
AC_SEARCH_LIBS([exp], [m])
dnl Checks for libblkid
AC_ARG_WITH([blkid],
AS_HELP_STRING([--without-blkid], [Ignore presence of blkid and disable it]))
AS_IF([test "x$with_blkid" != "xno"],
[AC_SEARCH_LIBS([blkid_probe_all], [blkid], [have_blkid=yes], [have_blkid=no])],
[have_blkid=no])
AS_IF([test "x$have_blkid" = "xyes"],
[
AC_CHECK_HEADERS([blkid/blkid.h])
AC_CHECK_FUNCS([blkid_devno_to_devname blkid_get_tag_value])
],
[AS_IF([test "x$with_blkid" = "xyes"],
[AC_MSG_ERROR([blkid requested but not found])
])
])
dnl Checks for architecture
AC_C_BIGENDIAN
dnl Checks for compiler
AC_CHECK_CC_OPT([-Wall], CFLAGS="$CFLAGS -Wall", [])
AC_CHECK_CC_OPT([-Wextra], CFLAGS="$CFLAGS -Wextra", [])
AC_CHECK_CC_OPT([-Wuninitialized], CFLAGS="$CFLAGS -Wuninitialized", [])
AC_CHECK_CC_OPT([-Wshadow], CFLAGS="$CFLAGS -Wshadow", [])
dnl Disable warning about zero-length-bounds raised by gcc 10 on linux kernel header on the fm_extents field
AC_CHECK_CC_OPT([-Wno-zero-length-bounds], CFLAGS="$CFLAGS -Wno-zero-length-bounds", [])
dnl Checks for asm
AC_ARG_ENABLE([asm],
AS_HELP_STRING([--disable-asm], [Disable inline assembly]))
AS_IF([test "x$enable_asm" != "xno"],
[AC_DEFINE([HAVE_ASSEMBLY], [1], [Define to 1 if inline assembly should be used.])]
dnl AS_IF(HAVE_ASSEMBLY) NOT closed here
dnl Checks for AS supporting the SSE2 instructions.
AC_MSG_CHECKING([for sse2])
asmsse2=no
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if defined(__i386__) || defined(__x86_64__)
void f(void)
{
asm volatile("pxor %xmm0,%xmm1");
}
#else
#error not x86
#endif
]])],
[AC_DEFINE([HAVE_SSE2], [1], [Define to 1 if sse2 is supported by the assembler.]) asmsse2=yes])
AC_MSG_RESULT([$asmsse2])
dnl Checks for AS supporting the SSSE3 instructions.
AC_MSG_CHECKING([for ssse3])
asmssse3=no
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if defined(__i386__) || defined(__x86_64__)
void f(void)
{
asm volatile("pshufb %xmm0,%xmm1");
}
#else
#error not x86
#endif
]])],
[AC_DEFINE([HAVE_SSSE3], [1], [Define to 1 if ssse3 is supported by the assembler.]) asmssse3=yes])
AC_MSG_RESULT([$asmssse3])
dnl Checks for AS supporting the SSE4.2 instructions.
AC_MSG_CHECKING([for sse42])
asmsse42=no
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if defined(__i386__) || defined(__x86_64__)
unsigned f(unsigned crc, unsigned char b)
{
asm volatile("crc32b %1, %0" : "+r" (crc) : "rm" (b));
return crc;
}
#else
#error not x86
#endif
]])],
[AC_DEFINE([HAVE_SSE42], [1], [Define to 1 if sse4.2 is supported by the assembler.]) asmsse42=yes])
AC_MSG_RESULT([$asmsse42])
dnl Checks for AS supporting the AVX2 instructions.
AC_MSG_CHECKING([for avx2])
asmavx2=no
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if defined(__i386__) || defined(__x86_64__)
void f(void* ptr)
{
asm volatile("vbroadcasti128 %0, %%ymm0" : : "m" (ptr));
}
#else
#error not x86
#endif
]])],
[AC_DEFINE([HAVE_AVX2], [1], [Define to 1 if avx2 is supported by the assembler.]) asmavx2=yes])
AC_MSG_RESULT([$asmavx2])
dnl AS_IF(HAVE_ASSEMBLY) closed here
)
dnl Checks for test environment
AS_CASE([$host],
[*-*-mingw*],
[
TESTENV="$WINE"
FAILENV="$TESTENV"
],
[POSIX=1]
)
AM_CONDITIONAL(HAVE_POSIX, [test x"$POSIX" != x])
AC_ARG_ENABLE([profiler],
[AS_HELP_STRING([--enable-profiler],[enable the use of gprof for code coverage])],
[
CFLAGS="-O2 -pg -g -pthread"
],
[])
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage],[enable the use of gcov for code coverage])],
[
CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage -pthread"
],
[])
AC_ARG_ENABLE([valgrind],
[AS_HELP_STRING([--enable-valgrind],[enable the use of valgrind in testing])],
[
TESTENV="$VALGRIND --leak-check=full --error-exitcode=1"
FAILENV="$VALGRIND --error-exitcode=1"
CFLAGS="$CFLAGS -DCHECKER"
MEMORY_CHECKER=1
],
[])
AC_ARG_ENABLE([sgcheck],
[AS_HELP_STRING([--enable-sgcheck],[enable the use of sgcheck in testing])],
[
TESTENV="$VALGRIND --tool=exp-sgcheck --suppressions=valgrind.supp --error-exitcode=1"
FAILENV="$TESTENV"
CFLAGS="$CFLAGS -DCHECKER"
MEMORY_CHECKER=1
],
[])
AC_ARG_ENABLE([helgrind],
[AS_HELP_STRING([--enable-helgrind],[enable the use of helgrind in testing])],
[
TESTENV="$VALGRIND --tool=helgrind --fair-sched=try --error-exitcode=1"
FAILENV="$TESTENV"
CFLAGS="$CFLAGS -DCHECKER"
THREAD_CHECKER=1
],
[])
AC_ARG_ENABLE([drd],
[AS_HELP_STRING([--enable-drd],[enable the use of drd in testing])],
[
TESTENV="$VALGRIND --tool=drd --fair-sched=try --error-exitcode=1"
FAILENV="$TESTENV"
CFLAGS="$CFLAGS -DCHECKER"
THREAD_CHECKER=1
],
[])
AC_ARG_ENABLE([asan],
[AS_HELP_STRING([--enable-asan],[enable the use of AddressSanitizer in testing])],
[
CFLAGS="$CFLAGS -DCHECKER -fsanitize=address"
MEMORY_CHECKER=1
],
[])
AC_ARG_ENABLE([msan],
[AS_HELP_STRING([--enable-msan],[enable the use of MemorySanitizer in testing])],
[
CFLAGS="$CFLAGS -DCHECKER -fsanitize=memory"
MEMORY_CHECKER=1
],
[])
AC_ARG_ENABLE([ubsan],
[AS_HELP_STRING([--enable-ubsan],[enable the use of UndefinedBehaviourSanitizer in testing])],
[
CFLAGS="$CFLAGS -DCHECKER -fsanitize=undefined"
MEMORY_CHECKER=1
],
[])
AC_ARG_ENABLE([tsan],
[AS_HELP_STRING([--enable-tsan],[enable the use of ThreadSanitizer in testing])],
[
CFLAGS="$CFLAGS -DCHECKER -fsanitize=thread"
dnl Use MEMORY_CHECKER instead of THREAD_CHECKER to run the full and long test
MEMORY_CHECKER=1
],
[])
AM_CONDITIONAL(HAVE_MEMORY_CHECKER, [test x"$MEMORY_CHECKER" != x])
AM_CONDITIONAL(HAVE_THREAD_CHECKER, [test x"$THREAD_CHECKER" != x])
AC_ARG_ENABLE([sde],
[AS_HELP_STRING([--enable-sde],[enable the use of SDE emulator in testing])],
dnl p4p -> Pentium4 Prescott with SSE2
dnl mrm -> Merom with SSSE3
dnl nhm -> Nehalem with SSE4.2
dnl hsw -> Haswell with AVX2
dnl knl -> Knights Landing with AVX512
[
TESTENV_SSE2="$SDE -p4p --"
TESTENV_SSSE3="$SDE -mrm --"
TESTENV_SSE42="$SDE -nhm --"
TESTENV_AVX2="$SDE -hsw --"
EMULATOR=1
# Target CPU compatible with P4 Prescott
CFLAGS="$CFLAGS -march=athlon64"
],
[])
AM_CONDITIONAL(HAVE_EMULATOR, [test x"$EMULATOR" != x])
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug],[enable debugging])],
[
CFLAGS="-Og -g -pthread -Wall -Wextra"
AC_CHECK_CC_OPT([-rdynamic], CFLAGS="$CFLAGS -rdynamic", [])
],
[])
AC_ARG_ENABLE([warning-as-error],
[AS_HELP_STRING([--enable-warning-as-error],[stop build on warnings])],
[
AC_CHECK_CC_OPT([-Werror], CFLAGS="$CFLAGS -Werror", [])
dnl This avoid the Darwin error: clang: error: argument unused during compilation: '-pthread'
dnl See: https://llvm.org/bugs/show_bug.cgi?id=7798
AC_CHECK_CC_OPT([-Wno-error=unused-command-line-argument], CFLAGS="$CFLAGS -Wno-error=unused-command-line-argument", [])
],
[])
AC_ARG_ENABLE([warning],
[AS_HELP_STRING([--enable-warning],[enable extra warning])],
[
AC_CHECK_CC_OPT([-Wpointer-arith], CFLAGS="$CFLAGS -Wpointer-arith", [])
AC_CHECK_CC_OPT([-Wcast-qual], CFLAGS="$CFLAGS -Wcast-qual", [])
AC_CHECK_CC_OPT([-Wunused], CFLAGS="$CFLAGS -Wunused", [])
AC_CHECK_CC_OPT([-Wunreachable-code], CFLAGS="$CFLAGS -Wunreachable-code", [])
AC_CHECK_CC_OPT([-Wpadded], CFLAGS="$CFLAGS -Wpadded", [])
AC_CHECK_CC_OPT([-Weverything], CFLAGS="$CFLAGS -Weverything", [])
],
[])
AC_ARG_VAR([TESTENV], [Test environment])
AC_ARG_VAR([FAILENV], [Test environment for failing tests])
AC_ARG_VAR([TESTENV_SSE2], [Test environment for SSE2])
AC_ARG_VAR([TESTENV_SSSE3], [Test environment for SSSE3])
AC_ARG_VAR([TESTENV_SSE42], [Test environment for SSE42])
AC_ARG_VAR([TESTENV_AVX2], [Test environment for AVX2])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
snapraid-12.1/configure.windows-x64 0000775 0000000 0000000 00000000113 14166610522 0017253 0 ustar 00root root 0000000 0000000 ./configure --host=x86_64-w64-mingw32.static --build=`./config.guess` $@
snapraid-12.1/configure.windows-x86 0000775 0000000 0000000 00000000111 14166610522 0017255 0 ustar 00root root 0000000 0000000 ./configure --host=i686-w64-mingw32.static --build=`./config.guess` $@
snapraid-12.1/makecov.sh 0000775 0000000 0000000 00000000276 14166610522 0015232 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# Run the Coverage test
#
make distclean
if ! ./configure --enable-coverage --enable-sde; then
exit 1
fi
if ! make lcov_reset check lcov_capture lcov_html; then
exit 1
fi
snapraid-12.1/makedist.sh 0000775 0000000 0000000 00000001042 14166610522 0015376 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
CHECK=check
DEBUG=
if test "x$1" = "x-f"; then
CHECK=all
fi
if test "x$1" = "x-d"; then
DEBUG=--enable-debug
fi
make distclean
# Reconfigure (with force) to get the latest revision from git
autoreconf -f
if ! ./configure.windows-x86 $DEBUG; then
exit 1
fi
if ! make distwindows-x86 distclean; then
exit 1
fi
if ! ./configure.windows-x64 $DEBUG; then
exit 1
fi
if ! make -j4 $CHECK; then
exit 1
fi
if ! make distwindows-x64 distclean; then
exit 1
fi
if ! ./configure ; then
exit 1
fi
if ! make dist; then
exit 1
fi
snapraid-12.1/makesan.sh 0000775 0000000 0000000 00000001760 14166610522 0015223 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# Run all the Sanitizers available
#
# Compiler to use
COMPILER=clang
# Options for configure
# --disable-asm
# Inline assembly is not supported by the Sanitizers
# --without-blkid
# External libraries are not supported by the Sanitizers
OPTIONS="--disable-asm --without-blkid"
# Source directory
SOURCE=`pwd`
# Dest directory
DEST=`mktemp -d`
make distclean
cd $DEST
# AddressSanitizer
if ! $SOURCE/configure --enable-asan $OPTIONS CC=$COMPILER; then
exit 1
fi
if ! make check distclean; then
exit 1
fi
# UndefinedBehaviourSanitizer
if ! $SOURCE/configure --enable-ubsan $OPTIONS CC=$COMPILER; then
exit 1
fi
if ! make check distclean; then
exit 1
fi
# MemorySanitizer
if ! $SOURCE/configure --enable-msan $OPTIONS CC=$COMPILER; then
exit 1
fi
if ! make check distclean; then
exit 1
fi
# ThreadSanitizer
if ! $SOURCE/configure --enable-tsan $OPTIONS CC=$COMPILER; then
exit 1
fi
if ! make check distclean; then
exit 1
fi
cd $SOURCE
if ! ./configure; then
exit 1
fi
snapraid-12.1/makescan.sh 0000775 0000000 0000000 00000000754 14166610522 0015370 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# Run the Coverity Scan static analyzer
#
rm -r cov-int
make distclean
# Reconfigure (with force) to get the latest revision from git
autoreconf -f
if ! ./configure ; then
exit 1
fi
export PATH=$PATH:contrib/cov-analysis-linux64-2020.09/bin
if ! cov-build --dir cov-int make; then
exit 1
fi
REVISION=`sh autover.sh`
tar czf snapraid-$REVISION.tgz cov-int
rm -r cov-int
echo snapraid-$REVISION.tgz ready to upload to https://scan.coverity.com/projects/1986/builds/new
snapraid-12.1/makesum.sh 0000775 0000000 0000000 00000000135 14166610522 0015241 0 ustar 00root root 0000000 0000000 #!/bin/sh
echo sha256 > CHECKSUMS
cd archive && sha256sum * | sort -k 2 -V >> ../CHECKSUMS
snapraid-12.1/maketest.sh 0000775 0000000 0000000 00000001346 14166610522 0015421 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
# Run all the Coverage and Valgrind tests
#
# Source directory
SOURCE=`pwd`
# Dest directory
DEST=`mktemp -d`
make distclean
cd $DEST
# Coverage
if ! $SOURCE/configure --enable-coverage --enable-sde; then
exit 1
fi
if ! make lcov_reset check lcov_capture lcov_html; then
exit 1
fi
cp -a cov $SOURCE/cov
if ! make distclean; then
exit 1
fi
# Valgrind
if ! $SOURCE/configure --enable-valgrind; then
exit 1
fi
if ! make check distclean; then
exit 1
fi
# Helgrind
if ! $SOURCE/configure --enable-helgrind; then
exit 1
fi
if ! make check distclean; then
exit 1
fi
# Drd
if ! $SOURCE/configure --enable-drd; then
exit 1
fi
if ! make check distclean; then
exit 1
fi
cd $SOURCE
if ! ./configure; then
exit 1
fi
snapraid-12.1/raid/ 0000775 0000000 0000000 00000000000 14166610522 0014160 5 ustar 00root root 0000000 0000000 snapraid-12.1/raid/COPYING 0000664 0000000 0000000 00000043076 14166610522 0015225 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C) 19yy
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
snapraid-12.1/raid/check.c 0000664 0000000 0000000 00000011422 14166610522 0015401 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2015 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "internal.h"
#include "combo.h"
#include "gf.h"
/**
* Validate the provided failed blocks.
*
* This function checks if the specified failed blocks satisfy the redundancy
* information using the data from the known valid parity blocks.
*
* It's similar at raid_check(), just with a different format for arguments.
*
* The number of failed blocks @nr must be strictly less than the number of
* parities @nv, because you need one more parity to validate the recovering.
*
* No data or parity blocks are modified.
*
* @nr Number of failed data blocks.
* @id[] Vector of @nr indexes of the failed data blocks.
* The indexes start from 0. They must be in order.
* @nv Number of valid parity blocks.
* @ip[] Vector of @nv indexes of the valid parity blocks.
* The indexes start from 0. They must be in order.
* @nd Number of data blocks.
* @size Size of the blocks pointed by @v. It must be a multiplier of 64.
* @v Vector of pointers to the blocks of data and parity.
* It has (@nd + @ip[@nv - 1] + 1) elements. The starting elements are the
* blocks for data, following with the parity blocks.
* Each block has @size bytes.
* @return 0 if the check is satisfied. -1 otherwise.
*/
static int raid_validate(int nr, int *id, int nv, int *ip, int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
const uint8_t *T[RAID_PARITY_MAX][RAID_PARITY_MAX];
uint8_t G[RAID_PARITY_MAX * RAID_PARITY_MAX];
uint8_t V[RAID_PARITY_MAX * RAID_PARITY_MAX];
size_t i;
int j, k, l;
BUG_ON(nr >= nv);
/* setup the coefficients matrix */
for (j = 0; j < nr; ++j)
for (k = 0; k < nr; ++k)
G[j * nr + k] = A(ip[j], id[k]);
/* invert it to solve the system of linear equations */
raid_invert(G, V, nr);
/* get multiplication tables */
for (j = 0; j < nr; ++j)
for (k = 0; k < nr; ++k)
T[j][k] = table(V[j * nr + k]);
/* check all positions */
for (i = 0; i < size; ++i) {
uint8_t p[RAID_PARITY_MAX];
/* get parity */
for (j = 0; j < nv; ++j)
p[j] = v[nd + ip[j]][i];
/* compute delta parity, skipping broken disks */
for (j = 0, k = 0; j < nd; ++j) {
uint8_t b;
/* skip broken disks */
if (k < nr && id[k] == j) {
++k;
continue;
}
b = v[j][i];
for (l = 0; l < nv; ++l)
p[l] ^= gfmul[b][gfgen[ip[l]][j]];
}
/* reconstruct data */
for (j = 0; j < nr; ++j) {
uint8_t b = 0;
int idj = id[j];
/* recompute the data */
for (k = 0; k < nr; ++k)
b ^= T[j][k][p[k]];
/* add the parity contribution of the reconstructed data */
for (l = nr; l < nv; ++l)
p[l] ^= gfmul[b][gfgen[ip[l]][idj]];
}
/* check that the final parity is 0 */
for (l = nr; l < nv; ++l)
if (p[l] != 0)
return -1;
}
return 0;
}
int raid_check(int nr, int *ir, int nd, int np, size_t size, void **v)
{
/* valid parity index */
int ip[RAID_PARITY_MAX];
int vp;
int rd;
int i, j;
/* enforce limit on size */
BUG_ON(size % 64 != 0);
/* enforce limit on number of failures */
BUG_ON(nr >= np); /* >= because we check with extra parity */
BUG_ON(np > RAID_PARITY_MAX);
/* enforce order in index vector */
BUG_ON(nr >= 2 && ir[0] >= ir[1]);
BUG_ON(nr >= 3 && ir[1] >= ir[2]);
BUG_ON(nr >= 4 && ir[2] >= ir[3]);
BUG_ON(nr >= 5 && ir[3] >= ir[4]);
BUG_ON(nr >= 6 && ir[4] >= ir[5]);
/* enforce limit on index vector */
BUG_ON(nr > 0 && ir[nr-1] >= nd + np);
/* count failed data disk */
rd = 0;
while (rd < nr && ir[rd] < nd)
++rd;
/* put valid parities into ip[] */
vp = 0;
for (i = rd, j = 0; j < np; ++j) {
/* if parity is failed */
if (i < nr && ir[i] == nd + j) {
/* skip broken parity */
++i;
} else {
/* store valid parity */
ip[vp] = j;
++vp;
}
}
return raid_validate(rd, ir, vp, ip, nd, size, v);
}
int raid_scan(int *ir, int nd, int np, size_t size, void **v)
{
int r;
/* check the special case of no failure */
if (np != 0 && raid_check(0, 0, nd, np, size, v) == 0)
return 0;
/* for each number of possible failures */
for (r = 1; r < np; ++r) {
/* try all combinations of r failures on n disks */
combination_first(r, nd + np, ir);
do {
/* verify if the combination is a valid one */
if (raid_check(r, ir, nd, np, size, v) == 0)
return r;
} while (combination_next(r, nd + np, ir));
}
/* no solution found */
return -1;
}
snapraid-12.1/raid/combo.h 0000664 0000000 0000000 00000006530 14166610522 0015434 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __RAID_COMBO_H
#define __RAID_COMBO_H
#include
/**
* Get the first permutation with repetition of r of n elements.
*
* Typical use is with permutation_next() in the form :
*
* int i[R];
* permutation_first(R, N, i);
* do {
* code using i[0], i[1], ..., i[R-1]
* } while (permutation_next(R, N, i));
*
* It's equivalent at the code :
*
* for(i[0]=0;i[0]= n) {
/* if we are at the first level, we have finished */
if (i == 0)
return 0;
/* increase the previous position */
--i;
goto recurse;
}
++i;
/* initialize all the next positions, if any */
while (i < r) {
c[i] = 0;
++i;
}
return 1;
}
/**
* Get the first combination without repetition of r of n elements.
*
* Typical use is with combination_next() in the form :
*
* int i[R];
* combination_first(R, N, i);
* do {
* code using i[0], i[1], ..., i[R-1]
* } while (combination_next(R, N, i));
*
* It's equivalent at the code :
*
* for(i[0]=0;i[0]= h) {
/* if we are at the first level, we have finished */
if (i == 0)
return 0;
/* increase the previous position */
--i;
--h;
goto recurse;
}
++i;
/* initialize all the next positions, if any */
while (i < r) {
/* each position start at the next value of the previous one */
c[i] = c[i - 1] + 1;
++i;
}
return 1;
}
#endif
snapraid-12.1/raid/cpu.h 0000664 0000000 0000000 00000022300 14166610522 0015115 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __RAID_CPU_H
#define __RAID_CPU_H
#ifdef CONFIG_X86
static inline void raid_cpuid(uint32_t func_eax, uint32_t sub_ecx, uint32_t *reg)
{
asm volatile (
#if defined(__i386__) && defined(__PIC__)
/* allow compilation in PIC mode saving ebx */
"xchgl %%ebx, %1\n"
"cpuid\n"
"xchgl %%ebx, %1\n"
: "=a" (reg[0]), "=r" (reg[1]), "=c" (reg[2]), "=d" (reg[3])
: "0" (func_eax), "2" (sub_ecx)
#else
"cpuid\n"
: "=a" (reg[0]), "=b" (reg[1]), "=c" (reg[2]), "=d" (reg[3])
: "0" (func_eax), "2" (sub_ecx)
#endif
);
}
static inline void raid_xgetbv(uint32_t* reg)
{
/* get the value of the Extended Control Register ecx=0 */
asm volatile (
/* uses a direct encoding of the XGETBV instruction as only recent */
/* assemblers support it. */
/* the next line is equivalent at: "xgetbv\n" */
".byte 0x0f, 0x01, 0xd0\n"
: "=a" (reg[0]), "=d" (reg[3])
: "c" (0)
);
}
#define CPU_VENDOR_MAX 13
static inline void raid_cpu_info(char *vendor, unsigned *family, unsigned *model)
{
uint32_t reg[4];
unsigned f, ef, m, em;
raid_cpuid(0, 0, reg);
((uint32_t*)vendor)[0] = reg[1];
((uint32_t*)vendor)[1] = reg[3];
((uint32_t*)vendor)[2] = reg[2];
vendor[12] = 0;
raid_cpuid(1, 0, reg);
f = (reg[0] >> 8) & 0xF;
ef = (reg[0] >> 20) & 0xFF;
m = (reg[0] >> 4) & 0xF;
em = (reg[0] >> 16) & 0xF;
if (strcmp(vendor, "AuthenticAMD") == 0) {
if (f < 15) {
*family = f;
*model = m;
} else {
*family = f + ef;
*model = m + (em << 4);
}
} else {
*family = f + ef;
*model = m + (em << 4);
}
}
static inline int raid_cpu_match_sse(uint32_t cpuid_1_ecx, uint32_t cpuid_1_edx)
{
uint32_t reg[4];
raid_cpuid(1, 0, reg);
if ((reg[2] & cpuid_1_ecx) != cpuid_1_ecx)
return 0;
if ((reg[3] & cpuid_1_edx) != cpuid_1_edx)
return 0;
return 1;
}
static inline int raid_cpu_match_avx(uint32_t cpuid_1_ecx, uint32_t cpuid_7_ebx, uint32_t xcr0)
{
uint32_t reg[4];
raid_cpuid(1, 0, reg);
if ((reg[2] & cpuid_1_ecx) != cpuid_1_ecx)
return 0;
raid_xgetbv(reg);
if ((reg[0] & xcr0) != xcr0)
return 0;
raid_cpuid(7, 0, reg);
if ((reg[1] & cpuid_7_ebx) != cpuid_7_ebx)
return 0;
return 1;
}
static inline int raid_cpu_has_sse2(void)
{
/*
* IntelŽ 64 and IA-32 Architectures Software Developer's Manual
* 325462-048US September 2013
*
* 11.6.2 Checking for SSE/SSE2 Support
* Before an application attempts to use the SSE and/or SSE2 extensions, it should check
* that they are present on the processor:
* 1. Check that the processor supports the CPUID instruction. Bit 21 of the EFLAGS
* register can be used to check processor's support the CPUID instruction.
* 2. Check that the processor supports the SSE and/or SSE2 extensions (true if
* CPUID.01H:EDX.SSE[bit 25] = 1 and/or CPUID.01H:EDX.SSE2[bit 26] = 1).
*/
return raid_cpu_match_sse(
0,
1 << 26); /* SSE2 */
}
static inline int raid_cpu_has_ssse3(void)
{
/*
* IntelŽ 64 and IA-32 Architectures Software Developer's Manual
* 325462-048US September 2013
*
* 12.7.2 Checking for SSSE3 Support
* Before an application attempts to use the SSSE3 extensions, the application should
* follow the steps illustrated in Section 11.6.2, "Checking for SSE/SSE2 Support."
* Next, use the additional step provided below:
* Check that the processor supports SSSE3 (if CPUID.01H:ECX.SSSE3[bit 9] = 1).
*/
return raid_cpu_match_sse(
1 << 9, /* SSSE3 */
1 << 26); /* SSE2 */
}
static inline int raid_cpu_has_crc32(void)
{
/*
* IntelŽ 64 and IA-32 Architectures Software Developer's Manual
* 325462-048US September 2013
*
* 12.12.3 Checking for SSE4.2 Support
* ...
* Before an application attempts to use the CRC32 instruction, it must check
* that the processor supports SSE4.2 (if CPUID.01H:ECX.SSE4_2[bit 20] = 1).
*/
return raid_cpu_match_sse(
1 << 20, /* CRC32 */
0);
}
static inline int raid_cpu_has_avx2(void)
{
/*
* Intel Architecture Instruction Set Extensions Programming Reference
* 319433-022 October 2014
*
* 14.3 Detection of AVX instructions
* 1) Detect CPUID.1:ECX.OSXSAVE[bit 27] = 1 (XGETBV enabled for application use1)
* 2) Issue XGETBV and verify that XCR0[2:1] = `11b' (XMM state and YMM state are enabled by OS).
* 3) detect CPUID.1:ECX.AVX[bit 28] = 1 (AVX instructions supported).
* (Step 3 can be done in any order relative to 1 and 2)
*
* 14.7.1 Detection of AVX2
* Hardware support for AVX2 is indicated by CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]=1.
* Application Software must identify that hardware supports AVX, after that it must
* also detect support for AVX2 by checking CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5].
*/
return raid_cpu_match_avx(
(1 << 27) | (1 << 28), /* OSXSAVE and AVX */
1 << 5, /* AVX2 */
3 << 1); /* OS saves XMM and YMM registers */
}
static inline int raid_cpu_has_avx512bw(void)
{
/*
* Intel Architecture Instruction Set Extensions Programming Reference
* 319433-022 October 2014
*
* 2.2 Detection of 512-bit Instruction Groups of Intel AVX-512 Family
* 1) Detect CPUID.1:ECX.OSXSAVE[bit 27] = 1 (XGETBV enabled for application use)
* 2) Execute XGETBV and verify that XCR0[7:5] = `111b' (OPMASK state, upper 256-bit of
* ZMM0-ZMM15 and ZMM16-ZMM31 state are enabled by OS) and that XCR0[2:1] = `11b'
* (XMM state and YMM state are enabled by OS).
* 3) Verify both CPUID.0x7.0:EBX.AVX512F[bit 16] = 1, CPUID.0x7.0:EBX.AVX512BW[bit 30] = 1.
*/
/* note that intentionally we don't check for AVX and AVX2 */
/* because the documentation doesn't require that */
return raid_cpu_match_avx(
1 << 27, /* XSAVE/XGETBV */
(1 << 16) | (1 << 30), /* AVX512F and AVX512BW */
(3 << 1) | (7 << 5)); /* OS saves XMM, YMM and ZMM registers */
}
/**
* Check if it's an Intel Atom CPU.
*/
static inline int raid_cpu_is_atom(unsigned family, unsigned model)
{
if (family != 6)
return 0;
/*
* x86 Architecture CPUID
* http://www.sandpile.org/x86/cpuid.htm
*
* Intel Atom
* 1C (28) Atom (45 nm) with 512 KB on-die L2
* 26 (38) Atom (45 nm) with 512 KB on-die L2
* 36 (54) Atom (32 nm) with 512 KB on-die L2
* 27 (39) Atom (32 nm) with 512 KB on-die L2
* 35 (53) Atom (?? nm) with ??? KB on-die L2
* 4A (74) Atom 2C (22 nm) 1 MB L2 + PowerVR (TGR)
* 5A (90) Atom 4C (22 nm) 2 MB L2 + PowerVR (ANN)
* 37 (55) Atom 4C (22 nm) 2 MB L2 + Intel Gen7 (BYT)
* 4C (76) Atom 4C (14 nm) 2 MB L2 + Intel Gen8 (BSW)
* 5D (93) Atom 4C (28 nm TSMC) 1 MB L2 + Mali (SoFIA)
* 4D (77) Atom 8C (22 nm) 4 MB L2 (AVN)
* ?? Atom ?C (14 nm) ? MB L2 (DVN)
*/
return model == 28 || model == 38 || model == 54
|| model == 39 || model == 53 || model == 74
|| model == 90 || model == 55 || model == 76
|| model == 93 || model == 77;
}
/**
* Check if the processor has a slow MULT implementation.
* If yes, it's better to use a hash not based on multiplication.
*/
static inline int raid_cpu_has_slowmult(void)
{
char vendor[CPU_VENDOR_MAX];
unsigned family;
unsigned model;
/*
* In some cases Murmur3 based on MUL instruction,
* is a LOT slower than Spooky2 based on SHIFTs.
*/
raid_cpu_info(vendor, &family, &model);
if (strcmp(vendor, "GenuineIntel") == 0) {
/*
* Intel Atom (Model 28)
* murmur3:378 MB/s, spooky2:3413 MB/s (x86)
*
* Intel Atom (Model 77)
* murmur3:1311 MB/s, spooky2:4056 MB/s (x64)
*/
if (raid_cpu_is_atom(family, model))
return 1;
}
return 0;
}
/**
* Check if the processor has a slow extended set of SSE registers.
* If yes, it's better to limit the unroll to the firsrt 8 registers.
*/
static inline int raid_cpu_has_slowextendedreg(void)
{
char vendor[CPU_VENDOR_MAX];
unsigned family;
unsigned model;
/*
* In some cases the PAR2 implementation using 16 SSE registers
* is a LITTLE slower than the one using only the first 8 registers.
* This doesn't happen for PARZ.
*/
raid_cpu_info(vendor, &family, &model);
if (strcmp(vendor, "AuthenticAMD") == 0) {
/*
* AMD Bulldozer
* par2_sse2:4922 MB/s, par2_sse2e:4465 MB/s
*/
if (family == 21)
return 1;
}
if (strcmp(vendor, "GenuineIntel") == 0) {
/*
* Intel Atom (Model 77)
* par2_sse2:5686 MB/s, par2_sse2e:5250 MB/s
* parz_sse2:3100 MB/s, parz_sse2e:3400 MB/s
* par3_sse3:1921 MB/s, par3_sse3e:1813 MB/s
* par4_sse3:1175 MB/s, par4_sse3e:1113 MB/s
* par5_sse3:876 MB/s, par5_sse3e:675 MB/s
* par6_sse3:705 MB/s, par6_sse3e:529 MB/s
*
* Intel Atom (Model 77) "Avoton C2750"
* par2_sse2:5661 MB/s, par2_sse2e:5382 MB/s
* parz_sse2:3110 MB/s, parz_sse2e:3450 MB/s
* par3_sse3:1769 MB/s, par3_sse3e:1856 MB/s
* par4_sse3:1221 MB/s, par4_sse3e:1141 MB/s
* par5_sse3:910 MB/s, par5_sse3e:675 MB/s
* par6_sse3:720 MB/s, par6_sse3e:534 MB/s
*/
if (raid_cpu_is_atom(family, model))
return 1;
}
return 0;
}
#endif
#endif
snapraid-12.1/raid/gf.h 0000664 0000000 0000000 00000005117 14166610522 0014731 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __RAID_GF_H
#define __RAID_GF_H
/*
* Galois field operations.
*
* Basic range checks are implemented using BUG_ON().
*/
/*
* GF a*b.
*/
static __always_inline uint8_t mul(uint8_t a, uint8_t b)
{
return gfmul[a][b];
}
/*
* GF 1/a.
* Not defined for a == 0.
*/
static __always_inline uint8_t inv(uint8_t v)
{
BUG_ON(v == 0); /* division by zero */
return gfinv[v];
}
/*
* GF 2^a.
*/
static __always_inline uint8_t pow2(int v)
{
BUG_ON(v < 0 || v > 254); /* invalid exponent */
return gfexp[v];
}
/*
* Gets the multiplication table for a specified value.
*/
static __always_inline const uint8_t *table(uint8_t v)
{
return gfmul[v];
}
/*
* Gets the generator matrix coefficient for parity 'p' and disk 'd'.
*/
static __always_inline uint8_t A(int p, int d)
{
return gfgen[p][d];
}
/*
* Dereference as uint8_t
*/
#define v_8(p) (*(uint8_t *)&(p))
/*
* Dereference as uint32_t
*/
#define v_32(p) (*(uint32_t *)&(p))
/*
* Dereference as uint64_t
*/
#define v_64(p) (*(uint64_t *)&(p))
/*
* Multiply each byte of a uint32 by 2 in the GF(2^8).
*/
static __always_inline uint32_t x2_32(uint32_t v)
{
uint32_t mask = v & 0x80808080U;
mask = (mask << 1) - (mask >> 7);
v = (v << 1) & 0xfefefefeU;
v ^= mask & 0x1d1d1d1dU;
return v;
}
/*
* Multiply each byte of a uint64 by 2 in the GF(2^8).
*/
static __always_inline uint64_t x2_64(uint64_t v)
{
uint64_t mask = v & 0x8080808080808080ULL;
mask = (mask << 1) - (mask >> 7);
v = (v << 1) & 0xfefefefefefefefeULL;
v ^= mask & 0x1d1d1d1d1d1d1d1dULL;
return v;
}
/*
* Divide each byte of a uint32 by 2 in the GF(2^8).
*/
static __always_inline uint32_t d2_32(uint32_t v)
{
uint32_t mask = v & 0x01010101U;
mask = (mask << 8) - mask;
v = (v >> 1) & 0x7f7f7f7fU;
v ^= mask & 0x8e8e8e8eU;
return v;
}
/*
* Divide each byte of a uint64 by 2 in the GF(2^8).
*/
static __always_inline uint64_t d2_64(uint64_t v)
{
uint64_t mask = v & 0x0101010101010101ULL;
mask = (mask << 8) - mask;
v = (v >> 1) & 0x7f7f7f7f7f7f7f7fULL;
v ^= mask & 0x8e8e8e8e8e8e8e8eULL;
return v;
}
#endif
snapraid-12.1/raid/helper.c 0000664 0000000 0000000 00000003564 14166610522 0015613 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "internal.h"
#define RAID_SWAP(a, b) \
do { \
if (v[a] > v[b]) { \
int t = v[a]; \
v[a] = v[b]; \
v[b] = t; \
} \
} while (0)
void raid_sort(int n, int *v)
{
/* sorting networks generated with Batcher's Merge-Exchange */
switch (n) {
case 2:
RAID_SWAP(0, 1);
break;
case 3:
RAID_SWAP(0, 2);
RAID_SWAP(0, 1);
RAID_SWAP(1, 2);
break;
case 4:
RAID_SWAP(0, 2);
RAID_SWAP(1, 3);
RAID_SWAP(0, 1);
RAID_SWAP(2, 3);
RAID_SWAP(1, 2);
break;
case 5:
RAID_SWAP(0, 4);
RAID_SWAP(0, 2);
RAID_SWAP(1, 3);
RAID_SWAP(2, 4);
RAID_SWAP(0, 1);
RAID_SWAP(2, 3);
RAID_SWAP(1, 4);
RAID_SWAP(1, 2);
RAID_SWAP(3, 4);
break;
case 6:
RAID_SWAP(0, 4);
RAID_SWAP(1, 5);
RAID_SWAP(0, 2);
RAID_SWAP(1, 3);
RAID_SWAP(2, 4);
RAID_SWAP(3, 5);
RAID_SWAP(0, 1);
RAID_SWAP(2, 3);
RAID_SWAP(4, 5);
RAID_SWAP(1, 4);
RAID_SWAP(1, 2);
RAID_SWAP(3, 4);
break;
}
}
void raid_insert(int n, int *v, int i)
{
/* we don't use binary search because this is intended */
/* for very small vectors and we want to optimize the case */
/* of elements inserted already in order */
/* insert at the end */
v[n] = i;
/* swap until in the correct position */
while (n > 0 && v[n - 1] > v[n]) {
/* swap */
int t = v[n - 1];
v[n - 1] = v[n];
v[n] = t;
/* previous position */
--n;
}
}
snapraid-12.1/raid/helper.h 0000664 0000000 0000000 00000002345 14166610522 0015614 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __RAID_HELPER_H
#define __RAID_HELPER_H
/**
* Inserts an integer in a sorted vector.
*
* This function can be used to insert indexes in order, ready to be used for
* calling raid_rec().
*
* @n Number of integers currently in the vector.
* @v Vector of integers already sorted.
* It must have extra space for the new element at the end.
* @i Value to insert.
*/
void raid_insert(int n, int *v, int i);
/**
* Sorts a small vector of integers.
*
* If you have indexes not in order, you can use this function to sort them
* before calling raid_rec().
*
* @n Number of integers. No more than RAID_PARITY_MAX.
* @v Vector of integers.
*/
void raid_sort(int n, int *v);
#endif
snapraid-12.1/raid/int.c 0000664 0000000 0000000 00000025225 14166610522 0015124 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "internal.h"
#include "gf.h"
/*
* GEN1 (RAID5 with xor) 32bit C implementation
*/
void raid_gen1_int32(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
int d, l;
size_t i;
uint32_t p0;
uint32_t p1;
l = nd - 1;
p = v[nd];
for (i = 0; i < size; i += 8) {
p0 = v_32(v[l][i]);
p1 = v_32(v[l][i + 4]);
for (d = l - 1; d >= 0; --d) {
p0 ^= v_32(v[d][i]);
p1 ^= v_32(v[d][i + 4]);
}
v_32(p[i]) = p0;
v_32(p[i + 4]) = p1;
}
}
/*
* GEN1 (RAID5 with xor) 64bit C implementation
*/
void raid_gen1_int64(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
int d, l;
size_t i;
uint64_t p0;
uint64_t p1;
l = nd - 1;
p = v[nd];
for (i = 0; i < size; i += 16) {
p0 = v_64(v[l][i]);
p1 = v_64(v[l][i + 8]);
for (d = l - 1; d >= 0; --d) {
p0 ^= v_64(v[d][i]);
p1 ^= v_64(v[d][i + 8]);
}
v_64(p[i]) = p0;
v_64(p[i + 8]) = p1;
}
}
/*
* GEN2 (RAID6 with powers of 2) 32bit C implementation
*/
void raid_gen2_int32(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *q;
int d, l;
size_t i;
uint32_t d0, q0, p0;
uint32_t d1, q1, p1;
l = nd - 1;
p = v[nd];
q = v[nd + 1];
for (i = 0; i < size; i += 8) {
q0 = p0 = v_32(v[l][i]);
q1 = p1 = v_32(v[l][i + 4]);
for (d = l - 1; d >= 0; --d) {
d0 = v_32(v[d][i]);
d1 = v_32(v[d][i + 4]);
p0 ^= d0;
p1 ^= d1;
q0 = x2_32(q0);
q1 = x2_32(q1);
q0 ^= d0;
q1 ^= d1;
}
v_32(p[i]) = p0;
v_32(p[i + 4]) = p1;
v_32(q[i]) = q0;
v_32(q[i + 4]) = q1;
}
}
/*
* GEN2 (RAID6 with powers of 2) 64bit C implementation
*/
void raid_gen2_int64(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *q;
int d, l;
size_t i;
uint64_t d0, q0, p0;
uint64_t d1, q1, p1;
l = nd - 1;
p = v[nd];
q = v[nd + 1];
for (i = 0; i < size; i += 16) {
q0 = p0 = v_64(v[l][i]);
q1 = p1 = v_64(v[l][i + 8]);
for (d = l - 1; d >= 0; --d) {
d0 = v_64(v[d][i]);
d1 = v_64(v[d][i + 8]);
p0 ^= d0;
p1 ^= d1;
q0 = x2_64(q0);
q1 = x2_64(q1);
q0 ^= d0;
q1 ^= d1;
}
v_64(p[i]) = p0;
v_64(p[i + 8]) = p1;
v_64(q[i]) = q0;
v_64(q[i + 8]) = q1;
}
}
/*
* GEN3 (triple parity with Cauchy matrix) 8bit C implementation
*
* Note that instead of a generic multiplication table, likely resulting
* in multiple cache misses, a precomputed table could be used.
* But this is only a kind of reference function, and we are not really
* interested in speed.
*/
void raid_gen3_int8(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *q;
uint8_t *r;
int d, l;
size_t i;
uint8_t d0, r0, q0, p0;
l = nd - 1;
p = v[nd];
q = v[nd + 1];
r = v[nd + 2];
for (i = 0; i < size; i += 1) {
p0 = q0 = r0 = 0;
for (d = l; d > 0; --d) {
d0 = v_8(v[d][i]);
p0 ^= d0;
q0 ^= gfmul[d0][gfgen[1][d]];
r0 ^= gfmul[d0][gfgen[2][d]];
}
/* first disk with all coefficients at 1 */
d0 = v_8(v[0][i]);
p0 ^= d0;
q0 ^= d0;
r0 ^= d0;
v_8(p[i]) = p0;
v_8(q[i]) = q0;
v_8(r[i]) = r0;
}
}
/*
* GEN4 (quad parity with Cauchy matrix) 8bit C implementation
*
* Note that instead of a generic multiplication table, likely resulting
* in multiple cache misses, a precomputed table could be used.
* But this is only a kind of reference function, and we are not really
* interested in speed.
*/
void raid_gen4_int8(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *q;
uint8_t *r;
uint8_t *s;
int d, l;
size_t i;
uint8_t d0, s0, r0, q0, p0;
l = nd - 1;
p = v[nd];
q = v[nd + 1];
r = v[nd + 2];
s = v[nd + 3];
for (i = 0; i < size; i += 1) {
p0 = q0 = r0 = s0 = 0;
for (d = l; d > 0; --d) {
d0 = v_8(v[d][i]);
p0 ^= d0;
q0 ^= gfmul[d0][gfgen[1][d]];
r0 ^= gfmul[d0][gfgen[2][d]];
s0 ^= gfmul[d0][gfgen[3][d]];
}
/* first disk with all coefficients at 1 */
d0 = v_8(v[0][i]);
p0 ^= d0;
q0 ^= d0;
r0 ^= d0;
s0 ^= d0;
v_8(p[i]) = p0;
v_8(q[i]) = q0;
v_8(r[i]) = r0;
v_8(s[i]) = s0;
}
}
/*
* GEN5 (penta parity with Cauchy matrix) 8bit C implementation
*
* Note that instead of a generic multiplication table, likely resulting
* in multiple cache misses, a precomputed table could be used.
* But this is only a kind of reference function, and we are not really
* interested in speed.
*/
void raid_gen5_int8(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *q;
uint8_t *r;
uint8_t *s;
uint8_t *t;
int d, l;
size_t i;
uint8_t d0, t0, s0, r0, q0, p0;
l = nd - 1;
p = v[nd];
q = v[nd + 1];
r = v[nd + 2];
s = v[nd + 3];
t = v[nd + 4];
for (i = 0; i < size; i += 1) {
p0 = q0 = r0 = s0 = t0 = 0;
for (d = l; d > 0; --d) {
d0 = v_8(v[d][i]);
p0 ^= d0;
q0 ^= gfmul[d0][gfgen[1][d]];
r0 ^= gfmul[d0][gfgen[2][d]];
s0 ^= gfmul[d0][gfgen[3][d]];
t0 ^= gfmul[d0][gfgen[4][d]];
}
/* first disk with all coefficients at 1 */
d0 = v_8(v[0][i]);
p0 ^= d0;
q0 ^= d0;
r0 ^= d0;
s0 ^= d0;
t0 ^= d0;
v_8(p[i]) = p0;
v_8(q[i]) = q0;
v_8(r[i]) = r0;
v_8(s[i]) = s0;
v_8(t[i]) = t0;
}
}
/*
* GEN6 (hexa parity with Cauchy matrix) 8bit C implementation
*
* Note that instead of a generic multiplication table, likely resulting
* in multiple cache misses, a precomputed table could be used.
* But this is only a kind of reference function, and we are not really
* interested in speed.
*/
void raid_gen6_int8(int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *q;
uint8_t *r;
uint8_t *s;
uint8_t *t;
uint8_t *u;
int d, l;
size_t i;
uint8_t d0, u0, t0, s0, r0, q0, p0;
l = nd - 1;
p = v[nd];
q = v[nd + 1];
r = v[nd + 2];
s = v[nd + 3];
t = v[nd + 4];
u = v[nd + 5];
for (i = 0; i < size; i += 1) {
p0 = q0 = r0 = s0 = t0 = u0 = 0;
for (d = l; d > 0; --d) {
d0 = v_8(v[d][i]);
p0 ^= d0;
q0 ^= gfmul[d0][gfgen[1][d]];
r0 ^= gfmul[d0][gfgen[2][d]];
s0 ^= gfmul[d0][gfgen[3][d]];
t0 ^= gfmul[d0][gfgen[4][d]];
u0 ^= gfmul[d0][gfgen[5][d]];
}
/* first disk with all coefficients at 1 */
d0 = v_8(v[0][i]);
p0 ^= d0;
q0 ^= d0;
r0 ^= d0;
s0 ^= d0;
t0 ^= d0;
u0 ^= d0;
v_8(p[i]) = p0;
v_8(q[i]) = q0;
v_8(r[i]) = r0;
v_8(s[i]) = s0;
v_8(t[i]) = t0;
v_8(u[i]) = u0;
}
}
/*
* Recover failure of one data block at index id[0] using parity at index
* ip[0] for any RAID level.
*
* Starting from the equation:
*
* Pd = A[ip[0],id[0]] * Dx
*
* and solving we get:
*
* Dx = A[ip[0],id[0]]^-1 * Pd
*/
void raid_rec1_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *pa;
const uint8_t *T;
uint8_t G;
uint8_t V;
size_t i;
(void)nr; /* unused, it's always 1 */
/* if it's RAID5 uses the faster function */
if (ip[0] == 0) {
raid_rec1of1(id, nd, size, vv);
return;
}
/* setup the coefficients matrix */
G = A(ip[0], id[0]);
/* invert it to solve the system of linear equations */
V = inv(G);
/* get multiplication tables */
T = table(V);
/* compute delta parity */
raid_delta_gen(1, id, ip, nd, size, vv);
p = v[nd + ip[0]];
pa = v[id[0]];
for (i = 0; i < size; ++i) {
/* delta */
uint8_t Pd = p[i] ^ pa[i];
/* reconstruct */
pa[i] = T[Pd];
}
}
/*
* Recover failure of two data blocks at indexes id[0],id[1] using parity at
* indexes ip[0],ip[1] for any RAID level.
*
* Starting from the equations:
*
* Pd = A[ip[0],id[0]] * Dx + A[ip[0],id[1]] * Dy
* Qd = A[ip[1],id[0]] * Dx + A[ip[1],id[1]] * Dy
*
* we solve inverting the coefficients matrix.
*/
void raid_rec2_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
uint8_t *p;
uint8_t *pa;
uint8_t *q;
uint8_t *qa;
const int N = 2;
const uint8_t *T[N][N];
uint8_t G[N * N];
uint8_t V[N * N];
size_t i;
int j, k;
(void)nr; /* unused, it's always 2 */
/* if it's RAID6 recovering with P and Q uses the faster function */
if (ip[0] == 0 && ip[1] == 1) {
raid_rec2of2_int8(id, ip, nd, size, vv);
return;
}
/* setup the coefficients matrix */
for (j = 0; j < N; ++j)
for (k = 0; k < N; ++k)
G[j * N + k] = A(ip[j], id[k]);
/* invert it to solve the system of linear equations */
raid_invert(G, V, N);
/* get multiplication tables */
for (j = 0; j < N; ++j)
for (k = 0; k < N; ++k)
T[j][k] = table(V[j * N + k]);
/* compute delta parity */
raid_delta_gen(2, id, ip, nd, size, vv);
p = v[nd + ip[0]];
q = v[nd + ip[1]];
pa = v[id[0]];
qa = v[id[1]];
for (i = 0; i < size; ++i) {
/* delta */
uint8_t Pd = p[i] ^ pa[i];
uint8_t Qd = q[i] ^ qa[i];
/* reconstruct */
pa[i] = T[0][0][Pd] ^ T[0][1][Qd];
qa[i] = T[1][0][Pd] ^ T[1][1][Qd];
}
}
/*
* Recover failure of N data blocks at indexes id[N] using parity at indexes
* ip[N] for any RAID level.
*
* Starting from the N equations, with 0<=i