mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-23 23:33:15 +03:00
Compare commits
645 Commits
dev-dct-cm
...
old-beta1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6556dce8b | ||
|
|
aa8d8bc8b5 | ||
|
|
0800dcfdc4 | ||
|
|
12b0101e94 | ||
|
|
021b391a02 | ||
|
|
d11e2b6057 | ||
|
|
264d90e7e5 | ||
|
|
f9f3da9e78 | ||
|
|
6435b49153 | ||
|
|
f9aa2941cf | ||
|
|
5a933d4bee | ||
|
|
5536aea0df | ||
|
|
976666d216 | ||
|
|
9a5bcd4392 | ||
|
|
812060a118 | ||
|
|
089b5052e6 | ||
|
|
874e5d72f4 | ||
|
|
6b11de1329 | ||
|
|
2245a7ad8d | ||
|
|
ee5ec1b870 | ||
|
|
74ccfe851b | ||
|
|
e1c24bd5a2 | ||
|
|
1349b79728 | ||
|
|
d294d7604c | ||
|
|
01df2cf464 | ||
|
|
45b7322488 | ||
|
|
b3055e992f | ||
|
|
3da548565c | ||
|
|
a975e85548 | ||
|
|
c4b5ade752 | ||
|
|
a4b61b0794 | ||
|
|
f6011184b8 | ||
|
|
74de118c6e | ||
|
|
9c25e77c17 | ||
|
|
440113e67e | ||
|
|
a11b60c445 | ||
|
|
a6052681ad | ||
|
|
482d50536a | ||
|
|
25c79a4fcd | ||
|
|
02946144ac | ||
|
|
71a360e9a3 | ||
|
|
f7912d88b1 | ||
|
|
975101d7d2 | ||
|
|
ef58c5ff55 | ||
|
|
e10221804a | ||
|
|
284ed9ee0e | ||
|
|
5d7b961997 | ||
|
|
c699a5c4b4 | ||
|
|
1f4ceb89cf | ||
|
|
0934ca0040 | ||
|
|
4738f892c2 | ||
|
|
33004fcf33 | ||
|
|
34d214c166 | ||
|
|
a56cd92a1e | ||
|
|
7251348507 | ||
|
|
01cd5c84d6 | ||
|
|
e210599fa6 | ||
|
|
dbe7cee7e9 | ||
|
|
7370d88ceb | ||
|
|
34458e0c57 | ||
|
|
05c8c3abf2 | ||
|
|
e9d464b4d3 | ||
|
|
6968c3ab9b | ||
|
|
131a8a9650 | ||
|
|
379ecbf9a9 | ||
|
|
7b9dfa9a28 | ||
|
|
fbd0f5eed2 | ||
|
|
5970f904ae | ||
|
|
8cbcb2868d | ||
|
|
8ff2a4b026 | ||
|
|
ae14d205a5 | ||
|
|
ec5d560ec5 | ||
|
|
fb543b53c0 | ||
|
|
39c16422e2 | ||
|
|
4d5b273ebe | ||
|
|
ed6a860fad | ||
|
|
423e579292 | ||
|
|
ee11aa9e75 | ||
|
|
c46d20fa92 | ||
|
|
dc8d17574c | ||
|
|
5c17cd04c8 | ||
|
|
67ada02076 | ||
|
|
32d94c2eaf | ||
|
|
687b39a12a | ||
|
|
705af407bf | ||
|
|
080052da2e | ||
|
|
ef735fd92a | ||
|
|
17c16dcafc | ||
|
|
a89b3018fb | ||
|
|
851e2ebd32 | ||
|
|
1225ce7fe8 | ||
|
|
7e77a31b96 | ||
|
|
d146b9002f | ||
|
|
1f9d567b23 | ||
|
|
ed1b3a023c | ||
|
|
1ca18f501a | ||
|
|
49588ccd98 | ||
|
|
098dedc092 | ||
|
|
b06f6b9545 | ||
|
|
ba3cb94999 | ||
|
|
74c67fbf4b | ||
|
|
32b46e4910 | ||
|
|
ecf5539ed2 | ||
|
|
ac9db4e4d5 | ||
|
|
0eb96094b0 | ||
|
|
30b3ac7dc5 | ||
|
|
0092790c7d | ||
|
|
28909d8a51 | ||
|
|
b20cfbb7b6 | ||
|
|
97639bd0a8 | ||
|
|
161ec73c96 | ||
|
|
957d6bea15 | ||
|
|
f8b6e5b414 | ||
|
|
1ab450870e | ||
|
|
de45f4884c | ||
|
|
5da1f3e7c8 | ||
|
|
983014952b | ||
|
|
55298019a3 | ||
|
|
a1ffc3f271 | ||
|
|
2fb60aa997 | ||
|
|
f5ec76537a | ||
|
|
728491fd2b | ||
|
|
d9d3f2b9e4 | ||
|
|
3fe4864f65 | ||
|
|
0b156f22a4 | ||
|
|
35b1d93813 | ||
|
|
d770851ac0 | ||
|
|
989e7b1033 | ||
|
|
c4e0eb7b49 | ||
|
|
71f5d0dac7 | ||
|
|
561b0c4381 | ||
|
|
995fbc7330 | ||
|
|
10ab8949c4 | ||
|
|
c441202fea | ||
|
|
ca261b0bee | ||
|
|
52f3709f67 | ||
|
|
c2ca6187fe | ||
|
|
671a13d295 | ||
|
|
14c3e2eccf | ||
|
|
08e5b852c2 | ||
|
|
1c9606c824 | ||
|
|
3cd47b5c9b | ||
|
|
aedc729087 | ||
|
|
5f7cfa3fa9 | ||
|
|
0083f30af5 | ||
|
|
4a06f05ef5 | ||
|
|
8f37cadce8 | ||
|
|
a11603ca6c | ||
|
|
86274842e9 | ||
|
|
cf9c955a44 | ||
|
|
55d828c35f | ||
|
|
cdff28aca6 | ||
|
|
b9da39274f | ||
|
|
c379aa5782 | ||
|
|
9dcabac9dd | ||
|
|
794f3a2b9f | ||
|
|
2066121b7c | ||
|
|
0c4067f143 | ||
|
|
8aa69243b7 | ||
|
|
8697263bde | ||
|
|
ea25c4f65c | ||
|
|
82ac3ebd7e | ||
|
|
13cb94909c | ||
|
|
b57ca2a763 | ||
|
|
83c49e9745 | ||
|
|
e15771d78d | ||
|
|
6edc4920ba | ||
|
|
302bb1bd93 | ||
|
|
529b1bceee | ||
|
|
42cd47d32e | ||
|
|
711d884c2e | ||
|
|
183d1c4674 | ||
|
|
faed63a0bb | ||
|
|
53bff262f8 | ||
|
|
3251a708e4 | ||
|
|
b5dbdbf7b2 | ||
|
|
a9649e92c9 | ||
|
|
b561f1fa8b | ||
|
|
cecd7491b5 | ||
|
|
55a66322b5 | ||
|
|
155c31a2d7 | ||
|
|
a89ce91089 | ||
|
|
b897fe6700 | ||
|
|
548a351b06 | ||
|
|
4b1da57ca1 | ||
|
|
529aec7f25 | ||
|
|
1661e545cb | ||
|
|
ec71d08878 | ||
|
|
ac258b7dd7 | ||
|
|
0f57876233 | ||
|
|
1d25a3693d | ||
|
|
45fa428bf1 | ||
|
|
177fa80f1a | ||
|
|
3261261bfe | ||
|
|
d4de7934f8 | ||
|
|
c3475af809 | ||
|
|
b12f707812 | ||
|
|
22c0c34d60 | ||
|
|
a60b66f230 | ||
|
|
83f6e93628 | ||
|
|
222b5f0229 | ||
|
|
30aa383e26 | ||
|
|
676b401294 | ||
|
|
ebf57159de | ||
|
|
199d2aafec | ||
|
|
81952f56fd | ||
|
|
c5bac82b43 | ||
|
|
081b86109c | ||
|
|
8ac2028a75 | ||
|
|
264fed1c9f | ||
|
|
dd59f7b2c7 | ||
|
|
9245a760db | ||
|
|
b61b32bbc3 | ||
|
|
09b3914f5d | ||
|
|
fe644e4c9e | ||
|
|
7b09bf2156 | ||
|
|
987d0aae66 | ||
|
|
9cbcbc1c22 | ||
|
|
cfc4e2bc60 | ||
|
|
a03405fa81 | ||
|
|
d5c9ccbe6e | ||
|
|
e52772d65f | ||
|
|
1e3259e728 | ||
|
|
e905a20a60 | ||
|
|
88e2be7a33 | ||
|
|
8939131600 | ||
|
|
ba37ebff8b | ||
|
|
28f4cb7e07 | ||
|
|
db1e7102cd | ||
|
|
07eb7a5830 | ||
|
|
b7c6c685fa | ||
|
|
212134df70 | ||
|
|
6eeb5528f5 | ||
|
|
54fad845c9 | ||
|
|
d6c0de6fc7 | ||
|
|
649c8649f7 | ||
|
|
da2f53d1b1 | ||
|
|
405139e3b8 | ||
|
|
4f8d347171 | ||
|
|
bf0db4876c | ||
|
|
47a14884d6 | ||
|
|
3a7bbc8b08 | ||
|
|
1b1d65372c | ||
|
|
fd2faaa16e | ||
|
|
0609cdb9ea | ||
|
|
d3bb140f89 | ||
|
|
b31dc66628 | ||
|
|
09476171a6 | ||
|
|
33dee813b5 | ||
|
|
bb4e73c40b | ||
|
|
b1f23ffa94 | ||
|
|
b0e8cec1e7 | ||
|
|
5077ae19bc | ||
|
|
0d8447bf59 | ||
|
|
c6cf08a274 | ||
|
|
dc49ae519e | ||
|
|
904539476a | ||
|
|
3fbf02dc82 | ||
|
|
c9392a840d | ||
|
|
d164e8ab72 | ||
|
|
6dc62c9fb6 | ||
|
|
87a9684d66 | ||
|
|
94525e2f44 | ||
|
|
b408b1b3b9 | ||
|
|
27c2f09e32 | ||
|
|
19bc4d3349 | ||
|
|
f2b6c424d6 | ||
|
|
a49d4453e9 | ||
|
|
65e50087b9 | ||
|
|
2d90f759d9 | ||
|
|
4230ac7674 | ||
|
|
d96e9182e9 | ||
|
|
68c87b9616 | ||
|
|
7f8e9a0b6d | ||
|
|
81a229f2a5 | ||
|
|
8be7ae2733 | ||
|
|
846bca4cb1 | ||
|
|
f36f353789 | ||
|
|
939a2731ed | ||
|
|
835dab97ff | ||
|
|
fa904b53be | ||
|
|
0ec52dddce | ||
|
|
c289355a3a | ||
|
|
02a13a5a18 | ||
|
|
6cf2a0281b | ||
|
|
120d35f9af | ||
|
|
2b15d5e7b3 | ||
|
|
fc167bd3f0 | ||
|
|
91b04abf05 | ||
|
|
77faac8740 | ||
|
|
43b3d54855 | ||
|
|
69e9b85700 | ||
|
|
0b6d132759 | ||
|
|
7c233c6c0c | ||
|
|
c35b290fa4 | ||
|
|
3d95cfb367 | ||
|
|
b90fc3a56e | ||
|
|
1ef3fdccf5 | ||
|
|
02b7f77bd8 | ||
|
|
0ac7ead922 | ||
|
|
da9d0e03ce | ||
|
|
120f65f672 | ||
|
|
200a14caa4 | ||
|
|
35bf6da8e2 | ||
|
|
f08f70276c | ||
|
|
1ae50fd95b | ||
|
|
40512beb47 | ||
|
|
0d7f9b2c94 | ||
|
|
52f42140a7 | ||
|
|
3f6c50297f | ||
|
|
f72d80afc5 | ||
|
|
7c5cb13b22 | ||
|
|
d728750eb2 | ||
|
|
02a70e5667 | ||
|
|
44e51ea5fa | ||
|
|
87e201460a | ||
|
|
039bd945e2 | ||
|
|
e9e52d2b4b | ||
|
|
2bf92e7399 | ||
|
|
5b0df241f0 | ||
|
|
76f5b05eff | ||
|
|
40fb6c998f | ||
|
|
33f50a342d | ||
|
|
81523ab68a | ||
|
|
2bf8cc62cf | ||
|
|
1ae8247af3 | ||
|
|
5ef32227ec | ||
|
|
6456e773bd | ||
|
|
234fe53ca3 | ||
|
|
7c93e7a7b3 | ||
|
|
8afc6c7f4b | ||
|
|
4609d0fa3a | ||
|
|
d452c035c6 | ||
|
|
45113c8f5a | ||
|
|
0acdd3c62b | ||
|
|
96d7d0a33e | ||
|
|
b6b280267b | ||
|
|
6e6d253b1a | ||
|
|
d92c105db2 | ||
|
|
906db728d6 | ||
|
|
c4b7411565 | ||
|
|
de06396046 | ||
|
|
ee6bfeb8e3 | ||
|
|
058347321f | ||
|
|
feefe49324 | ||
|
|
187381a9a2 | ||
|
|
993dfa4368 | ||
|
|
7e35a16440 | ||
|
|
e4eeb15926 | ||
|
|
634e0db26d | ||
|
|
56855c23e1 | ||
|
|
0b00f742e3 | ||
|
|
b7ab3f673c | ||
|
|
be04ea1e35 | ||
|
|
1f8e695802 | ||
|
|
2d82b2c64f | ||
|
|
d076caf473 | ||
|
|
c7abdefa31 | ||
|
|
ba772c0bca | ||
|
|
5bad234119 | ||
|
|
c7e7baaf23 | ||
|
|
36658a671b | ||
|
|
045f2e10ba | ||
|
|
fb5a7db66d | ||
|
|
ba7d33982e | ||
|
|
c62279a755 | ||
|
|
17fa1a7ffb | ||
|
|
e89ceac351 | ||
|
|
0b8c30c109 | ||
|
|
9ab0f463cc | ||
|
|
6433dda7b8 | ||
|
|
fa7a2f4be4 | ||
|
|
ba90e16505 | ||
|
|
008f710203 | ||
|
|
df2740f126 | ||
|
|
2db89d143e | ||
|
|
0525d49da3 | ||
|
|
e2b0745882 | ||
|
|
92e804fc50 | ||
|
|
67abf45576 | ||
|
|
d2c9c814e7 | ||
|
|
22f8881a64 | ||
|
|
4ab20322fe | ||
|
|
5370eeecea | ||
|
|
ba71cb5dd7 | ||
|
|
9aad6c2c52 | ||
|
|
4d9627f20c | ||
|
|
c142492e91 | ||
|
|
6bf8d9e207 | ||
|
|
4f9a6168c1 | ||
|
|
38397f99aa | ||
|
|
f8686d0e75 | ||
|
|
549e3c8f9d | ||
|
|
bb56225b95 | ||
|
|
f00be261ba | ||
|
|
9cd94f26d0 | ||
|
|
4d8f5c80a7 | ||
|
|
24a23acc3d | ||
|
|
ca8703cfd8 | ||
|
|
6dcbb5b2f8 | ||
|
|
a84fdddb2a | ||
|
|
38bb2f8ceb | ||
|
|
23f5ef4345 | ||
|
|
ef8a2a9054 | ||
|
|
96103d0e36 | ||
|
|
ff5f6748df | ||
|
|
1c1fd6c366 | ||
|
|
32d37d00cb | ||
|
|
82f6cda966 | ||
|
|
f1ff8ff0d0 | ||
|
|
756c72902f | ||
|
|
73f8f0bbd0 | ||
|
|
18ed528f5d | ||
|
|
8fd2f136bc | ||
|
|
0524b1bf67 | ||
|
|
15716f65ce | ||
|
|
d46bdba332 | ||
|
|
760728110a | ||
|
|
12d0a194ca | ||
|
|
4104543508 | ||
|
|
5c211db015 | ||
|
|
2dc6180f8d | ||
|
|
e222a34b69 | ||
|
|
ef17d95063 | ||
|
|
853502e5d7 | ||
|
|
c18e297e77 | ||
|
|
c5a49599ba | ||
|
|
df9da9edf5 | ||
|
|
e2200fd050 | ||
|
|
c6207f5d9c | ||
|
|
4302b7ff6b | ||
|
|
50a7923438 | ||
|
|
ab416445c8 | ||
|
|
a54698d43c | ||
|
|
c5a77cc1c0 | ||
|
|
a9ffa811fc | ||
|
|
080a2608e0 | ||
|
|
57f2e83d6a | ||
|
|
5b030139d3 | ||
|
|
0da1ff42d1 | ||
|
|
2c599b7baa | ||
|
|
5c8af8d21a | ||
|
|
026f3cfde2 | ||
|
|
f6349180e8 | ||
|
|
aa6421921c | ||
|
|
7d41d2dab2 | ||
|
|
f0b4d18f93 | ||
|
|
6750f06e10 | ||
|
|
b2bd38fa9e | ||
|
|
3482a01e22 | ||
|
|
6335467552 | ||
|
|
4a39e65b62 | ||
|
|
c50a23e918 | ||
|
|
1e76b72b98 | ||
|
|
b94cf39eef | ||
|
|
fef254ffff | ||
|
|
e5495863a2 | ||
|
|
3b4df2abf0 | ||
|
|
aef2aee6a4 | ||
|
|
d0d9519149 | ||
|
|
685df1d2c5 | ||
|
|
08e6b6f2e7 | ||
|
|
66c887d0f3 | ||
|
|
22e9960697 | ||
|
|
64aa6e1f2d | ||
|
|
7a93ed9d04 | ||
|
|
a905e922e9 | ||
|
|
f9f08fc720 | ||
|
|
8d402d76d0 | ||
|
|
46fda6281c | ||
|
|
a14dbe1ea6 | ||
|
|
18810a4c16 | ||
|
|
147bc80dba | ||
|
|
c7a484195a | ||
|
|
4968eb6503 | ||
|
|
a6f2d698a9 | ||
|
|
ea5ed93ea5 | ||
|
|
e1140134c6 | ||
|
|
5ed11e012e | ||
|
|
5380bd39ca | ||
|
|
2ee2685688 | ||
|
|
782002245b | ||
|
|
7fc0905843 | ||
|
|
72ecb99e54 | ||
|
|
c863507d08 | ||
|
|
cff86c9093 | ||
|
|
0479dfcc54 | ||
|
|
68dd67f21c | ||
|
|
540f6858b5 | ||
|
|
b61e791a4f | ||
|
|
d0986f9482 | ||
|
|
112cb0dc28 | ||
|
|
0d3d7fdcf2 | ||
|
|
5d6b89ef3b | ||
|
|
ed0b26c09e | ||
|
|
ae292bd920 | ||
|
|
6c85a90723 | ||
|
|
852592066c | ||
|
|
96e1bc9b44 | ||
|
|
b41d81ed31 | ||
|
|
e241ec2244 | ||
|
|
16e1f1a94c | ||
|
|
7a68c42b26 | ||
|
|
37ccc2e118 | ||
|
|
4192fe1ab2 | ||
|
|
d5c743d7bb | ||
|
|
11814d63e8 | ||
|
|
b753656d50 | ||
|
|
f7e87611fc | ||
|
|
1fb0e1900e | ||
|
|
954a9731e0 | ||
|
|
65c3364ad8 | ||
|
|
3d72b7dccc | ||
|
|
13ee569f06 | ||
|
|
d79ef23a75 | ||
|
|
5d0797d4ba | ||
|
|
47a8d7475f | ||
|
|
4939053121 | ||
|
|
b525bf554e | ||
|
|
f00285d2b2 | ||
|
|
040f8d6eda | ||
|
|
66d905325c | ||
|
|
8b0cd95e73 | ||
|
|
d867cca6d9 | ||
|
|
a28f736369 | ||
|
|
5c3a71cc59 | ||
|
|
cef6dadb08 | ||
|
|
36be817a3e | ||
|
|
02f571f081 | ||
|
|
157159e487 | ||
|
|
02ada9f800 | ||
|
|
6fcf9a97bb | ||
|
|
17a5d8799f | ||
|
|
31f3fe7a22 | ||
|
|
89e46d3d83 | ||
|
|
885795e67d | ||
|
|
92bfb53dd4 | ||
|
|
4cecbeb115 | ||
|
|
5971480f55 | ||
|
|
05bebea511 | ||
|
|
76fa6c5cfb | ||
|
|
633b68b518 | ||
|
|
6913d8e995 | ||
|
|
f3654e6f8d | ||
|
|
d685dbcf22 | ||
|
|
6a57fa079e | ||
|
|
0a91d145ba | ||
|
|
c2866e799d | ||
|
|
d8cffcaae7 | ||
|
|
30abca7be2 | ||
|
|
edce87f3fb | ||
|
|
66bac98fc2 | ||
|
|
59156de92b | ||
|
|
e0d7d10600 | ||
|
|
daaf862257 | ||
|
|
9de53d4b59 | ||
|
|
f1571e2d46 | ||
|
|
bd28d06298 | ||
|
|
a24e4655eb | ||
|
|
20a6c8d8e5 | ||
|
|
98d264faf4 | ||
|
|
321902a9b5 | ||
|
|
8df5d06f9a | ||
|
|
e69ea529cc | ||
|
|
15405b1119 | ||
|
|
d2f97ce2da | ||
|
|
543ca631e9 | ||
|
|
f184886db1 | ||
|
|
8432ab4324 | ||
|
|
6c05b37ca3 | ||
|
|
35f4beeb47 | ||
|
|
cbad7caa68 | ||
|
|
b0388a4012 | ||
|
|
df3fab4d55 | ||
|
|
da49f88a03 | ||
|
|
e28feceb06 | ||
|
|
50496a164d | ||
|
|
6f1dce1572 | ||
|
|
6847776ae7 | ||
|
|
67bd53bdd8 | ||
|
|
e735abfdfd | ||
|
|
1de93a2d6d | ||
|
|
36f9e7c742 | ||
|
|
9462763bbb | ||
|
|
4ae0880ea6 | ||
|
|
6ae2b6c835 | ||
|
|
a0f180fd48 | ||
|
|
bf1cf89914 | ||
|
|
297a047fb4 | ||
|
|
52ffc15ffc | ||
|
|
e478c9c693 | ||
|
|
d004f28074 | ||
|
|
bc68ed8b1d | ||
|
|
04555ae650 | ||
|
|
e8f62085be | ||
|
|
f430bffe2a | ||
|
|
1f0520634f | ||
|
|
902d4c31fb | ||
|
|
17364ac09f | ||
|
|
0b889f8f81 | ||
|
|
40e349ff35 | ||
|
|
c943b1b1df | ||
|
|
912bc1d4e1 | ||
|
|
cacb1533a3 | ||
|
|
f0feaca9d7 | ||
|
|
b6656f171b | ||
|
|
6206ab3931 | ||
|
|
c35fc58b1f | ||
|
|
deed8abed7 | ||
|
|
7151ad23f0 | ||
|
|
0166d938af | ||
|
|
6194aeddb0 | ||
|
|
903dbf2c30 | ||
|
|
9380f9ff57 | ||
|
|
259ed95486 | ||
|
|
2ebc92681e | ||
|
|
195a1ffe13 | ||
|
|
a8c2978185 | ||
|
|
140f97a457 | ||
|
|
7f94445a1e | ||
|
|
82a89aec65 | ||
|
|
7e95110232 | ||
|
|
ec4aaaad89 | ||
|
|
1b790fde24 | ||
|
|
aaccea731e | ||
|
|
29e31d7610 | ||
|
|
aa51f4a98f | ||
|
|
e6ccd12f00 | ||
|
|
b134315df1 | ||
|
|
7f34dffa13 | ||
|
|
fa239e78c9 | ||
|
|
707a6c4d6a | ||
|
|
e5da303b43 | ||
|
|
84ccd66331 | ||
|
|
ad8cc2baea | ||
|
|
7c4cf70309 | ||
|
|
c3211e9b4f | ||
|
|
268d94c983 | ||
|
|
0bcacbba58 | ||
|
|
8cdc26add9 | ||
|
|
e0b2238886 | ||
|
|
369a2e4029 | ||
|
|
c4089e3b51 | ||
|
|
9e2e9bc5b8 | ||
|
|
a9e44426ed |
1
BUGS
Normal file
1
BUGS
Normal file
@@ -0,0 +1 @@
|
||||
LVM2's device-mapper driver and ext3 are incompatible at the moment.
|
||||
340
COPYING
Normal file
340
COPYING
Normal file
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 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
|
||||
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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.
|
||||
|
||||
<signature of Ty Coon>, 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.
|
||||
483
COPYING.LIB
Normal file
483
COPYING.LIB
Normal file
@@ -0,0 +1,483 @@
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, 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 library, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link a program with the library, you must provide
|
||||
complete object files to the recipients so that they can relink them
|
||||
with the library, after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, 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 companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary
|
||||
GNU General Public License, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary license.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, 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 library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete 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 distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
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 Library or any portion
|
||||
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
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 Library, 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 Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you 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.
|
||||
|
||||
If distribution of 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 satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. 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.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library 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.
|
||||
|
||||
9. 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 Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
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.
|
||||
|
||||
11. 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 Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library 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 Library.
|
||||
|
||||
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.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library 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.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Library 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 Library
|
||||
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 Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
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
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "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
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. 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 LIBRARY 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
|
||||
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. 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.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
36
INSTALL
Normal file
36
INSTALL
Normal file
@@ -0,0 +1,36 @@
|
||||
LVM2 installation
|
||||
=================
|
||||
|
||||
1) Install device-mapper
|
||||
|
||||
Ensure the device-mapper has been installed on the machine.
|
||||
|
||||
The device-mapper should be in the kernel (look for 'device-mapper'
|
||||
messages in the kernel logs) and /usr/include/libdevmapper.h should
|
||||
be present.
|
||||
|
||||
The device-mapper is available from:
|
||||
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
|
||||
|
||||
|
||||
2) Generate custom makefiles.
|
||||
|
||||
Run the 'configure' script from the top directory.
|
||||
|
||||
If you do not have GNU readline (http://www.gnu.org/directory/readline.html)
|
||||
installed use
|
||||
./configure --disable-readline
|
||||
|
||||
|
||||
3) Build and install LVM2.
|
||||
|
||||
Run 'make install' from the top directory.
|
||||
|
||||
|
||||
4) Create a configuration file
|
||||
|
||||
The tools will work fine without a configuration file being
|
||||
present, but you ought to review the example file in doc/example.conf.
|
||||
For example, specifying the devices that LVM2 is to use should
|
||||
make the tools run more efficiently - and avoid scanning /dev/cdrom!
|
||||
|
||||
18
INTRO
Normal file
18
INTRO
Normal file
@@ -0,0 +1,18 @@
|
||||
An introduction to LVM2
|
||||
=======================
|
||||
|
||||
Background
|
||||
|
||||
|
||||
Compatibility with LVM1
|
||||
|
||||
|
||||
New features
|
||||
|
||||
|
||||
Missing features
|
||||
|
||||
|
||||
Future enhancements
|
||||
|
||||
|
||||
34
Makefile.in
Normal file
34
Makefile.in
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software
|
||||
#
|
||||
# This LVM 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 LVM 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 Library General Public
|
||||
# License along with this LVM library; if not, write to the Free
|
||||
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
# MA 02111-1307, USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SUBDIRS = include man lib tools
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS += test/mm test/device test/format1 test/regex test/filters
|
||||
endif
|
||||
|
||||
include make.tmpl
|
||||
|
||||
lib: include
|
||||
tools: include lib
|
||||
|
||||
|
||||
27
README
27
README
@@ -1,2 +1,25 @@
|
||||
This is pretty much empty so far...if you can't see subdirectories,
|
||||
try 'cvs -f update'
|
||||
This directory contains a beta release of LMV2, the new version of
|
||||
the userland LVM tools designed for the new device-mapper for
|
||||
the Linux kernel.
|
||||
|
||||
The device-mapper needs to be installed before compiling these LVM2 tools.
|
||||
|
||||
For more information about LVM2 read the INTRO file.
|
||||
Installation instructions are in INSTALL.
|
||||
|
||||
This is beta-quality software, released for testing purposes only.
|
||||
There is no warranty - see COPYING and COPYING.LIB.
|
||||
|
||||
Tarballs are available from:
|
||||
ftp://ftp.sistina.com/pub/LVM2/tools/
|
||||
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
|
||||
|
||||
To access the CVS tree use:
|
||||
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs login
|
||||
CVS password: cvs1
|
||||
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs checkout LVM2
|
||||
|
||||
Mailing list for discussion/bug reports etc.
|
||||
lvm-devel@sistina.com
|
||||
Subscribe from http://lists.sistina.com/mailman/listinfo/lvm-devel
|
||||
|
||||
|
||||
29
TODO
Normal file
29
TODO
Normal file
@@ -0,0 +1,29 @@
|
||||
before 2.0
|
||||
-----------
|
||||
|
||||
vgexport
|
||||
vgimport
|
||||
snapshots
|
||||
device-mapper support for 2.5 kernel series
|
||||
review FIXMEs
|
||||
extra validation & full consistency checks in format1 with LVM1
|
||||
partial activation
|
||||
error message review
|
||||
locking during metadata changes
|
||||
format2 with atomic transactions
|
||||
bidirectional format1/format2 migration tool
|
||||
persistent minors
|
||||
stats
|
||||
pvmove
|
||||
review tool exit codes for LVM1 compatibility
|
||||
|
||||
before 2.1
|
||||
----------
|
||||
|
||||
e2fsadm
|
||||
lvmdiskscan
|
||||
lvmsadc
|
||||
lvmsar
|
||||
pvdata
|
||||
vgsplit
|
||||
vgmknodes
|
||||
148
configure.in
Normal file
148
configure.in
Normal file
@@ -0,0 +1,148 @@
|
||||
################################################################################
|
||||
##
|
||||
## Copyright 1999-2000 Sistina Software, Inc.
|
||||
##
|
||||
## This is free software released under the GNU General Public License.
|
||||
## There is no warranty for this software. See the file COPYING for
|
||||
## details.
|
||||
##
|
||||
## See the file CONTRIBUTORS for a list of contributors.
|
||||
##
|
||||
## This file is maintained by:
|
||||
## AJ Lewis <lewis@sistina.com>
|
||||
##
|
||||
## File name: configure.in
|
||||
##
|
||||
## Description: Input file for autoconf. Generates the configure script
|
||||
## that tries to keep everything nice and portable. It also
|
||||
## simplifies distribution package building considerably.
|
||||
################################################################################
|
||||
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(lib/device/dev-cache.h)
|
||||
|
||||
dnl setup the directory where autoconf has auxilary files
|
||||
AC_CONFIG_AUX_DIR(autoconf)
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_AWK
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_RANLIB
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h unistd.h)
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_STRUCT_ST_RDEV
|
||||
AC_HEADER_TIME
|
||||
|
||||
dnl -- prefix is /usr by default, the exec_prefix default is setup later
|
||||
AC_PREFIX_DEFAULT(/usr)
|
||||
|
||||
dnl -- setup the ownership of the files
|
||||
AC_ARG_WITH(user,
|
||||
[ --with-user=USER Set the owner of installed files ],
|
||||
[ OWNER="$withval" ],
|
||||
[ OWNER="root" ])
|
||||
|
||||
dnl -- setup the group ownership of the files
|
||||
AC_ARG_WITH(group,
|
||||
[ --with-group=GROUP Set the group owner of installed files ],
|
||||
[ GROUP="$withval" ],
|
||||
[ GROUP="root" ])
|
||||
|
||||
AC_ARG_ENABLE(jobs, [ --enable-jobs=NUM Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=)
|
||||
|
||||
dnl Enables staticly linked tools
|
||||
AC_ARG_ENABLE(static_link, [ --enable-static_link Use this to link the tools to the liblvm library
|
||||
statically. Default is dynamic linking], STATIC_LINK=$enableval, STATIC_LINK=no)
|
||||
|
||||
dnl Disable readline
|
||||
AC_ARG_ENABLE(readline, [ --disable-readline Disable readline support], \
|
||||
READLINE=$enableval, READLINE=yes)
|
||||
|
||||
dnl Mess with default exec_prefix
|
||||
if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]];
|
||||
then exec_prefix="";
|
||||
fi;
|
||||
|
||||
dnl Checks for library functions.
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_TYPE_SIGNAL
|
||||
AC_FUNC_VPRINTF
|
||||
AC_CHECK_FUNCS(mkdir rmdir uname)
|
||||
|
||||
dnl check for termcap (Shamelessly copied from parted 1.4.17)
|
||||
if test x$READLINE = xyes; then
|
||||
AC_SEARCH_LIBS(tgetent, ncurses curses termcap termlib, ,
|
||||
AC_MSG_ERROR(
|
||||
termcap could not be found which is required for the
|
||||
--enable-readline option (which is enabled by default). Either disable readline
|
||||
support with --disable-readline or download and install termcap from:
|
||||
ftp.gnu.org/gnu/termcap
|
||||
Note: if you are using precompiled packages you will also need the development
|
||||
package as well (which may be called termcap-devel or something similar).
|
||||
Note: (n)curses also seems to work as a substitute for termcap. This was
|
||||
not found either - but you could try installing that as well.
|
||||
)
|
||||
exit
|
||||
)
|
||||
fi
|
||||
|
||||
dnl Check for readline (Shamelessly copied from parted 1.4.17)
|
||||
if test x$READLINE = xyes; then
|
||||
AC_CHECK_LIB(readline, readline, ,
|
||||
AC_MSG_ERROR(
|
||||
GNU Readline could not be found which is required for the
|
||||
--enable-readline option (which is enabled by default). Either disable readline
|
||||
support with --disable-readline or download and install readline from:
|
||||
ftp.gnu.org/gnu/readline
|
||||
Note: if you are using precompiled packages you will also need the development
|
||||
package as well (which may be called readline-devel or something similar).
|
||||
)
|
||||
exit
|
||||
)
|
||||
AC_CHECK_FUNC(rl_completion_matches, HAVE_RL_COMPLETION_MATCHES=yes,
|
||||
HAVE_RL_COMPLETION_MATCHES=no)
|
||||
fi
|
||||
|
||||
if test "-f VERSION"; then
|
||||
LVM_VERSION="\"`cat VERSION`\""
|
||||
else
|
||||
LVM_VERSION="Unknown"
|
||||
fi
|
||||
|
||||
AC_SUBST(JOBS)
|
||||
AC_SUBST(STATIC_LINK)
|
||||
AC_SUBST(READLINE)
|
||||
AC_SUBST(HAVE_RL_COMPLETION_MATCHES)
|
||||
AC_SUBST(OWNER)
|
||||
AC_SUBST(GROUP)
|
||||
AC_SUBST(LIBS)
|
||||
AC_SUBST(LVM_VERSION)
|
||||
dnl First and last lines should not contain files to generate in order to
|
||||
dnl keep utility scripts running properly
|
||||
AC_OUTPUT( \
|
||||
Makefile \
|
||||
make.tmpl \
|
||||
include/Makefile \
|
||||
lib/Makefile \
|
||||
man/Makefile \
|
||||
tools/Makefile \
|
||||
tools/version.h \
|
||||
test/mm/Makefile \
|
||||
test/device/Makefile \
|
||||
test/format1/Makefile \
|
||||
test/regex/Makefile \
|
||||
test/filters/Makefile \
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
Wow! This is really incredible documentation!
|
||||
137
doc/example.conf
Normal file
137
doc/example.conf
Normal file
@@ -0,0 +1,137 @@
|
||||
# This is an example configuration file for the LVM2 system. It
|
||||
# contains the default settings that would be used if there was no
|
||||
# /etc/lvm/lvm.conf file.
|
||||
# Refer to 'man lvm.conf' for further information.
|
||||
|
||||
|
||||
# This section allows the user to configure which block devices should
|
||||
# be used by the LVM system.
|
||||
devices {
|
||||
|
||||
# where do you want your volume groups to appear ?
|
||||
dir = "/dev"
|
||||
|
||||
# An array of directories that contain the device nodes you wish
|
||||
# to use with LVM2.
|
||||
scan = "/dev"
|
||||
|
||||
# A very important option, that allows you to tune the LVM2 system
|
||||
# to just look at a restricted set of devices that you're
|
||||
# interested in.
|
||||
|
||||
# The filter consists of an array of regular expressions. These
|
||||
# expressions can be delimited by a character of your choice, and
|
||||
# prefixed with either an 'a' (for accept) or 'r' (for reject).
|
||||
# ATM you cannot use anchors (^ or $) in your regular expression.
|
||||
|
||||
# By default we accept every block device:
|
||||
filter = "a/.*/"
|
||||
|
||||
# When testing I like to work with just loopback devices:
|
||||
# filter = ["a/loop/", "r/.*/"]
|
||||
|
||||
# Or maybe all loops and ide drives except hdc:
|
||||
# filter =["a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|"]
|
||||
|
||||
# The results of all the filtering are cached on disk to avoid
|
||||
# rescanning dud devices (which can take a very long time). By
|
||||
# default this cache file is hidden in the /etc/lvm directory, it
|
||||
# is human readable to aid filter debugging.
|
||||
cache = "/etc/lvm/.cache"
|
||||
|
||||
# You can turn off writing this cache file by setting this to 0.
|
||||
write_cache_state = 1
|
||||
}
|
||||
|
||||
# A section that allows the user to configure the nature of the
|
||||
# information that LVM2 reports.
|
||||
log {
|
||||
|
||||
# Where should the log of error and debug messages go ? By
|
||||
# default there is no log.
|
||||
#file = "/var/log/lvm2.log"
|
||||
|
||||
# Should we overwrite the last log. By default we append.
|
||||
overwrite = 0
|
||||
|
||||
# There are 9 log levels, with 9 being the most verbose.
|
||||
level = 3
|
||||
|
||||
# Controls the messages sent to stdout or stderr while running
|
||||
# LVM2. There are three levels of verbosity, 3 being the most
|
||||
# verbose.
|
||||
verbose = 0
|
||||
|
||||
# Should we send log messages through syslog?
|
||||
# 1 is yes; 0 is no.
|
||||
syslog = 1
|
||||
|
||||
# Choose format of output messages
|
||||
# Whether or not (1 or 0) to indent messages according to their severity
|
||||
indent = 1
|
||||
|
||||
# Whether or not (1 or 0) to display the command name on each line output
|
||||
command_names = 0
|
||||
|
||||
# A prefix to use before the message text (but after the command name,
|
||||
# if selected)
|
||||
prefix = " "
|
||||
|
||||
# To make the messages look similar to the original LVM use:
|
||||
# indent = 0
|
||||
# command_names = 1
|
||||
# prefix = " -- "
|
||||
}
|
||||
|
||||
# Configuration of metadata backups and archiving. In LVM2 when we
|
||||
# talk about a 'backup' we mean making a copy of the metadata for the
|
||||
# *current* system. The 'archive' contains old metadata configurations.
|
||||
# Backups are stored in a human readeable text format.
|
||||
backup {
|
||||
|
||||
# Should we maintain a backup of the current metadata configuration ?
|
||||
# Use 1 for Yes; 0 for No.
|
||||
# Think very hard before turning this off.
|
||||
backup = 1
|
||||
|
||||
# Where shall we keep it ?
|
||||
backup_dir = "/etc/lvm/backup"
|
||||
|
||||
|
||||
# Should we maintain an archive of old metadata configurations.
|
||||
# Use 1 for Yes; 0 for No.
|
||||
# On by default. Think very hard before turning this off.
|
||||
archive = 1
|
||||
|
||||
# Where should archived files go ?
|
||||
archive_dir = "/etc/lvm/archive"
|
||||
|
||||
# What is the minimum number of archive files you wish to keep ?
|
||||
retain_min = 10
|
||||
|
||||
# What is the minimum time you wish to keep an archive file for ?
|
||||
retain_days = 30
|
||||
}
|
||||
|
||||
# Settings for the running LVM2 in shell mode.
|
||||
shell {
|
||||
|
||||
# Number of lines of history to store in ~/.lvm_history
|
||||
history_size = 100
|
||||
}
|
||||
|
||||
# Miscellaneous global settings
|
||||
global {
|
||||
|
||||
# The file creation mask for any files and directories created.
|
||||
# Interpreted as octal if the first digit is zero.
|
||||
umask = 077
|
||||
|
||||
# Allow other users to read the files
|
||||
#umask = 022
|
||||
|
||||
# Enabling test mode means that no changes to the on disk metadata
|
||||
# will be made. Equivalent to having the -t option on every
|
||||
# command. Defaults to off.
|
||||
test = 0
|
||||
}
|
||||
52
doc/pvmove_outline.txt
Normal file
52
doc/pvmove_outline.txt
Normal file
@@ -0,0 +1,52 @@
|
||||
Let's say we have an LV, made up of three segments of different PV's,
|
||||
I've also added in the device major:minor as this will be useful
|
||||
later:
|
||||
|
||||
+-----------------------------+
|
||||
| PV1 | PV2 | PV3 | 254:3
|
||||
+----------+---------+--------+
|
||||
|
||||
|
||||
Now our hero decides to PV move PV2 to PV4:
|
||||
|
||||
1. Suspend our LV (254:3), this starts queueing all io, and flushes
|
||||
all pending io. Once the suspend has completed we are free to change
|
||||
the mapping table.
|
||||
|
||||
2. Set up *another* (254:4) device with the mapping table of our LV.
|
||||
|
||||
3. Load a new mapping table into (254:3) that has identity targets for
|
||||
parts that aren't moving, and a mirror target for parts that are.
|
||||
|
||||
4. Unsuspend (254:3)
|
||||
|
||||
So now we have:
|
||||
destination of copy
|
||||
+--------------------->--------------+
|
||||
| |
|
||||
+-----------------------------+ + -----------+
|
||||
| Identity | mirror | Ident. | 254:3 | PV4 |
|
||||
+----------+---------+--------+ +------------+
|
||||
| | |
|
||||
\/ \/ \/
|
||||
+-----------------------------+
|
||||
| PV1 | PV2 | PV3 | 254:4
|
||||
+----------+---------+--------+
|
||||
|
||||
Any writes to segment2 of the LV get intercepted by the mirror target
|
||||
who checks that that chunk has been copied to the new destination, if
|
||||
it hasn't it queues the initial copy and defers the current io until
|
||||
it has finished. Then the current io is written to *both* PV2 and the
|
||||
PV4.
|
||||
|
||||
5. When the copying has completed 254:3 is suspended/pending flushed.
|
||||
|
||||
6. 254:4 is taken down
|
||||
|
||||
7. metadata is updated on disk
|
||||
|
||||
8. 254:3 has new mapping table loaded:
|
||||
|
||||
+-----------------------------+
|
||||
| PV1 | PV4 | PV3 | 254:3
|
||||
+----------+---------+--------+
|
||||
@@ -1 +0,0 @@
|
||||
The driver directory
|
||||
29
include/.symlinks
Normal file
29
include/.symlinks
Normal file
@@ -0,0 +1,29 @@
|
||||
../lib/activate/activate.h
|
||||
../lib/config/config.h
|
||||
../lib/datastruct/bitset.h
|
||||
../lib/datastruct/btree.h
|
||||
../lib/datastruct/hash.h
|
||||
../lib/datastruct/list.h
|
||||
../lib/datastruct/lvm-types.h
|
||||
../lib/device/dev-cache.h
|
||||
../lib/device/device.h
|
||||
../lib/display/display.h
|
||||
../lib/filters/filter-composite.h
|
||||
../lib/filters/filter-persistent.h
|
||||
../lib/filters/filter-regex.h
|
||||
../lib/filters/filter.h
|
||||
../lib/format1/format1.h
|
||||
../lib/format1/lvm1_label.h
|
||||
../lib/format_text/format-text.h
|
||||
../lib/label/label.h
|
||||
../lib/label/uuid-map.h
|
||||
../lib/log/log.h
|
||||
../lib/metadata/metadata.h
|
||||
../lib/mm/dbg_malloc.h
|
||||
../lib/mm/pool.h
|
||||
../lib/mm/xlate.h
|
||||
../lib/misc/lvm-file.h
|
||||
../lib/misc/lvm-string.h
|
||||
../lib/regex/matcher.h
|
||||
../lib/uuid/uuid.h
|
||||
../lib/vgcache/vgcache.h
|
||||
43
include/Makefile.in
Normal file
43
include/Makefile.in
Normal file
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software
|
||||
#
|
||||
# This LVM 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 LVM 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 Library General Public
|
||||
# License along with this LVM library; if not, write to the Free
|
||||
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
# MA 02111-1307, USA
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
LN_S = @LN_S@
|
||||
|
||||
all: .symlinks_created
|
||||
|
||||
.symlinks_created: .symlinks
|
||||
find . -maxdepth 1 -type l -exec $(RM) \{\} \;
|
||||
for i in `cat .symlinks`; do $(LN_S) $$i ; done
|
||||
touch $@
|
||||
|
||||
distclean:
|
||||
find . -maxdepth 1 -type l -exec $(RM) \{\} \;
|
||||
$(RM) Makefile .include_symlinks .symlinks_created
|
||||
|
||||
clean:
|
||||
|
||||
install:
|
||||
|
||||
.PHONY: clean distclean all install
|
||||
|
||||
63
lib/Makefile.in
Normal file
63
lib/Makefile.in
Normal file
@@ -0,0 +1,63 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
#
|
||||
# This file is released under the GPL.
|
||||
#
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES=\
|
||||
activate/activate.c \
|
||||
activate/fs.c \
|
||||
activate/names.c \
|
||||
config/config.c \
|
||||
datastruct/bitset.c \
|
||||
datastruct/btree.c \
|
||||
datastruct/hash.c \
|
||||
device/dev-cache.c \
|
||||
device/dev-io.c \
|
||||
device/device.c \
|
||||
display/display.c \
|
||||
filters/filter-composite.c \
|
||||
filters/filter-persistent.c \
|
||||
filters/filter-regex.c \
|
||||
filters/filter.c \
|
||||
format1/disk-rep.c \
|
||||
format1/format1.c \
|
||||
format1/import-export.c \
|
||||
format1/import-extents.c \
|
||||
format1/layout.c \
|
||||
format1/lvm1_label.c \
|
||||
format1/vg_number.c \
|
||||
format_text/archive.c \
|
||||
format_text/export.c \
|
||||
format_text/flags.c \
|
||||
format_text/format-text.c \
|
||||
format_text/import.c \
|
||||
label/label.c \
|
||||
label/uuid-map.c \
|
||||
log/log.c \
|
||||
metadata/lv_manip.c \
|
||||
metadata/merge.c \
|
||||
metadata/metadata.c \
|
||||
metadata/pv_map.c \
|
||||
misc/lvm-file.c \
|
||||
mm/dbg_malloc.c \
|
||||
mm/pool.c \
|
||||
regex/matcher.c \
|
||||
regex/parse_rx.c \
|
||||
regex/ttree.c \
|
||||
uuid/uuid.c \
|
||||
vgcache/vgcache.c
|
||||
|
||||
TARGETS=liblvm.a
|
||||
|
||||
include ../make.tmpl
|
||||
|
||||
liblvm.a: $(OBJECTS)
|
||||
$(RM) $@
|
||||
$(AR) r $@ $(OBJECTS)
|
||||
$(RANLIB) $@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Base library directory
|
||||
452
lib/activate/activate.c
Normal file
452
lib/activate/activate.c
Normal file
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "metadata.h"
|
||||
#include "activate.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
#include "fs.h"
|
||||
#include "lvm-string.h"
|
||||
#include "names.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
int library_version(char *version, size_t size)
|
||||
{
|
||||
if (!dm_get_library_version(version, size))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dm_task *_setup_task_with_name(struct logical_volume *lv,
|
||||
const char *lv_name,
|
||||
int task)
|
||||
{
|
||||
char name[128];
|
||||
struct dm_task *dmt;
|
||||
|
||||
if (!(dmt = dm_task_create(task))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!build_dm_name(name, sizeof(name), lv->vg->name, lv_name)) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dm_task_set_name(dmt, name);
|
||||
|
||||
return dmt;
|
||||
}
|
||||
|
||||
static struct dm_task *_setup_task(struct logical_volume *lv, int task)
|
||||
{
|
||||
return _setup_task_with_name(lv, lv->name, task);
|
||||
}
|
||||
|
||||
int driver_version(char *version, size_t size)
|
||||
{
|
||||
int r = 0;
|
||||
struct dm_task *dmt;
|
||||
|
||||
log_very_verbose("Getting driver version");
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_VERSION))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
log_error("Failed to get driver version");
|
||||
|
||||
if (!dm_task_get_driver_version(dmt, version, size))
|
||||
goto out;
|
||||
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int lv_info(struct logical_volume *lv, struct dm_info *info)
|
||||
{
|
||||
int r = 0;
|
||||
struct dm_task *dmt;
|
||||
|
||||
log_very_verbose("Getting device info for %s", lv->name);
|
||||
if (!(dmt = _setup_task(lv, DM_DEVICE_INFO))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dm_task_get_info(dmt, info)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
return r;
|
||||
}
|
||||
|
||||
int lv_rename(const char *old_name, struct logical_volume *lv)
|
||||
{
|
||||
int r = 0;
|
||||
char new_name[PATH_MAX];
|
||||
struct dm_task *dmt;
|
||||
|
||||
if (test_mode())
|
||||
return 0;
|
||||
|
||||
if (!(dmt = _setup_task_with_name(lv, old_name, DM_DEVICE_RENAME))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!build_dm_name(new_name, sizeof(new_name),
|
||||
lv->vg->name, lv->name)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_task_set_newname(dmt, new_name)) {
|
||||
stack;
|
||||
r = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
stack;
|
||||
r = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
fs_rename_lv(old_name, lv);
|
||||
|
||||
end:
|
||||
dm_task_destroy(dmt);
|
||||
return r;
|
||||
}
|
||||
|
||||
int lv_active(struct logical_volume *lv)
|
||||
{
|
||||
int r = -1;
|
||||
struct dm_info info;
|
||||
|
||||
if (!lv_info(lv, &info)) {
|
||||
stack;
|
||||
return r;
|
||||
}
|
||||
|
||||
log_very_verbose("%s is%s active", lv->name, info.exists ? "":" not");
|
||||
return info.exists;
|
||||
}
|
||||
|
||||
int lv_suspended(struct logical_volume *lv)
|
||||
{
|
||||
int r = -1;
|
||||
struct dm_info info;
|
||||
|
||||
if (!lv_info(lv, &info)) {
|
||||
stack;
|
||||
return r;
|
||||
}
|
||||
|
||||
log_very_verbose("%s is%s suspended", lv->name,
|
||||
info.suspended ? "":" not");
|
||||
return info.suspended;
|
||||
}
|
||||
|
||||
int lv_open_count(struct logical_volume *lv)
|
||||
{
|
||||
int r = -1;
|
||||
struct dm_info info;
|
||||
|
||||
if (!lv_info(lv, &info)) {
|
||||
stack;
|
||||
return r;
|
||||
}
|
||||
|
||||
log_very_verbose("%s is open %d time(s)", lv->name, info.open_count);
|
||||
return info.open_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit a target for a given segment.
|
||||
*/
|
||||
static int _emit_target(struct dm_task *dmt, struct stripe_segment *seg)
|
||||
{
|
||||
char params[1024];
|
||||
uint64_t esize = seg->lv->vg->extent_size;
|
||||
uint32_t s, stripes = seg->stripes;
|
||||
int w = 0, tw = 0, error = 0;
|
||||
const char *no_space =
|
||||
"Insufficient space to write target parameters.";
|
||||
char *filler = "/dev/ioerror";
|
||||
char *target;
|
||||
|
||||
if (stripes == 1) {
|
||||
if (!seg->area[0].pv) {
|
||||
target = "error";
|
||||
error = 1;
|
||||
}
|
||||
else
|
||||
target = "linear";
|
||||
}
|
||||
|
||||
if (stripes > 1) {
|
||||
target = "striped";
|
||||
tw = lvm_snprintf(params, sizeof(params), "%u %u ",
|
||||
stripes, seg->stripe_size);
|
||||
|
||||
if (tw < 0) {
|
||||
log_err(no_space);
|
||||
return 0;
|
||||
}
|
||||
|
||||
w = tw;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
for (s = 0; s < stripes; s++, w += tw) {
|
||||
if (!seg->area[s].pv)
|
||||
tw = lvm_snprintf(
|
||||
params + w, sizeof(params) - w,
|
||||
"%s 0%s", filler,
|
||||
s == (stripes - 1) ? "" : " ");
|
||||
else
|
||||
tw = lvm_snprintf(
|
||||
params + w, sizeof(params) - w,
|
||||
"%s %" PRIu64 "%s",
|
||||
dev_name(seg->area[s].pv->dev),
|
||||
(seg->area[s].pv->pe_start +
|
||||
(esize * seg->area[s].pe)),
|
||||
s == (stripes - 1) ? "" : " ");
|
||||
|
||||
if (tw < 0) {
|
||||
log_err(no_space);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_very_verbose("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
|
||||
esize * seg->le, esize * seg->len,
|
||||
target, params);
|
||||
|
||||
if (!dm_task_add_target(dmt, esize * seg->le, esize * seg->len,
|
||||
target, params)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _load(struct logical_volume *lv, int task)
|
||||
{
|
||||
int r = 0;
|
||||
struct dm_task *dmt;
|
||||
struct list *segh;
|
||||
struct stripe_segment *seg;
|
||||
|
||||
log_very_verbose("Generating devmapper parameters for %s", lv->name);
|
||||
if (!(dmt = _setup_task(lv, task))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_iterate(segh, &lv->segments) {
|
||||
seg = list_item(segh, struct stripe_segment);
|
||||
if (!_emit_target(dmt, seg)) {
|
||||
log_error("Unable to activate logical volume '%s'",
|
||||
lv->name);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!((lv->status & LVM_WRITE) && (lv->vg->status & LVM_WRITE))) {
|
||||
if (!dm_task_set_ro(dmt))
|
||||
log_error("Failed to set %s read-only during "
|
||||
"activation.", lv->name);
|
||||
else
|
||||
log_very_verbose("Activating %s read-only", lv->name);
|
||||
}
|
||||
|
||||
|
||||
if (!(r = dm_task_run(dmt)))
|
||||
stack;
|
||||
|
||||
log_verbose("Logical volume %s%s activated", lv->name,
|
||||
r == 1 ? "" : " not");
|
||||
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* FIXME: Always display error msg */
|
||||
int lv_activate(struct logical_volume *lv)
|
||||
{
|
||||
if (test_mode())
|
||||
return 0;
|
||||
|
||||
log_very_verbose("Activating %s", lv->name);
|
||||
return _load(lv, DM_DEVICE_CREATE) && fs_add_lv(lv);
|
||||
}
|
||||
|
||||
int _suspend(struct logical_volume *lv, int sus)
|
||||
{
|
||||
int r;
|
||||
struct dm_task *dmt;
|
||||
int task = sus ? DM_DEVICE_SUSPEND : DM_DEVICE_RESUME;
|
||||
|
||||
log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", lv->name);
|
||||
if (!(dmt = _setup_task(lv, task))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(r = dm_task_run(dmt)))
|
||||
log_err("Couldn't %s device '%s'", sus ? "suspend" : "resume",
|
||||
lv->name);
|
||||
|
||||
dm_task_destroy(dmt);
|
||||
return r;
|
||||
}
|
||||
|
||||
int lv_suspend(struct logical_volume *lv)
|
||||
{
|
||||
return _suspend(lv, 1);
|
||||
}
|
||||
|
||||
int lv_reactivate(struct logical_volume *lv)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (test_mode())
|
||||
return 0;
|
||||
|
||||
if (!lv_suspended(lv) && !_suspend(lv, 1)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = _load(lv, DM_DEVICE_RELOAD);
|
||||
|
||||
if (!_suspend(lv, 0)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int lv_deactivate(struct logical_volume *lv)
|
||||
{
|
||||
int r;
|
||||
struct dm_task *dmt;
|
||||
|
||||
log_very_verbose("Deactivating %s", lv->name);
|
||||
if (test_mode())
|
||||
return 0;
|
||||
|
||||
if (!(dmt = _setup_task(lv, DM_DEVICE_REMOVE))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(r = dm_task_run(dmt)))
|
||||
stack;
|
||||
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
fs_del_lv(lv);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int activate_lvs_in_vg(struct volume_group *vg)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct logical_volume *lv;
|
||||
int count = 0;
|
||||
|
||||
list_iterate(lvh, &vg->lvs) {
|
||||
lv = list_item(lvh, struct lv_list)->lv;
|
||||
count += (!lv_active(lv) && lv_activate(lv));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int lv_update_write_access(struct logical_volume *lv)
|
||||
{
|
||||
struct dm_info info;
|
||||
|
||||
if (!lv_info(lv, &info)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!info.exists || info.suspended)
|
||||
/* Noop */
|
||||
return 1;
|
||||
|
||||
return lv_reactivate(lv);
|
||||
}
|
||||
|
||||
int deactivate_lvs_in_vg(struct volume_group *vg)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct logical_volume *lv;
|
||||
int count = 0;
|
||||
|
||||
list_iterate(lvh, &vg->lvs) {
|
||||
lv = list_item(lvh, struct lv_list)->lv;
|
||||
count += ((lv_active(lv) == 1) && lv_deactivate(lv));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int lvs_in_vg_activated(struct volume_group *vg)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct logical_volume *lv;
|
||||
int count = 0;
|
||||
|
||||
list_iterate(lvh, &vg->lvs) {
|
||||
lv = list_item(lvh, struct lv_list)->lv;
|
||||
count += (lv_active(lv) == 1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int lvs_in_vg_opened(struct volume_group *vg)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct logical_volume *lv;
|
||||
int count = 0;
|
||||
|
||||
list_iterate(lvh, &vg->lvs) {
|
||||
lv = list_item(lvh, struct lv_list)->lv;
|
||||
count += (lv_open_count(lv) == 1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
52
lib/activate/activate.h
Normal file
52
lib/activate/activate.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef LVM_ACTIVATE_H
|
||||
#define LVM_ACTIVATE_H
|
||||
|
||||
#include <libdevmapper.h>
|
||||
|
||||
/* FIXME Snapshot handling? */
|
||||
|
||||
int driver_version(char *version, size_t size);
|
||||
int library_version(char *version, size_t size);
|
||||
|
||||
int lv_active(struct logical_volume *lv);
|
||||
int lv_suspended(struct logical_volume *lv);
|
||||
int lv_open_count(struct logical_volume *lv);
|
||||
int lv_info(struct logical_volume *lv, struct dm_info *info);
|
||||
int lv_rename(const char *old_name, struct logical_volume *lv);
|
||||
|
||||
int lv_activate(struct logical_volume *lv);
|
||||
int lv_reactivate(struct logical_volume *lv);
|
||||
int lv_deactivate(struct logical_volume *lv);
|
||||
int lv_suspend(struct logical_volume *lv);
|
||||
|
||||
/*
|
||||
* Return number of LVs in the VG that are
|
||||
* active.
|
||||
*/
|
||||
int lvs_in_vg_activated(struct volume_group *vg);
|
||||
int lvs_in_vg_opened(struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Test for (lv->status & LVM_WRITE)
|
||||
*/
|
||||
int lv_update_write_access(struct logical_volume *lv);
|
||||
|
||||
/*
|
||||
* Activate all LVs in the VG. Ignore any that
|
||||
* are already active. Return number
|
||||
* activated.
|
||||
*/
|
||||
int activate_lvs_in_vg(struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Deactivate all LVs in the VG
|
||||
*/
|
||||
int deactivate_lvs_in_vg(struct volume_group *vg);
|
||||
|
||||
#endif
|
||||
31
lib/activate/dmfs-driver.h
Normal file
31
lib/activate/dmfs-driver.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef DMFS_INTERFACE_H
|
||||
#define DMFS_INTERFACE_H
|
||||
|
||||
struct dmfs;
|
||||
|
||||
struct dmfs *dmfs_create(void);
|
||||
void dmfs_destroy(struct dmfs *dm);
|
||||
|
||||
int dmfs_dev_is_present(struct dmfs *dm, const char *dev);
|
||||
int dmfs_dev_is_active(struct dmfs *dm, const char *dev);
|
||||
|
||||
int dmfs_table_is_present(struct dmfs *dm, const char *dev, const char *table);
|
||||
int dmfs_table_is_active(struct dmfs *dm, const char *dev, const char *table);
|
||||
|
||||
int dmfs_dev_create(struct dmfs *dm, const char *name);
|
||||
int dmfs_dev_load_table(struct dmfs *dm, const char *dev,
|
||||
const char *table, const char *file);
|
||||
int dmfs_dev_drop_table(struct dmfs *dm, const char *dev, const char *table);
|
||||
|
||||
int dmfs_dev_activate_table(struct dmfs *dm, const char *dev,
|
||||
const char *table);
|
||||
|
||||
int dmfs_dev_deactivate(struct dmfs *dm, const char *dev);
|
||||
|
||||
#endif
|
||||
160
lib/activate/fs.c
Normal file
160
lib/activate/fs.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fs.h"
|
||||
#include "log.h"
|
||||
#include "names.h"
|
||||
|
||||
#include <libdevmapper.h>
|
||||
|
||||
|
||||
/*
|
||||
* Lazy programmer: I'm just going to always try
|
||||
* and create/remove the vg directory, and not say
|
||||
* anything if it fails.
|
||||
*/
|
||||
static int _mk_dir(struct volume_group *vg)
|
||||
{
|
||||
char vg_path[PATH_MAX];
|
||||
|
||||
if (!build_vg_path(vg_path, sizeof(vg_path),
|
||||
vg->cmd->dev_dir, vg->name)) {
|
||||
log_error("Couldn't construct name of volume group directory.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_very_verbose("Creating directory %s", vg_path);
|
||||
mkdir(vg_path, 0555);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _rm_dir(struct volume_group *vg)
|
||||
{
|
||||
char vg_path[PATH_MAX];
|
||||
|
||||
if (!build_vg_path(vg_path, sizeof(vg_path),
|
||||
vg->cmd->dev_dir, vg->name)) {
|
||||
log_error("Couldn't construct name of volume group dir for %s",
|
||||
vg->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_very_verbose("Removing directory %s", vg_path);
|
||||
rmdir(vg_path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _mk_link(struct logical_volume *lv)
|
||||
{
|
||||
char lv_path[PATH_MAX], link_path[PATH_MAX];
|
||||
struct stat buf;
|
||||
|
||||
if (!build_dm_path(lv_path, sizeof(lv_path), lv->vg->name, lv->name)) {
|
||||
log_error("Couldn't create destination pathname for "
|
||||
"logical volume link for %s", lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!build_lv_link_path(link_path, sizeof(link_path),
|
||||
lv->vg->cmd->dev_dir,
|
||||
lv->vg->name, lv->name)) {
|
||||
log_error("Couldn't create source pathname for "
|
||||
"logical volume link %s", lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lstat(link_path, &buf)) {
|
||||
if (!S_ISLNK(buf.st_mode)) {
|
||||
log_error("Symbolic link %s not created: file exists",
|
||||
link_path);
|
||||
return 0;
|
||||
}
|
||||
if (unlink(link_path) < 0) {
|
||||
log_sys_error("unlink", link_path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
log_very_verbose("Linking %s to %s", link_path, lv_path);
|
||||
if (symlink(lv_path, link_path) < 0) {
|
||||
log_sys_error("symlink", link_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _rm_link(struct logical_volume *lv, const char *lv_name)
|
||||
{
|
||||
struct stat buf;
|
||||
char link_path[PATH_MAX];
|
||||
|
||||
if (!lv_name)
|
||||
lv_name = lv->name;
|
||||
|
||||
if (!build_lv_link_path(link_path, sizeof(link_path),
|
||||
lv->vg->cmd->dev_dir,
|
||||
lv->vg->name, lv->name)) {
|
||||
log_error("Couldn't determine link pathname.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_very_verbose("Removing link %s", link_path);
|
||||
if (lstat(link_path, &buf) || !S_ISLNK(buf.st_mode)) {
|
||||
log_error("%s not symbolic link - not removing",
|
||||
link_path);
|
||||
return 0;
|
||||
}
|
||||
if (unlink(link_path) < 0) {
|
||||
log_sys_error("unlink", link_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fs_add_lv(struct logical_volume *lv)
|
||||
{
|
||||
if (!_mk_dir(lv->vg) ||
|
||||
!_mk_link(lv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fs_del_lv(struct logical_volume *lv)
|
||||
{
|
||||
if (!_rm_link(lv, NULL) ||
|
||||
!_rm_dir(lv->vg)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fs_rename_lv(const char *old_name, struct logical_volume *lv)
|
||||
{
|
||||
if (!_rm_link(lv, old_name))
|
||||
stack;
|
||||
|
||||
if (!_mk_link(lv))
|
||||
stack;
|
||||
|
||||
return 1;
|
||||
}
|
||||
23
lib/activate/fs.h
Normal file
23
lib/activate/fs.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FS_H
|
||||
#define _LVM_FS_H
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
/*
|
||||
* These calls, private to the activate unit, set
|
||||
* up the volume group directory in /dev and the
|
||||
* symbolic links to the dm device.
|
||||
*/
|
||||
|
||||
int fs_add_lv(struct logical_volume *lv);
|
||||
int fs_del_lv(struct logical_volume *lv);
|
||||
int fs_rename_lv(const char *old_name, struct logical_volume *lv);
|
||||
|
||||
|
||||
#endif
|
||||
88
lib/activate/names.c
Normal file
88
lib/activate/names.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "names.h"
|
||||
#include "lvm-string.h"
|
||||
#include "log.h"
|
||||
#include "limits.h"
|
||||
|
||||
#include <libdevmapper.h>
|
||||
|
||||
/*
|
||||
* The volume group name and the logical volume name are
|
||||
* seperated by a single ':', any colons in the vg name are
|
||||
* doubled up to form a pair.
|
||||
*/
|
||||
int build_dm_name(char *buffer, size_t len,
|
||||
const char *vg_name, const char *lv_name)
|
||||
{
|
||||
char *out;
|
||||
const char *in;
|
||||
|
||||
for (out = buffer, in = vg_name; len && *in; len--) {
|
||||
if (*in == ':') {
|
||||
*out++ = ':';
|
||||
if (!--len)
|
||||
break;
|
||||
}
|
||||
|
||||
*out++ = *in++;
|
||||
len--;
|
||||
}
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (lvm_snprintf(out, len, ":%s", lv_name) == -1) {
|
||||
log_err("Couldn't build logical volume name.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int build_dm_path(char *buffer, size_t len,
|
||||
const char *vg_name, const char *lv_name)
|
||||
{
|
||||
char dev_name[PATH_MAX];
|
||||
|
||||
if (!build_dm_name(dev_name, sizeof(dev_name), vg_name, lv_name)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lvm_snprintf(buffer, len, "%s/%s", dm_dir(), dev_name) == -1) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int build_vg_path(char *buffer, size_t len,
|
||||
const char *dev_dir, const char *vg_name)
|
||||
{
|
||||
if (lvm_snprintf(buffer, len, "%s%s", dev_dir, vg_name) == -1) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int build_lv_link_path(char *buffer, size_t len, const char *dev_dir,
|
||||
const char *vg_name, const char *lv_name)
|
||||
{
|
||||
if (lvm_snprintf(buffer, len, "%s%s/%s",
|
||||
dev_dir, vg_name, lv_name) == -1) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
50
lib/activate/names.h
Normal file
50
lib/activate/names.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_NAMES_H
|
||||
#define _LVM_NAMES_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Functions that build up useful paths to devices, sym-links
|
||||
* etc. Names are passed in as strings, rather than via the
|
||||
* appropriate metadata structures, so we can use it for renaming
|
||||
* devices.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* The name of the device-mapper device for a particular LV.
|
||||
* eg, vg0:music
|
||||
*/
|
||||
int build_dm_name(char *buffer, size_t len,
|
||||
const char *vg_name, const char *lv_name);
|
||||
|
||||
/*
|
||||
* The path of the device-mapper device for a particular LV.
|
||||
* eg, /dev/device-mapper/vg0:music
|
||||
*/
|
||||
int build_dm_path(char *buffer, size_t len,
|
||||
const char *vg_name, const char *lv_name);
|
||||
|
||||
/*
|
||||
* Path to the volume group directory.
|
||||
* eg, /dev/vg0
|
||||
*/
|
||||
int build_vg_path(char *buffer, size_t len,
|
||||
const char *dev_dir, const char *vg_name);
|
||||
|
||||
/*
|
||||
* Path to the symbolic link that lives in the volume group
|
||||
* directory.
|
||||
* eg, /dev/vg0/music
|
||||
*/
|
||||
int build_lv_link_path(char *buffer, size_t len,
|
||||
const char *dev_dir,
|
||||
const char *vg_name, const char *lv_name);
|
||||
|
||||
#endif
|
||||
40
lib/activate/table-build.c
Normal file
40
lib/activate/table-build.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "table-build.c"
|
||||
|
||||
static void _print_run(FILE *fp, struct logical_volume *lv)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
int build_table(struct volume_group *vg, struct logical_volume *lv,
|
||||
const char *file)
|
||||
{
|
||||
int i;
|
||||
uint64_t sector = 0;
|
||||
uint64_t pe_size = vg->extent_size;
|
||||
uint64_t dest;
|
||||
struct pe_specifier *pes;
|
||||
FILE *fp = fopen(file, "w");
|
||||
|
||||
if (!fp) {
|
||||
log_err("couldn't open '%s' to write table", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < lv->le_count; i++) {
|
||||
pes = lv->map + i;
|
||||
dest = pes->pv->pe_start + (pe_size * pes->pe);
|
||||
fprintf(fp, "%ull %ull linear %s %ull\n",
|
||||
sector, pe_size, pes->pv->dev->name, dest);
|
||||
sector += pe_size;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
13
lib/activate/table-build.h
Normal file
13
lib/activate/table-build.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_BUILD_H
|
||||
#define TABLE_BUILD_H
|
||||
|
||||
int build_table(struct volume_group *vg, struct logical_volume *lv,
|
||||
const char *file);
|
||||
|
||||
#endif
|
||||
674
lib/config/config.c
Normal file
674
lib/config/config.c
Normal file
@@ -0,0 +1,674 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "pool.h"
|
||||
#include "log.h"
|
||||
|
||||
enum {
|
||||
TOK_INT,
|
||||
TOK_FLOAT,
|
||||
TOK_STRING,
|
||||
TOK_EQ,
|
||||
TOK_SECTION_B,
|
||||
TOK_SECTION_E,
|
||||
TOK_ARRAY_B,
|
||||
TOK_ARRAY_E,
|
||||
TOK_IDENTIFIER,
|
||||
TOK_COMMA,
|
||||
TOK_EOF
|
||||
};
|
||||
|
||||
struct parser {
|
||||
const char *fb, *fe; /* file limits */
|
||||
|
||||
int t; /* token limits and type */
|
||||
const char *tb, *te;
|
||||
|
||||
int fd; /* descriptor for file being parsed */
|
||||
int line; /* line number we are on */
|
||||
|
||||
struct pool *mem;
|
||||
};
|
||||
|
||||
struct cs {
|
||||
struct config_file cf;
|
||||
struct pool *mem;
|
||||
};
|
||||
|
||||
static void _get_token(struct parser *p);
|
||||
static void _eat_space(struct parser *p);
|
||||
static struct config_node *_file(struct parser *p);
|
||||
static struct config_node *_section(struct parser *p);
|
||||
static struct config_value *_value(struct parser *p);
|
||||
static struct config_value *_type(struct parser *p);
|
||||
static int _match_aux(struct parser *p, int t);
|
||||
static struct config_value *_create_value(struct parser *p);
|
||||
static struct config_node *_create_node(struct parser *p);
|
||||
static char *_dup_tok(struct parser *p);
|
||||
|
||||
#define MAX_INDENT 32
|
||||
|
||||
#define match(t) do {\
|
||||
if (!_match_aux(p, (t))) {\
|
||||
log_error("Parse error at line %d: unexpected token", p->line); \
|
||||
return 0;\
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
|
||||
static int _tok_match(const char *str, const char *b, const char *e)
|
||||
{
|
||||
while (*str && (b != e)) {
|
||||
if (*str++ != *b++)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return !(*str || (b != e));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* public interface
|
||||
*/
|
||||
struct config_file *create_config_file(void)
|
||||
{
|
||||
struct cs *c;
|
||||
struct pool *mem = pool_create(10 * 1024);
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(c = pool_alloc(mem, sizeof(*c)))) {
|
||||
stack;
|
||||
pool_destroy(mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->mem = mem;
|
||||
c->cf.root = (struct config_node *)NULL;
|
||||
return &c->cf;
|
||||
}
|
||||
|
||||
void destroy_config_file(struct config_file *cf)
|
||||
{
|
||||
pool_destroy(((struct cs *) cf)->mem);
|
||||
}
|
||||
|
||||
int read_config(struct config_file *cf, const char *file)
|
||||
{
|
||||
struct cs *c = (struct cs *) cf;
|
||||
struct parser *p;
|
||||
struct stat info;
|
||||
int r = 1, fd;
|
||||
|
||||
if (!(p = pool_alloc(c->mem, sizeof(*p)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
p->mem = c->mem;
|
||||
|
||||
/* memory map the file */
|
||||
if (stat(file, &info) || S_ISDIR(info.st_mode)) {
|
||||
log_sys_error("stat", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((fd = open(file, O_RDONLY)) < 0) {
|
||||
log_sys_error("open", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->fb = mmap((caddr_t) 0, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (p->fb == MAP_FAILED) {
|
||||
log_sys_error("mmap", file);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
p->fe = p->fb + info.st_size;
|
||||
|
||||
/* parse */
|
||||
p->tb = p->te = p->fb;
|
||||
p->line = 1;
|
||||
_get_token(p);
|
||||
if (!(cf->root = _file(p))) {
|
||||
stack;
|
||||
r = 0;
|
||||
}
|
||||
|
||||
/* unmap the file */
|
||||
if (munmap((char *) p->fb, info.st_size)) {
|
||||
log_sys_error("munmap", file);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _write_value(FILE *fp, struct config_value *v)
|
||||
{
|
||||
switch (v->type) {
|
||||
case CFG_STRING:
|
||||
fprintf(fp, "\"%s\"", v->v.str);
|
||||
break;
|
||||
|
||||
case CFG_FLOAT:
|
||||
fprintf(fp, "%f", v->v.r);
|
||||
break;
|
||||
|
||||
case CFG_INT:
|
||||
fprintf(fp, "%d", v->v.i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int _write_config(struct config_node *n, FILE *fp, int level)
|
||||
{
|
||||
char space[MAX_INDENT + 1];
|
||||
int l = (level < MAX_INDENT) ? level : MAX_INDENT;
|
||||
int i;
|
||||
|
||||
if (!n)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
space[i] = ' ';
|
||||
space[i] = '\0';
|
||||
|
||||
while (n) {
|
||||
fprintf(fp, "%s%s", space, n->key);
|
||||
if (!n->v) {
|
||||
/* it's a sub section */
|
||||
fprintf(fp, " {\n");
|
||||
_write_config(n->child, fp, level + 1);
|
||||
fprintf(fp, "%s}", space);
|
||||
} else {
|
||||
/* it's a value */
|
||||
struct config_value *v = n->v;
|
||||
fprintf(fp, "=");
|
||||
if (v->next) {
|
||||
fprintf(fp, "[");
|
||||
while (v) {
|
||||
_write_value(fp, v);
|
||||
v = v->next;
|
||||
if (v)
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
fprintf(fp, "]");
|
||||
} else
|
||||
_write_value(fp, v);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
n = n->sib;
|
||||
}
|
||||
/* FIXME: add error checking */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int write_config(struct config_file *cf, const char *file)
|
||||
{
|
||||
int r = 1;
|
||||
FILE *fp = fopen(file, "w");
|
||||
if (!fp) {
|
||||
log_sys_error("open", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_config(cf->root, fp, 0)) {
|
||||
stack;
|
||||
r = 0;
|
||||
}
|
||||
fclose(fp);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* parser
|
||||
*/
|
||||
static struct config_node *_file(struct parser *p)
|
||||
{
|
||||
struct config_node *root = NULL, *n, *l = NULL;
|
||||
while (p->t != TOK_EOF) {
|
||||
if (!(n = _section(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!root)
|
||||
root = n;
|
||||
else
|
||||
l->sib = n;
|
||||
l = n;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
static struct config_node *_section(struct parser *p)
|
||||
{
|
||||
/* IDENTIFIER '{' VALUE* '}' */
|
||||
struct config_node *root, *n, *l = NULL;
|
||||
if (!(root = _create_node(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(root->key = _dup_tok(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
match (TOK_IDENTIFIER);
|
||||
|
||||
if (p->t == TOK_SECTION_B) {
|
||||
match(TOK_SECTION_B);
|
||||
while (p->t != TOK_SECTION_E) {
|
||||
if (!(n = _section(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!root->child)
|
||||
root->child = n;
|
||||
else
|
||||
l->sib = n;
|
||||
l = n;
|
||||
}
|
||||
match(TOK_SECTION_E);
|
||||
} else {
|
||||
match(TOK_EQ);
|
||||
if (!(root->v = _value(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static struct config_value *_value(struct parser *p)
|
||||
{
|
||||
/* '[' TYPE* ']' | TYPE */
|
||||
struct config_value *h = 0, *l, *ll = 0;
|
||||
if (p->t == TOK_ARRAY_B) {
|
||||
match (TOK_ARRAY_B);
|
||||
while (p->t != TOK_ARRAY_E) {
|
||||
if (!(l = _type(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!h)
|
||||
h = l;
|
||||
else
|
||||
ll->next = l;
|
||||
ll = l;
|
||||
|
||||
if (p->t == TOK_COMMA)
|
||||
match(TOK_COMMA);
|
||||
}
|
||||
match(TOK_ARRAY_E);
|
||||
} else
|
||||
h = _type(p);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static struct config_value *_type(struct parser *p)
|
||||
{
|
||||
/* [0-9]+ | [0-9]*\.[0-9]* | ".*" */
|
||||
struct config_value *v = _create_value(p);
|
||||
|
||||
switch (p->t) {
|
||||
case TOK_INT:
|
||||
v->type = CFG_INT;
|
||||
v->v.i = strtol(p->tb, 0, 0); /* FIXME: check error */
|
||||
match(TOK_INT);
|
||||
break;
|
||||
|
||||
case TOK_FLOAT:
|
||||
v->type = CFG_FLOAT;
|
||||
v->v.r = strtod(p->tb, 0); /* FIXME: check error */
|
||||
match(TOK_FLOAT);
|
||||
break;
|
||||
|
||||
case TOK_STRING:
|
||||
v->type = CFG_STRING;
|
||||
|
||||
p->tb++, p->te--; /* strip "'s */
|
||||
if (!(v->v.str = _dup_tok(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
p->te++;
|
||||
match(TOK_STRING);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Parse error at line %d: expected a value", p->line);
|
||||
return 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static int _match_aux(struct parser *p, int t)
|
||||
{
|
||||
if (p->t != t)
|
||||
return 0;
|
||||
|
||||
_get_token(p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* tokeniser
|
||||
*/
|
||||
static void _get_token(struct parser *p)
|
||||
{
|
||||
p->tb = p->te;
|
||||
_eat_space(p);
|
||||
if (p->tb == p->fe) {
|
||||
p->t = TOK_EOF;
|
||||
return;
|
||||
}
|
||||
|
||||
p->t = TOK_INT; /* fudge so the fall through for
|
||||
floats works */
|
||||
switch (*p->te) {
|
||||
case '{':
|
||||
p->t = TOK_SECTION_B;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
p->t = TOK_SECTION_E;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
p->t = TOK_ARRAY_B;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case ']':
|
||||
p->t = TOK_ARRAY_E;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
p->t = TOK_COMMA;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '=':
|
||||
p->t = TOK_EQ;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
p->t = TOK_STRING;
|
||||
p->te++;
|
||||
while ((p->te != p->fe) && (*p->te != '"')) {
|
||||
if ((*p->te == '\\') && (p->te + 1 != p->fe))
|
||||
p->te++;
|
||||
p->te++;
|
||||
}
|
||||
|
||||
if (p->te != p->fe)
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
p->t = TOK_FLOAT;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
p->te++;
|
||||
while (p->te != p->fe) {
|
||||
if (*p->te == '.') {
|
||||
if (p->t == TOK_FLOAT)
|
||||
break;
|
||||
p->t = TOK_FLOAT;
|
||||
} else if (!isdigit((int) *p->te))
|
||||
break;
|
||||
p->te++;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
p->t = TOK_IDENTIFIER;
|
||||
while ((p->te != p->fe) && !isspace(*p->te) &&
|
||||
(*p->te != '#') && (*p->te != '='))
|
||||
p->te++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _eat_space(struct parser *p)
|
||||
{
|
||||
while (p->tb != p->fe) {
|
||||
if (*p->te == '#') {
|
||||
while ((p->te != p->fe) && (*p->te != '\n'))
|
||||
p->te++;
|
||||
p->line++;
|
||||
}
|
||||
|
||||
else if (isspace(*p->te)) {
|
||||
while ((p->te != p->fe) && isspace(*p->te)) {
|
||||
if (*p->te == '\n')
|
||||
p->line++;
|
||||
p->te++;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
return;
|
||||
|
||||
p->tb = p->te;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* memory management
|
||||
*/
|
||||
static struct config_value *_create_value(struct parser *p)
|
||||
{
|
||||
struct config_value *v = pool_alloc(p->mem, sizeof(*v));
|
||||
memset(v, 0, sizeof(*v));
|
||||
return v;
|
||||
}
|
||||
|
||||
static struct config_node *_create_node(struct parser *p)
|
||||
{
|
||||
struct config_node *n = pool_alloc(p->mem, sizeof(*n));
|
||||
memset(n, 0, sizeof(*n));
|
||||
return n;
|
||||
}
|
||||
|
||||
static char *_dup_tok(struct parser *p)
|
||||
{
|
||||
int len = p->te - p->tb;
|
||||
char *str = pool_alloc(p->mem, len + 1);
|
||||
if (!str) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
strncpy(str, p->tb, len);
|
||||
str[len] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
struct config_node *find_config_node(struct config_node *cn,
|
||||
const char *path, char sep)
|
||||
{
|
||||
const char *e;
|
||||
|
||||
while (cn) {
|
||||
/* trim any leading slashes */
|
||||
while (*path && (*path == sep))
|
||||
path++;
|
||||
|
||||
/* find the end of this segment */
|
||||
for (e = path; *e && (*e != sep); e++)
|
||||
;
|
||||
|
||||
/* hunt for the node */
|
||||
while (cn) {
|
||||
if (_tok_match(cn->key, path, e))
|
||||
break;
|
||||
|
||||
cn = cn->sib;
|
||||
}
|
||||
|
||||
if (cn && *e)
|
||||
cn = cn->child;
|
||||
else
|
||||
break; /* don't move into the last node */
|
||||
|
||||
path = e;
|
||||
}
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
const char *
|
||||
find_config_str(struct config_node *cn,
|
||||
const char *path, char sep, const char *fail)
|
||||
{
|
||||
struct config_node *n = find_config_node(cn, path, sep);
|
||||
|
||||
if (n && n->v->type == CFG_STRING) {
|
||||
log_very_verbose("Setting %s to %s", path, n->v->v.str);
|
||||
return n->v->v.str;
|
||||
}
|
||||
|
||||
if (fail)
|
||||
log_very_verbose("%s not found in config: defaulting to %s",
|
||||
path, fail);
|
||||
return fail;
|
||||
}
|
||||
|
||||
int find_config_int(struct config_node *cn, const char *path,
|
||||
char sep, int fail)
|
||||
{
|
||||
struct config_node *n = find_config_node(cn, path, sep);
|
||||
|
||||
if (n && n->v->type == CFG_INT) {
|
||||
log_very_verbose("Setting %s to %d", path, n->v->v.i);
|
||||
return n->v->v.i;
|
||||
}
|
||||
|
||||
log_very_verbose("%s not found in config: defaulting to %d",
|
||||
path, fail);
|
||||
return fail;
|
||||
}
|
||||
|
||||
float find_config_float(struct config_node *cn, const char *path,
|
||||
char sep, float fail)
|
||||
{
|
||||
struct config_node *n = find_config_node(cn, path, sep);
|
||||
|
||||
if (n && n->v->type == CFG_FLOAT) {
|
||||
log_very_verbose("Setting %s to %f", path, n->v->v.r);
|
||||
return n->v->v.r;
|
||||
}
|
||||
|
||||
log_very_verbose("%s not found in config: defaulting to %f",
|
||||
path, fail);
|
||||
|
||||
return fail;
|
||||
|
||||
}
|
||||
|
||||
static int _str_in_array(const char *str, const char *values[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; values[i]; i++)
|
||||
if (!strcasecmp(str, values[i]))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _str_to_bool(const char *str, int fail)
|
||||
{
|
||||
static const char *_true_values[] = {"y", "yes", "on", "true", NULL};
|
||||
static const char *_false_values[] = {"n", "no", "off", "false", NULL};
|
||||
|
||||
if (_str_in_array(str, _true_values))
|
||||
return 1;
|
||||
|
||||
if (_str_in_array(str, _false_values))
|
||||
return 0;
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
int find_config_bool(struct config_node *cn, const char *path,
|
||||
char sep, int fail)
|
||||
{
|
||||
struct config_node *n = find_config_node(cn, path, sep);
|
||||
struct config_value *v;
|
||||
|
||||
if (!n)
|
||||
return fail;
|
||||
|
||||
v = n->v;
|
||||
|
||||
switch (v->type) {
|
||||
case CFG_INT:
|
||||
return v->v.i ? 1 : 0;
|
||||
|
||||
case CFG_STRING:
|
||||
return _str_to_bool(v->v.str, fail);
|
||||
}
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
int get_config_uint32(struct config_node *cn, const char *path,
|
||||
char sep, uint32_t *result)
|
||||
{
|
||||
struct config_node *n;
|
||||
|
||||
n = find_config_node(cn, path, sep);
|
||||
|
||||
if (!n || !n->v || n->v->type != CFG_INT)
|
||||
return 0;
|
||||
|
||||
*result = n->v->v.i;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int get_config_uint64(struct config_node *cn, const char *path,
|
||||
char sep, uint64_t *result)
|
||||
{
|
||||
struct config_node *n;
|
||||
|
||||
n = find_config_node(cn, path, sep);
|
||||
|
||||
if (!n || !n->v || n->v->type != CFG_INT)
|
||||
return 0;
|
||||
|
||||
/* FIXME Support 64-bit value! */
|
||||
*result = (uint64_t) n->v->v.i;
|
||||
return 1;
|
||||
}
|
||||
|
||||
72
lib/config/config.h
Normal file
72
lib/config/config.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_CONFIG_H
|
||||
#define _LVM_CONFIG_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
enum {
|
||||
CFG_STRING,
|
||||
CFG_FLOAT,
|
||||
CFG_INT,
|
||||
};
|
||||
|
||||
struct config_value {
|
||||
int type;
|
||||
union {
|
||||
int i;
|
||||
float r;
|
||||
char *str;
|
||||
} v;
|
||||
struct config_value *next; /* for arrays */
|
||||
};
|
||||
|
||||
struct config_node {
|
||||
char *key;
|
||||
struct config_node *sib, *child;
|
||||
struct config_value *v;
|
||||
};
|
||||
|
||||
struct config_file {
|
||||
struct config_node *root;
|
||||
};
|
||||
|
||||
struct config_file *create_config_file(void);
|
||||
void destroy_config_file(struct config_file *cf);
|
||||
|
||||
int read_config(struct config_file *cf, const char *file);
|
||||
int write_config(struct config_file *cf, const char *file);
|
||||
|
||||
struct config_node *find_config_node(struct config_node *cn,
|
||||
const char *path, char seperator);
|
||||
|
||||
const char *find_config_str(struct config_node *cn,
|
||||
const char *path, char sep, const char *fail);
|
||||
|
||||
int find_config_int(struct config_node *cn, const char *path,
|
||||
char sep, int fail);
|
||||
|
||||
float find_config_float(struct config_node *cn, const char *path,
|
||||
char sep, float fail);
|
||||
|
||||
/*
|
||||
* Understands (0, ~0), (y, n), (yes, no), (on,
|
||||
* off), (true, false).
|
||||
*/
|
||||
int find_config_bool(struct config_node *cn, const char *path,
|
||||
char sep, int fail);
|
||||
|
||||
|
||||
|
||||
int get_config_uint32(struct config_node *cn, const char *path,
|
||||
char sep, uint32_t *result);
|
||||
|
||||
int get_config_uint64(struct config_node *cn, const char *path,
|
||||
char sep, uint64_t *result);
|
||||
|
||||
#endif
|
||||
|
||||
79
lib/datastruct/bitset.c
Normal file
79
lib/datastruct/bitset.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "bitset.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* FIXME: calculate this. */
|
||||
#define INT_SHIFT 5
|
||||
|
||||
bitset_t bitset_create(struct pool *mem, unsigned num_bits)
|
||||
{
|
||||
int n = (num_bits / BITS_PER_INT) + 2;
|
||||
int size = sizeof(int) * n;
|
||||
unsigned *bs = pool_zalloc(mem, size);
|
||||
|
||||
if (!bs)
|
||||
return NULL;
|
||||
|
||||
*bs = num_bits;
|
||||
return bs;
|
||||
}
|
||||
|
||||
void bitset_destroy(bitset_t bs)
|
||||
{
|
||||
dbg_free(bs);
|
||||
}
|
||||
|
||||
void bit_union(bitset_t out, bitset_t in1, bitset_t in2)
|
||||
{
|
||||
int i;
|
||||
for(i = (in1[0] / BITS_PER_INT) + 1; i; i--)
|
||||
out[i] = in1[i] | in2[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: slow
|
||||
*/
|
||||
static inline int _test_word(uint32_t test, int bit)
|
||||
{
|
||||
while (bit < BITS_PER_INT) {
|
||||
if (test & (0x1 << bit))
|
||||
return bit;
|
||||
bit++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bit_get_next(bitset_t bs, int last_bit)
|
||||
{
|
||||
int bit, word;
|
||||
uint32_t test;
|
||||
|
||||
last_bit++; /* otherwise we'll return the same bit again */
|
||||
|
||||
while(last_bit < bs[0]) {
|
||||
word = last_bit >> INT_SHIFT;
|
||||
test = bs[word + 1];
|
||||
bit = last_bit & (BITS_PER_INT - 1);
|
||||
|
||||
if ((bit = _test_word(test, bit)) >= 0)
|
||||
return (word * BITS_PER_INT) + bit;
|
||||
|
||||
last_bit = last_bit - (last_bit & (BITS_PER_INT - 1)) +
|
||||
BITS_PER_INT;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bit_get_first(bitset_t bs)
|
||||
{
|
||||
return bit_get_next(bs, -1);
|
||||
}
|
||||
46
lib/datastruct/bitset.h
Normal file
46
lib/datastruct/bitset.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_BITSET_H
|
||||
#define _LVM_BITSET_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
#include "pool.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
typedef uint32_t *bitset_t;
|
||||
|
||||
bitset_t bitset_create(struct pool *mem, unsigned num_bits);
|
||||
|
||||
void bit_union(bitset_t out, bitset_t in1, bitset_t in2);
|
||||
int bit_get_first(bitset_t bs);
|
||||
int bit_get_next(bitset_t bs, int last_bit);
|
||||
|
||||
|
||||
#define BITS_PER_INT (sizeof(int) * CHAR_BIT)
|
||||
|
||||
#define bit(bs, i) \
|
||||
(bs[(i / BITS_PER_INT) + 1] & (0x1 << (i & (BITS_PER_INT - 1))))
|
||||
|
||||
#define bit_set(bs, i) \
|
||||
(bs[(i / BITS_PER_INT) + 1] |= (0x1 << (i & (BITS_PER_INT - 1))))
|
||||
|
||||
#define bit_clear(bs, i) \
|
||||
(bs[(i / BITS_PER_INT) + 1] &= ~(0x1 << (i & (BITS_PER_INT - 1))))
|
||||
|
||||
#define bit_set_all(bs) \
|
||||
memset(bs + 1, -1, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
|
||||
|
||||
#define bit_clear_all(bs) \
|
||||
memset(bs + 1, 0, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
|
||||
|
||||
#define bit_copy(bs1, bs2) \
|
||||
memcpy(bs1 + 1, bs2 + 1, ((*bs1 / BITS_PER_INT) + 1) * sizeof(int))
|
||||
|
||||
#endif
|
||||
130
lib/datastruct/btree.c
Normal file
130
lib/datastruct/btree.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "btree.h"
|
||||
#include "log.h"
|
||||
|
||||
struct node {
|
||||
uint32_t key;
|
||||
struct node *l, *r, *p;
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct btree {
|
||||
struct pool *mem;
|
||||
struct node *root;
|
||||
};
|
||||
|
||||
struct btree *btree_create(struct pool *mem)
|
||||
{
|
||||
struct btree *t = pool_alloc(mem, sizeof(*t));
|
||||
|
||||
if (t) {
|
||||
t->mem = mem;
|
||||
t->root = NULL;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shuffle the bits in a key, to try and remove
|
||||
* any ordering.
|
||||
*/
|
||||
static uint32_t _shuffle(uint32_t k)
|
||||
{
|
||||
#if 1
|
||||
return ((k & 0xff) << 24 |
|
||||
(k & 0xff00) << 8 |
|
||||
(k & 0xff0000) >> 8 |
|
||||
(k & 0xff000000) >> 24);
|
||||
#else
|
||||
return k;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct node **_lookup(struct node **c, uint32_t key, struct node **p)
|
||||
{
|
||||
*p = NULL;
|
||||
while (*c) {
|
||||
*p = *c;
|
||||
if ((*c)->key == key)
|
||||
break;
|
||||
|
||||
if (key < (*c)->key)
|
||||
c = &(*c)->l;
|
||||
|
||||
else
|
||||
c = &(*c)->r;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void *btree_lookup(struct btree *t, uint32_t k)
|
||||
{
|
||||
uint32_t key = _shuffle(k);
|
||||
struct node *p, **c = _lookup(&t->root, key, &p);
|
||||
return (*c) ? (*c)->data : NULL;
|
||||
}
|
||||
|
||||
int btree_insert(struct btree *t, uint32_t k, void *data)
|
||||
{
|
||||
uint32_t key = _shuffle(k);
|
||||
struct node *p, **c = _lookup(&t->root, key, &p), *n;
|
||||
|
||||
if (!*c) {
|
||||
if (!(n = pool_alloc(t->mem, sizeof(*n)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
n->key = key;
|
||||
n->data = data;
|
||||
n->l = n->r = NULL;
|
||||
n->p = p;
|
||||
|
||||
*c = n;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *btree_get_data(struct btree_iter *it)
|
||||
{
|
||||
return ((struct node *) it)->data;
|
||||
}
|
||||
|
||||
static inline struct node *_left(struct node *n)
|
||||
{
|
||||
while (n->l)
|
||||
n = n->l;
|
||||
return n;
|
||||
}
|
||||
|
||||
struct btree_iter *btree_first(struct btree *t)
|
||||
{
|
||||
if (!t->root)
|
||||
return NULL;
|
||||
|
||||
return (struct btree_iter *) _left(t->root);
|
||||
}
|
||||
|
||||
struct btree_iter *btree_next(struct btree_iter *it)
|
||||
{
|
||||
struct node *n = (struct node *) it;
|
||||
uint32_t k = n->key;
|
||||
|
||||
if (n->r)
|
||||
return (struct btree_iter *) _left(n->r);
|
||||
|
||||
do
|
||||
n = n->p;
|
||||
while (n && k > n->key);
|
||||
|
||||
return (struct btree_iter *) n;
|
||||
}
|
||||
26
lib/datastruct/btree.h
Normal file
26
lib/datastruct/btree.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_BTREE_H
|
||||
#define _LVM_BTREE_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
#include "pool.h"
|
||||
|
||||
struct btree;
|
||||
|
||||
struct btree *btree_create(struct pool *mem);
|
||||
|
||||
void *btree_lookup(struct btree *t, uint32_t k);
|
||||
int btree_insert(struct btree *t, uint32_t k, void *data);
|
||||
|
||||
struct btree_iter;
|
||||
void *btree_get_data(struct btree_iter *it);
|
||||
|
||||
struct btree_iter *btree_first(struct btree *t);
|
||||
struct btree_iter *btree_next(struct btree_iter *it);
|
||||
|
||||
#endif
|
||||
226
lib/datastruct/hash.c
Normal file
226
lib/datastruct/hash.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "hash.h"
|
||||
#include "log.h"
|
||||
|
||||
struct hash_node {
|
||||
struct hash_node *next;
|
||||
void *data;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
struct hash_table {
|
||||
int num_nodes;
|
||||
int num_slots;
|
||||
struct hash_node **slots;
|
||||
};
|
||||
|
||||
/* Permutation of the Integers 0 through 255 */
|
||||
static unsigned char _nums[] = {
|
||||
1, 14,110, 25, 97,174,132,119,138,170,125,118, 27,233,140, 51,
|
||||
87,197,177,107,234,169, 56, 68, 30, 7,173, 73,188, 40, 36, 65,
|
||||
49,213,104,190, 57,211,148,223, 48,115, 15, 2, 67,186,210, 28,
|
||||
12,181,103, 70, 22, 58, 75, 78,183,167,238,157,124,147,172,144,
|
||||
176,161,141, 86, 60, 66,128, 83,156,241, 79, 46,168,198, 41,254,
|
||||
178, 85,253,237,250,154,133, 88, 35,206, 95,116,252,192, 54,221,
|
||||
102,218,255,240, 82,106,158,201, 61, 3, 89, 9, 42,155,159, 93,
|
||||
166, 80, 50, 34,175,195,100, 99, 26,150, 16,145, 4, 33, 8,189,
|
||||
121, 64, 77, 72,208,245,130,122,143, 55,105,134, 29,164,185,194,
|
||||
193,239,101,242, 5,171,126, 11, 74, 59,137,228,108,191,232,139,
|
||||
6, 24, 81, 20,127, 17, 91, 92,251,151,225,207, 21, 98,113,112,
|
||||
84,226, 18,214,199,187, 13, 32, 94,220,224,212,247,204,196, 43,
|
||||
249,236, 45,244,111,182,153,136,129, 90,217,202, 19,165,231, 71,
|
||||
230,142, 96,227, 62,179,246,114,162, 53,160,215,205,180, 47,109,
|
||||
44, 38, 31,149,135, 0,216, 52, 63, 23, 37, 69, 39,117,146,184,
|
||||
163,200,222,235,248,243,219, 10,152,131,123,229,203, 76,120,209
|
||||
};
|
||||
|
||||
static struct hash_node *_create_node(const char *str)
|
||||
{
|
||||
/* remember sizeof(n) includes an extra char from key[1],
|
||||
so not adding 1 to the strlen as you would expect */
|
||||
struct hash_node *n = dbg_malloc(sizeof(*n) + strlen(str));
|
||||
|
||||
if (n)
|
||||
strcpy(n->key, str);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned _hash(const char *str)
|
||||
{
|
||||
unsigned long int h = 0, g;
|
||||
while (*str) {
|
||||
h <<= 4;
|
||||
h += _nums[(int) *str++];
|
||||
g = h & ((unsigned long) 0xf << 16u);
|
||||
if (g) {
|
||||
h ^= g >> 16u;
|
||||
h ^= g >> 5u;
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
struct hash_table *hash_create(unsigned size_hint)
|
||||
{
|
||||
size_t len;
|
||||
unsigned new_size = 16u;
|
||||
struct hash_table *hc = dbg_malloc(sizeof(*hc));
|
||||
|
||||
if (!hc) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(hc, 0, sizeof(*hc));
|
||||
|
||||
/* round size hint up to a power of two */
|
||||
while (new_size < size_hint)
|
||||
new_size = new_size << 1;
|
||||
|
||||
hc->num_slots = new_size;
|
||||
len = sizeof(*(hc->slots)) * new_size;
|
||||
if (!(hc->slots = dbg_malloc(len))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
memset(hc->slots, 0, len);
|
||||
return hc;
|
||||
|
||||
bad:
|
||||
dbg_free(hc->slots);
|
||||
dbg_free(hc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _free_nodes(struct hash_table *t)
|
||||
{
|
||||
struct hash_node *c, *n;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < t->num_slots; i++)
|
||||
for (c = t->slots[i]; c; c = n) {
|
||||
n = c->next;
|
||||
dbg_free(c);
|
||||
}
|
||||
}
|
||||
|
||||
void hash_destroy(struct hash_table *t)
|
||||
{
|
||||
_free_nodes(t);
|
||||
dbg_free(t->slots);
|
||||
dbg_free(t);
|
||||
}
|
||||
|
||||
static inline struct hash_node **_find(struct hash_table *t, const char *key)
|
||||
{
|
||||
unsigned h = _hash(key) & (t->num_slots - 1);
|
||||
struct hash_node **c;
|
||||
|
||||
for(c = &t->slots[h]; *c; c = &((*c)->next))
|
||||
if(!strcmp(key, (*c)->key))
|
||||
break;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void *hash_lookup(struct hash_table *t, const char *key)
|
||||
{
|
||||
struct hash_node **c = _find(t, key);
|
||||
return *c ? (*c)->data : 0;
|
||||
}
|
||||
|
||||
int hash_insert(struct hash_table *t, const char *key, void *data)
|
||||
{
|
||||
struct hash_node **c = _find(t, key);
|
||||
|
||||
if(*c)
|
||||
(*c)->data = data;
|
||||
else {
|
||||
struct hash_node *n = _create_node(key);
|
||||
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
n->data = data;
|
||||
n->next = 0;
|
||||
*c = n;
|
||||
t->num_nodes++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void hash_remove(struct hash_table *t, const char *key)
|
||||
{
|
||||
struct hash_node **c = _find(t, key);
|
||||
|
||||
if (*c) {
|
||||
struct hash_node *old = *c;
|
||||
*c = (*c)->next;
|
||||
dbg_free(old);
|
||||
t->num_nodes--;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned hash_get_num_entries(struct hash_table *t)
|
||||
{
|
||||
return t->num_nodes;
|
||||
}
|
||||
|
||||
void hash_iterate(struct hash_table *t, iterate_fn f)
|
||||
{
|
||||
struct hash_node *c;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < t->num_slots; i++)
|
||||
for (c = t->slots[i]; c; c = c->next)
|
||||
f(c->data);
|
||||
}
|
||||
|
||||
void hash_wipe(struct hash_table *t)
|
||||
{
|
||||
_free_nodes(t);
|
||||
memset(t->slots, 0, sizeof(struct hash_node *) * t->num_slots);
|
||||
t->num_nodes = 0;
|
||||
}
|
||||
|
||||
char *hash_get_key(struct hash_table *t, struct hash_node *n)
|
||||
{
|
||||
return n->key;
|
||||
}
|
||||
|
||||
void *hash_get_data(struct hash_table *t, struct hash_node *n)
|
||||
{
|
||||
return n->data;
|
||||
}
|
||||
|
||||
static struct hash_node *_next_slot(struct hash_table *t, unsigned int s)
|
||||
{
|
||||
struct hash_node *c = NULL;
|
||||
int i;
|
||||
|
||||
for (i = s; i < t->num_slots && !c; i++)
|
||||
c = t->slots[i];
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
struct hash_node *hash_get_first(struct hash_table *t)
|
||||
{
|
||||
return _next_slot(t, 0);
|
||||
}
|
||||
|
||||
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n)
|
||||
{
|
||||
unsigned int h = _hash(n->key) & (t->num_slots - 1);
|
||||
return n->next ? n->next : _next_slot(t, h + 1);
|
||||
}
|
||||
|
||||
32
lib/datastruct/hash.h
Normal file
32
lib/datastruct/hash.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_HASH_H
|
||||
#define _LVM_HASH_H
|
||||
|
||||
struct hash_table;
|
||||
struct hash_node;
|
||||
|
||||
typedef void (*iterate_fn)(void *data);
|
||||
|
||||
struct hash_table *hash_create(unsigned size_hint);
|
||||
void hash_destroy(struct hash_table *t);
|
||||
void hash_wipe(struct hash_table *t);
|
||||
|
||||
void *hash_lookup(struct hash_table *t, const char *key);
|
||||
int hash_insert(struct hash_table *t, const char *key, void *data);
|
||||
void hash_remove(struct hash_table *t, const char *key);
|
||||
|
||||
unsigned hash_get_num_entries(struct hash_table *t);
|
||||
void hash_iterate(struct hash_table *t, iterate_fn f);
|
||||
|
||||
char *hash_get_key(struct hash_table *t, struct hash_node *n);
|
||||
void *hash_get_data(struct hash_table *t, struct hash_node *n);
|
||||
struct hash_node *hash_get_first(struct hash_table *t);
|
||||
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n);
|
||||
|
||||
#endif
|
||||
|
||||
72
lib/datastruct/list.h
Normal file
72
lib/datastruct/list.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LIST_H
|
||||
#define _LVM_LIST_H
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
struct list {
|
||||
struct list *n, *p;
|
||||
};
|
||||
|
||||
static inline void list_init(struct list *head) {
|
||||
head->n = head->p = head;
|
||||
}
|
||||
|
||||
static inline void list_add(struct list *head, struct list *elem) {
|
||||
assert(head->n);
|
||||
|
||||
elem->n = head;
|
||||
elem->p = head->p;
|
||||
|
||||
head->p->n = elem;
|
||||
head->p = elem;
|
||||
}
|
||||
|
||||
static inline void list_add_h(struct list *head, struct list *elem) {
|
||||
assert(head->n);
|
||||
|
||||
elem->n = head->n;
|
||||
elem->p = head;
|
||||
|
||||
head->n->p = elem;
|
||||
head->n = elem;
|
||||
}
|
||||
|
||||
static inline void list_del(struct list *elem) {
|
||||
elem->n->p = elem->p;
|
||||
elem->p->n = elem->n;
|
||||
}
|
||||
|
||||
static inline int list_empty(struct list *head) {
|
||||
return head->n == head;
|
||||
}
|
||||
|
||||
#define list_iterate(v, head) \
|
||||
for (v = (head)->n; v != head; v = v->n)
|
||||
|
||||
#define list_iterate_safe(v, t, head) \
|
||||
for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
|
||||
|
||||
static inline int list_size(struct list *head) {
|
||||
int s = 0;
|
||||
struct list *v;
|
||||
|
||||
list_iterate(v, head)
|
||||
s++;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#define list_item(v, t) \
|
||||
((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->list))
|
||||
|
||||
/* Given a known element in a known structure, locate the struct list */
|
||||
#define list_head(v, t, e) \
|
||||
(((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->list)
|
||||
|
||||
#endif
|
||||
20
lib/datastruct/lvm-types.h
Normal file
20
lib/datastruct/lvm-types.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_TYPES_H
|
||||
#define _LVM_TYPES_H
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
struct str_list {
|
||||
struct list list;
|
||||
char *str;
|
||||
};
|
||||
|
||||
#endif
|
||||
402
lib/device/dev-cache.c
Normal file
402
lib/device/dev-cache.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "dev-cache.h"
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "hash.h"
|
||||
#include "list.h"
|
||||
#include "lvm-types.h"
|
||||
#include "btree.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <dirent.h>
|
||||
#include <linux/kdev_t.h>
|
||||
|
||||
/*
|
||||
* FIXME: really need to seperate names from the devices since
|
||||
* multiple names can point to the same device.
|
||||
*/
|
||||
|
||||
struct dev_iter {
|
||||
struct btree_iter *current;
|
||||
struct dev_filter *filter;
|
||||
};
|
||||
|
||||
struct dir_list {
|
||||
struct list list;
|
||||
char dir[0];
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct pool *mem;
|
||||
struct hash_table *names;
|
||||
struct btree *devices;
|
||||
|
||||
int has_scanned;
|
||||
struct list dirs;
|
||||
|
||||
} _cache;
|
||||
|
||||
|
||||
#define _alloc(x) pool_alloc(_cache.mem, (x))
|
||||
#define _free(x) pool_free(_cache.mem, (x))
|
||||
|
||||
static int _insert(const char *path, int rec);
|
||||
|
||||
static struct device *_create_dev(dev_t d)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
if (!(dev = _alloc(sizeof(*dev)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_init(&dev->aliases);
|
||||
dev->dev = d;
|
||||
dev->fd = -1;
|
||||
return dev;
|
||||
}
|
||||
|
||||
static int _add_alias(struct device *dev, const char *path)
|
||||
{
|
||||
struct str_list *sl = _alloc(sizeof(*sl));
|
||||
|
||||
if (!sl) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(sl->str = pool_strdup(_cache.mem, path))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(&dev->aliases, &sl->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Either creates a new dev, or adds an alias to
|
||||
* an existing dev.
|
||||
*/
|
||||
static int _insert_dev(const char *path, dev_t d)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
/* is this device already registered ? */
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices, d))) {
|
||||
/* create new device */
|
||||
if (!(dev = _create_dev(d))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(btree_insert(_cache.devices, d, dev))) {
|
||||
log_err("Couldn't insert device into binary tree.");
|
||||
_free(dev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_add_alias(dev, path)) {
|
||||
log_err("Couldn't add alias to dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!hash_insert(_cache.names, path, dev)) {
|
||||
log_err("Couldn't add name to hash in dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *_join(const char *dir, const char *name)
|
||||
{
|
||||
int len = strlen(dir) + strlen(name) + 2;
|
||||
char *r = dbg_malloc(len);
|
||||
if (r)
|
||||
snprintf(r, len, "%s/%s", dir, name);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get rid of extra slashes in the path string.
|
||||
*/
|
||||
static void _collapse_slashes(char *str)
|
||||
{
|
||||
char *ptr;
|
||||
int was_slash = 0;
|
||||
|
||||
for (ptr = str; *ptr; ptr++) {
|
||||
if (*ptr == '/') {
|
||||
if (was_slash)
|
||||
continue;
|
||||
|
||||
was_slash = 1;
|
||||
} else
|
||||
was_slash = 0;
|
||||
*str++ = *ptr;
|
||||
}
|
||||
|
||||
*str = *ptr;
|
||||
}
|
||||
|
||||
static int _insert_dir(const char *dir)
|
||||
{
|
||||
int n, dirent_count, r = 1;
|
||||
struct dirent **dirent;
|
||||
char *path;
|
||||
|
||||
dirent_count = scandir(dir, &dirent, NULL, alphasort);
|
||||
if (dirent_count > 0) {
|
||||
for (n = 0; n < dirent_count; n++) {
|
||||
if (dirent[n]->d_name[0] == '.') {
|
||||
free(dirent[n]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(path = _join(dir, dirent[n]->d_name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_collapse_slashes(path);
|
||||
r &= _insert(path, 1);
|
||||
dbg_free(path);
|
||||
|
||||
free(dirent[n]);
|
||||
}
|
||||
free(dirent);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _insert(const char *path, int rec)
|
||||
{
|
||||
struct stat info;
|
||||
int r = 0;
|
||||
|
||||
if (stat(path, &info) < 0) {
|
||||
log_sys_very_verbose("stat", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (S_ISDIR(info.st_mode)) { /* add a directory */
|
||||
if (rec)
|
||||
r = _insert_dir(path);
|
||||
|
||||
} else { /* add a device */
|
||||
if (!S_ISBLK(info.st_mode)) {
|
||||
log_debug("%s: Not a block device", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_insert_dev(path, info.st_rdev)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = 1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _full_scan(void)
|
||||
{
|
||||
struct list *dh;
|
||||
|
||||
if (_cache.has_scanned)
|
||||
return;
|
||||
|
||||
list_iterate(dh, &_cache.dirs) {
|
||||
struct dir_list *dl = list_item(dh, struct dir_list);
|
||||
_insert_dir(dl->dir);
|
||||
};
|
||||
|
||||
_cache.has_scanned = 1;
|
||||
}
|
||||
|
||||
int dev_cache_init(void)
|
||||
{
|
||||
_cache.names = NULL;
|
||||
|
||||
if (!(_cache.mem = pool_create(10 * 1024))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(_cache.names = hash_create(128))) {
|
||||
stack;
|
||||
pool_destroy(_cache.mem);
|
||||
_cache.mem = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(_cache.devices = btree_create(_cache.mem))) {
|
||||
log_err("Couldn't create binary tree for dev-cache.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_init(&_cache.dirs);
|
||||
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
dev_cache_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _check_closed(struct device *dev)
|
||||
{
|
||||
if (dev->fd >= 0)
|
||||
log_err("Device '%s' has been left open.", dev_name(dev));
|
||||
}
|
||||
|
||||
static inline void _check_for_open_devices(void)
|
||||
{
|
||||
hash_iterate(_cache.names, (iterate_fn)_check_closed);
|
||||
}
|
||||
|
||||
void dev_cache_exit(void)
|
||||
{
|
||||
_check_for_open_devices();
|
||||
|
||||
pool_destroy(_cache.mem);
|
||||
if (_cache.names)
|
||||
hash_destroy(_cache.names);
|
||||
}
|
||||
|
||||
int dev_cache_add_dir(const char *path)
|
||||
{
|
||||
struct dir_list *dl;
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st)) {
|
||||
log_error("Ignoring %s: %s", path, strerror(errno));
|
||||
/* But don't fail */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
log_error("Ignoring %s: Not a directory", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1)))
|
||||
return 0;
|
||||
|
||||
strcpy(dl->dir, path);
|
||||
list_add(&_cache.dirs, &dl->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check cached device name is still valid before returning it */
|
||||
/* This should be a rare occurrence */
|
||||
/* FIXME Make rest of code pass/cache struct device instead of dev_name */
|
||||
const char *dev_name_confirmed(struct device *dev)
|
||||
{
|
||||
struct stat buf;
|
||||
char *name;
|
||||
int r;
|
||||
|
||||
while ((r = stat(name = list_item(dev->aliases.n,
|
||||
struct str_list)->str, &buf)) ||
|
||||
(buf.st_rdev != dev->dev)) {
|
||||
if (r < 0)
|
||||
log_sys_error("stat", name);
|
||||
log_error("Path %s no longer valid for device(%d,%d)",
|
||||
name, (int) MAJOR(dev->dev), (int) MINOR(dev->dev));
|
||||
|
||||
/* Remove the incorrect hash entry */
|
||||
hash_remove(_cache.names, name);
|
||||
|
||||
/* Leave list alone if there isn't an alternative name */
|
||||
/* so dev_name will always find something to return. */
|
||||
/* Otherwise add the name to the correct device. */
|
||||
if (list_size(&dev->aliases) > 1) {
|
||||
list_del(dev->aliases.n);
|
||||
if (!r)
|
||||
_insert(name, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_error("Aborting - please provide new pathname for what "
|
||||
"used to be %s", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
|
||||
struct device *dev_cache_get(const char *name, struct dev_filter *f)
|
||||
{
|
||||
struct stat buf;
|
||||
struct device *d = (struct device *) hash_lookup(_cache.names, name);
|
||||
|
||||
/* If the entry's wrong, remove it */
|
||||
if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) {
|
||||
hash_remove(_cache.names, name);
|
||||
d = NULL;
|
||||
}
|
||||
|
||||
if (!d) {
|
||||
_insert(name, 0);
|
||||
d = (struct device *) hash_lookup(_cache.names, name);
|
||||
}
|
||||
|
||||
return (d && (!f || f->passes_filter(f, d))) ? d : NULL;
|
||||
}
|
||||
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f)
|
||||
{
|
||||
struct dev_iter *di = dbg_malloc(sizeof(*di));
|
||||
|
||||
if (!di)
|
||||
return NULL;
|
||||
|
||||
_full_scan();
|
||||
di->current = btree_first(_cache.devices);
|
||||
di->filter = f;
|
||||
|
||||
return di;
|
||||
}
|
||||
|
||||
void dev_iter_destroy(struct dev_iter *iter)
|
||||
{
|
||||
dbg_free(iter);
|
||||
}
|
||||
|
||||
static inline struct device *_iter_next(struct dev_iter *iter)
|
||||
{
|
||||
struct device *d = btree_get_data(iter->current);
|
||||
iter->current = btree_next(iter->current);
|
||||
return d;
|
||||
}
|
||||
|
||||
struct device *dev_iter_get(struct dev_iter *iter)
|
||||
{
|
||||
while (iter->current) {
|
||||
struct device *d = _iter_next(iter);
|
||||
if (!iter->filter ||
|
||||
iter->filter->passes_filter(iter->filter, d))
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
42
lib/device/dev-cache.h
Normal file
42
lib/device/dev-cache.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DEV_CACHE_H
|
||||
#define _LVM_DEV_CACHE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "lvm-types.h"
|
||||
#include "device.h"
|
||||
|
||||
/*
|
||||
* predicate for devices.
|
||||
*/
|
||||
struct dev_filter {
|
||||
int (*passes_filter)(struct dev_filter *f, struct device *dev);
|
||||
void (*destroy)(struct dev_filter *f);
|
||||
void *private;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The global device cache.
|
||||
*/
|
||||
int dev_cache_init(void);
|
||||
void dev_cache_exit(void);
|
||||
|
||||
int dev_cache_add_dir(const char *path);
|
||||
struct device *dev_cache_get(const char *name, struct dev_filter *f);
|
||||
|
||||
|
||||
/*
|
||||
* Object for iterating through the cache.
|
||||
*/
|
||||
struct dev_iter;
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f);
|
||||
void dev_iter_destroy(struct dev_iter *iter);
|
||||
struct device *dev_iter_get(struct dev_iter *iter);
|
||||
|
||||
#endif
|
||||
235
lib/device/dev-io.c
Normal file
235
lib/device/dev-io.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
#include "lvm-types.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.h> // UGH!!! for BLKSSZGET
|
||||
|
||||
int dev_get_size(struct device *dev, uint64_t *size)
|
||||
{
|
||||
int fd;
|
||||
long s;
|
||||
const char *name = dev_name(dev);
|
||||
|
||||
log_very_verbose("Getting size of %s", name);
|
||||
if ((fd = open(name, O_RDONLY)) < 0) {
|
||||
log_sys_error("open", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: add 64 bit ioctl */
|
||||
if (ioctl(fd, BLKGETSIZE, &s) < 0) {
|
||||
log_sys_error("ioctl BLKGETSIZE", name);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
*size = (uint64_t) s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dev_get_sectsize(struct device *dev, uint32_t *size)
|
||||
{
|
||||
int fd;
|
||||
int s;
|
||||
const char *name = dev_name(dev);
|
||||
|
||||
log_very_verbose("Getting size of %s", name);
|
||||
if ((fd = open(name, O_RDONLY)) < 0) {
|
||||
log_sys_error("open", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ioctl(fd, BLKSSZGET, &s) < 0) {
|
||||
log_sys_error("ioctl BLKSSZGET", name);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
*size = (uint32_t) s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dev_open(struct device *dev, int flags)
|
||||
{
|
||||
struct stat buf;
|
||||
const char *name = dev_name_confirmed(dev);
|
||||
|
||||
if (!name) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dev->fd >= 0) {
|
||||
log_error("Device '%s' has already been opened", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((stat(name, &buf) < 0) || (buf.st_rdev != dev->dev)) {
|
||||
log_error("%s: stat failed: Has device name changed?", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((dev->fd = open(name, flags)) < 0) {
|
||||
log_sys_error("open", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((fstat(dev->fd, &buf) < 0) || (buf.st_rdev != dev->dev)) {
|
||||
log_error("%s: fstat failed: Has device name changed?", name);
|
||||
dev_close(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dev_close(struct device *dev)
|
||||
{
|
||||
if (dev->fd < 0) {
|
||||
log_error("Attempt to close device '%s' "
|
||||
"which is not open.", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (close(dev->fd))
|
||||
log_sys_error("close", dev_name(dev));
|
||||
|
||||
dev->fd = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: factor common code out.
|
||||
*/
|
||||
int _read(int fd, void *buf, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
int tot = 0;
|
||||
|
||||
while (tot < count) {
|
||||
do
|
||||
n = read(fd, buf, count - tot);
|
||||
while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
|
||||
|
||||
if (n <= 0)
|
||||
return tot ? tot : n;
|
||||
|
||||
tot += n;
|
||||
buf += n;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
int64_t dev_read(struct device *dev, uint64_t offset,
|
||||
int64_t len, void *buffer)
|
||||
{
|
||||
const char *name = dev_name(dev);
|
||||
int fd = dev->fd;
|
||||
|
||||
if (fd < 0) {
|
||||
log_err("Attempt to read an unopened device (%s).", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) < 0) {
|
||||
log_sys_error("lseek", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _read(fd, buffer, len);
|
||||
}
|
||||
|
||||
int _write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
int tot = 0;
|
||||
|
||||
/* Skip all writes */
|
||||
if (test_mode())
|
||||
return count;
|
||||
|
||||
while (tot < count) {
|
||||
do
|
||||
n = write(fd, buf, count - tot);
|
||||
while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
|
||||
|
||||
if (n <= 0)
|
||||
return tot ? tot : n;
|
||||
|
||||
tot += n;
|
||||
buf += n;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
int64_t dev_write(struct device *dev, uint64_t offset,
|
||||
int64_t len, void *buffer)
|
||||
{
|
||||
const char *name = dev_name(dev);
|
||||
int fd = dev->fd;
|
||||
|
||||
if (fd < 0) {
|
||||
log_error("Attempt to write to unopened device %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) < 0) {
|
||||
log_sys_error("lseek", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _write(fd, buffer, len);
|
||||
}
|
||||
|
||||
int dev_zero(struct device *dev, uint64_t offset, int64_t len)
|
||||
{
|
||||
int64_t r, s;
|
||||
char buffer[4096];
|
||||
const char *name = dev_name(dev);
|
||||
int fd = dev->fd;
|
||||
|
||||
if (fd < 0) {
|
||||
log_error("Attempt to zero part of an unopened device %s",
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) < 0) {
|
||||
log_sys_error("lseek", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
while (1) {
|
||||
s = len > sizeof(buffer) ? sizeof(buffer) : len;
|
||||
r = _write(fd, buffer, s);
|
||||
|
||||
if (r <= 0)
|
||||
break;
|
||||
|
||||
len -= r;
|
||||
if (!len) {
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: Always display error */
|
||||
return (len == 0);
|
||||
}
|
||||
214
lib/device/device.c
Normal file
214
lib/device/device.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 Library General Public
|
||||
* License along with this LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "dev-cache.h"
|
||||
#include "metadata.h"
|
||||
#include "device.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/genhd.h>
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
int _get_partition_type(struct dev_filter *filter, struct device *d);
|
||||
|
||||
#define MINOR_PART(dm, d) (MINOR((d)->dev) % dev_max_partitions(dm, (d)->dev))
|
||||
|
||||
int is_whole_disk(struct dev_filter *filter, struct device *d)
|
||||
{
|
||||
return (MINOR_PART(dm, d)) ? 0 : 1;
|
||||
}
|
||||
|
||||
int is_extended_partition(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
return (MINOR_PART(dm, d) > 4) ? 1 : 0;
|
||||
}
|
||||
|
||||
struct device *dev_primary(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
struct device *ret;
|
||||
|
||||
ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
|
||||
/* FIXME: Needs replacing with a 'refresh' */
|
||||
if (!ret) {
|
||||
init_dev_scan(dm);
|
||||
ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
int pt;
|
||||
|
||||
pt = _get_partition_type(dm, d);
|
||||
|
||||
if (!pt) {
|
||||
if (is_whole_disk(dm, d))
|
||||
/* FIXME: Overloaded pt=0 in error cases */
|
||||
return 1;
|
||||
else {
|
||||
log_error
|
||||
("%s: missing partition table "
|
||||
"on partitioned device", d->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_whole_disk(dm, d)) {
|
||||
log_error("%s: looks to possess partition table", d->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check part type */
|
||||
if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
|
||||
log_error("%s: invalid partition type 0x%x "
|
||||
"(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pt == LVM_PARTITION) {
|
||||
log_error
|
||||
("%s: old LVM partition type found - please change to 0x%x",
|
||||
d->name, LVM_NEW_PARTITION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _get_partition_type(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
int pv_handle = -1;
|
||||
struct device *primary;
|
||||
ssize_t read_ret;
|
||||
ssize_t bytes_read = 0;
|
||||
char *buffer;
|
||||
unsigned short *s_buffer;
|
||||
struct partition *part;
|
||||
loff_t offset = 0;
|
||||
loff_t extended_offset = 0;
|
||||
int part_sought;
|
||||
int part_found = 0;
|
||||
int first_partition = 1;
|
||||
int extended_partition = 0;
|
||||
int p;
|
||||
|
||||
if (!(primary = dev_primary(dm, d))) {
|
||||
log_error
|
||||
("Failed to find main device containing partition %s",
|
||||
d->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(buffer = dbg_malloc(SECTOR_SIZE))) {
|
||||
log_error("Failed to allocate partition table buffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get partition table */
|
||||
if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
|
||||
log_error("%s: open failed: %s", primary->name,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
s_buffer = (unsigned short *) buffer;
|
||||
part = (struct partition *) (buffer + 0x1be);
|
||||
part_sought = MINOR_PART(dm, d);
|
||||
|
||||
do {
|
||||
bytes_read = 0;
|
||||
|
||||
if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
|
||||
log_error("%s: llseek failed: %s",
|
||||
primary->name, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((bytes_read < SECTOR_SIZE) &&
|
||||
(read_ret =
|
||||
read(pv_handle, buffer + bytes_read,
|
||||
SECTOR_SIZE - bytes_read)) != -1)
|
||||
bytes_read += read_ret;
|
||||
|
||||
if (read_ret == -1) {
|
||||
log_error("%s: read failed: %s", primary->name,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s_buffer[255] == 0xAA55) {
|
||||
if (is_whole_disk(dm, d))
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
extended_partition = 0;
|
||||
|
||||
/* Loop through primary partitions */
|
||||
for (p = 0; p < 4; p++) {
|
||||
if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
|
||||
part[p].sys_ind == LINUX_EXTENDED_PARTITION
|
||||
|| part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
|
||||
extended_partition = 1;
|
||||
offset = extended_offset + part[p].start_sect;
|
||||
if (extended_offset == 0)
|
||||
extended_offset = part[p].start_sect;
|
||||
if (first_partition == 1)
|
||||
part_found++;
|
||||
} else if (first_partition == 1) {
|
||||
if (p == part_sought) {
|
||||
if (part[p].sys_ind == 0) {
|
||||
/* missing primary? */
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
part_found++;
|
||||
} else if (!part[p].sys_ind)
|
||||
part_found++;
|
||||
|
||||
if (part_sought == part_found)
|
||||
return part[p].sys_ind;
|
||||
|
||||
}
|
||||
first_partition = 0;
|
||||
}
|
||||
while (extended_partition == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
58
lib/device/device.h
Normal file
58
lib/device/device.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DEVICE_H
|
||||
#define _LVM_DEVICE_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
#include "list.h"
|
||||
|
||||
/*
|
||||
* All devices in LVM will be represented by one of these.
|
||||
* pointer comparisons are valid.
|
||||
*/
|
||||
struct device {
|
||||
struct list aliases; /* struct str_list from lvm-types.h */
|
||||
dev_t dev;
|
||||
|
||||
/* private */
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct device_list {
|
||||
struct list list;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/*
|
||||
* All io should use these routines.
|
||||
*/
|
||||
int dev_get_size(struct device *dev, uint64_t *size);
|
||||
int dev_get_sectsize(struct device *dev, uint32_t *size);
|
||||
|
||||
int dev_open(struct device *dev, int flags);
|
||||
int dev_close(struct device *dev);
|
||||
|
||||
int64_t dev_read(struct device *dev,
|
||||
uint64_t offset, int64_t len, void *buffer);
|
||||
int64_t dev_write(struct device *dev,
|
||||
uint64_t offset, int64_t len, void *buffer);
|
||||
int dev_zero(struct device *dev, uint64_t offset, int64_t len);
|
||||
|
||||
|
||||
static inline const char *dev_name(struct device *dev) {
|
||||
return list_item(dev->aliases.n, struct str_list)->str;
|
||||
}
|
||||
|
||||
/* Return a valid device name from the alias list; NULL otherwise */
|
||||
const char *dev_name_confirmed(struct device *dev);
|
||||
|
||||
static inline int is_lvm_partition(const char *name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
521
lib/display/display.c
Normal file
521
lib/display/display.c
Normal file
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 Library General Public
|
||||
* License along with this LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "metadata.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "display.h"
|
||||
#include "activate.h"
|
||||
#include "uuid.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SIZE_BUF 128
|
||||
|
||||
char *display_size(unsigned long long size, size_len_t sl)
|
||||
{
|
||||
int s;
|
||||
ulong byte = 1024 * 1024 * 1024;
|
||||
char *size_buf = NULL;
|
||||
char *size_str[][2] = {
|
||||
{"Terabyte", "TB"},
|
||||
{"Gigabyte", "GB"},
|
||||
{"Megabyte", "MB"},
|
||||
{"Kilobyte", "KB"},
|
||||
{"", ""}
|
||||
};
|
||||
|
||||
if (!(size_buf = dbg_malloc(SIZE_BUF))) {
|
||||
log_error("no memory for size display buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size == 0LL)
|
||||
sprintf(size_buf, "0");
|
||||
else {
|
||||
s = 0;
|
||||
while (size_str[s] && size < byte)
|
||||
s++, byte /= 1024;
|
||||
snprintf(size_buf, SIZE_BUF - 1,
|
||||
"%.2f %s", (float) size / byte, size_str[s][sl]);
|
||||
}
|
||||
|
||||
/* Caller to deallocate */
|
||||
return size_buf;
|
||||
}
|
||||
|
||||
void pvdisplay_colons(struct physical_volume *pv)
|
||||
{
|
||||
char uuid[64];
|
||||
|
||||
if (!pv)
|
||||
return;
|
||||
|
||||
if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
|
||||
stack;
|
||||
return;
|
||||
}
|
||||
|
||||
log_print("%s:%s:%" PRIu64 ":-1:%u:%u:-1:%" PRIu64 ":%u:%u:%u:%s",
|
||||
dev_name(pv->dev), pv->vg_name, pv->size,
|
||||
/* FIXME pv->pv_number, Derive or remove? */
|
||||
pv->status, /* FIXME Support old or new format here? */
|
||||
pv->status & ALLOCATABLE_PV, /* FIXME remove? */
|
||||
/* FIXME pv->lv_cur, Remove? */
|
||||
pv->pe_size / 2,
|
||||
pv->pe_count,
|
||||
pv->pe_count - pv->pe_allocated,
|
||||
pv->pe_allocated, *uuid ? uuid : "none");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void pvdisplay_full(struct physical_volume *pv)
|
||||
{
|
||||
char uuid[64];
|
||||
char *size, *size1; /*, *size2; */
|
||||
|
||||
uint64_t pe_free;
|
||||
|
||||
if (!pv)
|
||||
return;
|
||||
|
||||
if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
|
||||
stack;
|
||||
return;
|
||||
}
|
||||
|
||||
log_print("--- %sPhysical volume ---", pv->pe_size ? "" : "NEW ");
|
||||
log_print("PV Name %s", dev_name(pv->dev));
|
||||
log_print("VG Name %s%s", pv->vg_name,
|
||||
pv->status & EXPORTED_VG ? " (exported)" : "");
|
||||
|
||||
size = display_size(pv->size / 2, SIZE_SHORT);
|
||||
if (pv->pe_size && pv->pe_count) {
|
||||
size1 = display_size((pv->size - pv->pe_count * pv->pe_size)
|
||||
/ 2, SIZE_SHORT);
|
||||
|
||||
/******** FIXME display LVM on-disk data size
|
||||
size2 = display_size(pv->size / 2, SIZE_SHORT);
|
||||
********/
|
||||
|
||||
log_print("PV Size %s" " / not usable %s", /* [LVM: %s]", */
|
||||
size, size1); /* , size2); */
|
||||
|
||||
dbg_free(size1);
|
||||
/* dbg_free(size2); */
|
||||
} else
|
||||
log_print("PV Size %s", size);
|
||||
dbg_free(size);
|
||||
|
||||
/*********FIXME Anything use this?
|
||||
log_print("PV# %u", pv->pv_number);
|
||||
**********/
|
||||
|
||||
pe_free = pv->pe_count - pv->pe_allocated;
|
||||
if (pv->pe_count && (pv->status & ALLOCATABLE_PV))
|
||||
log_print("Allocatable yes %s",
|
||||
(!pe_free && pv->pe_count) ? "(but full)" : "");
|
||||
else
|
||||
log_print("Allocatable NO");
|
||||
|
||||
/*********FIXME
|
||||
log_print("Cur LV %u", pv->lv_cur);
|
||||
*********/
|
||||
log_print("PE Size (KByte) %" PRIu64, pv->pe_size / 2);
|
||||
log_print("Total PE %u", pv->pe_count);
|
||||
log_print("Free PE %" PRIu64, pe_free);
|
||||
log_print("Allocated PE %u", pv->pe_allocated);
|
||||
|
||||
#ifdef LVM_FUTURE
|
||||
printf("Stale PE %u", pv->pe_stale);
|
||||
#endif
|
||||
|
||||
log_print("PV UUID %s", *uuid ? uuid : "none");
|
||||
log_print(" ");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int pvdisplay_short(struct volume_group *vg, struct physical_volume *pv)
|
||||
{
|
||||
if (!pv)
|
||||
return 0;
|
||||
|
||||
log_print("PV Name %s ", dev_name(pv->dev));
|
||||
/* FIXME pv->pv_number); */
|
||||
log_print("PV Status %sallocatable",
|
||||
(pv->status & ALLOCATABLE_PV) ? "" : "NOT ");
|
||||
log_print("Total PE / Free PE %u / %u",
|
||||
pv->pe_count, pv->pe_count - pv->pe_allocated);
|
||||
|
||||
log_print(" ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lvdisplay_colons(struct logical_volume *lv)
|
||||
{
|
||||
int inkernel;
|
||||
struct dm_info info;
|
||||
inkernel = lv_info(lv, &info) && info.exists;
|
||||
|
||||
log_print("%s%s/%s:%s:%d:%d:-1:%d:%" PRIu64 ":%d:-1:%d:%d:%d:%d",
|
||||
lv->vg->cmd->dev_dir,
|
||||
lv->vg->name,
|
||||
lv->name,
|
||||
lv->vg->name,
|
||||
(lv->status & (LVM_READ | LVM_WRITE)) >> 8,
|
||||
inkernel ? 1 : 0,
|
||||
/* FIXME lv->lv_number, */
|
||||
inkernel ? info.open_count : 0, lv->size, lv->le_count,
|
||||
/* FIXME Add num allocated to struct! lv->lv_allocated_le, */
|
||||
((lv->status & ALLOC_STRICT) +
|
||||
(lv->status & ALLOC_CONTIGUOUS) * 2), lv->read_ahead,
|
||||
inkernel ? info.major : -1,
|
||||
inkernel ? info.minor : -1
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int lvdisplay_full(struct logical_volume *lv)
|
||||
{
|
||||
char *size;
|
||||
uint32_t alloc;
|
||||
struct dm_info info;
|
||||
int inkernel;
|
||||
|
||||
inkernel = lv_info(lv, &info) && info.exists;
|
||||
|
||||
log_print("--- Logical volume ---");
|
||||
|
||||
log_print("LV Name %s%s/%s", lv->vg->cmd->dev_dir,
|
||||
lv->vg->name, lv->name);
|
||||
log_print("VG Name %s", lv->vg->name);
|
||||
|
||||
log_print("LV Write Access %s",
|
||||
(lv->status & LVM_WRITE) ? "read/write" : "read only");
|
||||
|
||||
/******* FIXME Snapshot
|
||||
if (lv->status & (LVM_SNAPSHOT_ORG | LVM_SNAPSHOT)) {
|
||||
if (lvm_tab_vg_read_with_pv_and_lv(vg_name, &vg) < 0) {
|
||||
ret = -LVM_ELV_SHOW_VG_READ_WITH_PV_AND_LV;
|
||||
goto lv_show_end;
|
||||
}
|
||||
printf("LV snapshot status ");
|
||||
if (vg_check_active(vg_name) == TRUE) {
|
||||
vg_t *vg_core;
|
||||
if ((ret = vg_status_with_pv_and_lv(vg_name, &vg_core)) == 0) {
|
||||
lv_t *lv_ptr =
|
||||
vg_core->
|
||||
lv[lv_get_index_by_name(vg_core, lv->lv_name)];
|
||||
if (lv_ptr->lv_access & LV_SNAPSHOT) {
|
||||
if (lv_ptr->lv_status & LV_ACTIVE)
|
||||
printf("active ");
|
||||
else
|
||||
printf("INACTIVE ");
|
||||
}
|
||||
if (lv_ptr->lv_access & LV_SNAPSHOT_ORG) {
|
||||
printf("source of\n");
|
||||
while (lv_ptr->lv_snapshot_next != NULL) {
|
||||
lv_ptr = lv_ptr->lv_snapshot_next;
|
||||
printf(" %s [%s]\n",
|
||||
lv_ptr->lv_name,
|
||||
(lv_ptr->
|
||||
lv_status & LV_ACTIVE) ? "active" :
|
||||
"INACTIVE");
|
||||
}
|
||||
vg_free(vg_core, TRUE);
|
||||
} else {
|
||||
printf("destination for %s\n",
|
||||
lv_ptr->lv_snapshot_org->lv_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("INACTIVE ");
|
||||
if (lv->lv_access & LV_SNAPSHOT_ORG)
|
||||
printf("original\n");
|
||||
else
|
||||
printf("snapshot\n");
|
||||
}
|
||||
}
|
||||
***********/
|
||||
|
||||
if (inkernel && info.suspended)
|
||||
log_print("LV Status suspended");
|
||||
else
|
||||
log_print("LV Status %savailable",
|
||||
inkernel ? "" : "NOT ");
|
||||
|
||||
/********* FIXME lv_number
|
||||
log_print("LV # %u", lv->lv_number + 1);
|
||||
************/
|
||||
|
||||
if (inkernel)
|
||||
log_print("# open %u", info.open_count);
|
||||
|
||||
/********
|
||||
#ifdef LVM_FUTURE
|
||||
printf("Mirror copies %u\n", lv->lv_mirror_copies);
|
||||
printf("Consistency recovery ");
|
||||
if (lv->lv_recovery | LV_BADBLOCK_ON)
|
||||
printf("bad blocks\n");
|
||||
else
|
||||
printf("none\n");
|
||||
printf("Schedule %u\n", lv->lv_schedule);
|
||||
#endif
|
||||
********/
|
||||
|
||||
size = display_size(lv->size / 2, SIZE_SHORT);
|
||||
log_print("LV Size %s", size);
|
||||
dbg_free(size);
|
||||
|
||||
log_print("Current LE %u", lv->le_count);
|
||||
|
||||
/********** FIXME allocation
|
||||
log_print("Allocated LE %u", lv->allocated_le);
|
||||
**********/
|
||||
|
||||
/********** FIXME Snapshot
|
||||
if (lv->lv_access & LV_SNAPSHOT) {
|
||||
printf("snapshot chunk size %s\n",
|
||||
(dummy = lvm_show_size(lv->lv_chunk_size / 2, SHORT)));
|
||||
dbg_free(dummy);
|
||||
dummy = NULL;
|
||||
if (lv->lv_remap_end > 0) {
|
||||
lv_remap_ptr = lv->lv_remap_ptr;
|
||||
if (lv_remap_ptr > lv->lv_remap_end)
|
||||
lv_remap_ptr = lv->lv_remap_end;
|
||||
dummy = lvm_show_size(lv_remap_ptr *
|
||||
lv->lv_chunk_size / 2, SHORT);
|
||||
dummy1 = lvm_show_size(lv->lv_remap_end *
|
||||
lv->lv_chunk_size / 2, SHORT);
|
||||
printf("Allocated to snapshot %.2f%% [%s/%s]\n",
|
||||
(float) lv_remap_ptr * 100 / lv->lv_remap_end,
|
||||
dummy, dummy1);
|
||||
dbg_free(dummy);
|
||||
dbg_free(dummy1);
|
||||
dummy =
|
||||
lvm_show_size((vg->
|
||||
lv[lv_get_index_by_number
|
||||
(vg,
|
||||
lv->lv_number)]->lv_size -
|
||||
lv->lv_remap_end * lv->lv_chunk_size) / 2,
|
||||
SHORT);
|
||||
printf("Allocated to COW-table %s\n", dummy);
|
||||
dbg_free(dummy);
|
||||
}
|
||||
}
|
||||
******************/
|
||||
|
||||
log_print("Segments %u", list_size(&lv->segments));
|
||||
|
||||
/********* FIXME Stripes & stripesize for each segment
|
||||
log_print("Stripe size (KByte) %u", lv->stripesize / 2);
|
||||
***********/
|
||||
|
||||
/**************
|
||||
#ifdef LVM_FUTURE
|
||||
printf("Bad block ");
|
||||
if (lv->lv_badblock == LV_BADBLOCK_ON)
|
||||
printf("on\n");
|
||||
else
|
||||
printf("off\n");
|
||||
#endif
|
||||
***************/
|
||||
|
||||
/* FIXME next free == ALLOC_SIMPLE */
|
||||
alloc = lv->status & (ALLOC_STRICT | ALLOC_CONTIGUOUS);
|
||||
log_print("Allocation %s%s%s%s",
|
||||
!(alloc & (ALLOC_STRICT | ALLOC_CONTIGUOUS)) ? "next free" :
|
||||
"", (alloc == ALLOC_STRICT) ? "strict" : "",
|
||||
(alloc == ALLOC_CONTIGUOUS) ? "contiguous" : "",
|
||||
(alloc ==
|
||||
(ALLOC_STRICT | ALLOC_CONTIGUOUS)) ? "strict/contiguous" :
|
||||
"");
|
||||
|
||||
log_print("Read ahead sectors %u", lv->read_ahead);
|
||||
|
||||
/****************
|
||||
#ifdef LVM_FUTURE
|
||||
printf("IO Timeout (seconds) ");
|
||||
if (lv->lv_io_timeout == 0)
|
||||
printf("default\n\n");
|
||||
else
|
||||
printf("%lu\n\n", lv->lv_io_timeout);
|
||||
#endif
|
||||
*************/
|
||||
|
||||
if (inkernel)
|
||||
log_print("Block device %d:%d", info.major,
|
||||
info.minor);
|
||||
|
||||
log_print(" ");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _display_stripe(struct stripe_segment *seg, int s, const char *pre)
|
||||
{
|
||||
uint32_t len = seg->len / seg->stripes;
|
||||
|
||||
log_print("%sphysical volume\t%s", pre,
|
||||
seg->area[s].pv ? dev_name(seg->area[s].pv->dev) : "Missing");
|
||||
|
||||
if (seg->area[s].pv)
|
||||
log_print("%sphysical extents\t%d to %d", pre,
|
||||
seg->area[s].pe, seg->area[s].pe + len - 1);
|
||||
}
|
||||
|
||||
int lvdisplay_segments(struct logical_volume *lv)
|
||||
{
|
||||
int s;
|
||||
struct list *segh;
|
||||
struct stripe_segment *seg;
|
||||
|
||||
log_print("--- Segments ---");
|
||||
|
||||
list_iterate (segh, &lv->segments) {
|
||||
seg = list_item(segh, struct stripe_segment);
|
||||
|
||||
log_print("logical extent %d to %d:",
|
||||
seg->le, seg->le + seg->len - 1);
|
||||
|
||||
if (seg->stripes == 1)
|
||||
_display_stripe(seg, 0, " ");
|
||||
|
||||
else {
|
||||
log_print(" stripes\t\t%d", seg->stripes);
|
||||
log_print(" stripe size\t\t%d", seg->stripe_size);
|
||||
|
||||
for (s = 0; s < seg->stripes; s++) {
|
||||
log_print(" stripe %d:", s);
|
||||
_display_stripe(seg, s, " ");
|
||||
}
|
||||
}
|
||||
log_print(" ");
|
||||
}
|
||||
|
||||
log_print(" ");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void vgdisplay_extents(struct volume_group *vg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void vgdisplay_full(struct volume_group *vg)
|
||||
{
|
||||
uint32_t access;
|
||||
char *s1;
|
||||
char uuid[64];
|
||||
|
||||
log_print("--- Volume group ---");
|
||||
log_print("VG Name %s", vg->name);
|
||||
log_print("System ID %s", vg->system_id);
|
||||
access = vg->status & (LVM_READ | LVM_WRITE);
|
||||
log_print("VG Access %s%s%s%s",
|
||||
access == (LVM_READ | LVM_WRITE) ? "read/write" : "",
|
||||
access == LVM_READ ? "read" : "",
|
||||
access == LVM_WRITE ? "write" : "",
|
||||
access == 0 ? "error" : "");
|
||||
log_print("VG Status %s%sresizable",
|
||||
vg->status & EXPORTED_VG ? "exported/" : "",
|
||||
vg->status & RESIZEABLE_VG ? "" : "NOT ");
|
||||
/******* FIXME vg number
|
||||
log_print ("VG # %u\n", vg->vg_number);
|
||||
********/
|
||||
if (vg->status & CLUSTERED) {
|
||||
log_print("Clustered yes");
|
||||
log_print("Shared %s",
|
||||
vg->status & SHARED ? "yes" : "no");
|
||||
}
|
||||
log_print("MAX LV %u", vg->max_lv);
|
||||
log_print("Cur LV %u", vg->lv_count);
|
||||
/****** FIXME Open LVs
|
||||
log_print ( "Open LV %u", vg->lv_open);
|
||||
*******/
|
||||
/****** FIXME Max LV Size
|
||||
log_print ( "MAX LV Size %s",
|
||||
( s1 = display_size ( LVM_LV_SIZE_MAX(vg) / 2, SIZE_SHORT)));
|
||||
free ( s1);
|
||||
*********/
|
||||
log_print("Max PV %u", vg->max_pv);
|
||||
log_print("Cur PV %u", vg->pv_count);
|
||||
/******* FIXME act PVs
|
||||
log_print ( "Act PV %u", vg->pv_act);
|
||||
*********/
|
||||
|
||||
s1 = display_size(vg->extent_count * vg->extent_size / 2, SIZE_SHORT);
|
||||
log_print("VG Size %s", s1);
|
||||
dbg_free(s1);
|
||||
|
||||
s1 = display_size(vg->extent_size / 2, SIZE_SHORT);
|
||||
log_print("PE Size %s", s1);
|
||||
dbg_free(s1);
|
||||
|
||||
log_print("Total PE %u", vg->extent_count);
|
||||
|
||||
s1 =
|
||||
display_size((vg->extent_count - vg->free_count) *
|
||||
vg->extent_size / 2, SIZE_SHORT);
|
||||
log_print("Alloc PE / Size %u / %s",
|
||||
vg->extent_count - vg->free_count, s1);
|
||||
dbg_free(s1);
|
||||
|
||||
s1 = display_size(vg->free_count * vg->extent_size / 2, SIZE_SHORT);
|
||||
log_print("Free PE / Size %u / %s", vg->free_count, s1);
|
||||
dbg_free(s1);
|
||||
|
||||
if (!id_write_format(&vg->id, uuid, sizeof(uuid))) {
|
||||
stack;
|
||||
return;
|
||||
}
|
||||
|
||||
log_print("VG UUID %s", uuid);
|
||||
log_print(" ");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void vgdisplay_colons(struct volume_group *vg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void vgdisplay_short(struct volume_group *vg)
|
||||
{
|
||||
char *s1, *s2, *s3;
|
||||
s1 = display_size(vg->extent_count * vg->extent_size / 2, SIZE_SHORT);
|
||||
s2 =
|
||||
display_size((vg->extent_count - vg->free_count) * vg->extent_size /
|
||||
2, SIZE_SHORT);
|
||||
s3 = display_size(vg->free_count * vg->extent_size / 2, SIZE_SHORT);
|
||||
log_print("\"%s\" %-9s [%-9s used / %s free]", vg->name,
|
||||
/********* FIXME if "open" print "/used" else print "/idle"??? ******/
|
||||
s1, s2, s3);
|
||||
dbg_free(s1);
|
||||
dbg_free(s2);
|
||||
dbg_free(s3);
|
||||
return;
|
||||
}
|
||||
47
lib/display/display.h
Normal file
47
lib/display/display.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 Library General Public
|
||||
* License along with this LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DISPLAY_H
|
||||
#define _LVM_DISPLAY_H
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {SIZE_LONG=0, SIZE_SHORT=1} size_len_t;
|
||||
|
||||
/* Specify size in KB */
|
||||
char *display_size(unsigned long long size, size_len_t sl);
|
||||
char *display_uuid(char *uuidstr);
|
||||
|
||||
void pvdisplay_colons(struct physical_volume *pv);
|
||||
void pvdisplay_full(struct physical_volume *pv);
|
||||
int pvdisplay_short(struct volume_group *vg, struct physical_volume *pv);
|
||||
|
||||
void lvdisplay_colons(struct logical_volume *lv);
|
||||
int lvdisplay_segments(struct logical_volume *lv);
|
||||
int lvdisplay_full(struct logical_volume *lv);
|
||||
|
||||
void vgdisplay_extents(struct volume_group *vg);
|
||||
void vgdisplay_full(struct volume_group *vg);
|
||||
void vgdisplay_colons(struct volume_group *vg);
|
||||
void vgdisplay_short(struct volume_group *vg);
|
||||
|
||||
#endif
|
||||
71
lib/filters/filter-composite.c
Normal file
71
lib/filters/filter-composite.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "filter-composite.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static int _and_p(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct dev_filter **filters = (struct dev_filter **) f->private;
|
||||
|
||||
while (*filters) {
|
||||
if (!(*filters)->passes_filter(*filters, dev))
|
||||
return 0;
|
||||
filters++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
struct dev_filter **filters = (struct dev_filter **) f->private;
|
||||
|
||||
while (*filters) {
|
||||
(*filters)->destroy(*filters);
|
||||
filters++;
|
||||
}
|
||||
|
||||
dbg_free(f->private);
|
||||
dbg_free(f);
|
||||
}
|
||||
|
||||
|
||||
struct dev_filter *composite_filter_create(int n, ...)
|
||||
{
|
||||
struct dev_filter **filters = dbg_malloc(sizeof(*filters) * (n + 1));
|
||||
struct dev_filter *cf;
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
if (!filters) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(cf = dbg_malloc(sizeof(*cf)))) {
|
||||
stack;
|
||||
dbg_free(filters);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
struct dev_filter *f = va_arg(ap, struct dev_filter *);
|
||||
filters[i] = f;
|
||||
}
|
||||
filters[i] = NULL;
|
||||
va_end(ap);
|
||||
|
||||
cf->passes_filter = _and_p;
|
||||
cf->destroy = _destroy;
|
||||
cf->private = filters;
|
||||
|
||||
return cf;
|
||||
}
|
||||
14
lib/filters/filter-composite.h
Normal file
14
lib/filters/filter-composite.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FILTER_COMPOSITE_H
|
||||
#define _LVM_FILTER_COMPOSITE_H
|
||||
|
||||
#include "dev-cache.h"
|
||||
|
||||
struct dev_filter *composite_filter_create(int n, ...);
|
||||
|
||||
#endif
|
||||
242
lib/filters/filter-persistent.c
Normal file
242
lib/filters/filter-persistent.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "dev-cache.h"
|
||||
#include "hash.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "filter-persistent.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct pfilter {
|
||||
char *file;
|
||||
struct hash_table *devices;
|
||||
struct dev_filter *real;
|
||||
};
|
||||
|
||||
/*
|
||||
* entries in the table can be in one of these
|
||||
* states.
|
||||
*/
|
||||
#define PF_BAD_DEVICE ((void *) 1)
|
||||
#define PF_GOOD_DEVICE ((void *) 2)
|
||||
|
||||
static int _init_hash(struct pfilter *pf)
|
||||
{
|
||||
if (pf->devices)
|
||||
hash_destroy(pf->devices);
|
||||
|
||||
pf->devices = hash_create(128);
|
||||
return pf->devices ? 1 : 0;
|
||||
}
|
||||
|
||||
int persistent_filter_wipe(struct dev_filter *f)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
|
||||
hash_wipe(pf->devices);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_array(struct pfilter *pf, struct config_file *cf,
|
||||
const char *path, void *data)
|
||||
{
|
||||
struct config_node *cn;
|
||||
struct config_value *cv;
|
||||
|
||||
if (!(cn = find_config_node(cf->root, path, '/'))) {
|
||||
log_very_verbose("Couldn't find %s array in '%s'",
|
||||
path, pf->file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* iterate through the array, adding
|
||||
* devices as we go.
|
||||
*/
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
if (cv->type != CFG_STRING) {
|
||||
log_verbose("Devices array contains a value "
|
||||
"which is not a string ... ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hash_insert(pf->devices, cv->v.str, data))
|
||||
log_verbose("Couldn't add '%s' to filter ... ignoring",
|
||||
cv->v.str);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_filter_load(struct dev_filter *f)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
|
||||
int r = 0;
|
||||
struct config_file *cf;
|
||||
|
||||
if (!(cf = create_config_file())) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!read_config(cf, pf->file)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
_read_array(pf, cf, "persistent_filter_cache/valid_devices",
|
||||
PF_GOOD_DEVICE);
|
||||
_read_array(pf, cf, "persistent_filter_cache/invalid_devices",
|
||||
PF_BAD_DEVICE);
|
||||
|
||||
if (hash_get_num_entries(pf->devices))
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
destroy_config_file(cf);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _write_array(struct pfilter *pf, FILE *fp, const char *path,
|
||||
void *data)
|
||||
{
|
||||
void *d;
|
||||
int first = 1;
|
||||
struct hash_node *n;
|
||||
|
||||
for (n = hash_get_first(pf->devices); n;
|
||||
n = hash_get_next(pf->devices, n)) {
|
||||
d = hash_get_data(pf->devices, n);
|
||||
|
||||
if (d != data)
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, ",\n");
|
||||
else {
|
||||
fprintf(fp, "\t%s=[\n", path);
|
||||
first = 0;
|
||||
}
|
||||
|
||||
fprintf(fp, "\t\t\"%s\"", hash_get_key(pf->devices, n));
|
||||
}
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "\n\t]\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int persistent_filter_dump(struct dev_filter *f)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
|
||||
FILE *fp;
|
||||
|
||||
if (!hash_get_num_entries(pf->devices)) {
|
||||
log_very_verbose("Internal persistent device cache empty "
|
||||
"- not writing to %s", pf->file);
|
||||
return 0;
|
||||
}
|
||||
log_very_verbose("Dumping persistent device cache to %s", pf->file);
|
||||
|
||||
fp = fopen(pf->file, "w");
|
||||
if (!fp) {
|
||||
log_sys_error("fopen", pf->file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(fp, "# This file is automatically maintained by lvm.\n\n");
|
||||
fprintf(fp, "persistent_filter_cache {\n");
|
||||
|
||||
_write_array(pf, fp, "valid_devices", PF_GOOD_DEVICE);
|
||||
_write_array(pf, fp, "invalid_devices", PF_BAD_DEVICE);
|
||||
|
||||
fprintf(fp, "}\n");
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _lookup_p(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
void *l = hash_lookup(pf->devices, dev_name(dev));
|
||||
struct str_list *sl;
|
||||
struct list *ah;
|
||||
|
||||
if (!l) {
|
||||
l = pf->real->passes_filter(pf->real, dev) ?
|
||||
PF_GOOD_DEVICE : PF_BAD_DEVICE;
|
||||
|
||||
list_iterate(ah, &dev->aliases) {
|
||||
sl = list_item(ah, struct str_list);
|
||||
hash_insert(pf->devices, sl->str, l);
|
||||
}
|
||||
}
|
||||
|
||||
return l == PF_GOOD_DEVICE;
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
|
||||
hash_destroy(pf->devices);
|
||||
dbg_free(pf->file);
|
||||
pf->real->destroy(pf->real);
|
||||
dbg_free(pf);
|
||||
dbg_free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *persistent_filter_create(struct dev_filter *real,
|
||||
const char *file)
|
||||
{
|
||||
struct pfilter *pf;
|
||||
struct dev_filter *f = NULL;
|
||||
|
||||
if (!(pf = dbg_malloc(sizeof(*pf)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
memset(pf, 0, sizeof(*pf));
|
||||
|
||||
if (!(pf->file = dbg_malloc(strlen(file) + 1))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
strcpy(pf->file, file);
|
||||
pf->real = real;
|
||||
|
||||
if (!(_init_hash(pf))) {
|
||||
log_error("Couldn't create hash table for persistent filter.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(f = dbg_malloc(sizeof(*f)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
f->passes_filter = _lookup_p;
|
||||
f->destroy = _destroy;
|
||||
f->private = pf;
|
||||
|
||||
return f;
|
||||
|
||||
bad:
|
||||
dbg_free(pf->file);
|
||||
if (pf->devices)
|
||||
hash_destroy(pf->devices);
|
||||
dbg_free(pf);
|
||||
dbg_free(f);
|
||||
return NULL;
|
||||
}
|
||||
19
lib/filters/filter-persistent.h
Normal file
19
lib/filters/filter-persistent.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FILTER_PERSISTENT_H
|
||||
#define _LVM_FILTER_PERSISTENT_H
|
||||
|
||||
#include "dev-cache.h"
|
||||
|
||||
struct dev_filter *persistent_filter_create(struct dev_filter *f,
|
||||
const char *file);
|
||||
|
||||
int persistent_filter_wipe(struct dev_filter *f);
|
||||
int persistent_filter_load(struct dev_filter *f);
|
||||
int persistent_filter_dump(struct dev_filter *f);
|
||||
|
||||
#endif
|
||||
228
lib/filters/filter-regex.c
Normal file
228
lib/filters/filter-regex.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "pool.h"
|
||||
#include "filter-regex.h"
|
||||
#include "matcher.h"
|
||||
#include "device.h"
|
||||
#include "bitset.h"
|
||||
#include "log.h"
|
||||
#include "list.h"
|
||||
|
||||
struct rfilter {
|
||||
struct pool *mem;
|
||||
bitset_t accept;
|
||||
struct matcher *engine;
|
||||
};
|
||||
|
||||
static int _extract_pattern(struct pool *mem, const char *pat,
|
||||
char **regex, bitset_t accept, int index)
|
||||
{
|
||||
char sep, *r, *ptr;
|
||||
|
||||
/*
|
||||
* is this an accept or reject pattern
|
||||
*/
|
||||
switch (*pat) {
|
||||
case 'a':
|
||||
bit_set(accept, index);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
bit_clear(accept, index);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_info("pattern must begin with 'a' or 'r'");
|
||||
return 0;
|
||||
}
|
||||
pat++;
|
||||
|
||||
/*
|
||||
* get the seperator
|
||||
*/
|
||||
switch (*pat) {
|
||||
case '(':
|
||||
sep = ')';
|
||||
break;
|
||||
|
||||
case '[':
|
||||
sep = ']';
|
||||
break;
|
||||
|
||||
case '{':
|
||||
sep = '}';
|
||||
break;
|
||||
|
||||
default:
|
||||
sep = *pat;
|
||||
}
|
||||
pat++;
|
||||
|
||||
/*
|
||||
* copy the regex
|
||||
*/
|
||||
if (!(r = pool_strdup(mem, pat))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* trim the trailing character, having checked it's sep.
|
||||
*/
|
||||
ptr = r + strlen(r) - 1;
|
||||
if (*ptr != sep) {
|
||||
log_info("invalid seperator at end of regex");
|
||||
return 0;
|
||||
}
|
||||
*ptr = '\0';
|
||||
|
||||
regex[index] = r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _build_matcher(struct rfilter *rf, struct config_value *val)
|
||||
{
|
||||
struct pool *scratch;
|
||||
struct config_value *v;
|
||||
char **regex;
|
||||
int count = 0, i, r = 0;
|
||||
|
||||
if (!(scratch = pool_create(1024))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* count how many patterns we have.
|
||||
*/
|
||||
for (v = val; v; v = v->next) {
|
||||
|
||||
if (v->type != CFG_STRING) {
|
||||
log_info("filter patterns must be enclosed in quotes");
|
||||
goto out;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate space for them
|
||||
*/
|
||||
if (!(regex = pool_alloc(scratch, sizeof(*regex) * count))) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* create the accept/reject bitset
|
||||
*/
|
||||
rf->accept = bitset_create(rf->mem, count);
|
||||
|
||||
/*
|
||||
* fill the array back to front because we
|
||||
* want the opposite precedence to what
|
||||
* the matcher gives.
|
||||
*/
|
||||
for (v = val, i = count - 1; v; v = v->next, i--)
|
||||
if (!_extract_pattern(scratch, v->v.str,
|
||||
regex, rf->accept, i)) {
|
||||
log_info("invalid filter pattern");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* build the matcher.
|
||||
*/
|
||||
if (!(rf->engine = matcher_create(rf->mem,
|
||||
(const char **) regex, count)))
|
||||
stack;
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
pool_destroy(scratch);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _accept_p(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct list *ah;
|
||||
int m, first = 1, rejected = 0;
|
||||
struct rfilter *rf = (struct rfilter *) f->private;
|
||||
struct str_list *sl;
|
||||
|
||||
list_iterate(ah, &dev->aliases) {
|
||||
sl = list_item(ah, struct str_list);
|
||||
m = matcher_run(rf->engine, sl->str);
|
||||
|
||||
if (m >= 0) {
|
||||
if (bit(rf->accept, m)) {
|
||||
|
||||
if (!first) {
|
||||
list_del(&sl->list);
|
||||
list_add_h(&dev->aliases, &sl->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
rejected = 1;
|
||||
}
|
||||
|
||||
first = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pass everything that doesn't match
|
||||
* anything.
|
||||
*/
|
||||
return !rejected;
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
struct rfilter *rf = (struct rfilter *) f->private;
|
||||
pool_destroy(rf->mem);
|
||||
}
|
||||
|
||||
struct dev_filter *regex_filter_create(struct config_value *patterns)
|
||||
{
|
||||
struct pool *mem = pool_create(10 * 1024);
|
||||
struct rfilter *rf;
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(rf = pool_alloc(mem, sizeof(*rf)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
rf->mem = mem;
|
||||
|
||||
if (!_build_matcher(rf, patterns)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(f = pool_zalloc(mem, sizeof(*f)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
f->passes_filter = _accept_p;
|
||||
f->destroy = _destroy;
|
||||
f->private = rf;
|
||||
return f;
|
||||
|
||||
bad:
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
23
lib/filters/filter-regex.h
Normal file
23
lib/filters/filter-regex.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FILTER_REGEX_H
|
||||
#define _LVM_FILTER_REGEX_H
|
||||
|
||||
#include "config.h"
|
||||
#include "dev-cache.h"
|
||||
|
||||
/*
|
||||
* patterns must be an array of strings of the form:
|
||||
* [ra]<sep><regex><sep>, eg,
|
||||
* r/cdrom/ - reject cdroms
|
||||
* a|loop/[0-4]| - accept loops 0 to 4
|
||||
* r|.*| - reject everything else
|
||||
*/
|
||||
|
||||
struct dev_filter *regex_filter_create(struct config_value *patterns);
|
||||
|
||||
#endif
|
||||
187
lib/filters/filter.c
Normal file
187
lib/filters/filter.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* lvm 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, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* lvm 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 GNU CC; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "dev-cache.h"
|
||||
#include "filter.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/kdev_t.h>
|
||||
|
||||
#define NUMBER_OF_MAJORS 256
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int max_partitions;
|
||||
} device_info_t;
|
||||
|
||||
static int _md_major = -1;
|
||||
|
||||
static device_info_t device_info[] = {
|
||||
{"ide", 16}, /* IDE disk */
|
||||
{"sd", 16}, /* SCSI disk */
|
||||
{"md", 16}, /* Multiple Disk driver (SoftRAID) */
|
||||
{"loop", 16}, /* Loop device */
|
||||
{"dasd", 4}, /* DASD disk (IBM S/390, zSeries) */
|
||||
{"dac960", 8}, /* DAC960 */
|
||||
{"nbd", 16}, /* Network Block Device */
|
||||
{"ida", 16}, /* Compaq SMART2 */
|
||||
{"cciss", 16}, /* Compaq CCISS array */
|
||||
{"ubd", 16}, /* User-mode virtual block device */
|
||||
{"ataraid", 16}, /* ATA Raid */
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
static int *scan_proc_dev(const char *proc);
|
||||
|
||||
static int passes_lvm_type_device_filter(struct dev_filter *f,
|
||||
struct device *dev)
|
||||
{
|
||||
int fd;
|
||||
const char *name = dev_name(dev);
|
||||
|
||||
/* Is this a recognised device type? */
|
||||
if (!(((int *) f->private)[MAJOR(dev->dev)]))
|
||||
return 0;
|
||||
|
||||
/* Check it's accessible */
|
||||
if ((fd = open(name, O_RDONLY)) < 0) {
|
||||
log_debug("Unable to open %s: %s", name, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dev_filter *lvm_type_filter_create(const char *proc)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!(f = dbg_malloc(sizeof (struct dev_filter)))) {
|
||||
log_error("LVM type filter allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->passes_filter = passes_lvm_type_device_filter;
|
||||
f->destroy = lvm_type_filter_destroy;
|
||||
|
||||
if (!(f->private = scan_proc_dev(proc)))
|
||||
return NULL;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
int md_major(void)
|
||||
{
|
||||
return _md_major;
|
||||
}
|
||||
|
||||
void lvm_type_filter_destroy(struct dev_filter *f)
|
||||
{
|
||||
dbg_free(f->private);
|
||||
dbg_free(f);
|
||||
return;
|
||||
}
|
||||
|
||||
static int *scan_proc_dev(const char *proc)
|
||||
{
|
||||
char line[80];
|
||||
char proc_devices[PATH_MAX];
|
||||
FILE *pd = NULL;
|
||||
int ret = 0;
|
||||
int i, j = 0;
|
||||
int line_maj = 0;
|
||||
int blocksection = 0;
|
||||
int dev_len = 0;
|
||||
|
||||
int *max_partitions_by_major;
|
||||
|
||||
if (!(max_partitions_by_major = dbg_malloc(sizeof (int) * NUMBER_OF_MAJORS))) {
|
||||
log_error("Filter failed to allocate max_partitions_by_major");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lvm_snprintf(proc_devices, sizeof(proc_devices),
|
||||
"%s/devices", proc) < 0) {
|
||||
log_error("Failed to create /proc/devices string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(pd = fopen(proc_devices, "r"))) {
|
||||
log_sys_error("fopen", proc_devices);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(max_partitions_by_major, 0, sizeof (int) * NUMBER_OF_MAJORS);
|
||||
while (fgets(line, 80, pd) != NULL) {
|
||||
i = 0;
|
||||
while (line[i] == ' ' && line[i] != '\0')
|
||||
i++;
|
||||
|
||||
/* If it's not a number it may be name of section */
|
||||
line_maj = atoi(((char *) (line + i)));
|
||||
if (!line_maj) {
|
||||
blocksection = (line[i] == 'B') ? 1 : 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We only want block devices ... */
|
||||
if (!blocksection)
|
||||
continue;
|
||||
|
||||
/* Find the start of the device major name */
|
||||
while (line[i] != ' ' && line[i] != '\0')
|
||||
i++;
|
||||
while (line[i] == ' ' && line[i] != '\0')
|
||||
i++;
|
||||
|
||||
/* Look for md device */
|
||||
if (!strncmp("md", line + i, 2) && isspace(*(line + i + 2)))
|
||||
_md_major = line_maj;
|
||||
|
||||
/* Go through the valid device names and if there is a
|
||||
match store max number of partitions */
|
||||
for (j = 0; device_info[j].name != NULL; j++) {
|
||||
|
||||
dev_len = strlen(device_info[j].name);
|
||||
if (dev_len <= strlen(line + i)
|
||||
&& !strncmp(device_info[j].name, line + i, dev_len)
|
||||
&& (line_maj < NUMBER_OF_MAJORS)) {
|
||||
max_partitions_by_major[line_maj] =
|
||||
device_info[j].max_partitions;
|
||||
ret++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(pd);
|
||||
return max_partitions_by_major;
|
||||
}
|
||||
31
lib/filters/filter.h
Normal file
31
lib/filters/filter.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* lvm 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, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* lvm 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 GNU CC; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FILTER_H
|
||||
#define _LVM_FILTER_H
|
||||
|
||||
struct dev_filter *lvm_type_filter_create();
|
||||
|
||||
void lvm_type_filter_destroy(struct dev_filter *f);
|
||||
|
||||
int md_major(void);
|
||||
|
||||
#endif
|
||||
|
||||
656
lib/format1/disk-rep.c
Normal file
656
lib/format1/disk-rep.c
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "xlate.h"
|
||||
#include "log.h"
|
||||
#include "vgcache.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/kdev_t.h>
|
||||
|
||||
#define fail do {stack; return 0;} while(0)
|
||||
#define xx16(v) disk->v = xlate16(disk->v)
|
||||
#define xx32(v) disk->v = xlate32(disk->v)
|
||||
#define xx64(v) disk->v = xlate64(disk->v)
|
||||
|
||||
/*
|
||||
* Functions to perform the endian conversion
|
||||
* between disk and core. The same code works
|
||||
* both ways of course.
|
||||
*/
|
||||
static void _xlate_pvd(struct pv_disk *disk)
|
||||
{
|
||||
xx16(version);
|
||||
|
||||
xx32(pv_on_disk.base);
|
||||
xx32(pv_on_disk.size);
|
||||
xx32(vg_on_disk.base);
|
||||
xx32(vg_on_disk.size);
|
||||
xx32(pv_uuidlist_on_disk.base);
|
||||
xx32(pv_uuidlist_on_disk.size);
|
||||
xx32(lv_on_disk.base);
|
||||
xx32(lv_on_disk.size);
|
||||
xx32(pe_on_disk.base);
|
||||
xx32(pe_on_disk.size);
|
||||
|
||||
xx32(pv_major);
|
||||
xx32(pv_number);
|
||||
xx32(pv_status);
|
||||
xx32(pv_allocatable);
|
||||
xx32(pv_size);
|
||||
xx32(lv_cur);
|
||||
xx32(pe_size);
|
||||
xx32(pe_total);
|
||||
xx32(pe_allocated);
|
||||
xx32(pe_start);
|
||||
}
|
||||
|
||||
static void _xlate_lvd(struct lv_disk *disk)
|
||||
{
|
||||
xx32(lv_access);
|
||||
xx32(lv_status);
|
||||
xx32(lv_open);
|
||||
xx32(lv_dev);
|
||||
xx32(lv_number);
|
||||
xx32(lv_mirror_copies);
|
||||
xx32(lv_recovery);
|
||||
xx32(lv_schedule);
|
||||
xx32(lv_size);
|
||||
xx32(lv_snapshot_minor);
|
||||
xx16(lv_chunk_size);
|
||||
xx16(dummy);
|
||||
xx32(lv_allocated_le);
|
||||
xx32(lv_stripes);
|
||||
xx32(lv_stripesize);
|
||||
xx32(lv_badblock);
|
||||
xx32(lv_allocation);
|
||||
xx32(lv_io_timeout);
|
||||
xx32(lv_read_ahead);
|
||||
}
|
||||
|
||||
static void _xlate_vgd(struct vg_disk *disk)
|
||||
{
|
||||
xx32(vg_number);
|
||||
xx32(vg_access);
|
||||
xx32(vg_status);
|
||||
xx32(lv_max);
|
||||
xx32(lv_cur);
|
||||
xx32(lv_open);
|
||||
xx32(pv_max);
|
||||
xx32(pv_cur);
|
||||
xx32(pv_act);
|
||||
xx32(dummy);
|
||||
xx32(vgda);
|
||||
xx32(pe_size);
|
||||
xx32(pe_total);
|
||||
xx32(pe_allocated);
|
||||
xx32(pvg_total);
|
||||
}
|
||||
|
||||
static void _xlate_extents(struct pe_disk *extents, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
extents[i].lv_num = xlate16(extents[i].lv_num);
|
||||
extents[i].le_num = xlate16(extents[i].le_num);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle both minor metadata formats.
|
||||
*/
|
||||
static int _munge_formats(struct pv_disk *pvd)
|
||||
{
|
||||
uint32_t pe_start;
|
||||
|
||||
switch (pvd->version) {
|
||||
case 1:
|
||||
pvd->pe_start = ((pvd->pe_on_disk.base +
|
||||
pvd->pe_on_disk.size) / SECTOR_SIZE);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pvd->version = 1;
|
||||
pe_start = pvd->pe_start * SECTOR_SIZE;
|
||||
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_pvd(struct device *dev, struct pv_disk *pvd)
|
||||
{
|
||||
if (dev_read(dev, 0, sizeof(*pvd), pvd) != sizeof(*pvd)) {
|
||||
log_very_verbose("Failed to read PV data from %s",
|
||||
dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
_xlate_pvd(pvd);
|
||||
|
||||
if (pvd->id[0] != 'H' || pvd->id[1] != 'M') {
|
||||
log_very_verbose("%s does not have a valid PV identifier",
|
||||
dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_munge_formats(pvd)) {
|
||||
log_very_verbose("Unknown metadata version %d found on %s",
|
||||
pvd->version, dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_lvd(struct device *dev, ulong pos, struct lv_disk *disk)
|
||||
{
|
||||
if (dev_read(dev, pos, sizeof(*disk), disk) != sizeof(*disk))
|
||||
fail;
|
||||
|
||||
_xlate_lvd(disk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_vgd(struct disk_list *data)
|
||||
{
|
||||
struct vg_disk *vgd = &data->vgd;
|
||||
ulong pos = data->pvd.vg_on_disk.base;
|
||||
if (dev_read(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
|
||||
fail;
|
||||
|
||||
_xlate_vgd(vgd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_uuids(struct disk_list *data)
|
||||
{
|
||||
int num_read = 0;
|
||||
struct uuid_list *ul;
|
||||
char buffer[NAME_LEN];
|
||||
ulong pos = data->pvd.pv_uuidlist_on_disk.base;
|
||||
ulong end = pos + data->pvd.pv_uuidlist_on_disk.size;
|
||||
|
||||
while (pos < end && num_read < data->vgd.pv_cur) {
|
||||
if (dev_read(data->dev, pos, sizeof(buffer), buffer) !=
|
||||
sizeof(buffer))
|
||||
fail;
|
||||
|
||||
if (!(ul = pool_alloc(data->mem, sizeof(*ul))))
|
||||
fail;
|
||||
|
||||
memcpy(ul->uuid, buffer, NAME_LEN);
|
||||
ul->uuid[NAME_LEN - 1] = '\0';
|
||||
|
||||
list_add(&data->uuids, &ul->list);
|
||||
|
||||
pos += NAME_LEN;
|
||||
num_read++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int _check_lvd(struct lv_disk *lvd)
|
||||
{
|
||||
return !(lvd->lv_name[0] == '\0');
|
||||
}
|
||||
|
||||
static int _read_lvs(struct disk_list *data)
|
||||
{
|
||||
int i, read = 0;
|
||||
ulong pos;
|
||||
struct lvd_list *ll;
|
||||
struct vg_disk *vgd = &data->vgd;
|
||||
|
||||
for (i = 0; (i < vgd->lv_max) && (read < vgd->lv_cur); i++) {
|
||||
pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
|
||||
ll = pool_alloc(data->mem, sizeof(*ll));
|
||||
|
||||
if (!ll)
|
||||
fail;
|
||||
|
||||
if (!_read_lvd(data->dev, pos, &ll->lvd))
|
||||
fail;
|
||||
|
||||
if (!_check_lvd(&ll->lvd))
|
||||
continue;
|
||||
|
||||
read++;
|
||||
list_add(&data->lvds, &ll->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_extents(struct disk_list *data)
|
||||
{
|
||||
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
|
||||
struct pe_disk *extents = pool_alloc(data->mem, len);
|
||||
ulong pos = data->pvd.pe_on_disk.base;
|
||||
|
||||
if (!extents)
|
||||
fail;
|
||||
|
||||
if (dev_read(data->dev, pos, len, extents) != len)
|
||||
fail;
|
||||
|
||||
_xlate_extents(extents, data->pvd.pe_total);
|
||||
data->extents = extents;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If exported, remove "PV_EXP" from end of VG name
|
||||
*/
|
||||
static void _munge_exported_vg(struct disk_list *data)
|
||||
{
|
||||
int l, s;
|
||||
|
||||
/* Return if PV not in a VG or VG not exported */
|
||||
if ((!*data->pvd.vg_name) ||
|
||||
!(data->vgd.vg_status & VG_EXPORTED))
|
||||
return;
|
||||
|
||||
l = strlen(data->pvd.vg_name);
|
||||
s = sizeof(EXPORTED_TAG);
|
||||
if (!strncmp(data->pvd.vg_name + l - s + 1, EXPORTED_TAG, s))
|
||||
data->pvd.vg_name[l - s + 1] = '\0';
|
||||
|
||||
data->pvd.pv_status |= VG_EXPORTED;
|
||||
}
|
||||
|
||||
static struct disk_list *__read_disk(struct device *dev, struct pool *mem,
|
||||
const char *vg_name)
|
||||
{
|
||||
struct disk_list *data = pool_alloc(mem, sizeof(*data));
|
||||
const char *name = dev_name(dev);
|
||||
|
||||
if (!data) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
data->mem = mem;
|
||||
list_init(&data->uuids);
|
||||
list_init(&data->lvds);
|
||||
|
||||
if (!read_pvd(dev, &data->pvd)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* is it an orphan ?
|
||||
*/
|
||||
if (!*data->pvd.vg_name) {
|
||||
log_very_verbose("%s is not a member of any VG", name);
|
||||
|
||||
/* Update VG cache */
|
||||
vgcache_add(data->pvd.vg_name, dev);
|
||||
|
||||
return (vg_name) ? NULL : data;
|
||||
}
|
||||
|
||||
if (!_read_vgd(data)) {
|
||||
log_error("Failed to read VG data from PV (%s)", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* If VG is exported, set VG name back to the real name */
|
||||
_munge_exported_vg(data);
|
||||
|
||||
/* Update VG cache with what we found */
|
||||
vgcache_add(data->pvd.vg_name, dev);
|
||||
|
||||
if (vg_name && strcmp(vg_name, data->pvd.vg_name)) {
|
||||
log_very_verbose("%s is not a member of the VG %s",
|
||||
name, vg_name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_uuids(data)) {
|
||||
log_error("Failed to read PV uuid list from %s", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_lvs(data)) {
|
||||
log_error("Failed to read LV's from %s", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_extents(data)) {
|
||||
log_error("Failed to read extents from %s", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
log_very_verbose("Found %s in %sVG %s", name,
|
||||
(data->vgd.vg_status & VG_EXPORTED) ? "exported " : "",
|
||||
data->pvd.vg_name);
|
||||
|
||||
return data;
|
||||
|
||||
bad:
|
||||
pool_free(data->mem, data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct disk_list *read_disk(struct device *dev, struct pool *mem,
|
||||
const char *vg_name)
|
||||
{
|
||||
struct disk_list *r;
|
||||
|
||||
if (!dev_open(dev, O_RDONLY)) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = __read_disk(dev, mem, vg_name);
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _add_pv_to_list(struct list *head, struct disk_list *data)
|
||||
{
|
||||
struct list *pvdh;
|
||||
struct pv_disk *pvd;
|
||||
|
||||
list_iterate(pvdh, head) {
|
||||
pvd = &list_item(pvdh, struct disk_list)->pvd;
|
||||
if (!strncmp(data->pvd.pv_uuid, pvd->pv_uuid,
|
||||
sizeof(pvd->pv_uuid))) {
|
||||
if (MAJOR(data->dev->dev) != md_major()) {
|
||||
log_very_verbose("Ignoring duplicate PV %s on "
|
||||
"%s", pvd->pv_uuid,
|
||||
dev_name(data->dev));
|
||||
return;
|
||||
}
|
||||
log_very_verbose("Duplicate PV %s - using md %s",
|
||||
pvd->pv_uuid, dev_name(data->dev));
|
||||
list_del(pvdh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
list_add(head, &data->list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a list of pv_d's structures, allocated from mem.
|
||||
* We keep track of the first object allocated form the pool
|
||||
* so we can free off all the memory if something goes wrong.
|
||||
*/
|
||||
int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
|
||||
struct pool *mem, struct list *head)
|
||||
{
|
||||
struct dev_iter *iter;
|
||||
struct device *dev;
|
||||
struct disk_list *data = NULL;
|
||||
|
||||
struct list *pvdh, *pvdh2;
|
||||
|
||||
/* Fast path if we already saw this VG and cached the list of PVs */
|
||||
if ((pvdh = vgcache_find(vg_name))) {
|
||||
list_iterate(pvdh2, pvdh) {
|
||||
dev = list_item(pvdh2, struct pvdev_list)->dev;
|
||||
if (!(data = read_disk(dev, mem, vg_name)))
|
||||
break;
|
||||
_add_pv_to_list(head, data);
|
||||
}
|
||||
|
||||
/* Did we find the whole VG? */
|
||||
if (!vg_name || !*vg_name ||
|
||||
(data && *data->pvd.vg_name &&
|
||||
list_size(head) == data->vgd.pv_cur))
|
||||
return 1;
|
||||
|
||||
/* Something changed. Remove the hints. */
|
||||
list_init(head);
|
||||
vgcache_del(vg_name);
|
||||
}
|
||||
|
||||
if (!(iter = dev_iter_create(filter))) {
|
||||
log_error("read_pvs_in_vg: dev_iter_create failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise do a complete scan */
|
||||
for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
|
||||
if ((data = read_disk(dev, mem, vg_name))) {
|
||||
_add_pv_to_list(head, data);
|
||||
}
|
||||
}
|
||||
dev_iter_destroy(iter);
|
||||
|
||||
if (list_empty(head))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_vgd(struct disk_list *data)
|
||||
{
|
||||
struct vg_disk *vgd = &data->vgd;
|
||||
ulong pos = data->pvd.vg_on_disk.base;
|
||||
|
||||
_xlate_vgd(vgd);
|
||||
if (dev_write(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
|
||||
fail;
|
||||
|
||||
_xlate_vgd(vgd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_uuids(struct disk_list *data)
|
||||
{
|
||||
struct uuid_list *ul;
|
||||
struct list *uh;
|
||||
ulong pos = data->pvd.pv_uuidlist_on_disk.base;
|
||||
ulong end = pos + data->pvd.pv_uuidlist_on_disk.size;
|
||||
|
||||
list_iterate(uh, &data->uuids) {
|
||||
if (pos >= end) {
|
||||
log_error("Too many uuids to fit on %s",
|
||||
dev_name(data->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ul = list_item(uh, struct uuid_list);
|
||||
if (dev_write(data->dev, pos, NAME_LEN, ul->uuid) != NAME_LEN)
|
||||
fail;
|
||||
|
||||
pos += NAME_LEN;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_lvd(struct device *dev, ulong pos, struct lv_disk *disk)
|
||||
{
|
||||
_xlate_lvd(disk);
|
||||
if (dev_write(dev, pos, sizeof(*disk), disk) != sizeof(*disk))
|
||||
fail;
|
||||
|
||||
_xlate_lvd(disk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_lvs(struct disk_list *data)
|
||||
{
|
||||
struct list *lvh;
|
||||
ulong pos;
|
||||
|
||||
pos = data->pvd.lv_on_disk.base;
|
||||
|
||||
if (!dev_zero(data->dev, pos, data->pvd.lv_on_disk.size)) {
|
||||
log_error("Couldn't zero lv area on device '%s'",
|
||||
dev_name(data->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_iterate(lvh, &data->lvds) {
|
||||
struct lvd_list *ll = list_item(lvh, struct lvd_list);
|
||||
|
||||
if (!_write_lvd(data->dev, pos, &ll->lvd))
|
||||
fail;
|
||||
|
||||
pos += sizeof(struct lv_disk);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_extents(struct disk_list *data)
|
||||
{
|
||||
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
|
||||
struct pe_disk *extents = data->extents;
|
||||
ulong pos = data->pvd.pe_on_disk.base;
|
||||
|
||||
_xlate_extents(extents, data->pvd.pe_total);
|
||||
if (dev_write(data->dev, pos, len, extents) != len)
|
||||
fail;
|
||||
|
||||
_xlate_extents(extents, data->pvd.pe_total);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_pvd(struct disk_list *data)
|
||||
{
|
||||
char *buf;
|
||||
ulong pos = data->pvd.pv_on_disk.base;
|
||||
ulong size = data->pvd.pv_on_disk.size;
|
||||
|
||||
if(size < sizeof(struct pv_disk)) {
|
||||
log_error("Invalid PV structure size.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure that the gap between the PV structure and
|
||||
the next one is zeroed in order to make non LVM tools
|
||||
happy (idea from AED) */
|
||||
buf = dbg_malloc(size);
|
||||
if(!buf) {
|
||||
log_err("Couldn't allocate temporary PV buffer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(buf, 0, size);
|
||||
memcpy(buf, &data->pvd, sizeof(struct pv_disk));
|
||||
|
||||
_xlate_pvd((struct pv_disk *)buf);
|
||||
if (dev_write(data->dev, pos, size, buf) != size) {
|
||||
dbg_free(buf);
|
||||
fail;
|
||||
}
|
||||
|
||||
dbg_free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* assumes the device has been opened.
|
||||
*/
|
||||
static int __write_all_pvd(struct disk_list *data)
|
||||
{
|
||||
const char *pv_name = dev_name(data->dev);
|
||||
|
||||
if (!_write_pvd(data)) {
|
||||
log_error("Failed to write PV structure onto %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!test_mode())
|
||||
vgcache_add(data->pvd.vg_name, data->dev);
|
||||
/*
|
||||
* Stop here for orphan pv's.
|
||||
*/
|
||||
if (data->pvd.vg_name[0] == '\0')
|
||||
return 1;
|
||||
|
||||
if (!_write_vgd(data)) {
|
||||
log_error("Failed to write VG data to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_uuids(data)) {
|
||||
log_error("Failed to write PV uuid list to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_lvs(data)) {
|
||||
log_error("Failed to write LV's to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_extents(data)) {
|
||||
log_error("Failed to write extents to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* opens the device and hands to the above fn.
|
||||
*/
|
||||
static int _write_all_pvd(struct disk_list *data)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!dev_open(data->dev, O_WRONLY)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = __write_all_pvd(data);
|
||||
|
||||
if (!dev_close(data->dev))
|
||||
stack;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes all the given pv's to disk. Does very
|
||||
* little sanity checking, so make sure correct
|
||||
* data is passed to here.
|
||||
*/
|
||||
int write_disks(struct list *pvs)
|
||||
{
|
||||
struct list *pvh;
|
||||
struct disk_list *dl;
|
||||
|
||||
list_iterate(pvh, pvs) {
|
||||
dl = list_item(pvh, struct disk_list);
|
||||
if (!(_write_all_pvd(dl)))
|
||||
fail;
|
||||
|
||||
log_very_verbose("Successfully wrote data to %s",
|
||||
dev_name(dl->dev));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
245
lib/format1/disk-rep.h
Normal file
245
lib/format1/disk-rep.h
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef DISK_REP_FORMAT1_H
|
||||
#define DISK_REP_FORMAT1_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
#include "metadata.h"
|
||||
#include "pool.h"
|
||||
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
#define MAX_PV 256
|
||||
#define MAX_LV 256
|
||||
#define MAX_VG 99
|
||||
|
||||
#define MIN_PE_SIZE (8192L / SECTOR_SIZE) /* 8 KB in sectors */
|
||||
#define MAX_PE_SIZE (16L * 1024L * 1024L / SECTOR_SIZE * 1024)
|
||||
#define PE_SIZE_PV_SIZE_REL 5 /* PV size must be at least 5 times PE size */
|
||||
#define MAX_LE_TOTAL 65534 /* 2^16 - 2 */
|
||||
#define MAX_PE_TOTAL ((uint32_t) -2)
|
||||
|
||||
|
||||
#define UNMAPPED_EXTENT 0
|
||||
|
||||
/* volume group */
|
||||
#define VG_ACTIVE 0x01 /* vg_status */
|
||||
#define VG_EXPORTED 0x02 /* " */
|
||||
#define VG_EXTENDABLE 0x04 /* " */
|
||||
|
||||
#define VG_READ 0x01 /* vg_access */
|
||||
#define VG_WRITE 0x02 /* " */
|
||||
#define VG_CLUSTERED 0x04 /* " */
|
||||
#define VG_SHARED 0x08 /* " */
|
||||
|
||||
/* logical volume */
|
||||
#define LV_ACTIVE 0x01 /* lv_status */
|
||||
#define LV_SPINDOWN 0x02 /* " */
|
||||
|
||||
#define LV_READ 0x01 /* lv_access */
|
||||
#define LV_WRITE 0x02 /* " */
|
||||
#define LV_SNAPSHOT 0x04 /* " */
|
||||
#define LV_SNAPSHOT_ORG 0x08 /* " */
|
||||
|
||||
#define LV_BADBLOCK_ON 0x01 /* lv_badblock */
|
||||
|
||||
#define LV_STRICT 0x01 /* lv_allocation */
|
||||
#define LV_CONTIGUOUS 0x02 /* " */
|
||||
|
||||
/* physical volume */
|
||||
#define PV_ACTIVE 0x01 /* pv_status */
|
||||
#define PV_ALLOCATABLE 0x02 /* pv_allocatable */
|
||||
|
||||
#define EXPORTED_TAG "PV_EXP" /* Identifier for exported PV */
|
||||
#define IMPORTED_TAG "PV_IMP" /* Identifier for imported PV */
|
||||
|
||||
|
||||
struct data_area {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct pv_disk {
|
||||
uint8_t id[2];
|
||||
uint16_t version; /* lvm version */
|
||||
struct data_area pv_on_disk;
|
||||
struct data_area vg_on_disk;
|
||||
struct data_area pv_uuidlist_on_disk;
|
||||
struct data_area lv_on_disk;
|
||||
struct data_area pe_on_disk;
|
||||
uint8_t pv_uuid[NAME_LEN];
|
||||
uint8_t vg_name[NAME_LEN];
|
||||
uint8_t system_id[NAME_LEN]; /* for vgexport/vgimport */
|
||||
uint32_t pv_major;
|
||||
uint32_t pv_number;
|
||||
uint32_t pv_status;
|
||||
uint32_t pv_allocatable;
|
||||
uint32_t pv_size;
|
||||
uint32_t lv_cur;
|
||||
uint32_t pe_size;
|
||||
uint32_t pe_total;
|
||||
uint32_t pe_allocated;
|
||||
|
||||
/* only present on version == 2 pv's */
|
||||
uint32_t pe_start;
|
||||
};
|
||||
|
||||
struct lv_disk {
|
||||
uint8_t lv_name[NAME_LEN];
|
||||
uint8_t vg_name[NAME_LEN];
|
||||
uint32_t lv_access;
|
||||
uint32_t lv_status;
|
||||
uint32_t lv_open;
|
||||
uint32_t lv_dev;
|
||||
uint32_t lv_number;
|
||||
uint32_t lv_mirror_copies; /* for future use */
|
||||
uint32_t lv_recovery; /* " */
|
||||
uint32_t lv_schedule; /* " */
|
||||
uint32_t lv_size;
|
||||
uint32_t lv_snapshot_minor; /* minor number of original */
|
||||
uint16_t lv_chunk_size; /* chunk size of snapshot */
|
||||
uint16_t dummy;
|
||||
uint32_t lv_allocated_le;
|
||||
uint32_t lv_stripes;
|
||||
uint32_t lv_stripesize;
|
||||
uint32_t lv_badblock; /* for future use */
|
||||
uint32_t lv_allocation;
|
||||
uint32_t lv_io_timeout; /* for future use */
|
||||
uint32_t lv_read_ahead;
|
||||
};
|
||||
|
||||
struct vg_disk {
|
||||
uint8_t vg_uuid[ID_LEN]; /* volume group UUID */
|
||||
uint8_t vg_name_dummy[NAME_LEN - ID_LEN]; /* rest of v1 VG name */
|
||||
uint32_t vg_number; /* volume group number */
|
||||
uint32_t vg_access; /* read/write */
|
||||
uint32_t vg_status; /* active or not */
|
||||
uint32_t lv_max; /* maximum logical volumes */
|
||||
uint32_t lv_cur; /* current logical volumes */
|
||||
uint32_t lv_open; /* open logical volumes */
|
||||
uint32_t pv_max; /* maximum physical volumes */
|
||||
uint32_t pv_cur; /* current physical volumes FU */
|
||||
uint32_t pv_act; /* active physical volumes */
|
||||
uint32_t dummy;
|
||||
uint32_t vgda; /* volume group descriptor arrays FU */
|
||||
uint32_t pe_size; /* physical extent size in sectors */
|
||||
uint32_t pe_total; /* total of physical extents */
|
||||
uint32_t pe_allocated; /* allocated physical extents */
|
||||
uint32_t pvg_total; /* physical volume groups FU */
|
||||
};
|
||||
|
||||
struct pe_disk {
|
||||
uint16_t lv_num;
|
||||
uint16_t le_num;
|
||||
};
|
||||
|
||||
|
||||
struct uuid_list {
|
||||
struct list list;
|
||||
char uuid[NAME_LEN];
|
||||
};
|
||||
|
||||
struct lvd_list {
|
||||
struct list list;
|
||||
struct lv_disk lvd;
|
||||
};
|
||||
|
||||
struct disk_list {
|
||||
struct list list;
|
||||
struct pool *mem;
|
||||
struct device *dev;
|
||||
|
||||
struct pv_disk pvd;
|
||||
struct vg_disk vgd;
|
||||
struct list uuids;
|
||||
struct list lvds;
|
||||
struct pe_disk *extents;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Layout constants.
|
||||
*/
|
||||
#define METADATA_ALIGN 4096UL
|
||||
#define PE_ALIGN (65536UL / SECTOR_SIZE)
|
||||
|
||||
#define METADATA_BASE 0UL
|
||||
#define PV_SIZE 1024UL
|
||||
#define VG_SIZE 4096UL
|
||||
|
||||
|
||||
/*
|
||||
* Functions to calculate layout info.
|
||||
*/
|
||||
int calculate_layout(struct disk_list *dl);
|
||||
int calculate_extent_count(struct physical_volume *pv);
|
||||
|
||||
|
||||
/*
|
||||
* Low level io routines which read/write
|
||||
* disk_lists.
|
||||
*/
|
||||
int read_pvd(struct device *dev, struct pv_disk *pvd);
|
||||
|
||||
struct disk_list *read_disk(struct device *dev, struct pool *mem,
|
||||
const char *vg_name);
|
||||
|
||||
int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
|
||||
struct pool *mem, struct list *results);
|
||||
|
||||
int write_disks(struct list *pvds);
|
||||
|
||||
|
||||
/*
|
||||
* Functions to translate to between disk and in
|
||||
* core structures.
|
||||
*/
|
||||
int import_pv(struct pool *mem, struct device *dev,
|
||||
struct volume_group *vg,
|
||||
struct physical_volume *pv, struct pv_disk *pvd);
|
||||
int export_pv(struct pool *mem, struct volume_group *vg,
|
||||
struct pv_disk *pvd, struct physical_volume *pv);
|
||||
|
||||
int import_vg(struct pool *mem,
|
||||
struct volume_group *vg, struct disk_list *dl,
|
||||
int partial);
|
||||
int export_vg(struct vg_disk *vgd, struct volume_group *vg);
|
||||
|
||||
int import_lv(struct pool *mem, struct logical_volume *lv,
|
||||
struct lv_disk *lvd);
|
||||
void export_lv(struct lv_disk *lvd, struct volume_group *vg,
|
||||
struct logical_volume *lv, const char *dev_dir);
|
||||
|
||||
int import_extents(struct pool *mem, struct volume_group *vg,
|
||||
struct list *pvds);
|
||||
int export_extents(struct disk_list *dl, int lv_num,
|
||||
struct logical_volume *lv,
|
||||
struct physical_volume *pv);
|
||||
|
||||
int import_pvs(struct pool *mem, struct volume_group *vg,
|
||||
struct list *pvds, struct list *results, int *count);
|
||||
|
||||
int import_lvs(struct pool *mem, struct volume_group *vg,
|
||||
struct list *pvds);
|
||||
int export_lvs(struct disk_list *dl, struct volume_group *vg,
|
||||
struct physical_volume *pv, const char *dev_dir);
|
||||
|
||||
int export_uuids(struct disk_list *dl, struct volume_group *vg);
|
||||
|
||||
void export_numbers(struct list *pvds, struct volume_group *vg);
|
||||
|
||||
void export_pv_act(struct list *pvds);
|
||||
|
||||
/* blech */
|
||||
int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
|
||||
int *result);
|
||||
int export_vg_number(struct list *pvds, const char *vg_name,
|
||||
struct dev_filter *filter);
|
||||
|
||||
|
||||
#endif
|
||||
536
lib/format1/format1.c
Normal file
536
lib/format1/format1.c
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "hash.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "display.h"
|
||||
|
||||
/* VG consistency checks */
|
||||
static int _check_vgs(struct list *pvs, int *partial)
|
||||
{
|
||||
struct list *pvh, *t;
|
||||
struct disk_list *dl = NULL;
|
||||
struct disk_list *first = NULL;
|
||||
|
||||
int pv_count = 0;
|
||||
int exported = -1;
|
||||
|
||||
*partial = 0;
|
||||
|
||||
/*
|
||||
* If there are exported and unexported PVs, ignore exported ones.
|
||||
* This means an active VG won't be affected if disks are inserted
|
||||
* bearing an exported VG with the same name.
|
||||
*/
|
||||
list_iterate(pvh, pvs) {
|
||||
dl = list_item(pvh, struct disk_list);
|
||||
|
||||
if (exported < 0) {
|
||||
exported = dl->pvd.pv_status & VG_EXPORTED;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exported != (dl->pvd.pv_status & VG_EXPORTED)) {
|
||||
/* Remove exported PVs */
|
||||
list_iterate_safe(pvh, t, pvs) {
|
||||
dl = list_item(pvh, struct disk_list);
|
||||
if (dl->pvd.pv_status & VG_EXPORTED)
|
||||
list_del(pvh);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Remove any PVs with VG structs that differ from the first */
|
||||
list_iterate_safe(pvh, t, pvs) {
|
||||
dl = list_item(pvh, struct disk_list);
|
||||
|
||||
if (!first)
|
||||
first = dl;
|
||||
|
||||
else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) {
|
||||
log_error("VG data differs between PVs %s and %s",
|
||||
dev_name(first->dev), dev_name(dl->dev));
|
||||
list_del(pvh);
|
||||
if (partial_mode()) {
|
||||
*partial = 1;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
pv_count++;
|
||||
}
|
||||
|
||||
/* On entry to fn, list known to be non-empty */
|
||||
if (pv_count != dl->vgd.pv_cur) {
|
||||
log_error("%d PV(s) found for VG %s: expected %d",
|
||||
pv_count, dl->pvd.vg_name, dl->vgd.pv_cur);
|
||||
if (!partial_mode())
|
||||
return 0;
|
||||
*partial = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct volume_group *_build_vg(struct pool *mem, struct list *pvs)
|
||||
{
|
||||
struct volume_group *vg = pool_alloc(mem, sizeof(*vg));
|
||||
struct disk_list *dl;
|
||||
int partial;
|
||||
|
||||
if (!vg)
|
||||
goto bad;
|
||||
|
||||
if (list_empty(pvs))
|
||||
goto bad;
|
||||
|
||||
memset(vg, 0, sizeof(*vg));
|
||||
|
||||
list_init(&vg->pvs);
|
||||
list_init(&vg->lvs);
|
||||
|
||||
if (!_check_vgs(pvs, &partial))
|
||||
goto bad;
|
||||
|
||||
dl = list_item(pvs->n, struct disk_list);
|
||||
|
||||
if (!import_vg(mem, vg, dl, partial))
|
||||
goto bad;
|
||||
|
||||
if (!import_pvs(mem, vg, pvs, &vg->pvs, &vg->pv_count))
|
||||
goto bad;
|
||||
|
||||
if (!import_lvs(mem, vg, pvs))
|
||||
goto bad;
|
||||
|
||||
if (!import_extents(mem, vg, pvs))
|
||||
goto bad;
|
||||
|
||||
return vg;
|
||||
|
||||
bad:
|
||||
stack;
|
||||
pool_free(mem, vg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct volume_group *_vg_read(struct format_instance *fi,
|
||||
const char *vg_name)
|
||||
{
|
||||
struct pool *mem = pool_create(1024 * 10);
|
||||
struct list pvs;
|
||||
struct volume_group *vg = NULL;
|
||||
list_init(&pvs);
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Strip dev_dir if present */
|
||||
vg_name = strip_dir(vg_name, fi->cmd->dev_dir);
|
||||
|
||||
if (!read_pvs_in_vg(vg_name, fi->cmd->filter, mem, &pvs)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(vg = _build_vg(fi->cmd->mem, &pvs))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
vg->cmd = fi->cmd;
|
||||
|
||||
bad:
|
||||
pool_destroy(mem);
|
||||
return vg;
|
||||
}
|
||||
|
||||
static struct disk_list *_flatten_pv(struct pool *mem, struct volume_group *vg,
|
||||
struct physical_volume *pv,
|
||||
const char *dev_dir)
|
||||
{
|
||||
struct disk_list *dl = pool_alloc(mem, sizeof(*dl));
|
||||
|
||||
if (!dl) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dl->mem = mem;
|
||||
dl->dev = pv->dev;
|
||||
|
||||
list_init(&dl->uuids);
|
||||
list_init(&dl->lvds);
|
||||
|
||||
if (!export_pv(mem, vg, &dl->pvd, pv) ||
|
||||
!export_vg(&dl->vgd, vg) ||
|
||||
!export_uuids(dl, vg) ||
|
||||
!export_lvs(dl, vg, pv, dev_dir) ||
|
||||
!calculate_layout(dl)) {
|
||||
stack;
|
||||
pool_free(mem, dl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dl;
|
||||
}
|
||||
|
||||
static int _flatten_vg(struct pool *mem, struct volume_group *vg,
|
||||
struct list *pvds, const char *dev_dir,
|
||||
struct dev_filter *filter)
|
||||
{
|
||||
struct list *pvh;
|
||||
struct pv_list *pvl;
|
||||
struct disk_list *data;
|
||||
|
||||
list_iterate(pvh, &vg->pvs) {
|
||||
pvl = list_item(pvh, struct pv_list);
|
||||
|
||||
if (!(data = _flatten_pv(mem, vg, pvl->pv, dev_dir))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(pvds, &data->list);
|
||||
}
|
||||
|
||||
export_numbers(pvds, vg);
|
||||
export_pv_act(pvds);
|
||||
|
||||
if (!export_vg_number(pvds, vg->name, filter)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
|
||||
{
|
||||
struct pool *mem = pool_create(1024 * 10);
|
||||
struct list pvds;
|
||||
int r = 0;
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vg->status & PARTIAL_VG) {
|
||||
log_error("Cannot change metadata for partial volume group %s",
|
||||
vg->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_init(&pvds);
|
||||
|
||||
r = (_flatten_vg(mem, vg, &pvds, fi->cmd->dev_dir, fi->cmd->filter) &&
|
||||
write_disks(&pvds));
|
||||
pool_destroy(mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct physical_volume *_pv_read(struct format_instance *fi,
|
||||
const char *name)
|
||||
{
|
||||
struct pool *mem = pool_create(1024);
|
||||
struct physical_volume *pv = NULL;
|
||||
struct disk_list *dl;
|
||||
struct device *dev;
|
||||
|
||||
log_very_verbose("Reading physical volume data %s from disk", name);
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(dev = dev_cache_get(name, fi->cmd->filter))) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(dl = read_disk(dev, mem, NULL))) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(pv = pool_alloc(fi->cmd->mem, sizeof(*pv)))) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!import_pv(fi->cmd->mem, dl->dev, NULL, pv, &dl->pvd)) {
|
||||
stack;
|
||||
pool_free(fi->cmd->mem, pv);
|
||||
pv = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
pool_destroy(mem);
|
||||
return pv;
|
||||
}
|
||||
|
||||
static struct list *_get_pvs(struct format_instance *fi)
|
||||
{
|
||||
struct pool *mem = pool_create(1024 * 10);
|
||||
struct list pvs, *results;
|
||||
uint32_t count;
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(results = pool_alloc(fi->cmd->mem, sizeof(*results)))) {
|
||||
stack;
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_init(&pvs);
|
||||
list_init(results);
|
||||
|
||||
if (!read_pvs_in_vg(NULL, fi->cmd->filter, mem, &pvs)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!import_pvs(fi->cmd->mem, NULL, &pvs, results, &count)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pool_destroy(mem);
|
||||
return results;
|
||||
|
||||
bad:
|
||||
pool_free(fi->cmd->mem, results);
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _find_vg_name(struct list *names, const char *vg)
|
||||
{
|
||||
struct list *nh;
|
||||
struct name_list *nl;
|
||||
|
||||
list_iterate(nh, names) {
|
||||
nl = list_item(nh, struct name_list);
|
||||
if (!strcmp(nl->name, vg))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct list *_get_vgs(struct format_instance *fi)
|
||||
{
|
||||
struct list *pvh;
|
||||
struct list *pvs, *names = pool_alloc(fi->cmd->mem, sizeof(*names));
|
||||
struct name_list *nl;
|
||||
|
||||
if (!names) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_init(names);
|
||||
|
||||
if (!(pvs = _get_pvs(fi))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_iterate(pvh, pvs) {
|
||||
struct pv_list *pvl = list_item(pvh, struct pv_list);
|
||||
|
||||
if (!(*pvl->pv->vg_name) ||
|
||||
_find_vg_name(names, pvl->pv->vg_name))
|
||||
continue;
|
||||
|
||||
if (!(nl = pool_alloc(fi->cmd->mem, sizeof(*nl)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(nl->name = pool_strdup(fi->cmd->mem,
|
||||
pvl->pv->vg_name))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_add(names, &nl->list);
|
||||
}
|
||||
|
||||
if (list_empty(names))
|
||||
goto bad;
|
||||
|
||||
return names;
|
||||
|
||||
bad:
|
||||
pool_free(fi->cmd->mem, names);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
/*
|
||||
* This works out pe_start and pe_count.
|
||||
*/
|
||||
if (!calculate_extent_count(pv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _lv_setup(struct format_instance *fi, struct logical_volume *lv)
|
||||
{
|
||||
if (lv->le_count > MAX_LE_TOTAL) {
|
||||
log_error("logical volumes cannot contain more than "
|
||||
"%d extents.", MAX_LE_TOTAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
|
||||
{
|
||||
struct pool *mem;
|
||||
struct disk_list *dl;
|
||||
struct list pvs;
|
||||
|
||||
list_init(&pvs);
|
||||
|
||||
if (*pv->vg_name || pv->pe_allocated ) {
|
||||
log_error("Assertion failed: can't _pv_write non-orphan PV "
|
||||
"(in VG %s)", pv->vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure any residual PE structure is gone */
|
||||
pv->pe_size = pv->pe_count = pv->pe_start = 0;
|
||||
|
||||
if (!(mem = pool_create(1024))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(dl = pool_alloc(mem, sizeof(*dl)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
dl->mem = mem;
|
||||
dl->dev = pv->dev;
|
||||
|
||||
if (!export_pv(mem, NULL, &dl->pvd, pv)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* must be set to be able to zero gap after PV structure in
|
||||
dev_write in order to make other disk tools happy */
|
||||
dl->pvd.pv_on_disk.base = METADATA_BASE;
|
||||
dl->pvd.pv_on_disk.size = PV_SIZE;
|
||||
|
||||
list_add(&pvs, &dl->list);
|
||||
if (!write_disks(&pvs)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pool_destroy(mem);
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
pool_destroy(mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _vg_setup(struct format_instance *fi, struct volume_group *vg)
|
||||
{
|
||||
/* just check max_pv and max_lv */
|
||||
if (vg->max_lv >= MAX_LV)
|
||||
vg->max_lv = MAX_LV - 1;
|
||||
|
||||
if (vg->max_pv >= MAX_PV)
|
||||
vg->max_pv = MAX_PV - 1;
|
||||
|
||||
if (vg->extent_size > MAX_PE_SIZE || vg->extent_size < MIN_PE_SIZE) {
|
||||
char *dummy, *dummy2;
|
||||
|
||||
log_error("Extent size must be between %s and %s",
|
||||
(dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT)),
|
||||
(dummy2 = display_size(MAX_PE_SIZE / 2, SIZE_SHORT)));
|
||||
|
||||
dbg_free(dummy);
|
||||
dbg_free(dummy2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vg->extent_size % MIN_PE_SIZE) {
|
||||
char *dummy;
|
||||
log_error("Extent size must be multiple of %s",
|
||||
(dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT)));
|
||||
dbg_free(dummy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Redundant? */
|
||||
if (vg->extent_size & (vg->extent_size - 1)) {
|
||||
log_error("Extent size must be power of 2");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void _destroy(struct format_instance *fi)
|
||||
{
|
||||
dbg_free(fi);
|
||||
}
|
||||
|
||||
|
||||
static struct format_handler _format1_ops = {
|
||||
get_vgs: _get_vgs,
|
||||
get_pvs: _get_pvs,
|
||||
pv_read: _pv_read,
|
||||
pv_setup: _pv_setup,
|
||||
pv_write: _pv_write,
|
||||
lv_setup: _lv_setup,
|
||||
vg_read: _vg_read,
|
||||
vg_setup: _vg_setup,
|
||||
vg_write: _vg_write,
|
||||
destroy: _destroy,
|
||||
};
|
||||
|
||||
struct format_instance *create_lvm1_format(struct cmd_context *cmd)
|
||||
{
|
||||
struct format_instance *fi = dbg_malloc(sizeof(*fi));
|
||||
|
||||
if (!fi) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fi->cmd = cmd;
|
||||
fi->ops = &_format1_ops;
|
||||
fi->private = NULL;
|
||||
|
||||
return fi;
|
||||
}
|
||||
14
lib/format1/format1.h
Normal file
14
lib/format1/format1.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FORMAT1_H
|
||||
#define _LVM_FORMAT1_H
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
struct format_instance *create_lvm1_format(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
||||
587
lib/format1/import-export.c
Normal file
587
lib/format1/import-export.c
Normal file
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* Translates between disk and in-core formats.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "hash.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
static int _check_vg_name(const char *name)
|
||||
{
|
||||
return strlen(name) < NAME_LEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts the last part of a path.
|
||||
*/
|
||||
static char *_create_lv_name(struct pool *mem, const char *full_name)
|
||||
{
|
||||
const char *ptr = strrchr(full_name, '/');
|
||||
|
||||
if (!ptr)
|
||||
ptr = full_name;
|
||||
else
|
||||
ptr++;
|
||||
|
||||
return pool_strdup(mem, ptr);
|
||||
}
|
||||
|
||||
int import_pv(struct pool *mem, struct device *dev,
|
||||
struct volume_group *vg,
|
||||
struct physical_volume *pv, struct pv_disk *pvd)
|
||||
{
|
||||
memset(pv, 0, sizeof(*pv));
|
||||
memcpy(&pv->id, pvd->pv_uuid, ID_LEN);
|
||||
|
||||
pv->dev = dev;
|
||||
if (!(pv->vg_name = pool_strdup(mem, pvd->vg_name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store system_id from first PV if PV belongs to a VG */
|
||||
if (vg && !*vg->system_id)
|
||||
strncpy(vg->system_id, pvd->system_id, NAME_LEN);
|
||||
|
||||
if (vg &&
|
||||
strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id)))
|
||||
log_very_verbose("System ID %s on %s differs from %s for "
|
||||
"volume group", pvd->system_id,
|
||||
dev_name(pv->dev), vg->system_id);
|
||||
|
||||
/*
|
||||
* If exported, we still need to flag in pv->status too because
|
||||
* we don't always have a struct volume_group when we need this.
|
||||
*/
|
||||
if (pvd->pv_status & VG_EXPORTED)
|
||||
pv->status |= EXPORTED_VG;
|
||||
|
||||
if (pvd->pv_allocatable)
|
||||
pv->status |= ALLOCATABLE_PV;
|
||||
|
||||
pv->size = pvd->pv_size;
|
||||
pv->pe_size = pvd->pe_size;
|
||||
pv->pe_start = pvd->pe_start;
|
||||
pv->pe_count = pvd->pe_total;
|
||||
pv->pe_allocated = pvd->pe_allocated;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _system_id(char *s, const char *prefix)
|
||||
{
|
||||
struct utsname uts;
|
||||
|
||||
if (uname(&uts) != 0) {
|
||||
log_sys_error("uname", "_system_id");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lvm_snprintf(s, NAME_LEN, "%s%s%lu",
|
||||
prefix, uts.nodename, time(NULL)) < 0) {
|
||||
log_error("Generated system_id too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_pv(struct pool *mem, struct volume_group *vg,
|
||||
struct pv_disk *pvd, struct physical_volume *pv)
|
||||
{
|
||||
memset(pvd, 0, sizeof(*pvd));
|
||||
|
||||
pvd->id[0] = 'H';
|
||||
pvd->id[1] = 'M';
|
||||
pvd->version = 1;
|
||||
|
||||
memcpy(pvd->pv_uuid, pv->id.uuid, ID_LEN);
|
||||
|
||||
if (!_check_vg_name(pv->vg_name)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(pvd->vg_name, 0, sizeof(pvd->vg_name));
|
||||
|
||||
if (pv->vg_name)
|
||||
strncpy(pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name));
|
||||
|
||||
/* Preserve existing system_id if it exists */
|
||||
if (vg && *vg->system_id)
|
||||
strncpy(pvd->system_id, vg->system_id, sizeof(pvd->system_id));
|
||||
|
||||
/* Is VG already exported or being exported? */
|
||||
if (vg && (vg->status & EXPORTED_VG)) {
|
||||
/* Does system_id need setting? */
|
||||
if (!*vg->system_id ||
|
||||
strncmp(vg->system_id, EXPORTED_TAG,
|
||||
sizeof(EXPORTED_TAG) - 1)) {
|
||||
if (!_system_id(pvd->system_id, EXPORTED_TAG)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (strlen(pvd->vg_name) + sizeof(EXPORTED_TAG) >
|
||||
sizeof(pvd->vg_name)) {
|
||||
log_error("Volume group name %s too long to export",
|
||||
pvd->vg_name);
|
||||
return 0;
|
||||
}
|
||||
strcat(pvd->vg_name, EXPORTED_TAG);
|
||||
}
|
||||
|
||||
/* Is VG being imported? */
|
||||
if (vg && !(vg->status & EXPORTED_VG) && *vg->system_id &&
|
||||
!strncmp(vg->system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) {
|
||||
if (!_system_id(pvd->system_id, IMPORTED_TAG)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate system_id if PV is in VG */
|
||||
if (!pvd->system_id || !*pvd->system_id)
|
||||
if (!_system_id(pvd->system_id, "")) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update internal system_id if we changed it */
|
||||
if (vg &&
|
||||
(!*vg->system_id ||
|
||||
strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id))))
|
||||
strncpy(vg->system_id, pvd->system_id, NAME_LEN);
|
||||
|
||||
//pvd->pv_major = MAJOR(pv->dev);
|
||||
|
||||
if (pv->status & ALLOCATABLE_PV)
|
||||
pvd->pv_allocatable = PV_ALLOCATABLE;
|
||||
|
||||
pvd->pv_size = pv->size;
|
||||
pvd->lv_cur = 0; /* this is set when exporting the lv list */
|
||||
pvd->pe_size = pv->pe_size;
|
||||
pvd->pe_total = pv->pe_count;
|
||||
pvd->pe_allocated = pv->pe_allocated;
|
||||
pvd->pe_start = pv->pe_start;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_vg(struct pool *mem,
|
||||
struct volume_group *vg, struct disk_list *dl,
|
||||
int partial)
|
||||
{
|
||||
struct vg_disk *vgd = &dl->vgd;
|
||||
memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
|
||||
|
||||
if (!_check_vg_name(dl->pvd.vg_name)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(vg->name = pool_strdup(mem, dl->pvd.vg_name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(vg->system_id = pool_alloc(mem, NAME_LEN))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*vg->system_id = '\0';
|
||||
|
||||
if (vgd->vg_status & VG_EXPORTED)
|
||||
vg->status |= EXPORTED_VG;
|
||||
|
||||
if (vgd->vg_status & VG_EXTENDABLE)
|
||||
vg->status |= RESIZEABLE_VG;
|
||||
|
||||
if (partial || (vgd->vg_access & VG_READ))
|
||||
vg->status |= LVM_READ;
|
||||
|
||||
if (!partial && (vgd->vg_access & VG_WRITE))
|
||||
vg->status |= LVM_WRITE;
|
||||
|
||||
if (vgd->vg_access & VG_CLUSTERED)
|
||||
vg->status |= CLUSTERED;
|
||||
|
||||
if (vgd->vg_access & VG_SHARED)
|
||||
vg->status |= SHARED;
|
||||
|
||||
vg->extent_size = vgd->pe_size;
|
||||
vg->extent_count = vgd->pe_total;
|
||||
vg->free_count = vgd->pe_total - vgd->pe_allocated;
|
||||
vg->max_lv = vgd->lv_max;
|
||||
vg->max_pv = vgd->pv_max;
|
||||
|
||||
if (partial)
|
||||
vg->status |= PARTIAL_VG;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_vg(struct vg_disk *vgd, struct volume_group *vg)
|
||||
{
|
||||
memset(vgd, 0, sizeof(*vgd));
|
||||
memcpy(vgd->vg_uuid, vg->id.uuid, ID_LEN);
|
||||
|
||||
if (vg->status & LVM_READ)
|
||||
vgd->vg_access |= VG_READ;
|
||||
|
||||
if (vg->status & LVM_WRITE)
|
||||
vgd->vg_access |= VG_WRITE;
|
||||
|
||||
if (vg->status & CLUSTERED)
|
||||
vgd->vg_access |= VG_CLUSTERED;
|
||||
|
||||
if (vg->status & SHARED)
|
||||
vgd->vg_access |= VG_SHARED;
|
||||
|
||||
if (vg->status & EXPORTED_VG)
|
||||
vgd->vg_status |= VG_EXPORTED;
|
||||
|
||||
if (vg->status & RESIZEABLE_VG)
|
||||
vgd->vg_status |= VG_EXTENDABLE;
|
||||
|
||||
vgd->lv_max = vg->max_lv;
|
||||
vgd->lv_cur = vg->lv_count;
|
||||
|
||||
vgd->pv_max = vg->max_pv;
|
||||
vgd->pv_cur = vg->pv_count;
|
||||
|
||||
vgd->pe_size = vg->extent_size;
|
||||
vgd->pe_total = vg->extent_count;
|
||||
vgd->pe_allocated = vg->extent_count - vg->free_count;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_lv(struct pool *mem, struct logical_volume *lv, struct lv_disk *lvd)
|
||||
{
|
||||
memset(&lv->id, 0, sizeof(lv->id));
|
||||
if (!(lv->name = _create_lv_name(mem, lvd->lv_name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lvd->lv_status & LV_SPINDOWN)
|
||||
lv->status |= SPINDOWN_LV;
|
||||
|
||||
if (lvd->lv_access & LV_READ)
|
||||
lv->status |= LVM_READ;
|
||||
|
||||
if (lvd->lv_access & LV_WRITE)
|
||||
lv->status |= LVM_WRITE;
|
||||
|
||||
if (lvd->lv_access & LV_SNAPSHOT)
|
||||
lv->status |= SNAPSHOT;
|
||||
|
||||
if (lvd->lv_access & LV_SNAPSHOT_ORG)
|
||||
lv->status |= SNAPSHOT_ORG;
|
||||
|
||||
if (lvd->lv_badblock)
|
||||
lv->status |= BADBLOCK_ON;
|
||||
|
||||
if (lvd->lv_allocation & LV_STRICT)
|
||||
lv->status |= ALLOC_STRICT;
|
||||
|
||||
if (lvd->lv_allocation & LV_CONTIGUOUS)
|
||||
lv->status |= ALLOC_CONTIGUOUS;
|
||||
else
|
||||
lv->status |= ALLOC_SIMPLE;
|
||||
|
||||
lv->read_ahead = lvd->lv_read_ahead;
|
||||
lv->size = lvd->lv_size;
|
||||
lv->le_count = lvd->lv_allocated_le;
|
||||
|
||||
list_init(&lv->segments);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void export_lv(struct lv_disk *lvd, struct volume_group *vg,
|
||||
struct logical_volume *lv, const char *dev_dir)
|
||||
{
|
||||
memset(lvd, 0, sizeof(*lvd));
|
||||
snprintf(lvd->lv_name, sizeof(lvd->lv_name), "%s%s/%s",
|
||||
dev_dir, vg->name, lv->name);
|
||||
|
||||
/* FIXME: Add 'if' test */
|
||||
_check_vg_name(vg->name);
|
||||
strcpy(lvd->vg_name, vg->name);
|
||||
|
||||
if (lv->status & LVM_READ)
|
||||
lvd->lv_access |= LV_READ;
|
||||
|
||||
if (lv->status & LVM_WRITE)
|
||||
lvd->lv_access |= LV_WRITE;
|
||||
|
||||
if (lv->status & SNAPSHOT)
|
||||
lvd->lv_access |= LV_SNAPSHOT;
|
||||
|
||||
if (lv->status & SNAPSHOT_ORG)
|
||||
lvd->lv_access |= LV_SNAPSHOT_ORG;
|
||||
|
||||
if (lv->status & SPINDOWN_LV)
|
||||
lvd->lv_status |= LV_SPINDOWN;
|
||||
|
||||
lvd->lv_read_ahead = lv->read_ahead;
|
||||
lvd->lv_stripes = list_item(lv->segments.n,
|
||||
struct stripe_segment)->stripes;
|
||||
lvd->lv_stripesize = list_item(lv->segments.n,
|
||||
struct stripe_segment)->stripe_size;
|
||||
|
||||
lvd->lv_size = lv->size;
|
||||
lvd->lv_allocated_le = lv->le_count;
|
||||
|
||||
if (lv->status & BADBLOCK_ON)
|
||||
lvd->lv_badblock = LV_BADBLOCK_ON;
|
||||
|
||||
if (lv->status & ALLOC_STRICT)
|
||||
lvd->lv_allocation |= LV_STRICT;
|
||||
|
||||
if (lv->status & ALLOC_CONTIGUOUS)
|
||||
lvd->lv_allocation |= LV_CONTIGUOUS;
|
||||
}
|
||||
|
||||
int export_extents(struct disk_list *dl, int lv_num,
|
||||
struct logical_volume *lv,
|
||||
struct physical_volume *pv)
|
||||
{
|
||||
struct list *segh;
|
||||
struct pe_disk *ped;
|
||||
struct stripe_segment *seg;
|
||||
uint32_t pe, s;
|
||||
|
||||
list_iterate (segh, &lv->segments) {
|
||||
seg = list_item(segh, struct stripe_segment);
|
||||
|
||||
for (s = 0; s < seg->stripes; s++) {
|
||||
if (seg->area[s].pv != pv)
|
||||
continue; /* not our pv */
|
||||
|
||||
for (pe = 0; pe < (seg->len / seg->stripes); pe++) {
|
||||
ped = &dl->extents[pe + seg->area[s].pe];
|
||||
ped->lv_num = lv_num;
|
||||
ped->le_num = (seg->le / seg->stripes) + pe +
|
||||
s * (lv->le_count / seg->stripes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_pvs(struct pool *mem, struct volume_group *vg,
|
||||
struct list *pvds, struct list *results, int *count)
|
||||
{
|
||||
struct list *pvdh;
|
||||
struct disk_list *dl;
|
||||
struct pv_list *pvl;
|
||||
|
||||
*count = 0;
|
||||
list_iterate(pvdh, pvds) {
|
||||
|
||||
dl = list_item(pvdh, struct disk_list);
|
||||
|
||||
if (!(pvl = pool_alloc(mem, sizeof(*pvl))) ||
|
||||
!(pvl->pv = pool_alloc(mem, sizeof(*pvl->pv)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!import_pv(mem, dl->dev, vg, pvl->pv, &dl->pvd)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(results, &pvl->list);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct logical_volume *_add_lv(struct pool *mem,
|
||||
struct volume_group *vg,
|
||||
struct lv_disk *lvd)
|
||||
{
|
||||
struct lv_list *ll;
|
||||
struct logical_volume *lv;
|
||||
|
||||
if (!(ll = pool_zalloc(mem, sizeof(*ll))) ||
|
||||
!(ll->lv = pool_zalloc(mem, sizeof(*ll->lv)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
lv = ll->lv;
|
||||
|
||||
if (!import_lv(mem, lv, lvd)) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add(&vg->lvs, &ll->list);
|
||||
lv->vg = vg;
|
||||
vg->lv_count++;
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
int import_lvs(struct pool *mem, struct volume_group *vg,
|
||||
struct list *pvds)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
struct lvd_list *ll;
|
||||
struct lv_disk *lvd;
|
||||
struct list *pvdh, *lvdh;
|
||||
|
||||
list_iterate(pvdh, pvds) {
|
||||
dl = list_item(pvdh, struct disk_list);
|
||||
list_iterate(lvdh, &dl->lvds) {
|
||||
ll = list_item(lvdh, struct lvd_list);
|
||||
lvd = &ll->lvd;
|
||||
|
||||
if (!find_lv(vg, lvd->lv_name) &&
|
||||
!_add_lv(mem, vg, lvd)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_lvs(struct disk_list *dl, struct volume_group *vg,
|
||||
struct physical_volume *pv, const char *dev_dir)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct lv_list *ll;
|
||||
struct lvd_list *lvdl;
|
||||
int lv_num = 0, len;
|
||||
|
||||
/*
|
||||
* setup the pv's extents array
|
||||
*/
|
||||
len = sizeof(struct pe_disk) * dl->pvd.pe_total;
|
||||
if (!(dl->extents = pool_alloc(dl->mem, len))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
memset(dl->extents, 0, len);
|
||||
|
||||
|
||||
list_iterate(lvh, &vg->lvs) {
|
||||
ll = list_item(lvh, struct lv_list);
|
||||
if (!(lvdl = pool_alloc(dl->mem, sizeof(*lvdl)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export_lv(&lvdl->lvd, vg, ll->lv, dev_dir);
|
||||
lvdl->lvd.lv_number = lv_num;
|
||||
if (!export_extents(dl, lv_num + 1, ll->lv, pv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(&dl->lvds, &lvdl->list);
|
||||
dl->pvd.lv_cur++;
|
||||
lv_num++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_uuids(struct disk_list *dl, struct volume_group *vg)
|
||||
{
|
||||
struct uuid_list *ul;
|
||||
struct pv_list *pvl;
|
||||
struct list *pvh;
|
||||
|
||||
list_iterate(pvh, &vg->pvs) {
|
||||
pvl = list_item(pvh, struct pv_list);
|
||||
if (!(ul = pool_alloc(dl->mem, sizeof(*ul)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(ul->uuid, 0, sizeof(ul->uuid));
|
||||
memcpy(ul->uuid, pvl->pv->id.uuid, ID_LEN);
|
||||
|
||||
list_add(&dl->uuids, &ul->list);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This calculates the nasty pv_number and
|
||||
* lv_number fields used by LVM1. Very
|
||||
* inefficient code.
|
||||
*/
|
||||
void export_numbers(struct list *pvds, struct volume_group *vg)
|
||||
{
|
||||
struct list *pvdh;
|
||||
struct disk_list *dl;
|
||||
int pv_num = 1;
|
||||
|
||||
list_iterate(pvdh, pvds) {
|
||||
dl = list_item(pvdh, struct disk_list);
|
||||
dl->pvd.pv_number = pv_num++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate vg_disk->pv_act.
|
||||
*/
|
||||
void export_pv_act(struct list *pvds)
|
||||
{
|
||||
struct list *pvdh;
|
||||
struct disk_list *dl;
|
||||
int act = 0;
|
||||
|
||||
list_iterate(pvdh, pvds) {
|
||||
dl = list_item(pvdh, struct disk_list);
|
||||
if (dl->pvd.pv_status & PV_ACTIVE)
|
||||
act++;
|
||||
}
|
||||
|
||||
list_iterate(pvdh, pvds) {
|
||||
dl = list_item(pvdh, struct disk_list);
|
||||
dl->vgd.pv_act = act;
|
||||
}
|
||||
}
|
||||
|
||||
int export_vg_number(struct list *pvds, const char *vg_name,
|
||||
struct dev_filter *filter)
|
||||
{
|
||||
struct list *pvdh;
|
||||
struct disk_list *dl;
|
||||
int vg_num;
|
||||
|
||||
if (!get_free_vg_number(filter, vg_name, &vg_num)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_iterate(pvdh, pvds) {
|
||||
dl = list_item(pvdh, struct disk_list);
|
||||
dl->vgd.vg_number = vg_num;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
369
lib/format1/import-extents.c
Normal file
369
lib/format1/import-extents.c
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "metadata.h"
|
||||
#include "hash.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "disk-rep.h"
|
||||
|
||||
/*
|
||||
* After much thought I have decided it is easier,
|
||||
* and probably no less efficient, to convert the
|
||||
* pe->le map to a full le->pe map, and then
|
||||
* process this to get the segments form that
|
||||
* we're after. Any code which goes directly from
|
||||
* the pe->le map to segments would be gladly
|
||||
* accepted, if it is less complicated than this
|
||||
* file.
|
||||
*/
|
||||
struct pe_specifier {
|
||||
struct physical_volume *pv;
|
||||
uint32_t pe;
|
||||
};
|
||||
|
||||
struct lv_map {
|
||||
struct logical_volume *lv;
|
||||
uint32_t stripes;
|
||||
uint32_t stripe_size;
|
||||
struct pe_specifier *map;
|
||||
};
|
||||
|
||||
static struct hash_table *_create_lv_maps(struct pool *mem,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
struct hash_table *maps = hash_create(32);
|
||||
struct list *llh;
|
||||
struct lv_list *ll;
|
||||
struct lv_map *lvm;
|
||||
|
||||
if (!maps) {
|
||||
log_err("Unable to create hash table for holding "
|
||||
"extent maps.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_iterate(llh, &vg->lvs) {
|
||||
ll = list_item(llh, struct lv_list);
|
||||
|
||||
if (!(lvm = pool_alloc(mem, sizeof(*lvm)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
lvm->lv = ll->lv;
|
||||
if (!(lvm->map = pool_zalloc(mem, sizeof(*lvm->map)
|
||||
* ll->lv->le_count))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!hash_insert(maps, ll->lv->name, lvm)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
|
||||
return maps;
|
||||
|
||||
bad:
|
||||
hash_destroy(maps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _fill_lv_array(struct lv_map **lvs,
|
||||
struct hash_table *maps, struct disk_list *dl)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct lv_map *lvm;
|
||||
|
||||
memset(lvs, 0, sizeof(*lvs) * MAX_LV);
|
||||
list_iterate(lvh, &dl->lvds) {
|
||||
struct lvd_list *ll = list_item(lvh, struct lvd_list);
|
||||
|
||||
if (!(lvm = hash_lookup(maps, strrchr(ll->lvd.lv_name, '/')
|
||||
+ 1))) {
|
||||
log_err("Physical volume (%s) contains an "
|
||||
"unknown logical volume (%s).",
|
||||
dev_name(dl->dev), ll->lvd.lv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lvm->stripes = ll->lvd.lv_stripes;
|
||||
lvm->stripe_size = ll->lvd.lv_stripesize;
|
||||
|
||||
lvs[ll->lvd.lv_number] = lvm;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _fill_maps(struct hash_table *maps, struct volume_group *vg,
|
||||
struct list *pvds)
|
||||
{
|
||||
struct list *pvdh;
|
||||
struct disk_list *dl;
|
||||
struct physical_volume *pv;
|
||||
struct lv_map *lvms[MAX_LV], *lvm;
|
||||
struct pe_disk *e;
|
||||
uint32_t i, lv_num, le;
|
||||
|
||||
list_iterate(pvdh, pvds) {
|
||||
dl = list_item(pvdh, struct disk_list);
|
||||
pv = find_pv(vg, dl->dev);
|
||||
e = dl->extents;
|
||||
|
||||
/* build an array of lv's for this pv */
|
||||
if (!_fill_lv_array(lvms, maps, dl)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < dl->pvd.pe_total; i++) {
|
||||
lv_num = e[i].lv_num;
|
||||
|
||||
if (lv_num == UNMAPPED_EXTENT)
|
||||
continue;
|
||||
|
||||
else {
|
||||
lv_num--;
|
||||
lvm = lvms[lv_num];
|
||||
|
||||
if (!lvm) {
|
||||
log_err("invalid lv in extent map");
|
||||
return 0;
|
||||
}
|
||||
|
||||
le = e[i].le_num;
|
||||
|
||||
if (le >= lvm->lv->le_count) {
|
||||
log_err("logical extent number "
|
||||
"out of bounds");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lvm->map[le].pv) {
|
||||
log_err("logical extent (%u) "
|
||||
"already mapped.", le);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lvm->map[le].pv = pv;
|
||||
lvm->map[le].pe = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_single_map(struct lv_map *lvm)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < lvm->lv->le_count; i++) {
|
||||
if (!lvm->map[i].pv) {
|
||||
log_err("Logical volume (%s) contains an incomplete "
|
||||
"mapping table.", lvm->lv->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_maps_are_complete(struct hash_table *maps)
|
||||
{
|
||||
struct hash_node *n;
|
||||
struct lv_map *lvm;
|
||||
|
||||
for (n = hash_get_first(maps); n; n = hash_get_next(maps, n)) {
|
||||
lvm = (struct lv_map *) hash_get_data(maps, n);
|
||||
|
||||
if (!_check_single_map(lvm)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct stripe_segment *_alloc_seg(struct pool *mem, uint32_t stripes)
|
||||
{
|
||||
struct stripe_segment *seg;
|
||||
uint32_t len = sizeof(*seg) + (stripes * sizeof(seg->area[0]));
|
||||
|
||||
if (!(seg = pool_zalloc(mem, len))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
static int _read_linear(struct pool *mem, struct lv_map *lvm)
|
||||
{
|
||||
uint32_t le = 0;
|
||||
struct stripe_segment *seg;
|
||||
|
||||
while (le < lvm->lv->le_count) {
|
||||
seg = _alloc_seg(mem, 1);
|
||||
|
||||
seg->lv = lvm->lv;
|
||||
seg->le = le;
|
||||
seg->len = 0;
|
||||
seg->stripe_size = 0;
|
||||
seg->stripes = 1;
|
||||
|
||||
seg->area[0].pv = lvm->map[le].pv;
|
||||
seg->area[0].pe = lvm->map[le].pe;
|
||||
|
||||
do
|
||||
seg->len++;
|
||||
|
||||
while ((lvm->map[le + seg->len].pv == seg->area[0].pv) &&
|
||||
(lvm->map[le + seg->len].pe == seg->area[0].pe +
|
||||
seg->len));
|
||||
|
||||
le += seg->len;
|
||||
|
||||
list_add(&lvm->lv->segments, &seg->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_stripe(struct lv_map *lvm, struct stripe_segment *seg,
|
||||
uint32_t base_le, uint32_t len)
|
||||
{
|
||||
uint32_t le, st;
|
||||
|
||||
le = base_le + seg->len;
|
||||
|
||||
/*
|
||||
* Is the next physical extent in every stripe adjacent to the last?
|
||||
*/
|
||||
for (st = 0; st < seg->stripes; st++)
|
||||
if ((lvm->map[le + st * len].pv != seg->area[st].pv) ||
|
||||
(lvm->map[le + st * len].pe != seg->area[st].pe + seg->len))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_stripes(struct pool *mem, struct lv_map *lvm)
|
||||
{
|
||||
uint32_t st, le = 0, len;
|
||||
struct stripe_segment *seg;
|
||||
|
||||
/*
|
||||
* Work out overall striped length
|
||||
*/
|
||||
if (lvm->lv->le_count % lvm->stripes) {
|
||||
log_error("Number of stripes (%u) incompatible "
|
||||
"with logical extent count (%u) for %s",
|
||||
lvm->stripes, lvm->lv->le_count,
|
||||
lvm->lv->name);
|
||||
}
|
||||
len = lvm->lv->le_count / lvm->stripes;
|
||||
|
||||
while (le < len) {
|
||||
if (!(seg = _alloc_seg(mem, lvm->stripes))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg->lv = lvm->lv;
|
||||
seg->stripe_size = lvm->stripe_size;
|
||||
seg->stripes = lvm->stripes;
|
||||
seg->le = seg->stripes * le;
|
||||
seg->len = 1;
|
||||
|
||||
/*
|
||||
* Set up start positions of each stripe in this segment
|
||||
*/
|
||||
for (st = 0; st < seg->stripes; st++) {
|
||||
seg->area[st].pv = lvm->map[le + st * len].pv;
|
||||
seg->area[st].pe = lvm->map[le + st * len].pe;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find how many blocks are contiguous in all stripes
|
||||
* and so can form part of this segment
|
||||
*/
|
||||
while (_check_stripe(lvm, seg, le, len))
|
||||
seg->len++;
|
||||
|
||||
le += seg->len;
|
||||
seg->len *= seg->stripes;
|
||||
|
||||
list_add(&lvm->lv->segments, &seg->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _build_segments(struct pool *mem, struct lv_map *lvm)
|
||||
{
|
||||
return (lvm->stripes > 1 ? _read_stripes(mem, lvm) :
|
||||
_read_linear(mem, lvm));
|
||||
}
|
||||
|
||||
static int _build_all_segments(struct pool *mem, struct hash_table *maps)
|
||||
{
|
||||
struct hash_node *n;
|
||||
struct lv_map *lvm;
|
||||
|
||||
for (n = hash_get_first(maps); n; n = hash_get_next(maps, n)) {
|
||||
lvm = (struct lv_map *) hash_get_data(maps, n);
|
||||
if (!_build_segments(mem, lvm)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_extents(struct pool *mem, struct volume_group *vg, struct list *pvds)
|
||||
{
|
||||
int r = 0;
|
||||
struct pool *scratch = pool_create(10 * 1024);
|
||||
struct hash_table *maps;
|
||||
|
||||
if (!scratch) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(maps = _create_lv_maps(scratch, vg))) {
|
||||
log_err("Couldn't allocate logical volume maps.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_fill_maps(maps, vg, pvds)) {
|
||||
log_err("Couldn't fill logical volume maps.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_build_all_segments(mem, maps)) {
|
||||
log_err("Couldn't build extent segments.");
|
||||
goto out;
|
||||
}
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
if (maps)
|
||||
hash_destroy(maps);
|
||||
pool_destroy(scratch);
|
||||
return r;
|
||||
}
|
||||
162
lib/format1/layout.c
Normal file
162
lib/format1/layout.c
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "log.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
|
||||
/*
|
||||
* Only works with powers of 2.
|
||||
*/
|
||||
static inline ulong _round_up(ulong n, ulong size)
|
||||
{
|
||||
size--;
|
||||
return (n + size) & ~size;
|
||||
}
|
||||
|
||||
static inline ulong _div_up(ulong n, ulong size)
|
||||
{
|
||||
return _round_up(n, size) / size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each chunk of metadata should be aligned to
|
||||
* METADATA_ALIGN.
|
||||
*/
|
||||
static uint32_t _next_base(struct data_area *area)
|
||||
{
|
||||
return _round_up(area->base + area->size, METADATA_ALIGN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick calculation based on pe_start.
|
||||
*/
|
||||
static int _adjust_pe_on_disk(struct pv_disk *pvd)
|
||||
{
|
||||
uint32_t pe_start = pvd->pe_start * SECTOR_SIZE;
|
||||
|
||||
if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size)
|
||||
return 0;
|
||||
|
||||
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _calc_simple_layout(struct pv_disk *pvd)
|
||||
{
|
||||
pvd->pv_on_disk.base = METADATA_BASE;
|
||||
pvd->pv_on_disk.size = PV_SIZE;
|
||||
|
||||
pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk);
|
||||
pvd->vg_on_disk.size = VG_SIZE;
|
||||
|
||||
pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk);
|
||||
pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN;
|
||||
|
||||
pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk);
|
||||
pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk);
|
||||
|
||||
pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk);
|
||||
pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk);
|
||||
}
|
||||
|
||||
int _check_vg_limits(struct disk_list *dl)
|
||||
{
|
||||
if (dl->vgd.lv_max > MAX_LV) {
|
||||
log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
|
||||
"for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
|
||||
dl->pvd.vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dl->vgd.pv_max > MAX_PV) {
|
||||
log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
|
||||
"for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
|
||||
dl->pvd.vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This assumes pe_count and pe_start have already
|
||||
* been calculated correctly.
|
||||
*/
|
||||
int calculate_layout(struct disk_list *dl)
|
||||
{
|
||||
struct pv_disk *pvd = &dl->pvd;
|
||||
|
||||
_calc_simple_layout(pvd);
|
||||
if (!_adjust_pe_on_disk(pvd)) {
|
||||
log_error("Insufficient space for metadata and PE's.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_check_vg_limits(dl))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* It may seem strange to have a struct
|
||||
* physical_volume in here, but the number of
|
||||
* extents that can fit on a disk *is* metadata
|
||||
* format dependant.
|
||||
*/
|
||||
int calculate_extent_count(struct physical_volume *pv)
|
||||
{
|
||||
struct pv_disk *pvd = dbg_malloc(sizeof(*pvd));
|
||||
uint32_t end;
|
||||
|
||||
if (!pvd) {
|
||||
stack;
|
||||
dbg_free(pvd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Guess how many extents will fit,
|
||||
* bearing in mind that one is going to be
|
||||
* knocked off at the start of the next
|
||||
* loop.
|
||||
*/
|
||||
pvd->pe_total = (pv->size / pv->pe_size);
|
||||
|
||||
if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
|
||||
log_error("Insufficient space for extents on %s",
|
||||
dev_name(pv->dev));
|
||||
dbg_free(pvd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
pvd->pe_total--;
|
||||
_calc_simple_layout(pvd);
|
||||
end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size + \
|
||||
SECTOR_SIZE - 1) / SECTOR_SIZE);
|
||||
|
||||
pvd->pe_start = _round_up(end, PE_ALIGN);
|
||||
|
||||
} while((pvd->pe_start + (pvd->pe_total * pv->pe_size)) > pv->size);
|
||||
|
||||
if (pvd->pe_total > MAX_PE_TOTAL) {
|
||||
log_error("Metadata extent limit (%u) exceeded for %s - "
|
||||
"%u required", MAX_PE_TOTAL, dev_name(pv->dev),
|
||||
pvd->pe_total);
|
||||
dbg_free(pvd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pv->pe_count = pvd->pe_total;
|
||||
pv->pe_start = pvd->pe_start;
|
||||
dbg_free(pvd);
|
||||
return 1;
|
||||
}
|
||||
145
lib/format1/lvm1_label.c
Normal file
145
lib/format1/lvm1_label.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "lvm1_label.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "disk-rep.h"
|
||||
#include "log.h"
|
||||
#include "label.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static void _not_supported(const char *op)
|
||||
{
|
||||
log_err("The '%s' operation is not supported for the lvm1 labeller.",
|
||||
op);
|
||||
}
|
||||
|
||||
static int _can_handle(struct labeller *l, struct device *dev)
|
||||
{
|
||||
struct pv_disk pvd;
|
||||
int r;
|
||||
|
||||
if (!dev_open(dev, O_RDONLY)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = read_pvd(dev, &pvd);
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _write(struct labeller *l,
|
||||
struct device *dev, struct label *label)
|
||||
{
|
||||
_not_supported("write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _remove(struct labeller *l, struct device *dev)
|
||||
{
|
||||
_not_supported("remove");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct label *_to_label(struct pv_disk *pvd)
|
||||
{
|
||||
struct label *l;
|
||||
struct lvm_label_info *info;
|
||||
|
||||
if (!(l = dbg_malloc(sizeof(*l)))) {
|
||||
log_err("Couldn't allocate label.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(info = (struct lvm_label_info *) dbg_strdup(pvd->vg_name))) {
|
||||
dbg_free(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(&l->id, &pvd->pv_uuid, sizeof(l->id));
|
||||
strcpy(l->volume_type, "lvm");
|
||||
l->version[0] = 1;
|
||||
l->version[0] = 0;
|
||||
l->version[0] = 0;
|
||||
l->extra_info = info;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int _read(struct labeller *l, struct device *dev, struct label **label)
|
||||
{
|
||||
struct pv_disk pvd;
|
||||
int r = 0;
|
||||
|
||||
if (!dev_open(dev, O_RDONLY)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = read_pvd(dev, &pvd);
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
if (!r) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the disk_list into a label structure.
|
||||
*/
|
||||
if (!(*label = _to_label(&pvd))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _destroy_label(struct labeller *l, struct label *label)
|
||||
{
|
||||
dbg_free(label->extra_info);
|
||||
dbg_free(label);
|
||||
}
|
||||
|
||||
static void _destroy(struct labeller *l)
|
||||
{
|
||||
dbg_free(l);
|
||||
}
|
||||
|
||||
|
||||
struct label_ops _lvm1_ops = {
|
||||
can_handle: _can_handle,
|
||||
write: _write,
|
||||
remove: _remove,
|
||||
read: _read,
|
||||
verify: _can_handle,
|
||||
destroy_label: _destroy_label,
|
||||
destroy: _destroy
|
||||
};
|
||||
|
||||
struct labeller *lvm1_labeller_create(void)
|
||||
{
|
||||
struct labeller *l;
|
||||
|
||||
if (!(l = dbg_malloc(sizeof(*l)))) {
|
||||
log_err("Couldn't allocate labeller object.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l->ops = &_lvm1_ops;
|
||||
l->private = NULL;
|
||||
|
||||
return l;
|
||||
}
|
||||
21
lib/format1/lvm1_label.h
Normal file
21
lib/format1/lvm1_label.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVM1_LABEL_H
|
||||
#define _LVM_LVM1_LABEL_H
|
||||
|
||||
/*
|
||||
* This is what the 'extra_info' field of the label will point to
|
||||
* if the label type is lvm1.
|
||||
*/
|
||||
struct lvm_label_info {
|
||||
char volume_group[0];
|
||||
};
|
||||
|
||||
|
||||
struct labeller *lvm1_labeller_create(void);
|
||||
|
||||
#endif
|
||||
61
lib/format1/vg_number.c
Normal file
61
lib/format1/vg_number.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "disk-rep.h"
|
||||
|
||||
/*
|
||||
* FIXME: Quick hack. We can use caching to
|
||||
* prevent a total re-read, even so vg_number
|
||||
* causes the tools to check *every* pv. Yuck.
|
||||
* Put in separate file so it wouldn't contaminate
|
||||
* other code.
|
||||
*/
|
||||
int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
|
||||
int *result)
|
||||
{
|
||||
struct list *pvh;
|
||||
struct list all_pvs;
|
||||
struct disk_list *dl;
|
||||
struct pool *mem = pool_create(10 * 1024);
|
||||
int numbers[MAX_VG], i, r = 0;
|
||||
|
||||
list_init(&all_pvs);
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!read_pvs_in_vg(NULL, filter, mem, &all_pvs)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(numbers, 0, sizeof(numbers));
|
||||
|
||||
list_iterate(pvh, &all_pvs) {
|
||||
dl = list_item(pvh, struct disk_list);
|
||||
if (!*dl->pvd.vg_name ||
|
||||
!strcmp(dl->pvd.vg_name, candidate_vg))
|
||||
continue;
|
||||
|
||||
numbers[dl->vgd.vg_number] = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_VG; i++) {
|
||||
if (!numbers[i]) {
|
||||
r = 1;
|
||||
*result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
pool_destroy(mem);
|
||||
return r;
|
||||
}
|
||||
421
lib/format_text/archive.c
Normal file
421
lib/format_text/archive.c
Normal file
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "format-text.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "config.h"
|
||||
#include "hash.h"
|
||||
#include "import-export.h"
|
||||
#include "lvm-string.h"
|
||||
#include "lvm-file.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
/*
|
||||
* The format instance is given a directory path upon creation.
|
||||
* Each file in this directory whose name is of the form
|
||||
* '(.*)_[0-9]*.vg' is a config file (see lib/config.[hc]), which
|
||||
* contains a description of a single volume group.
|
||||
*
|
||||
* The prefix ($1 from the above regex) of the config file gives
|
||||
* the volume group name.
|
||||
*
|
||||
* Backup files that have expired will be removed.
|
||||
*/
|
||||
|
||||
struct archive_c {
|
||||
uint32_t retain_days;
|
||||
uint32_t min_retains;
|
||||
|
||||
char *dir;
|
||||
|
||||
/*
|
||||
* An ordered list of previous archives. Each list
|
||||
* entered against the vg name. Most recent first.
|
||||
*/
|
||||
struct hash_table *vg_archives;
|
||||
|
||||
/*
|
||||
* Scratch pool. Contents of vg_archives come from here.
|
||||
*/
|
||||
struct pool *mem;
|
||||
};
|
||||
|
||||
/*
|
||||
* A list of these is built up for each volume group. Ordered
|
||||
* with the least recent at the head.
|
||||
*/
|
||||
struct archive_file {
|
||||
struct list list;
|
||||
|
||||
char *path;
|
||||
char *vg;
|
||||
int index;
|
||||
};
|
||||
|
||||
/*
|
||||
* This format is write only.
|
||||
*/
|
||||
static void _unsupported(const char *cmd)
|
||||
{
|
||||
log_err("The archive format doesn't support '%s'", cmd);
|
||||
}
|
||||
|
||||
static struct list *_get_vgs(struct format_instance *fi)
|
||||
{
|
||||
_unsupported("get_vgs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct list *_get_pvs(struct format_instance *fi)
|
||||
{
|
||||
_unsupported("get_pvs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct physical_volume *_pv_read(struct format_instance *fi,
|
||||
const char *pv_name)
|
||||
{
|
||||
_unsupported("pv_read");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
_unsupported("pv_setup");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
|
||||
{
|
||||
_unsupported("pv_write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _vg_setup(struct format_instance *fi, struct volume_group *vg)
|
||||
{
|
||||
_unsupported("vg_setup");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct volume_group *_vg_read(struct format_instance *fi,
|
||||
const char *vg_name)
|
||||
{
|
||||
_unsupported("vg_read");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _destroy(struct format_instance *fi)
|
||||
{
|
||||
struct archive_c *bc = (struct archive_c *) fi->private;
|
||||
if (bc->vg_archives)
|
||||
hash_destroy(bc->vg_archives);
|
||||
pool_destroy(bc->mem);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extract vg name and version number from a filename.
|
||||
*/
|
||||
static int _split_vg(const char *filename, char *vg, size_t vg_size,
|
||||
uint32_t *index)
|
||||
{
|
||||
int len, vg_len;
|
||||
char *dot, *underscore;
|
||||
|
||||
len = strlen(filename);
|
||||
if (len < 7)
|
||||
return 0;
|
||||
|
||||
dot = (char *) (filename + len - 3);
|
||||
if (strcmp(".vg", dot))
|
||||
return 0;
|
||||
|
||||
if (!(underscore = rindex(filename, '_')))
|
||||
return 0;
|
||||
|
||||
if (sscanf(underscore + 1, "%u", index) != 1)
|
||||
return 0;
|
||||
|
||||
vg_len = underscore - filename;
|
||||
if (vg_len + 1 > vg_size)
|
||||
return 0;
|
||||
|
||||
strncpy(vg, filename, vg_len);
|
||||
vg[vg_len] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _insert_file(struct list *head, struct archive_file *b)
|
||||
{
|
||||
struct list *bh;
|
||||
struct archive_file *bf;
|
||||
|
||||
if (list_empty(head)) {
|
||||
list_add(head, &b->list);
|
||||
return;
|
||||
}
|
||||
|
||||
/* index increases through list */
|
||||
list_iterate (bh, head) {
|
||||
bf = list_item(bh, struct archive_file);
|
||||
|
||||
if (bf->index > b->index) {
|
||||
list_add(&bf->list, &b->list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
list_add_h(&bf->list, &b->list);
|
||||
}
|
||||
|
||||
static int _scan_vg(struct archive_c *bc, const char *file,
|
||||
const char *vg_name, int index)
|
||||
{
|
||||
struct archive_file *b;
|
||||
struct list *files;
|
||||
|
||||
/*
|
||||
* Do we need to create a new list of archive files for
|
||||
* this vg ?
|
||||
*/
|
||||
if (!(files = hash_lookup(bc->vg_archives, vg_name))) {
|
||||
if (!(files = pool_alloc(bc->mem, sizeof(*files)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_init(files);
|
||||
if (!hash_insert(bc->vg_archives, vg_name, files)) {
|
||||
log_err("Couldn't insert archive file "
|
||||
"into hash table.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new archive file.
|
||||
*/
|
||||
if (!(b = pool_alloc(bc->mem, sizeof(*b)))) {
|
||||
log_err("Couldn't create new archive file.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
b->index = index;
|
||||
b->path = (char *)file;
|
||||
b->vg = (char *)vg_name;
|
||||
|
||||
/*
|
||||
* Insert it to the correct part of the list.
|
||||
*/
|
||||
_insert_file(files, b);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *_join(struct pool *mem, const char *dir, const char *name)
|
||||
{
|
||||
if (!pool_begin_object(mem, 32) ||
|
||||
!pool_grow_object(mem, dir, strlen(dir)) ||
|
||||
!pool_grow_object(mem, "/", 1) ||
|
||||
!pool_grow_object(mem, name, strlen(name)) ||
|
||||
!pool_grow_object(mem, "\0", 1)) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pool_end_object(mem);
|
||||
}
|
||||
|
||||
static int _scan_dir(struct archive_c *bc)
|
||||
{
|
||||
int r = 0, i, count, index;
|
||||
char vg_name[64], *path;
|
||||
struct dirent **dirent;
|
||||
|
||||
if ((count = scandir(bc->dir, &dirent, NULL, alphasort)) < 0) {
|
||||
log_err("Couldn't scan archive directory.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if ((dirent[i]->d_name[0] == '.') ||
|
||||
!_split_vg(dirent[i]->d_name, vg_name,
|
||||
sizeof(vg_name), &index))
|
||||
continue;
|
||||
|
||||
if (!(path = _join(bc->mem, bc->dir, dirent[i]->d_name))) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
_scan_vg(bc, path, vg_name, index);
|
||||
}
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
for (i = 0; i < count; i++)
|
||||
free(dirent[i]);
|
||||
free(dirent);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _scan_archives(struct archive_c *bc)
|
||||
{
|
||||
pool_empty(bc->mem);
|
||||
|
||||
if (bc->vg_archives)
|
||||
hash_destroy(bc->vg_archives);
|
||||
|
||||
if (!(bc->vg_archives = hash_create(128))) {
|
||||
log_err("Couldn't create hash table for scanning archives.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_scan_dir(bc)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
|
||||
{
|
||||
int r = 0, i, fd;
|
||||
unsigned int index = 0;
|
||||
struct archive_c *bc = (struct archive_c *) fi->private;
|
||||
struct archive_file *last;
|
||||
FILE *fp = NULL;
|
||||
char temp_file[PATH_MAX], archive_name[PATH_MAX];
|
||||
|
||||
if (!create_temp_name(bc->dir, temp_file, sizeof(temp_file), &fd)) {
|
||||
log_err("Couldn't create temporary archive name.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(fp = fdopen(fd, "w"))) {
|
||||
log_err("Couldn't create FILE object for archive.");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!text_vg_export(fp, vg)) {
|
||||
stack;
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/*
|
||||
* Now we want to rename this file to <vg>_index.vg.
|
||||
*/
|
||||
if (!_scan_archives(bc)) {
|
||||
log_err("Couldn't scan the archive directory (%s).", bc->dir);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((last = (struct archive_file *) hash_lookup(bc->vg_archives,
|
||||
vg->name))) {
|
||||
/* move to the last in the list */
|
||||
last = list_item(last->list.p, struct archive_file);
|
||||
index = last->index + 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (lvm_snprintf(archive_name, sizeof(archive_name),
|
||||
"%s/%s_%05d.vg",
|
||||
bc->dir, vg->name, index) < 0) {
|
||||
log_err("archive file name too long.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lvm_rename(temp_file, archive_name)) {
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
void archive_expire(struct format_instance *fi)
|
||||
{
|
||||
/* FIXME: finish */
|
||||
}
|
||||
|
||||
static struct format_handler _archive_handler = {
|
||||
get_vgs: _get_vgs,
|
||||
get_pvs: _get_pvs,
|
||||
pv_read: _pv_read,
|
||||
pv_setup: _pv_setup,
|
||||
pv_write: _pv_write,
|
||||
vg_setup: _vg_setup,
|
||||
vg_read: _vg_read,
|
||||
vg_write: _vg_write,
|
||||
destroy: _destroy
|
||||
};
|
||||
|
||||
struct format_instance *archive_format_create(struct cmd_context *cmd,
|
||||
const char *dir,
|
||||
uint32_t retain_days,
|
||||
uint32_t min_retains)
|
||||
{
|
||||
struct format_instance *fi;
|
||||
struct archive_c *bc = NULL;
|
||||
struct pool *mem = cmd->mem;
|
||||
|
||||
if (!(bc = pool_zalloc(mem, sizeof(*bc)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(bc->mem = pool_create(1024))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(bc->dir = pool_strdup(mem, dir))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
bc->retain_days = retain_days;
|
||||
bc->min_retains = min_retains;
|
||||
|
||||
if (!(fi = pool_alloc(mem, sizeof(*fi)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
fi->cmd = cmd;
|
||||
fi->ops = &_archive_handler;
|
||||
fi->private = bc;
|
||||
|
||||
return fi;
|
||||
|
||||
bad:
|
||||
if (bc->mem)
|
||||
pool_destroy(bc->mem);
|
||||
|
||||
pool_free(mem, bc);
|
||||
return NULL;
|
||||
}
|
||||
487
lib/format_text/export.c
Normal file
487
lib/format_text/export.c
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "import-export.h"
|
||||
#include "metadata.h"
|
||||
#include "log.h"
|
||||
#include "hash.h"
|
||||
#include "pool.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/*
|
||||
* The first half of this file deals with
|
||||
* exporting the vg, ie. writing it to a file.
|
||||
*/
|
||||
struct formatter {
|
||||
struct pool *mem; /* pv names allocated from here */
|
||||
struct hash_table *pv_names; /* dev_name -> pv_name (eg, pv1) */
|
||||
|
||||
FILE *fp; /* where we're writing to */
|
||||
int indent; /* current level of indentation */
|
||||
|
||||
int error;
|
||||
};
|
||||
|
||||
/*
|
||||
* Formatting functions.
|
||||
*/
|
||||
static void _out_size(struct formatter *f, uint64_t size,
|
||||
const char *fmt, ...)
|
||||
__attribute__ (( format (printf, 3, 4) ));
|
||||
|
||||
static void _out_hint(struct formatter *f, const char *fmt, ...)
|
||||
__attribute__ (( format (printf, 2, 3) ));
|
||||
|
||||
static void _out(struct formatter *f, const char *fmt, ...)
|
||||
__attribute__ (( format (printf, 2, 3) ));
|
||||
|
||||
|
||||
#define MAX_INDENT 5
|
||||
static void _inc_indent(struct formatter *f)
|
||||
{
|
||||
if (++f->indent > MAX_INDENT)
|
||||
f->indent = MAX_INDENT;
|
||||
}
|
||||
|
||||
static void _dec_indent(struct formatter *f)
|
||||
{
|
||||
if (!f->indent--) {
|
||||
log_error("Internal error tracking indentation");
|
||||
f->indent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Newline function for prettier layout.
|
||||
*/
|
||||
static void _nl(struct formatter *f)
|
||||
{
|
||||
fprintf(f->fp, "\n");
|
||||
}
|
||||
|
||||
#define COMMENT_TAB 6
|
||||
static void _out_with_comment(struct formatter *f, const char *comment,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
int i;
|
||||
char white_space[MAX_INDENT + 1];
|
||||
|
||||
if (ferror(f->fp))
|
||||
return;
|
||||
|
||||
for (i = 0; i < f->indent; i++)
|
||||
white_space[i] = '\t';
|
||||
white_space[i] = '\0';
|
||||
fprintf(f->fp, white_space);
|
||||
i = vfprintf(f->fp, fmt, ap);
|
||||
|
||||
if (comment) {
|
||||
/*
|
||||
* line comments up if possible.
|
||||
*/
|
||||
i += 8 * f->indent;
|
||||
i /= 8;
|
||||
i++;
|
||||
|
||||
do
|
||||
fputc('\t', f->fp);
|
||||
|
||||
while (++i < COMMENT_TAB);
|
||||
|
||||
fprintf(f->fp, comment);
|
||||
}
|
||||
fputc('\n', f->fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Formats a string, converting a size specified
|
||||
* in 512-byte sectors to a more human readable
|
||||
* form (eg, megabytes). We may want to lift this
|
||||
* for other code to use.
|
||||
*/
|
||||
static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
|
||||
{
|
||||
static char *_units[] = {
|
||||
"Kilobytes",
|
||||
"Megabytes",
|
||||
"Gigabytes",
|
||||
"Terrabytes",
|
||||
NULL
|
||||
};
|
||||
|
||||
int i;
|
||||
double d = (double) sectors;
|
||||
|
||||
/* to convert to K */
|
||||
d /= 2.0;
|
||||
|
||||
for (i = 0; (d > 1024.0) && _units[i]; i++)
|
||||
d /= 1024.0;
|
||||
|
||||
return lvm_snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Appends a comment giving a size in more easily
|
||||
* readable form (eg, 4M instead of 8096).
|
||||
*/
|
||||
static void _out_size(struct formatter *f, uint64_t size,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char buffer[64];
|
||||
va_list ap;
|
||||
|
||||
_sectors_to_units(size, buffer, sizeof(buffer));
|
||||
|
||||
va_start(ap, fmt);
|
||||
_out_with_comment(f, buffer, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Appends a comment indicating that the line is
|
||||
* only a hint.
|
||||
*/
|
||||
static void _out_hint(struct formatter *f, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_out_with_comment(f, "# Hint only", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* The normal output function.
|
||||
*/
|
||||
static void _out(struct formatter *f, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_out_with_comment(f, NULL, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static int _print_header(struct formatter *f, struct volume_group *vg)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
_out(f,
|
||||
"# This file was originally generated by the LVM2 library\n"
|
||||
"# Generated: %s\n", ctime(&t));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _print_vg(struct formatter *f, struct volume_group *vg)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
if (!id_write_format(&vg->id, buffer, sizeof(buffer))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_out(f, "id = \"%s\"", buffer);
|
||||
|
||||
if (!print_flags(vg->status, VG_FLAGS, buffer, sizeof(buffer))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_out(f, "status = %s", buffer);
|
||||
if (vg->system_id && *vg->system_id)
|
||||
_out(f, "system_id = \"%s\"", vg->system_id);
|
||||
_out_size(f, vg->extent_size, "extent_size = %u", vg->extent_size);
|
||||
_out(f, "max_lv = %u", vg->max_lv);
|
||||
_out(f, "max_pv = %u", vg->max_pv);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the pv%d name from the formatters hash
|
||||
* table.
|
||||
*/
|
||||
static inline const char *
|
||||
_get_pv_name(struct formatter *f, struct physical_volume *pv)
|
||||
{
|
||||
return (pv) ? (const char *)
|
||||
hash_lookup(f->pv_names, dev_name(pv->dev)) :
|
||||
"Missing";
|
||||
}
|
||||
|
||||
static int _print_pvs(struct formatter *f, struct volume_group *vg)
|
||||
{
|
||||
struct list *pvh;
|
||||
struct physical_volume *pv;
|
||||
char buffer[256];
|
||||
const char *name;
|
||||
|
||||
_out(f, "physical_volumes {");
|
||||
_inc_indent(f);
|
||||
|
||||
list_iterate (pvh, &vg->pvs) {
|
||||
|
||||
pv = list_item(pvh, struct pv_list)->pv;
|
||||
|
||||
if (!(name = _get_pv_name(f, pv))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_nl(f);
|
||||
_out(f, "%s {", name);
|
||||
_inc_indent(f);
|
||||
|
||||
if (!id_write_format(&pv->id, buffer, sizeof(buffer))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_out(f, "id = \"%s\"", buffer);
|
||||
_out_hint(f, "device = \"%s\"", dev_name(pv->dev));
|
||||
_nl(f);
|
||||
|
||||
if (!print_flags(pv->status, PV_FLAGS,
|
||||
buffer, sizeof(buffer))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_out(f, "status = %s", buffer);
|
||||
_out(f, "pe_start = %llu", pv->pe_start);
|
||||
_out_size(f, vg->extent_size * (uint64_t) pv->pe_count,
|
||||
"pe_count = %u", pv->pe_count);
|
||||
|
||||
_dec_indent(f);
|
||||
_out(f, "}");
|
||||
}
|
||||
|
||||
_dec_indent(f);
|
||||
_out(f, "}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _print_segment(struct formatter *f, struct volume_group *vg,
|
||||
int count, struct stripe_segment *seg)
|
||||
{
|
||||
int s;
|
||||
const char *name;
|
||||
|
||||
_out(f, "segment%u {", count);
|
||||
_inc_indent(f);
|
||||
|
||||
_out(f, "start_extent = %u", seg->le);
|
||||
_out_size(f, seg->len * vg->extent_size, "extent_count = %u", seg->len);
|
||||
_out(f, "stripes = %u", seg->stripes);
|
||||
|
||||
if (seg->stripes > 1)
|
||||
_out_size(f, seg->stripe_size,
|
||||
"stripe_size = %u", seg->stripe_size);
|
||||
|
||||
_nl(f);
|
||||
_out(f, "areas = [");
|
||||
_inc_indent(f);
|
||||
|
||||
for (s = 0; s < seg->stripes; s++) {
|
||||
if (!(name = _get_pv_name(f, seg->area[s].pv))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_out(f, "\"%s\", %u%s", name, seg->area[s].pe,
|
||||
(s == seg->stripes - 1) ? "" : ",");
|
||||
}
|
||||
|
||||
_dec_indent(f);
|
||||
_out(f, "]");
|
||||
|
||||
_dec_indent(f);
|
||||
_out(f, "}");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _count_segments(struct logical_volume *lv)
|
||||
{
|
||||
int r = 0;
|
||||
struct list *segh;
|
||||
|
||||
list_iterate (segh, &lv->segments)
|
||||
r++;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _print_lvs(struct formatter *f, struct volume_group *vg)
|
||||
{
|
||||
struct list *lvh, *segh;
|
||||
struct logical_volume *lv;
|
||||
struct stripe_segment *seg;
|
||||
char buffer[256];
|
||||
int seg_count;
|
||||
|
||||
_out(f, "logical_volumes {");
|
||||
_nl(f);
|
||||
_inc_indent(f);
|
||||
|
||||
list_iterate (lvh, &vg->lvs) {
|
||||
lv = list_item(lvh, struct lv_list)->lv;
|
||||
|
||||
_out(f, "%s {", lv->name);
|
||||
_inc_indent(f);
|
||||
|
||||
if (!print_flags(lv->status, LV_FLAGS,
|
||||
buffer, sizeof(buffer))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_out(f, "status = %s", buffer);
|
||||
_out(f, "read_ahead = %u", lv->read_ahead);
|
||||
_out(f, "segment_count = %u", _count_segments(lv));
|
||||
_nl(f);
|
||||
|
||||
seg_count = 1;
|
||||
list_iterate (segh, &lv->segments) {
|
||||
seg = list_item(segh, struct stripe_segment);
|
||||
|
||||
if (!_print_segment(f, vg, seg_count++, seg)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
_dec_indent(f);
|
||||
_out(f, "}");
|
||||
}
|
||||
|
||||
_dec_indent(f);
|
||||
_out(f, "}");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* In the text format we refer to pv's as 'pv1',
|
||||
* 'pv2' etc. This function builds a hash table
|
||||
* to enable a quick lookup from device -> name.
|
||||
*/
|
||||
static int _build_pv_names(struct formatter *f,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
int count = 0;
|
||||
struct list *pvh;
|
||||
struct physical_volume *pv;
|
||||
char buffer[32], *name;
|
||||
|
||||
if (!(f->mem = pool_create(512))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(f->pv_names = hash_create(128))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_iterate (pvh, &vg->pvs) {
|
||||
pv = list_item(pvh, struct pv_list)->pv;
|
||||
|
||||
if (lvm_snprintf(buffer, sizeof(buffer),
|
||||
"pv%d", count++) < 0) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(name = pool_strdup(f->mem, buffer))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!hash_insert(f->pv_names, dev_name(pv->dev), name)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
if (f->mem)
|
||||
pool_destroy(f->mem);
|
||||
|
||||
if (f->pv_names)
|
||||
hash_destroy(f->pv_names);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int text_vg_export(FILE *fp, struct volume_group *vg)
|
||||
{
|
||||
int r = 0;
|
||||
struct formatter *f;
|
||||
|
||||
if (!(f = dbg_malloc(sizeof(*f)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(f, 0, sizeof(*f));
|
||||
f->fp = fp;
|
||||
f->indent = 0;
|
||||
|
||||
if (!_build_pv_names(f, vg)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#define fail do {stack; goto out;} while(0)
|
||||
|
||||
if (!_print_header(f, vg))
|
||||
fail;
|
||||
|
||||
_out(f, "%s {", vg->name);
|
||||
_inc_indent(f);
|
||||
|
||||
if (!_print_vg(f, vg))
|
||||
fail;
|
||||
|
||||
_nl(f);
|
||||
|
||||
if (!_print_pvs(f, vg))
|
||||
fail;
|
||||
|
||||
_nl(f);
|
||||
|
||||
if (!_print_lvs(f, vg))
|
||||
fail;
|
||||
|
||||
#undef fail
|
||||
|
||||
_dec_indent(f);
|
||||
_out(f, "}");
|
||||
r = !ferror(f->fp);
|
||||
|
||||
out:
|
||||
if (f->mem)
|
||||
pool_destroy(f->mem);
|
||||
|
||||
if (f->pv_names)
|
||||
hash_destroy(f->pv_names);
|
||||
|
||||
dbg_free(f);
|
||||
return r;
|
||||
}
|
||||
161
lib/format_text/flags.c
Normal file
161
lib/format_text/flags.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "metadata.h"
|
||||
#include "import-export.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
/*
|
||||
* Bitsets held in the 'status' flags get
|
||||
* converted into arrays of strings.
|
||||
*/
|
||||
struct flag {
|
||||
int mask;
|
||||
char *description;
|
||||
};
|
||||
|
||||
static struct flag _vg_flags[] = {
|
||||
{EXPORTED_VG, "EXPORTED"},
|
||||
{RESIZEABLE_VG, "RESIZEABLE"},
|
||||
{PARTIAL_VG, "PARTIAL"},
|
||||
{LVM_READ, "READ"},
|
||||
{LVM_WRITE, "WRITE"},
|
||||
{CLUSTERED, "CLUSTERED"},
|
||||
{SHARED, "SHARED"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static struct flag _pv_flags[] = {
|
||||
{ALLOCATABLE_PV, "ALLOCATABLE"},
|
||||
{EXPORTED_VG, "EXPORTED"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static struct flag _lv_flags[] = {
|
||||
{LVM_READ, "READ"},
|
||||
{LVM_WRITE, "WRITE"},
|
||||
{ALLOC_SIMPLE, "ALLOC_SIMPLE"},
|
||||
{ALLOC_STRICT, "ALLOC_STRICT"},
|
||||
{ALLOC_CONTIGUOUS, "ALLOC_CONTIGUOUS"},
|
||||
{SNAPSHOT, "SNASHOT"},
|
||||
{SNAPSHOT_ORG, "SNAPSHOT_ORIGIN"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static struct flag *_get_flags(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case VG_FLAGS:
|
||||
return _vg_flags;
|
||||
|
||||
case PV_FLAGS:
|
||||
return _pv_flags;
|
||||
|
||||
case LV_FLAGS:
|
||||
return _lv_flags;
|
||||
}
|
||||
|
||||
log_err("Unknown flag set requested.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _emit(char **buffer, size_t *size, const char *fmt, ...)
|
||||
{
|
||||
size_t n;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(*buffer, *size, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (n < 0 || (n == *size))
|
||||
return 0;
|
||||
|
||||
*buffer += n;
|
||||
*size -= n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a bitset to an array of string values,
|
||||
* using one of the tables defined at the top of
|
||||
* the file.
|
||||
*/
|
||||
int print_flags(uint32_t status, int type, char *buffer, size_t size)
|
||||
{
|
||||
int f, first = 1;
|
||||
struct flag *flags;
|
||||
|
||||
if (!(flags = _get_flags(type))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_emit(&buffer, &size, "["))
|
||||
return 0;
|
||||
|
||||
for (f = 0; flags[f].mask; f++) {
|
||||
if (status & flags[f].mask) {
|
||||
if (!first) {
|
||||
if (!_emit(&buffer, &size, ", "))
|
||||
return 0;
|
||||
|
||||
} else
|
||||
first = 0;
|
||||
|
||||
if (!_emit(&buffer, &size, "\"%s\"",
|
||||
flags[f].description))
|
||||
return 0;
|
||||
|
||||
status &= ~flags[f].mask;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_emit(&buffer, &size, "]"))
|
||||
return 0;
|
||||
|
||||
if (status)
|
||||
log_error("Metadata inconsistency: Not all flags successfully "
|
||||
"exported.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_flags(uint32_t *status, int type, struct config_value *cv)
|
||||
{
|
||||
int f;
|
||||
uint32_t s = 0;
|
||||
struct flag *flags;
|
||||
|
||||
if (!(flags = _get_flags(type))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (cv) {
|
||||
if (cv->type != CFG_STRING) {
|
||||
log_err("Status value is not a string.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (f = 0; flags[f].description; f++)
|
||||
if (!strcmp(flags[f].description, cv->v.str)) {
|
||||
s |= flags[f].mask;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!flags[f].description) {
|
||||
log_err("Unknown status flag '%s'.", cv->v.str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cv = cv->next;
|
||||
}
|
||||
|
||||
*status = s;
|
||||
return 1;
|
||||
}
|
||||
210
lib/format_text/format-text.c
Normal file
210
lib/format_text/format-text.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "format-text.h"
|
||||
#include "import-export.h"
|
||||
|
||||
#include "lvm-file.h"
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "config.h"
|
||||
#include "hash.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* NOTE: Currently there can be only one vg per file.
|
||||
*/
|
||||
|
||||
struct text_c {
|
||||
char *path;
|
||||
struct uuid_map *um;
|
||||
};
|
||||
|
||||
static void _not_written(const char *cmd)
|
||||
{
|
||||
log_err("The text format is lacking an implementation for '%s'", cmd);
|
||||
}
|
||||
|
||||
static struct list *_get_vgs(struct format_instance *fi)
|
||||
{
|
||||
_not_written("_get_vgs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct list *_get_pvs(struct format_instance *fi)
|
||||
{
|
||||
_not_written("_get_vgs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct physical_volume *_pv_read(struct format_instance *fi,
|
||||
const char *pv_name)
|
||||
{
|
||||
_not_written("_get_vgs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
_not_written("_get_vgs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
|
||||
{
|
||||
_not_written("_get_vgs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _vg_setup(struct format_instance *fi, struct volume_group *vg)
|
||||
{
|
||||
_not_written("_get_vgs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct volume_group *_vg_read(struct format_instance *fi,
|
||||
const char *vg_name)
|
||||
{
|
||||
struct text_c *tc = (struct text_c *) fi->private;
|
||||
struct volume_group *vg;
|
||||
|
||||
if (!(vg = text_vg_import(fi->cmd, tc->path, tc->um))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently you can only have a single volume group per
|
||||
* text file (this restriction may remain). We need to
|
||||
* check that it contains the correct volume group.
|
||||
*/
|
||||
if (strcmp(vg_name, vg->name)) {
|
||||
pool_free(fi->cmd->mem, vg);
|
||||
log_err("'%s' does not contain volume group '%s'.",
|
||||
tc->path, vg_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vg;
|
||||
}
|
||||
|
||||
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
|
||||
{
|
||||
struct text_c *tc = (struct text_c *) fi->private;
|
||||
|
||||
FILE *fp;
|
||||
int fd;
|
||||
char *slash;
|
||||
char temp_file[PATH_MAX], temp_dir[PATH_MAX];
|
||||
|
||||
slash = rindex(tc->path, '/');
|
||||
|
||||
if (slash == 0)
|
||||
strcpy(temp_dir, ".");
|
||||
else if (slash - tc->path < PATH_MAX) {
|
||||
strncpy(temp_dir, tc->path, slash - tc->path);
|
||||
temp_dir[slash - tc->path] = '\0';
|
||||
|
||||
} else {
|
||||
log_error("Text format failed to determine directory.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!create_temp_name(temp_dir, temp_file, sizeof(temp_file), &fd)) {
|
||||
log_err("Couldn't create temporary text file name.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(fp = fdopen(fd, "w"))) {
|
||||
log_sys_error("fdopen", temp_file);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!text_vg_export(fp, vg)) {
|
||||
log_error("Failed to write metadata to %s.", temp_file);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fclose(fp)) {
|
||||
log_sys_error("fclose", tc->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rename(temp_file, tc->path)) {
|
||||
log_error("%s: rename to %s failed: %s", temp_file, tc->path,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _destroy(struct format_instance *fi)
|
||||
{
|
||||
struct text_c *tc = (struct text_c *) fi->private;
|
||||
|
||||
dbg_free(tc->path);
|
||||
dbg_free(tc);
|
||||
dbg_free(fi);
|
||||
}
|
||||
|
||||
static struct format_handler _text_handler = {
|
||||
get_vgs: _get_vgs,
|
||||
get_pvs: _get_pvs,
|
||||
pv_read: _pv_read,
|
||||
pv_setup: _pv_setup,
|
||||
pv_write: _pv_write,
|
||||
vg_setup: _vg_setup,
|
||||
vg_read: _vg_read,
|
||||
vg_write: _vg_write,
|
||||
destroy: _destroy
|
||||
};
|
||||
|
||||
struct format_instance *text_format_create(struct cmd_context *cmd,
|
||||
const char *file,
|
||||
struct uuid_map *um)
|
||||
{
|
||||
const char *no_alloc = "Couldn't allocate text format object.";
|
||||
|
||||
struct format_instance *fi;
|
||||
char *path;
|
||||
struct text_c *tc;
|
||||
|
||||
if (!(fi = dbg_malloc(sizeof(*fi)))) {
|
||||
log_err(no_alloc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(path = dbg_strdup(file))) {
|
||||
dbg_free(fi);
|
||||
log_err(no_alloc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(tc = dbg_malloc(sizeof(*tc)))) {
|
||||
dbg_free(fi);
|
||||
dbg_free(path);
|
||||
log_err(no_alloc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tc->path = path;
|
||||
tc->um = um;
|
||||
|
||||
fi->cmd = cmd;
|
||||
fi->ops = &_text_handler;
|
||||
fi->private = tc;
|
||||
|
||||
return fi;
|
||||
}
|
||||
37
lib/format_text/format-text.h
Normal file
37
lib/format_text/format-text.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FORMAT_TEXT_H
|
||||
#define _LVM_FORMAT_TEXT_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
#include "metadata.h"
|
||||
#include "uuid-map.h"
|
||||
|
||||
/*
|
||||
* The archive format is used to maintain a set of metadata backup files
|
||||
* in an archive directory.
|
||||
* 'retain_days' is the minimum number of days that an archive file must
|
||||
* be held for.
|
||||
*
|
||||
* 'min_archives' is the minimum number of archives required to be kept
|
||||
* for each volume group.
|
||||
*/
|
||||
struct format_instance *archive_format_create(struct cmd_context *cmd,
|
||||
const char *dir,
|
||||
uint32_t retain_days,
|
||||
uint32_t min_archives);
|
||||
|
||||
void backup_expire(struct format_instance *fi);
|
||||
|
||||
/*
|
||||
* The text format can read and write a volume_group to a file.
|
||||
*/
|
||||
struct format_instance *text_format_create(struct cmd_context *cmd,
|
||||
const char *file,
|
||||
struct uuid_map *um);
|
||||
|
||||
#endif
|
||||
31
lib/format_text/import-export.h
Normal file
31
lib/format_text/import-export.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_TEXT_IMPORT_EXPORT_H
|
||||
#define _LVM_TEXT_IMPORT_EXPORT_H
|
||||
|
||||
#include "config.h"
|
||||
#include "lvm-types.h"
|
||||
#include "metadata.h"
|
||||
#include "uuid-map.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
enum {
|
||||
VG_FLAGS,
|
||||
PV_FLAGS,
|
||||
LV_FLAGS
|
||||
};
|
||||
|
||||
int print_flags(uint32_t status, int type, char *buffer, size_t size);
|
||||
int read_flags(uint32_t *status, int type, struct config_value *cv);
|
||||
|
||||
|
||||
int text_vg_export(FILE *fp, struct volume_group *vg);
|
||||
struct volume_group *text_vg_import(struct cmd_context *cmd, const char *file,
|
||||
struct uuid_map *um);
|
||||
|
||||
#endif
|
||||
552
lib/format_text/import.c
Normal file
552
lib/format_text/import.c
Normal file
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "metadata.h"
|
||||
#include "import-export.h"
|
||||
#include "pool.h"
|
||||
#include "log.h"
|
||||
#include "uuid.h"
|
||||
#include "hash.h"
|
||||
|
||||
|
||||
typedef int (*section_fn)(struct pool *mem,
|
||||
struct volume_group *vg, struct config_node *pvn,
|
||||
struct config_node *vgn, struct hash_table *pv_hash,
|
||||
struct uuid_map *um);
|
||||
|
||||
#define _read_int32(root, path, result) \
|
||||
get_config_uint32(root, path, '/', result)
|
||||
|
||||
#define _read_int64(root, path, result) \
|
||||
get_config_uint64(root, path, '/', result)
|
||||
|
||||
static int _read_id(struct id *id, struct config_node *cn, const char *path)
|
||||
{
|
||||
struct config_value *cv;
|
||||
|
||||
if (!(cn = find_config_node(cn, path, '/'))) {
|
||||
log_err("Couldn't find uuid.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cv = cn->v;
|
||||
if (!cv || !cv->v.str) {
|
||||
log_err("uuid must be a string.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!id_read_format(id, cv->v.str)) {
|
||||
log_err("Invalid uuid.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_pv(struct pool *mem,
|
||||
struct volume_group *vg, struct config_node *pvn,
|
||||
struct config_node *vgn,
|
||||
struct hash_table *pv_hash,
|
||||
struct uuid_map *um)
|
||||
{
|
||||
struct physical_volume *pv;
|
||||
struct pv_list *pvl;
|
||||
struct config_node *cn;
|
||||
|
||||
if (!(pvl = pool_zalloc(mem, sizeof(*pvl))) ||
|
||||
!(pvl->pv = pool_zalloc(mem, sizeof(*pvl->pv)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pv = pvl->pv;
|
||||
|
||||
/*
|
||||
* Add the pv to the pv hash for quick lookup when we read
|
||||
* the lv segments.
|
||||
*/
|
||||
if (!hash_insert(pv_hash, pvn->key, pv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(pvn = pvn->child)) {
|
||||
log_err("Empty pv section.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_id(&pv->id, pvn, "id")) {
|
||||
log_err("Couldn't read uuid for volume group.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the uuid map to convert the uuid into a device.
|
||||
*/
|
||||
if (!(pv->dev = uuid_map_lookup(um, &pv->id))) {
|
||||
char buffer[64];
|
||||
|
||||
if (!id_write_format(&pv->id, buffer, sizeof(buffer))) {
|
||||
log_err("Couldn't find device.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_err("Couldn't find device with uuid '%s'.", buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(pv->vg_name = pool_strdup(mem, vg->name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(cn = find_config_node(pvn, "status", '/'))) {
|
||||
log_err("Couldn't find status flags for physical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(read_flags(&pv->status, PV_FLAGS, cn->v))) {
|
||||
log_err("Couldn't read status flags for physical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_int64(pvn, "pe_start", &pv->pe_start)) {
|
||||
log_err("Couldn't read extent size for volume group.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_int32(pvn, "pe_count", &pv->pe_count)) {
|
||||
log_err("Couldn't find extent count (pe_count) for "
|
||||
"physical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* adjust the volume group. */
|
||||
vg->extent_count += pv->pe_count;
|
||||
vg->free_count += pv->pe_count;
|
||||
|
||||
pv->pe_size = vg->extent_size;
|
||||
pv->size = pv->pe_size * (uint64_t) pv->pe_count;
|
||||
pv->pe_allocated = 0;
|
||||
|
||||
vg->pv_count++;
|
||||
list_add(&vg->pvs, &pvl->list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _insert_segment(struct logical_volume *lv,
|
||||
struct stripe_segment *seg)
|
||||
{
|
||||
struct list *segh;
|
||||
struct stripe_segment *comp;
|
||||
|
||||
list_iterate (segh, &lv->segments) {
|
||||
comp = list_item(segh, struct stripe_segment);
|
||||
|
||||
if (comp->le > seg->le) {
|
||||
list_add(&comp->list, &seg->list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lv->le_count += seg->len;
|
||||
list_add(&lv->segments, &seg->list);
|
||||
}
|
||||
|
||||
static int _read_segment(struct pool *mem, struct volume_group *vg,
|
||||
struct logical_volume *lv, struct config_node *sn,
|
||||
struct hash_table *pv_hash)
|
||||
{
|
||||
int s;
|
||||
uint32_t stripes;
|
||||
struct stripe_segment *seg;
|
||||
struct config_node *cn;
|
||||
struct config_value *cv;
|
||||
const char *seg_name = sn->key;
|
||||
|
||||
if (!(sn = sn->child)) {
|
||||
log_err("Empty segment section.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_int32(sn, "stripes", &stripes)) {
|
||||
log_err("Couldn't read 'stripes' for segment '%s'.",
|
||||
sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(seg = pool_zalloc(mem, sizeof(*seg) +
|
||||
(sizeof(seg->area[0]) * stripes)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
seg->stripes = stripes;
|
||||
|
||||
if (!_read_int32(sn, "start_extent", &seg->le)) {
|
||||
log_err("Couldn't read 'start_extent' for segment '%s'.",
|
||||
sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_int32(sn, "extent_count", &seg->len)) {
|
||||
log_err("Couldn't read 'extent_count' for segment '%s'.",
|
||||
sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seg->stripes == 0) {
|
||||
log_err("Zero stripes is *not* allowed for segment '%s'.",
|
||||
sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((seg->stripes != 1) &&
|
||||
!_read_int32(sn, "stripe_size", &seg->stripe_size)) {
|
||||
log_err("Couldn't read 'stripe_size' for segment '%s'.",
|
||||
sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(cn = find_config_node(sn, "areas", '/'))) {
|
||||
log_err("Couldn't find 'areas' array for segment '%s'.",
|
||||
sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the stripes from the 'areas' array.
|
||||
* FIXME: we could move this to a separate function.
|
||||
*/
|
||||
for (cv = cn->v, s = 0; cv && s < seg->stripes; s++, cv = cv->next) {
|
||||
|
||||
/* first we read the pv */
|
||||
const char *bad = "Badly formed areas array for segment '%s'.";
|
||||
struct physical_volume *pv;
|
||||
uint32_t allocated;
|
||||
|
||||
if (cv->type != CFG_STRING) {
|
||||
log_err(bad, sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(pv = hash_lookup(pv_hash, cv->v.str))) {
|
||||
log_err("Couldn't find physical volume '%s' for "
|
||||
"segment '%s'.",
|
||||
cn->v->v.str ? cn->v->v.str : "NULL",
|
||||
seg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg->area[s].pv = pv;
|
||||
|
||||
if (!(cv = cv->next)) {
|
||||
log_err(bad, sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cv->type != CFG_INT) {
|
||||
log_err(bad, sn->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg->area[s].pe = cv->v.i;
|
||||
|
||||
/*
|
||||
* Adjust the extent counts in the pv and vg.
|
||||
*/
|
||||
allocated = seg->len / seg->stripes;
|
||||
pv->pe_allocated += allocated;
|
||||
vg->free_count -= allocated;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check we read the correct number of stripes.
|
||||
*/
|
||||
if (cv || (s < seg->stripes)) {
|
||||
log_err("Incorrect number of stripes in 'area' array "
|
||||
"for segment '%s'.", seg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert into correct part of segment list.
|
||||
*/
|
||||
_insert_segment(lv, seg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_segments(struct pool *mem, struct volume_group *vg,
|
||||
struct logical_volume *lv, struct config_node *lvn,
|
||||
struct hash_table *pv_hash)
|
||||
{
|
||||
struct config_node *sn;
|
||||
int count = 0, seg_count;
|
||||
|
||||
for (sn = lvn; sn; sn = sn->sib) {
|
||||
|
||||
/*
|
||||
* All sub-sections are assumed to be segments.
|
||||
*/
|
||||
if (!sn->v) {
|
||||
if (!_read_segment(mem, vg, lv, sn, pv_hash)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_read_int32(lvn, "segment_count", &seg_count)) {
|
||||
log_err("Couldn't read segment count for logical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seg_count != count) {
|
||||
log_err("segment_count and actual number of segments "
|
||||
"disagree.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check there are no gaps or overlaps in the lv.
|
||||
*/
|
||||
if (!lv_check_segments(lv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge segments in case someones been editing things by hand.
|
||||
*/
|
||||
if (!lv_merge_segments(lv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_lv(struct pool *mem,
|
||||
struct volume_group *vg, struct config_node *lvn,
|
||||
struct config_node *vgn, struct hash_table *pv_hash,
|
||||
struct uuid_map *um)
|
||||
{
|
||||
struct logical_volume *lv;
|
||||
struct lv_list *lvl;
|
||||
struct config_node *cn;
|
||||
|
||||
if (!(lvl = pool_zalloc(mem, sizeof(*lvl))) ||
|
||||
!(lvl->lv = pool_zalloc(mem, sizeof(*lvl->lv)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv = lvl->lv;
|
||||
|
||||
if (!(lv->name = pool_strdup(mem, lvn->key))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(lvn = lvn->child)) {
|
||||
log_err("Empty logical volume section.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv->vg = vg;
|
||||
|
||||
|
||||
if (!(cn = find_config_node(lvn, "status", '/'))) {
|
||||
log_err("Couldn't find status flags for logical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(read_flags(&lv->status, LV_FLAGS, cn->v))) {
|
||||
log_err("Couldn't read status flags for logical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_int32(lvn, "read_ahead", &lv->read_ahead)) {
|
||||
log_err("Couldn't read 'read_ahead' value for "
|
||||
"logical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_init(&lv->segments);
|
||||
if (!_read_segments(mem, vg, lv, lvn, pv_hash)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
lv->size = (uint64_t) lv->le_count * (uint64_t) vg->extent_size;
|
||||
|
||||
vg->lv_count++;
|
||||
list_add(&vg->lvs, &lvl->list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_sections(const char *section, section_fn fn,
|
||||
struct pool *mem,
|
||||
struct volume_group *vg, struct config_node *vgn,
|
||||
struct hash_table *pv_hash,
|
||||
struct uuid_map *um)
|
||||
{
|
||||
struct config_node *n;
|
||||
|
||||
if (!(n = find_config_node(vgn, section, '/'))) {
|
||||
log_err("Couldn't find section '%s'.", section);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (n = n->child; n; n = n->sib) {
|
||||
if (!fn(mem, vg, n, vgn, pv_hash, um)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct volume_group *_read_vg(struct pool *mem, struct config_file *cf,
|
||||
struct uuid_map *um)
|
||||
{
|
||||
struct config_node *vgn = cf->root, *cn;
|
||||
struct volume_group *vg;
|
||||
struct hash_table *pv_hash = NULL;
|
||||
|
||||
if (!vgn) {
|
||||
log_err("Couldn't find volume group.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(vgn = cf->root)) {
|
||||
log_err("Couldn't find volume group in file.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(vg = pool_zalloc(mem, sizeof(*vg)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(vg->name = pool_strdup(mem, vgn->key))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
vgn = vgn->child;
|
||||
if (!_read_id(&vg->id, vgn, "id")) {
|
||||
log_err("Couldn't read uuid for volume group %s.",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(cn = find_config_node(vgn, "status", '/'))) {
|
||||
log_err("Couldn't find status flags for volume group %s.",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(read_flags(&vg->status, VG_FLAGS, cn->v))) {
|
||||
log_err("Couldn't read status flags for volume group %s.",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_int32(vgn, "extent_size", &vg->extent_size)) {
|
||||
log_err("Couldn't read extent size for volume group %s.",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* 'extent_count' and 'free_count' get filled in
|
||||
* implicitly when reading in the pv's and lv's.
|
||||
*/
|
||||
|
||||
if (!_read_int32(vgn, "max_lv", &vg->max_lv)) {
|
||||
log_err("Couldn't read 'max_lv' for volume group %s.",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_int32(vgn, "max_pv", &vg->max_pv)) {
|
||||
log_err("Couldn't read 'max_pv' for volume group %s.",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* The pv hash memoises the pv section names -> pv
|
||||
* structures.
|
||||
*/
|
||||
if (!(pv_hash = hash_create(32))) {
|
||||
log_err("Couldn't create hash table.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_init(&vg->pvs);
|
||||
if (!_read_sections("physical_volumes", _read_pv, mem, vg,
|
||||
vgn, pv_hash, um)) {
|
||||
log_err("Couldn't find all physical volumes for volume "
|
||||
"group %s.", vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_init(&vg->lvs);
|
||||
if (!_read_sections("logical_volumes", _read_lv, mem, vg,
|
||||
vgn, pv_hash, um)) {
|
||||
log_err("Couldn't read all logical volumes for volume "
|
||||
"group %s.", vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
hash_destroy(pv_hash);
|
||||
|
||||
/*
|
||||
* Finished.
|
||||
*/
|
||||
return vg;
|
||||
|
||||
bad:
|
||||
if (pv_hash)
|
||||
hash_destroy(pv_hash);
|
||||
|
||||
pool_free(mem, vg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct volume_group *text_vg_import(struct cmd_context *cmd,
|
||||
const char *file,
|
||||
struct uuid_map *um)
|
||||
{
|
||||
struct volume_group *vg = NULL;
|
||||
struct config_file *cf;
|
||||
|
||||
if (!(cf = create_config_file())) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!read_config(cf, file)) {
|
||||
log_error("Couldn't read volume group file.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(vg = _read_vg(cmd->mem, cf, um))) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vg->cmd = cmd;
|
||||
|
||||
out:
|
||||
destroy_config_file(cf);
|
||||
return vg;
|
||||
}
|
||||
67
lib/format_text/sample.vg
Normal file
67
lib/format_text/sample.vg
Normal file
@@ -0,0 +1,67 @@
|
||||
# An example volume group
|
||||
|
||||
# YYYY-MM-DD HH:MM:SS
|
||||
output_date = "2001-12-11 11:35:12"
|
||||
|
||||
sample_volume_group {
|
||||
|
||||
id = "ksjdlfksjldskjlsk"
|
||||
status = ["ACTIVE"]
|
||||
|
||||
extent_size = 8192 # 4 Megabytes
|
||||
|
||||
max_lv = 99
|
||||
max_pv = 255
|
||||
|
||||
physical_volumes {
|
||||
|
||||
pv1 {
|
||||
id = "lksjdflksdlsk"
|
||||
device = "/dev/hda1" # Hint only
|
||||
|
||||
status = ["ALLOCATABLE"]
|
||||
pe_start = 8192
|
||||
pe_count = 2048 # 8 Gigabytes
|
||||
}
|
||||
|
||||
pv2 {
|
||||
id = "lksjdflksdlsk"
|
||||
device = "/dev/hda2" # Hint only
|
||||
|
||||
status = ["ALLOCATABLE"]
|
||||
pe_start = 8192
|
||||
pe_count = 1024 # 4 Gigabytes
|
||||
}
|
||||
}
|
||||
|
||||
logical_volumes {
|
||||
|
||||
music {
|
||||
status = ["ACTIVE"]
|
||||
read_ahead = 1024
|
||||
|
||||
segment_count = 2
|
||||
|
||||
segment1 {
|
||||
start_extent = 0
|
||||
extent_count = 1024 # 4 Gigabytes
|
||||
stripes = 1
|
||||
|
||||
areas = [
|
||||
"pv1", 0
|
||||
]
|
||||
}
|
||||
|
||||
segment2 {
|
||||
start_extent = 1024
|
||||
extent_count = 2048 # 8 Gigabytes
|
||||
stripes = 2
|
||||
stripe_size = 32 # 16 Kilobytes
|
||||
|
||||
areas = [
|
||||
"pv1", 1024,
|
||||
"pv2", 0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
154
lib/label/label.c
Normal file
154
lib/label/label.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "label.h"
|
||||
#include "list.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
/*
|
||||
* Internal labeller struct.
|
||||
*/
|
||||
struct labeller_i {
|
||||
struct list list;
|
||||
|
||||
struct labeller *l;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
static struct list _labellers;
|
||||
|
||||
|
||||
static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
|
||||
{
|
||||
struct labeller_i *li;
|
||||
size_t len;
|
||||
|
||||
len = sizeof(*li) + strlen(name) + 1;
|
||||
|
||||
if (!(li = dbg_malloc(len))) {
|
||||
log_error("Couldn't allocate memory for labeller list object.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
li->l = l;
|
||||
strcpy(li->name, name);
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
static void _free_li(struct labeller_i *li)
|
||||
{
|
||||
dbg_free(li);
|
||||
}
|
||||
|
||||
|
||||
int label_init(void)
|
||||
{
|
||||
list_init(&_labellers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void label_exit(void)
|
||||
{
|
||||
struct list *c, *n;
|
||||
struct labeller_i *li;
|
||||
|
||||
for (c = _labellers.n; c != &_labellers; c = n) {
|
||||
n = c->n;
|
||||
li = list_item(c, struct labeller_i);
|
||||
_free_li(li);
|
||||
}
|
||||
}
|
||||
|
||||
int label_register_handler(const char *name, struct labeller *handler)
|
||||
{
|
||||
struct labeller_i *li;
|
||||
|
||||
if (!(li = _alloc_li(name, handler))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(&_labellers, &li->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct labeller *label_get_handler(const char *name)
|
||||
{
|
||||
struct list *lih;
|
||||
struct labeller_i *li;
|
||||
|
||||
list_iterate (lih, &_labellers) {
|
||||
li = list_item(lih, struct labeller_i);
|
||||
if (!strcmp(li->name, name))
|
||||
return li->l;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct labeller *_find_labeller(struct device *dev)
|
||||
{
|
||||
struct list *lih;
|
||||
struct labeller_i *li;
|
||||
|
||||
list_iterate (lih, &_labellers) {
|
||||
li = list_item(lih, struct labeller_i);
|
||||
if (li->l->ops->can_handle(li->l, dev))
|
||||
return li->l;
|
||||
}
|
||||
|
||||
log_debug("No label on device '%s'.", dev_name(dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int label_remove(struct device *dev)
|
||||
{
|
||||
struct labeller *l;
|
||||
|
||||
if (!(l = _find_labeller(dev))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return l->ops->remove(l, dev);
|
||||
}
|
||||
|
||||
int label_read(struct device *dev, struct label **result)
|
||||
{
|
||||
int r;
|
||||
struct list *lih;
|
||||
struct labeller_i *li;
|
||||
|
||||
list_iterate (lih, &_labellers) {
|
||||
li = list_item(lih, struct labeller_i);
|
||||
if ((r = li->l->ops->read(li->l, dev, result))) {
|
||||
(*result)->labeller = li->l;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("No label on device '%s'.", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int label_verify(struct device *dev)
|
||||
{
|
||||
struct labeller *l;
|
||||
|
||||
if (!(l = _find_labeller(dev))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return l->ops->verify(l, dev);
|
||||
}
|
||||
|
||||
void label_destroy(struct label *lab)
|
||||
{
|
||||
lab->labeller->ops->destroy_label(lab->labeller, lab);
|
||||
}
|
||||
90
lib/label/label.h
Normal file
90
lib/label/label.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LABEL_H
|
||||
#define _LVM_LABEL_H
|
||||
|
||||
#include "uuid.h"
|
||||
#include "device.h"
|
||||
|
||||
struct label {
|
||||
struct id id;
|
||||
|
||||
char volume_type[32];
|
||||
uint32_t version[3];
|
||||
|
||||
void *extra_info;
|
||||
|
||||
struct labeller *labeller;
|
||||
};
|
||||
|
||||
struct labeller;
|
||||
|
||||
struct label_ops {
|
||||
/*
|
||||
* Is the device labelled with this format ?
|
||||
*/
|
||||
int (*can_handle)(struct labeller *l, struct device *dev);
|
||||
|
||||
/*
|
||||
* Write a label to a volume.
|
||||
*/
|
||||
int (*write)(struct labeller *l,
|
||||
struct device *dev, struct label *label);
|
||||
|
||||
/*
|
||||
* Remove a label from a device.
|
||||
*/
|
||||
int (*remove)(struct labeller *l, struct device *dev);
|
||||
|
||||
/*
|
||||
* Read a label from a volume.
|
||||
*/
|
||||
int (*read)(struct labeller *l,
|
||||
struct device *dev, struct label **label);
|
||||
|
||||
/*
|
||||
* Additional consistency checks for the paranoid.
|
||||
*/
|
||||
int (*verify)(struct labeller *l, struct device *dev);
|
||||
|
||||
/*
|
||||
* Destroy a previously read label.
|
||||
*/
|
||||
void (*destroy_label)(struct labeller *l, struct label *label);
|
||||
|
||||
/*
|
||||
* Destructor.
|
||||
*/
|
||||
void (*destroy)(struct labeller *l);
|
||||
};
|
||||
|
||||
struct labeller {
|
||||
struct label_ops *ops;
|
||||
void *private;
|
||||
};
|
||||
|
||||
|
||||
int label_init(void);
|
||||
void label_exit(void);
|
||||
|
||||
int label_register_handler(const char *name, struct labeller *handler);
|
||||
|
||||
struct labeller *label_get_handler(const char *name);
|
||||
|
||||
int label_remove(struct device *dev);
|
||||
int label_read(struct device *dev, struct label **result);
|
||||
int label_verify(struct device *dev);
|
||||
void label_destroy(struct label *lab);
|
||||
|
||||
/*
|
||||
* We'll support two label types: the 'pretend the
|
||||
* LVM1 pv structure at the begining of the disk
|
||||
* is a label' hack, and pjc's 1 sector labels at
|
||||
* the front and back of the device.
|
||||
*/
|
||||
|
||||
#endif
|
||||
561
lib/label/lvm2_label.c
Normal file
561
lib/label/lvm2_label.c
Normal file
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2002 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "device.h"
|
||||
#include "dev-cache.h"
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "filter.h"
|
||||
#include "label.h"
|
||||
#include "lvm2_label.h"
|
||||
#include "xlate.h"
|
||||
|
||||
/* Label Magic is "LnXl" - error: imagination failure */
|
||||
#define LABEL_MAGIC 0x6c586e4c
|
||||
|
||||
/* Size of blocks that dev_get_size() returns the number of */
|
||||
#define BLOCK_SIZE 512
|
||||
|
||||
/* This is just the "struct lvm2_label" with the data pointer removed */
|
||||
struct label_ondisk
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t crc;
|
||||
uint64_t label1_loc;
|
||||
uint64_t label2_loc;
|
||||
uint16_t datalen;
|
||||
uint16_t pad;
|
||||
|
||||
uint32_t version[3];
|
||||
char disk_type[32];
|
||||
};
|
||||
|
||||
struct filter_private
|
||||
{
|
||||
void *mem;
|
||||
char disk_type[32];
|
||||
uint32_t version[3];
|
||||
int version_match;
|
||||
};
|
||||
|
||||
|
||||
/* Calculate CRC32 of a buffer */
|
||||
static uint32_t crc32(uint32_t initial, const unsigned char *databuf, size_t datalen)
|
||||
{
|
||||
static const u_int crctab[] = {
|
||||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
|
||||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
|
||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
|
||||
};
|
||||
uint32_t idx, crc = initial;
|
||||
|
||||
for (idx = 0; idx < datalen; idx++) {
|
||||
crc ^= *databuf++;
|
||||
crc = (crc >> 4) ^ crctab[crc & 0xf];
|
||||
crc = (crc >> 4) ^ crctab[crc & 0xf];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Calculate crc */
|
||||
static uint32_t calc_crc(struct label_ondisk *label, char *data)
|
||||
{
|
||||
uint32_t crcval = 0xffffffff;
|
||||
|
||||
crcval = crc32(crcval, (char *)&label->magic, sizeof(label->magic));
|
||||
crcval = crc32(crcval, (char *)&label->label1_loc, sizeof(label->label1_loc));
|
||||
crcval = crc32(crcval, (char *)&label->label2_loc, sizeof(label->label2_loc));
|
||||
crcval = crc32(crcval, (char *)&label->datalen, sizeof(label->datalen));
|
||||
crcval = crc32(crcval, (char *)&label->version, sizeof(label->version));
|
||||
crcval = crc32(crcval, (char *)label->disk_type, strlen(label->disk_type));
|
||||
crcval = crc32(crcval, (char *)data, label->datalen);
|
||||
|
||||
return crcval;
|
||||
}
|
||||
|
||||
/* Calculate the locations we should find the labels in */
|
||||
static inline void get_label_locations(uint64_t size, uint32_t sectsize, long *first, long *second)
|
||||
{
|
||||
*first = sectsize;
|
||||
*second = size*BLOCK_SIZE - sectsize;
|
||||
}
|
||||
|
||||
/* Read a label off disk */
|
||||
static int lvm2_label_read(struct labeller *l, struct device *dev, struct label **label)
|
||||
{
|
||||
uint64_t size;
|
||||
uint32_t sectsize;
|
||||
char *block;
|
||||
struct label_ondisk *ondisk;
|
||||
int status;
|
||||
int iter;
|
||||
long offset[2];
|
||||
|
||||
if (!dev_get_size(dev, &size))
|
||||
return 0;
|
||||
|
||||
if (!dev_get_sectsize(dev, §size))
|
||||
return 0;
|
||||
|
||||
if (!dev_open(dev, O_RDONLY))
|
||||
return 0;
|
||||
|
||||
block = dbg_malloc(sectsize);
|
||||
if (!block)
|
||||
{
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
ondisk = (struct label_ondisk *)block;
|
||||
get_label_locations(size, sectsize, &offset[0], &offset[1]);
|
||||
|
||||
/* If the first label is bad then use the second */
|
||||
for (iter = 0; iter <= 1; iter++)
|
||||
{
|
||||
status = dev_read(dev, offset[iter], sectsize, block);
|
||||
if (status)
|
||||
{
|
||||
struct label *incore;
|
||||
int i;
|
||||
int found_nul;
|
||||
|
||||
/* If the MAGIC doesn't match there's no point in
|
||||
carrying on */
|
||||
if (xlate32(ondisk->magic) != LABEL_MAGIC)
|
||||
continue;
|
||||
|
||||
/* Look for a NUL in the disk_type string so we don't
|
||||
SEGV is something has gone horribly wrong */
|
||||
found_nul = 0;
|
||||
for (i=0; i<sizeof(ondisk->disk_type); i++)
|
||||
if (ondisk->disk_type[i] == '\0')
|
||||
found_nul = 1;
|
||||
|
||||
if (!found_nul)
|
||||
continue;
|
||||
|
||||
incore = dbg_malloc(sizeof(struct label));
|
||||
if (incore == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy and convert endianness */
|
||||
strncpy(incore->volume_type, ondisk->disk_type, sizeof(incore->volume_type));
|
||||
incore->version[0] = xlate32(ondisk->version[0]);
|
||||
incore->version[1] = xlate32(ondisk->version[1]);
|
||||
incore->version[2] = xlate32(ondisk->version[2]);
|
||||
incore->extra_len = xlate16(ondisk->datalen);
|
||||
incore->extra_info = block + sizeof(struct label_ondisk);
|
||||
|
||||
/* Make sure datalen is a sensible size too */
|
||||
if (incore->extra_len > sectsize)
|
||||
continue;
|
||||
|
||||
/* Check Crc */
|
||||
if (xlate32(ondisk->crc) != calc_crc(ondisk, incore->extra_info))
|
||||
{
|
||||
log_error("Crc %d on device %s does not match. got %x, expected %x",
|
||||
iter, dev_name(dev), xlate32(ondisk->crc), calc_crc(ondisk, incore->extra_info));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check label locations match our view of the device */
|
||||
if (xlate64(ondisk->label1_loc) != offset[0])
|
||||
log_error("Label 1 location is wrong in label %d - check block size of the device\n",
|
||||
iter);
|
||||
if (xlate64(ondisk->label2_loc) != offset[1])
|
||||
log_error("Label 2 location is wrong in label %d - the size of the device must have changed\n",
|
||||
iter);
|
||||
|
||||
/* Copy to user's data area */
|
||||
*label = incore;
|
||||
incore->extra_info = dbg_malloc(incore->extra_len);
|
||||
if (!incore->extra_info)
|
||||
{
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
memcpy(incore->extra_info, block + sizeof(struct label_ondisk), incore->extra_len);
|
||||
|
||||
dbg_free(block);
|
||||
dev_close(dev);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
dbg_free(block);
|
||||
dev_close(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write a label to a device */
|
||||
static int lvm2_label_write(struct labeller *l, struct device *dev, struct label *label)
|
||||
{
|
||||
uint64_t size;
|
||||
uint32_t sectsize;
|
||||
char *block;
|
||||
struct label_ondisk *ondisk;
|
||||
int status1, status2;
|
||||
long offset[2];
|
||||
|
||||
if (!dev_get_size(dev, &size))
|
||||
return 0;
|
||||
|
||||
if (!dev_get_sectsize(dev, §size))
|
||||
return 0;
|
||||
|
||||
/* Can the metata fit in the remaining space ? */
|
||||
if (label->extra_len > sectsize - sizeof(struct label_ondisk))
|
||||
return 0;
|
||||
|
||||
block = dbg_malloc(sizeof(struct label_ondisk) + label->extra_len);
|
||||
if (!block)
|
||||
{
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
ondisk = (struct label_ondisk *)block;
|
||||
|
||||
get_label_locations(size, sectsize, &offset[0], &offset[1]);
|
||||
|
||||
/* Make into ondisk format */
|
||||
ondisk->magic = xlate32(LABEL_MAGIC);
|
||||
ondisk->version[0] = xlate32(label->version[0]);
|
||||
ondisk->version[1] = xlate32(label->version[1]);
|
||||
ondisk->version[2] = xlate32(label->version[2]);
|
||||
ondisk->label1_loc = xlate64(offset[0]);
|
||||
ondisk->label2_loc = xlate64(offset[1]);
|
||||
ondisk->datalen = xlate16(label->extra_len);
|
||||
strncpy(ondisk->disk_type, label->volume_type, sizeof(ondisk->disk_type));
|
||||
memcpy(block+sizeof(struct label_ondisk), label->extra_info, label->extra_len);
|
||||
ondisk->crc = xlate32(calc_crc(ondisk, label->extra_info));
|
||||
|
||||
/* Write metadata to disk */
|
||||
if (!dev_open(dev, O_RDWR))
|
||||
{
|
||||
dbg_free(block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
status1 = dev_write(dev, offset[0], sizeof(struct label_ondisk) + label->extra_len, block);
|
||||
if (!status1)
|
||||
log_error("Error writing label 1\n");
|
||||
|
||||
/* Write another at the end of the device */
|
||||
status2 = dev_write(dev, offset[1], sizeof(struct label_ondisk) + label->extra_len, block);
|
||||
if (!status2)
|
||||
{
|
||||
char zerobuf[sizeof(struct label_ondisk)];
|
||||
log_error("Error writing label 2\n");
|
||||
|
||||
/* Wipe the first label so it doesn't get confusing */
|
||||
memset(zerobuf, 0, sizeof(struct label_ondisk));
|
||||
if (!dev_write(dev, offset[0], sizeof(struct label_ondisk), zerobuf))
|
||||
log_error("Error erasing label 1\n");
|
||||
}
|
||||
|
||||
dbg_free(block);
|
||||
dev_close(dev);
|
||||
|
||||
return ((status1 != 0) && (status2 != 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return 1 for Yes, 0 for No */
|
||||
static int lvm2_is_labelled(struct labeller *l, struct device *dev)
|
||||
{
|
||||
struct label *label;
|
||||
int status;
|
||||
|
||||
status = lvm2_label_read(l, dev, &label);
|
||||
if (status) label_free(label);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Check the device is labelled and has the right format_type */
|
||||
static int _accept_format(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct label *l;
|
||||
int status;
|
||||
struct filter_private *fp = (struct filter_private *) f->private;
|
||||
|
||||
status = lvm2_label_read(NULL, dev, &l);
|
||||
|
||||
if (status)
|
||||
{
|
||||
if (strcmp(l->volume_type, fp->disk_type) == 0)
|
||||
{
|
||||
switch (fp->version_match)
|
||||
{
|
||||
case VERSION_MATCH_EQUAL:
|
||||
if (l->version[0] == fp->version[0] &&
|
||||
l->version[1] == fp->version[1] &&
|
||||
l->version[2] == fp->version[2])
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case VERSION_MATCH_LESSTHAN:
|
||||
if (l->version[0] == fp->version[0] &&
|
||||
l->version[1] < fp->version[1])
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case VERSION_MATCH_LESSEQUAL:
|
||||
if (l->version[0] == fp->version[0] &&
|
||||
l->version[1] <= fp->version[1])
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case VERSION_MATCH_ANY:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
label_free(l);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We just want to know if it's labelled or not */
|
||||
static int _accept_label(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
return lvm2_is_labelled(NULL, dev);
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
struct filter_private *fp = (struct filter_private *) f->private;
|
||||
}
|
||||
|
||||
/* A filter to find devices with a particular label type on them */
|
||||
struct dev_filter *lvm2_label_format_filter_create(char *disk_type, uint32_t version[3], int match_type)
|
||||
{
|
||||
struct pool *mem;
|
||||
struct filter_private *fp;
|
||||
struct dev_filter *f;
|
||||
|
||||
/* Validate the match type */
|
||||
if (match_type != VERSION_MATCH_EQUAL &&
|
||||
match_type != VERSION_MATCH_LESSTHAN &&
|
||||
match_type != VERSION_MATCH_LESSEQUAL &&
|
||||
match_type != VERSION_MATCH_ANY)
|
||||
return 0;
|
||||
|
||||
mem = pool_create(10 * 1024);
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(f = pool_zalloc(mem, sizeof(*f)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(fp = pool_zalloc(mem, sizeof(*fp)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
fp->mem = mem;
|
||||
strcpy(fp->disk_type, disk_type);
|
||||
fp->version[0] = version[0];
|
||||
fp->version[1] = version[1];
|
||||
fp->version[2] = version[2];
|
||||
fp->version_match = match_type;
|
||||
f->passes_filter = _accept_format;
|
||||
f->destroy = _destroy;
|
||||
f->private = fp;
|
||||
|
||||
return f;
|
||||
|
||||
bad:
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* A filter to find devices with any label on them */
|
||||
struct dev_filter *lvm2_label_filter_create()
|
||||
{
|
||||
struct pool *mem = pool_create(10 * 1024);
|
||||
struct filter_private *fp;
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(f = pool_zalloc(mem, sizeof(*f)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(fp = pool_zalloc(mem, sizeof(*fp)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
fp->mem = mem;
|
||||
f->passes_filter = _accept_label;
|
||||
f->destroy = _destroy;
|
||||
f->private = fp;
|
||||
|
||||
return f;
|
||||
|
||||
bad:
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return 1 if both labels are identical, 0 if not or there was an error */
|
||||
static int lvm2_labels_match(struct labeller *l, struct device *dev)
|
||||
{
|
||||
uint64_t size;
|
||||
uint32_t sectsize;
|
||||
char *block1;
|
||||
char *block2;
|
||||
struct label_ondisk *ondisk1;
|
||||
struct label_ondisk *ondisk2;
|
||||
int status = 0;
|
||||
long offset[2];
|
||||
|
||||
if (!dev_get_size(dev, &size))
|
||||
return 0;
|
||||
|
||||
if (!dev_get_sectsize(dev, §size))
|
||||
return 0;
|
||||
|
||||
/* Allocate some space for the blocks we are going to read in */
|
||||
block1 = dbg_malloc(sectsize);
|
||||
if (!block1)
|
||||
{
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
block2 = dbg_malloc(sectsize);
|
||||
if (!block2)
|
||||
{
|
||||
stack;
|
||||
dbg_free(block1);
|
||||
return 0;
|
||||
}
|
||||
ondisk1 = (struct label_ondisk *)block1;
|
||||
ondisk2 = (struct label_ondisk *)block2;
|
||||
|
||||
get_label_locations(size, sectsize, &offset[0], &offset[1]);
|
||||
|
||||
/* Fetch em */
|
||||
if (!dev_open(dev, O_RDONLY))
|
||||
goto finish;
|
||||
|
||||
if (!dev_read(dev, offset[0], sectsize, block1))
|
||||
goto finish;
|
||||
|
||||
if (!dev_read(dev, offset[1], sectsize, block2))
|
||||
goto finish;
|
||||
|
||||
dev_close(dev);
|
||||
|
||||
/* Is it labelled? */
|
||||
if (xlate32(ondisk1->magic) != LABEL_MAGIC)
|
||||
goto finish;
|
||||
|
||||
/* Compare the whole structs */
|
||||
if (memcmp(ondisk1, ondisk2, sizeof(struct label_ondisk)) != 0)
|
||||
goto finish;
|
||||
|
||||
/* OK, check the data area */
|
||||
if (memcmp(block1 + sizeof(struct label_ondisk),
|
||||
block2 + sizeof(struct label_ondisk),
|
||||
xlate16(ondisk1->datalen)) != 0)
|
||||
goto finish;
|
||||
|
||||
/* They match !! */
|
||||
status = 1;
|
||||
|
||||
finish:
|
||||
dbg_free(block2);
|
||||
dbg_free(block1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int lvm2_label_remove(struct labeller *l, struct device *dev)
|
||||
{
|
||||
uint64_t size;
|
||||
uint32_t sectsize;
|
||||
char block[BLOCK_SIZE];
|
||||
int status1, status2;
|
||||
long offset[2];
|
||||
|
||||
if (!dev_get_size(dev, &size))
|
||||
return 0;
|
||||
|
||||
if (!dev_get_sectsize(dev, §size))
|
||||
return 0;
|
||||
|
||||
if (!dev_open(dev, O_RDWR))
|
||||
{
|
||||
dbg_free(block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_label_locations(size, sectsize, &offset[0], &offset[1]);
|
||||
memset(block, 0, BLOCK_SIZE);
|
||||
|
||||
/* Blank out the first label */
|
||||
status1 = dev_write(dev, offset[0], BLOCK_SIZE, block);
|
||||
if (!status1)
|
||||
log_error("Error erasing label 1\n");
|
||||
|
||||
/* ...and the other at the end of the device */
|
||||
status2 = dev_write(dev, offset[1], BLOCK_SIZE, block);
|
||||
if (!status2)
|
||||
log_error("Error erasing label 2\n");
|
||||
|
||||
dev_close(dev);
|
||||
|
||||
return ((status1 != 0) && (status2 != 0));
|
||||
}
|
||||
|
||||
static void lvm2_label_destroy(struct labeller *l)
|
||||
{
|
||||
}
|
||||
|
||||
static struct label_ops handler_ops =
|
||||
{
|
||||
can_handle: lvm2_is_labelled,
|
||||
write: lvm2_label_write,
|
||||
remove: lvm2_label_remove,
|
||||
read: lvm2_label_read,
|
||||
verify: lvm2_labels_match,
|
||||
destroy: lvm2_label_destroy,
|
||||
};
|
||||
|
||||
static struct labeller this_labeller =
|
||||
{
|
||||
private : NULL,
|
||||
ops : &handler_ops,
|
||||
};
|
||||
|
||||
/* Don't know how this gets called... */
|
||||
void lvm2_label_init()
|
||||
{
|
||||
label_register_handler("LVM2", &this_labeller);
|
||||
}
|
||||
27
lib/label/lvm2_label.h
Normal file
27
lib/label/lvm2_label.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
struct lvm2_label
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t crc;
|
||||
uint64_t label1_loc;
|
||||
uint64_t label2_loc;
|
||||
uint16_t datalen;
|
||||
|
||||
char disk_type[32];
|
||||
uint32_t version[3];
|
||||
|
||||
char *data;
|
||||
};
|
||||
|
||||
#define VERSION_MATCH_EQUAL 1
|
||||
#define VERSION_MATCH_LESSTHAN 2
|
||||
#define VERSION_MATCH_LESSEQUAL 3
|
||||
#define VERSION_MATCH_ANY 4
|
||||
|
||||
extern struct dev_filter *lvm2_label_filter_create();
|
||||
extern struct dev_filter *lvm2_label_format_filter_create(char *disk_type, uint32_t version[3], int match_type);
|
||||
70
lib/label/uuid-map.c
Normal file
70
lib/label/uuid-map.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_UUID_MAP_H
|
||||
#define _LVM_UUID_MAP_H
|
||||
|
||||
#include "uuid-map.h"
|
||||
#include "dev-cache.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "label.h"
|
||||
|
||||
struct uuid_map {
|
||||
struct dev_filter *filter;
|
||||
};
|
||||
|
||||
|
||||
struct uuid_map *uuid_map_create(struct dev_filter *devices)
|
||||
{
|
||||
struct uuid_map *um;
|
||||
|
||||
if (!(um = dbg_malloc(sizeof(*um)))) {
|
||||
log_err("Couldn't allocate uuid_map object.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
um->filter = devices;
|
||||
return um;
|
||||
}
|
||||
|
||||
void uuid_map_destroy(struct uuid_map *um)
|
||||
{
|
||||
dbg_free(um);
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple, non-caching implementation to start with.
|
||||
*/
|
||||
struct device *uuid_map_lookup(struct uuid_map *um, struct id *id)
|
||||
{
|
||||
struct dev_iter *iter;
|
||||
struct device *dev;
|
||||
struct label *lab;
|
||||
|
||||
if (!(iter = dev_iter_create(um->filter))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((dev = dev_iter_get(iter))) {
|
||||
|
||||
if (!label_read(dev, &lab))
|
||||
continue;
|
||||
|
||||
if (id_equal(id, &lab->id)) {
|
||||
label_destroy(lab);
|
||||
break;
|
||||
}
|
||||
|
||||
label_destroy(lab);
|
||||
}
|
||||
|
||||
dev_iter_destroy(iter);
|
||||
return dev;
|
||||
}
|
||||
|
||||
#endif
|
||||
26
lib/label/uuid-map.h
Normal file
26
lib/label/uuid-map.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_UUID_MAP_H
|
||||
#define _LVM_UUID_MAP_H
|
||||
|
||||
#include "uuid.h"
|
||||
#include "dev-cache.h"
|
||||
|
||||
/*
|
||||
* Holds a mapping from uuid -> device.
|
||||
*/
|
||||
struct uuid_map;
|
||||
|
||||
struct uuid_map *uuid_map_create(struct dev_filter *devices);
|
||||
void uuid_map_destroy(struct uuid_map *um);
|
||||
|
||||
/*
|
||||
* Find the device with a particular uuid.
|
||||
*/
|
||||
struct device *uuid_map_lookup(struct uuid_map *um, struct id *id);
|
||||
|
||||
#endif
|
||||
166
lib/log/log.c
Normal file
166
lib/log/log.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
|
||||
static FILE *_log = 0;
|
||||
|
||||
static int _verbose_level = 0;
|
||||
static int _test = 0;
|
||||
static int _partial = 0;
|
||||
static int _debug_level = 0;
|
||||
static int _syslog = 0;
|
||||
static int _indent = 1;
|
||||
static int _log_cmd_name = 0;
|
||||
static char _cmd_name[30] = "";
|
||||
static char _msg_prefix[30] = " ";
|
||||
|
||||
void init_log(FILE *fp) {
|
||||
_log = fp;
|
||||
}
|
||||
|
||||
void init_syslog(int facility) {
|
||||
openlog("lvm", LOG_PID, facility);
|
||||
_syslog = 1;
|
||||
}
|
||||
|
||||
void fin_log() {
|
||||
_log = 0;
|
||||
}
|
||||
|
||||
void fin_syslog() {
|
||||
if (_syslog)
|
||||
closelog();
|
||||
_syslog = 0;
|
||||
}
|
||||
|
||||
void init_verbose(int level) {
|
||||
_verbose_level = level;
|
||||
}
|
||||
|
||||
void init_test(int level) {
|
||||
_test = level;
|
||||
if (_test)
|
||||
log_print("Test mode. Metadata will NOT be updated.");
|
||||
}
|
||||
|
||||
void init_partial(int level) {
|
||||
_partial = level;
|
||||
}
|
||||
|
||||
void init_cmd_name(int status) {
|
||||
_log_cmd_name = status;
|
||||
}
|
||||
|
||||
void set_cmd_name(const char *cmd) {
|
||||
if (!_log_cmd_name)
|
||||
return;
|
||||
strncpy(_cmd_name, cmd, sizeof(_cmd_name));
|
||||
_cmd_name[sizeof(_cmd_name) - 1] = '\0';
|
||||
}
|
||||
|
||||
void init_msg_prefix(const char *prefix) {
|
||||
strncpy(_msg_prefix, prefix, sizeof(_msg_prefix));
|
||||
_msg_prefix[sizeof(_msg_prefix) - 1] = '\0';
|
||||
}
|
||||
|
||||
void init_indent(int indent) {
|
||||
_indent = indent;
|
||||
}
|
||||
|
||||
int test_mode() {
|
||||
return _test;
|
||||
}
|
||||
|
||||
int partial_mode() {
|
||||
return _partial;
|
||||
}
|
||||
|
||||
void init_debug(int level) {
|
||||
_debug_level = level;
|
||||
}
|
||||
|
||||
int debug_level() {
|
||||
return _debug_level;
|
||||
}
|
||||
|
||||
void print_log(int level, const char *file, int line, const char *format, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
switch(level) {
|
||||
case _LOG_DEBUG:
|
||||
if (_verbose_level > 2 && format[1]) {
|
||||
printf("%s%s", _cmd_name, _msg_prefix);
|
||||
if (_indent)
|
||||
printf(" ");
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
}
|
||||
break;
|
||||
|
||||
case _LOG_INFO:
|
||||
if (_verbose_level > 1) {
|
||||
printf("%s%s", _cmd_name, _msg_prefix);
|
||||
if (_indent)
|
||||
printf(" ");
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
}
|
||||
break;
|
||||
case _LOG_NOTICE:
|
||||
if (_verbose_level) {
|
||||
printf("%s%s", _cmd_name, _msg_prefix);
|
||||
if (_indent)
|
||||
printf(" ");
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
}
|
||||
break;
|
||||
case _LOG_WARN:
|
||||
printf("%s%s", _cmd_name, _msg_prefix);
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
break;
|
||||
case _LOG_ERR:
|
||||
fprintf(stderr, "%s%s", _cmd_name, _msg_prefix);
|
||||
vfprintf(stderr, format, ap);
|
||||
fputc('\n',stderr);
|
||||
break;
|
||||
case _LOG_FATAL:
|
||||
default:
|
||||
fprintf(stderr, "%s%s", _cmd_name, _msg_prefix);
|
||||
vfprintf(stderr, format, ap);
|
||||
fputc('\n',stderr);
|
||||
break;
|
||||
;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (level > _debug_level)
|
||||
return;
|
||||
|
||||
if (_log) {
|
||||
fprintf(_log, "%s:%d %s%s", file, line, _cmd_name,
|
||||
_msg_prefix);
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(_log, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(_log, "\n");
|
||||
fflush(_log);
|
||||
}
|
||||
|
||||
if (_syslog) {
|
||||
va_start(ap, format);
|
||||
vsyslog(level, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
94
lib/log/log.h
Normal file
94
lib/log/log.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LOG_H
|
||||
#define _LVM_LOG_H
|
||||
|
||||
/*
|
||||
* printf()-style macros to use for messages:
|
||||
*
|
||||
* log_error - always print to stderr.
|
||||
* log_print - always print to stdout. Use this instead of printf.
|
||||
* log_verbose - print to stdout if verbose is set (-v)
|
||||
* log_very_verbose - print to stdout if verbose is set twice (-vv)
|
||||
* log_debug - print to stdout if verbose is set three times (-vvv)
|
||||
* (suppressed if single-character string such as with 'stack')
|
||||
*
|
||||
* In addition, messages will be logged to file or syslog if they
|
||||
* are more serious than the log level specified with the log/debug_level
|
||||
* parameter in the configuration file. These messages get the file
|
||||
* and line number prepended. 'stack' (without arguments) can be used
|
||||
* to log this information at debug level.
|
||||
*
|
||||
* log_sys_error and log_sys_very_verbose are for errors from system calls
|
||||
* e.g. log_sys_error("stat", filename);
|
||||
* /dev/fd/7: stat failed: No such file or directory
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define _LOG_DEBUG 7
|
||||
#define _LOG_INFO 6
|
||||
#define _LOG_NOTICE 5
|
||||
#define _LOG_WARN 4
|
||||
#define _LOG_ERR 3
|
||||
#define _LOG_FATAL 2
|
||||
|
||||
void init_log(FILE *fp);
|
||||
void fin_log(void);
|
||||
|
||||
void init_syslog(int facility);
|
||||
void fin_syslog(void);
|
||||
|
||||
void init_verbose(int level);
|
||||
void init_test(int level);
|
||||
void init_partial(int level);
|
||||
void init_debug(int level);
|
||||
void init_cmd_name(int status);
|
||||
void init_msg_prefix(const char *prefix);
|
||||
void init_indent(int indent);
|
||||
|
||||
void set_cmd_name(const char *cmd_name);
|
||||
|
||||
int test_mode(void);
|
||||
int partial_mode(void);
|
||||
int debug_level(void);
|
||||
|
||||
void print_log(int level, const char *file, int line, const char *format, ...)
|
||||
__attribute__ (( format (printf, 4, 5) ));
|
||||
|
||||
#define plog(l, x...) print_log(l, __FILE__, __LINE__ , ## x)
|
||||
|
||||
#define log_debug(x...) plog(_LOG_DEBUG, x)
|
||||
#define log_info(x...) plog(_LOG_INFO, x)
|
||||
#define log_notice(x...) plog(_LOG_NOTICE, x)
|
||||
#define log_warn(x...) plog(_LOG_WARN, x)
|
||||
#define log_err(x...) plog(_LOG_ERR, x)
|
||||
#define log_fatal(x...) plog(_LOG_FATAL, x)
|
||||
|
||||
#define stack log_debug( "s" ) /* Backtrace on error */
|
||||
|
||||
#define log_error(fmt, args...) log_err(fmt , ## args)
|
||||
#define log_print(fmt, args...) log_warn(fmt , ## args)
|
||||
#define log_verbose(fmt, args...) log_notice(fmt , ## args)
|
||||
#define log_very_verbose(fmt, args...) log_info(fmt , ## args)
|
||||
|
||||
/* Two System call equivalents */
|
||||
#define log_sys_error(x, y) \
|
||||
log_err("%s: %s failed: %s", y, x, strerror(errno))
|
||||
#define log_sys_very_verbose(x, y) \
|
||||
log_info("%s: %s failed: %s", y, x, strerror(errno))
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
547
lib/metadata/lv_manip.c
Normal file
547
lib/metadata/lv_manip.c
Normal file
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "metadata.h"
|
||||
#include "pv_map.h"
|
||||
#include "log.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* These functions adjust the pe counts in pv's
|
||||
* after we've added or removed segments.
|
||||
*/
|
||||
static void _get_extents(struct stripe_segment *seg)
|
||||
{
|
||||
int s, count;
|
||||
struct physical_volume *pv;
|
||||
|
||||
for (s = 0; s < seg->stripes; s++) {
|
||||
pv = seg->area[s].pv;
|
||||
count = seg->len / seg->stripes;
|
||||
pv->pe_allocated += count;
|
||||
}
|
||||
}
|
||||
|
||||
static void _put_extents(struct stripe_segment *seg)
|
||||
{
|
||||
int s, count;
|
||||
struct physical_volume *pv;
|
||||
|
||||
for (s = 0; s < seg->stripes; s++) {
|
||||
pv = seg->area[s].pv;
|
||||
count = seg->len / seg->stripes;
|
||||
|
||||
assert(pv->pe_allocated >= count);
|
||||
pv->pe_allocated -= count;
|
||||
}
|
||||
}
|
||||
|
||||
static struct stripe_segment *_alloc_segment(struct pool *mem, int stripes)
|
||||
{
|
||||
struct stripe_segment *seg;
|
||||
uint32_t len = sizeof(*seg) + (stripes * sizeof(seg->area[0]));
|
||||
|
||||
if (!(seg = pool_zalloc(mem, len))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
static int _alloc_stripe_area(struct logical_volume *lv, uint32_t stripes,
|
||||
uint32_t stripe_size,
|
||||
struct pv_area **areas, uint32_t *index)
|
||||
{
|
||||
uint32_t count = lv->le_count - *index;
|
||||
uint32_t per_area = count / stripes;
|
||||
uint32_t smallest = areas[stripes - 1]->count;
|
||||
uint32_t s;
|
||||
struct stripe_segment *seg;
|
||||
|
||||
if (smallest < per_area)
|
||||
per_area = smallest;
|
||||
|
||||
if (!(seg = _alloc_segment(lv->vg->cmd->mem, stripes))) {
|
||||
log_err("Couldn't allocate new stripe segment.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg->lv = lv;
|
||||
seg->le = *index;
|
||||
seg->len = per_area * stripes;
|
||||
seg->stripes = stripes;
|
||||
seg->stripe_size = stripe_size;
|
||||
|
||||
for (s = 0; s < stripes; s++) {
|
||||
struct pv_area *pva = areas[s];
|
||||
seg->area[s].pv = pva->map->pv;
|
||||
seg->area[s].pe = pva->start;
|
||||
consume_pv_area(pva, per_area);
|
||||
}
|
||||
|
||||
list_add(&lv->segments, &seg->list);
|
||||
*index += seg->len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _comp_area(const void *l, const void *r)
|
||||
{
|
||||
struct pv_area *lhs = *((struct pv_area **) l);
|
||||
struct pv_area *rhs = *((struct pv_area **) r);
|
||||
|
||||
if (lhs->count < rhs->count)
|
||||
return 1;
|
||||
|
||||
else if (lhs->count > rhs->count)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _alloc_striped(struct logical_volume *lv,
|
||||
struct list *pvms, uint32_t allocated,
|
||||
uint32_t stripes, uint32_t stripe_size)
|
||||
{
|
||||
int r = 0;
|
||||
struct list *pvmh;
|
||||
struct pv_area **areas;
|
||||
int pv_count = 0, index;
|
||||
struct pv_map *pvm;
|
||||
size_t len;
|
||||
|
||||
list_iterate (pvmh, pvms)
|
||||
pv_count++;
|
||||
|
||||
/* allocate an array of pv_areas, one candidate per pv */
|
||||
len = sizeof(*areas) * pv_count;
|
||||
if (!(areas = dbg_malloc(sizeof(*areas) * pv_count))) {
|
||||
log_err("Couldn't allocate areas array.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (allocated != lv->le_count) {
|
||||
|
||||
index = 0;
|
||||
list_iterate (pvmh, pvms) {
|
||||
pvm = list_item(pvmh, struct pv_map);
|
||||
|
||||
if (list_empty(&pvm->areas))
|
||||
continue;
|
||||
|
||||
areas[index++] = list_item(pvm->areas.n,
|
||||
struct pv_area);
|
||||
}
|
||||
|
||||
if (index < stripes) {
|
||||
log_error("Insufficient allocatable extents suitable "
|
||||
"for striping for logical volume "
|
||||
"%s: %u required",
|
||||
lv->name, lv->le_count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sort the areas so we allocate from the biggest */
|
||||
qsort(areas, index, sizeof(*areas), _comp_area);
|
||||
|
||||
if (!_alloc_stripe_area(lv, stripes, stripe_size, areas,
|
||||
&allocated)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
dbg_free(areas);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The heart of the allocation code. This function takes a
|
||||
* pv_area and allocates it to the lv. If the lv doesn't need
|
||||
* the complete area then the area is split, otherwise the area
|
||||
* is unlinked from the pv_map.
|
||||
*/
|
||||
static int _alloc_linear_area(struct logical_volume *lv, uint32_t *index,
|
||||
struct pv_map *map, struct pv_area *pva)
|
||||
{
|
||||
uint32_t count, remaining;
|
||||
struct stripe_segment *seg;
|
||||
|
||||
count = pva->count;
|
||||
remaining = lv->le_count - *index;
|
||||
if (count > remaining)
|
||||
count = remaining;
|
||||
|
||||
if (!(seg = _alloc_segment(lv->vg->cmd->mem, 1))) {
|
||||
log_err("Couldn't allocate new stripe segment.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg->lv = lv;
|
||||
seg->le = *index;
|
||||
seg->len = count;
|
||||
seg->stripe_size = 0;
|
||||
seg->stripes = 1;
|
||||
seg->area[0].pv = map->pv;
|
||||
seg->area[0].pe = pva->start;
|
||||
|
||||
list_add(&lv->segments, &seg->list);
|
||||
|
||||
consume_pv_area(pva, count);
|
||||
*index += count;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only one area per pv is allowed, so we search
|
||||
* for the biggest area, or the first area that
|
||||
* can complete the allocation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME: subsequent lvextends may not be contiguous.
|
||||
*/
|
||||
static int _alloc_contiguous(struct logical_volume *lv,
|
||||
struct list *pvms, uint32_t allocated)
|
||||
{
|
||||
struct list *tmp1;
|
||||
struct pv_map *pvm;
|
||||
struct pv_area *pva;
|
||||
|
||||
list_iterate(tmp1, pvms) {
|
||||
pvm = list_item(tmp1, struct pv_map);
|
||||
|
||||
if (list_empty(&pvm->areas))
|
||||
continue;
|
||||
|
||||
/* first item in the list is the biggest */
|
||||
pva = list_item(pvm->areas.n, struct pv_area);
|
||||
|
||||
if (!_alloc_linear_area(lv, &allocated, pvm, pva)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (allocated == lv->le_count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (allocated != lv->le_count) {
|
||||
log_error("Insufficient allocatable extents (%u) "
|
||||
"for logical volume %s: %u required",
|
||||
allocated, lv->name, lv->le_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Areas just get allocated in order until the lv
|
||||
* is full.
|
||||
*/
|
||||
static int _alloc_simple(struct logical_volume *lv,
|
||||
struct list *pvms, uint32_t allocated)
|
||||
{
|
||||
struct list *tmp1, *tmp2;
|
||||
struct pv_map *pvm;
|
||||
struct pv_area *pva;
|
||||
|
||||
list_iterate(tmp1, pvms) {
|
||||
pvm = list_item(tmp1, struct pv_map);
|
||||
|
||||
list_iterate(tmp2, &pvm->areas) {
|
||||
pva = list_item(tmp2, struct pv_area);
|
||||
if (!_alloc_linear_area(lv, &allocated, pvm, pva) ||
|
||||
(allocated == lv->le_count))
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (allocated != lv->le_count) {
|
||||
log_error("Insufficient allocatable logical extents (%u) "
|
||||
"for logical volume %s: %u required",
|
||||
allocated, lv->name, lv->le_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chooses a correct allocation policy.
|
||||
*/
|
||||
static int _allocate(struct volume_group *vg, struct logical_volume *lv,
|
||||
struct list *acceptable_pvs, uint32_t allocated,
|
||||
uint32_t stripes, uint32_t stripe_size)
|
||||
{
|
||||
int r = 0;
|
||||
struct pool *scratch;
|
||||
struct list *pvms, *old_tail = lv->segments.p, *segh;
|
||||
|
||||
if (!(scratch = pool_create(1024))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the sets of available areas on the pv's.
|
||||
*/
|
||||
if (!(pvms = create_pv_maps(scratch, vg, acceptable_pvs)))
|
||||
goto out;
|
||||
|
||||
if (stripes > 1)
|
||||
r = _alloc_striped(lv, pvms, allocated, stripes, stripe_size);
|
||||
|
||||
else if (lv->status & ALLOC_CONTIGUOUS)
|
||||
r = _alloc_contiguous(lv, pvms, allocated);
|
||||
|
||||
else if (lv->status & ALLOC_SIMPLE)
|
||||
r = _alloc_simple(lv, pvms, allocated);
|
||||
|
||||
else {
|
||||
log_error("Unknown allocation policy: "
|
||||
"unable to setup logical volume.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (r) {
|
||||
vg->free_count -= lv->le_count - allocated;
|
||||
|
||||
/*
|
||||
* Iterate through the new segments, updating pe
|
||||
* counts in pv's.
|
||||
*/
|
||||
for (segh = lv->segments.p; segh != old_tail; segh = segh->p)
|
||||
_get_extents(list_item(segh, struct stripe_segment));
|
||||
} else {
|
||||
/*
|
||||
* Put the segment list back how we found it.
|
||||
*/
|
||||
old_tail->n = &lv->segments;
|
||||
lv->segments.p = old_tail;
|
||||
}
|
||||
|
||||
out:
|
||||
pool_destroy(scratch);
|
||||
return r;
|
||||
}
|
||||
|
||||
static char *_generate_lv_name(struct volume_group *vg,
|
||||
char *buffer, size_t len)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct logical_volume *lv;
|
||||
int high = -1, i;
|
||||
|
||||
list_iterate(lvh, &vg->lvs) {
|
||||
lv = (list_item(lvh, struct lv_list)->lv);
|
||||
|
||||
if (sscanf(lv->name, "lvol%d", &i) != 1)
|
||||
continue;
|
||||
|
||||
if (i > high)
|
||||
high = i;
|
||||
}
|
||||
|
||||
if (lvm_snprintf(buffer, len, "lvol%d", high + 1) < 0)
|
||||
return NULL;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
struct logical_volume *lv_create(struct format_instance *fi,
|
||||
const char *name,
|
||||
uint32_t status,
|
||||
uint32_t stripes,
|
||||
uint32_t stripe_size,
|
||||
uint32_t extents,
|
||||
struct volume_group *vg,
|
||||
struct list *acceptable_pvs)
|
||||
{
|
||||
struct cmd_context *cmd = vg->cmd;
|
||||
struct lv_list *ll = NULL;
|
||||
struct logical_volume *lv;
|
||||
char dname[32];
|
||||
|
||||
if (!extents) {
|
||||
log_error("Unable to create logical volume %s with no extents",
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vg->free_count < extents) {
|
||||
log_error("Insufficient free extents (%u) in volume group %s: "
|
||||
"%u required", vg->free_count, vg->name, extents);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vg->max_lv == vg->lv_count) {
|
||||
log_error("Maximum number of logical volumes (%u) reached "
|
||||
"in volume group %s", vg->max_lv, vg->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stripes > list_size(acceptable_pvs)) {
|
||||
log_error("Number of stripes (%u) must not exceed "
|
||||
"number of physical volumes (%d)", stripes,
|
||||
list_size(acceptable_pvs));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!name && !(name = _generate_lv_name(vg, dname, sizeof(dname)))) {
|
||||
log_error("Failed to generate unique name for the new "
|
||||
"logical volume");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_verbose("Creating logical volume %s", name);
|
||||
|
||||
if (!(ll = pool_zalloc(cmd->mem, sizeof(*ll))) ||
|
||||
!(ll->lv = pool_zalloc(cmd->mem, sizeof(*ll->lv)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv = ll->lv;
|
||||
|
||||
strcpy(lv->id.uuid, "");
|
||||
|
||||
if (!(lv->name = pool_strdup(cmd->mem, name))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
lv->status = status;
|
||||
lv->read_ahead = 0;
|
||||
lv->size = extents * vg->extent_size;
|
||||
lv->le_count = extents;
|
||||
lv->vg = vg;
|
||||
list_init(&lv->segments);
|
||||
|
||||
if (!_allocate(vg, lv, acceptable_pvs, 0u, stripes, stripe_size)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (fi->ops->lv_setup && !fi->ops->lv_setup(fi, lv)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
vg->lv_count++;
|
||||
list_add(&vg->lvs, &ll->list);
|
||||
|
||||
return lv;
|
||||
|
||||
bad:
|
||||
if (ll)
|
||||
pool_free(cmd->mem, ll);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lv_reduce(struct format_instance *fi,
|
||||
struct logical_volume *lv, uint32_t extents)
|
||||
{
|
||||
struct list *segh;
|
||||
struct stripe_segment *seg;
|
||||
uint32_t count = extents;
|
||||
|
||||
for (segh = lv->segments.p;
|
||||
(segh != &lv->segments) && count;
|
||||
segh = segh->p) {
|
||||
seg = list_item(segh, struct stripe_segment);
|
||||
|
||||
if (seg->len <= count) {
|
||||
/* remove this segment completely */
|
||||
count -= seg->len;
|
||||
_put_extents(seg);
|
||||
list_del(segh);
|
||||
} else {
|
||||
/* reduce this segment */
|
||||
_put_extents(seg);
|
||||
seg->len -= count;
|
||||
_get_extents(seg);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lv->le_count -= extents;
|
||||
lv->size = lv->le_count * lv->vg->extent_size;
|
||||
|
||||
if (fi->ops->lv_setup && !fi->ops->lv_setup(fi, lv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_extend(struct format_instance *fi,
|
||||
struct logical_volume *lv,
|
||||
uint32_t stripes, uint32_t stripe_size,
|
||||
uint32_t extents,
|
||||
struct list *acceptable_pvs)
|
||||
{
|
||||
uint32_t old_le_count = lv->le_count;
|
||||
uint64_t old_size = lv->size;
|
||||
|
||||
lv->le_count += extents;
|
||||
lv->size += extents * lv->vg->extent_size;
|
||||
|
||||
/* FIXME: Format1 must ensure stripes is consistent with 1st seg */
|
||||
|
||||
if (!_allocate(lv->vg, lv, acceptable_pvs, old_le_count,
|
||||
stripes, stripe_size)) {
|
||||
lv->le_count = old_le_count;
|
||||
lv->size = old_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_merge_segments(lv)) {
|
||||
log_err("Couldn't merge segments after extending "
|
||||
"logical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fi->ops->lv_setup && !fi->ops->lv_setup(fi, lv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_remove(struct volume_group *vg, struct logical_volume *lv)
|
||||
{
|
||||
struct list *segh;
|
||||
struct lv_list *lvl;
|
||||
|
||||
/* find the lv list */
|
||||
if (!(lvl = find_lv_in_vg(vg, lv->name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iterate through the lv's segments freeing off the pe's */
|
||||
list_iterate (segh, &lv->segments)
|
||||
_put_extents(list_item(segh, struct stripe_segment));
|
||||
|
||||
vg->lv_count--;
|
||||
vg->free_count += lv->le_count;
|
||||
|
||||
list_del(&lvl->list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
59
lib/metadata/merge.c
Normal file
59
lib/metadata/merge.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "metadata.h"
|
||||
|
||||
/*
|
||||
* Returns success if the segments were
|
||||
* successfully merged. If the do merge, 'first'
|
||||
* will be adjusted to contain both areas.
|
||||
*/
|
||||
static int _merge(struct stripe_segment *first, struct stripe_segment *second)
|
||||
{
|
||||
int s;
|
||||
uint32_t width;
|
||||
|
||||
if (!first ||
|
||||
(first->stripes != second->stripes) ||
|
||||
(first->stripe_size != second->stripe_size))
|
||||
return 0;
|
||||
|
||||
for (s = 0; s < first->stripes; s++) {
|
||||
width = first->len / first->stripes;
|
||||
|
||||
if ((first->area[s].pv != second->area[s].pv) ||
|
||||
(first->area[s].pe + width != second->area[s].pe))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we should merge */
|
||||
first->len += second->len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_merge_segments(struct logical_volume *lv)
|
||||
{
|
||||
struct list *segh;
|
||||
struct stripe_segment *current, *prev = NULL;
|
||||
|
||||
list_iterate (segh, &lv->segments) {
|
||||
current = list_item(segh, struct stripe_segment);
|
||||
|
||||
if (_merge(prev, current))
|
||||
list_del(¤t->list);
|
||||
else
|
||||
prev = current;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_check_segments(struct logical_volume *lv)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
289
lib/metadata/metadata.c
Normal file
289
lib/metadata/metadata.c
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "device.h"
|
||||
#include "dev-cache.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg,
|
||||
const char *pv_name)
|
||||
{
|
||||
struct pv_list *pvl;
|
||||
struct physical_volume *pv;
|
||||
struct pool *mem = fi->cmd->mem;
|
||||
|
||||
log_verbose("Adding physical volume '%s' to volume group '%s'",
|
||||
pv_name, vg->name);
|
||||
|
||||
if (!(pvl = pool_alloc(mem, sizeof (*pvl)))) {
|
||||
log_error("pv_list allocation for '%s' failed", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(pv = fi->ops->pv_read(fi, pv_name))) {
|
||||
log_error("Failed to read existing physical volume '%s'",
|
||||
pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*pv->vg_name) {
|
||||
log_error("Physical volume '%s' is already in volume group "
|
||||
"'%s'", pv_name, pv->vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(pv->vg_name = pool_strdup(mem, vg->name))) {
|
||||
log_error("vg->name allocation failed for '%s'", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Units of 512-byte sectors */
|
||||
if (!dev_get_size(pv->dev, &pv->size)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Units of 512-byte sectors */
|
||||
pv->pe_size = vg->extent_size;
|
||||
|
||||
/*
|
||||
* The next two fields should be corrected
|
||||
* by fi->pv_setup.
|
||||
*/
|
||||
pv->pe_start = 0;
|
||||
pv->pe_count = pv->size / pv->pe_size;
|
||||
|
||||
pv->pe_allocated = 0;
|
||||
|
||||
if (!fi->ops->pv_setup(fi, pv, vg)) {
|
||||
log_error("Format-specific setup of physical volume '%s' "
|
||||
"failed.", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (find_pv_in_vg(vg, pv_name)) {
|
||||
log_error("Physical volume '%s' listed more than once.",
|
||||
pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vg->pv_count == vg->max_pv) {
|
||||
log_error("No space for '%s' - volume group '%s' "
|
||||
"holds max %d physical volume(s).", pv_name,
|
||||
vg->name, vg->max_pv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pvl->pv = pv;
|
||||
|
||||
list_add(&vg->pvs, &pvl->list);
|
||||
vg->pv_count++;
|
||||
vg->extent_count += pv->pe_count;
|
||||
vg->free_count += pv->pe_count;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vg_extend(struct format_instance *fi,
|
||||
struct volume_group *vg, int pv_count, char **pv_names)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* attach each pv */
|
||||
for (i = 0; i < pv_count; i++)
|
||||
if (!_add_pv_to_vg(fi, vg, pv_names[i])) {
|
||||
log_error("Unable to add physical volume '%s' to "
|
||||
"volume group '%s'.", pv_names[i], vg->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *strip_dir(const char *vg_name, const char *dev_dir)
|
||||
{
|
||||
int len = strlen(dev_dir);
|
||||
if (!strncmp(vg_name, dev_dir, len))
|
||||
vg_name += len;
|
||||
|
||||
return vg_name;
|
||||
}
|
||||
|
||||
struct volume_group *vg_create(struct format_instance *fi, const char *vg_name,
|
||||
uint32_t extent_size, int max_pv, int max_lv,
|
||||
int pv_count, char **pv_names)
|
||||
{
|
||||
struct volume_group *vg;
|
||||
struct pool *mem = fi->cmd->mem;
|
||||
|
||||
if (!(vg = pool_alloc(mem, sizeof (*vg)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* is this vg name already in use ? */
|
||||
init_partial(1);
|
||||
if (fi->ops->vg_read(fi, vg_name)) {
|
||||
log_err("A volume group called '%s' already exists.", vg_name);
|
||||
goto bad;
|
||||
}
|
||||
init_partial(0);
|
||||
|
||||
if (!id_create(&vg->id)) {
|
||||
log_err("Couldn't create uuid for volume group '%s'.",
|
||||
vg_name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* Strip dev_dir if present */
|
||||
vg_name = strip_dir(vg_name, fi->cmd->dev_dir);
|
||||
|
||||
vg->cmd = fi->cmd;
|
||||
|
||||
if (!(vg->name = pool_strdup(mem, vg_name))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
vg->status = (RESIZEABLE_VG | LVM_READ | LVM_WRITE);
|
||||
vg->system_id = pool_alloc(mem, NAME_LEN);
|
||||
*vg->system_id = '\0';
|
||||
|
||||
vg->extent_size = extent_size;
|
||||
vg->extent_count = 0;
|
||||
vg->free_count = 0;
|
||||
|
||||
vg->max_lv = max_lv;
|
||||
vg->max_pv = max_pv;
|
||||
|
||||
vg->pv_count = 0;
|
||||
list_init(&vg->pvs);
|
||||
|
||||
vg->lv_count = 0;
|
||||
list_init(&vg->lvs);
|
||||
|
||||
if (!fi->ops->vg_setup(fi, vg)) {
|
||||
log_error("Format specific setup of volume group '%s' failed.",
|
||||
vg_name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* attach the pv's */
|
||||
if (!vg_extend(fi, vg, pv_count, pv_names))
|
||||
goto bad;
|
||||
|
||||
return vg;
|
||||
|
||||
bad:
|
||||
pool_free(mem, vg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct physical_volume *pv_create(struct format_instance *fi,
|
||||
const char *name,
|
||||
struct id *id)
|
||||
{
|
||||
struct pool *mem = fi->cmd->mem;
|
||||
struct physical_volume *pv = pool_alloc(mem, sizeof (*pv));
|
||||
|
||||
if (!pv) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!id)
|
||||
id_create(&pv->id);
|
||||
else
|
||||
memcpy(&pv->id, id, sizeof(*id));
|
||||
|
||||
if (!(pv->dev = dev_cache_get(name, fi->cmd->filter))) {
|
||||
log_err("Couldn't find device '%s'", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(pv->vg_name = pool_alloc(mem, NAME_LEN))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
*pv->vg_name = 0;
|
||||
pv->status = ALLOCATABLE_PV;
|
||||
|
||||
if (!dev_get_size(pv->dev, &pv->size)) {
|
||||
log_err("Couldn't get size of device '%s'", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pv->pe_size = 0;
|
||||
pv->pe_start = 0;
|
||||
pv->pe_count = 0;
|
||||
pv->pe_allocated = 0;
|
||||
return pv;
|
||||
|
||||
bad:
|
||||
pool_free(mem, pv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name)
|
||||
{
|
||||
struct list *pvh;
|
||||
struct pv_list *pvl;
|
||||
|
||||
list_iterate(pvh, &vg->pvs) {
|
||||
pvl = list_item(pvh, struct pv_list);
|
||||
if (pvl->pv->dev == dev_cache_get(pv_name, vg->cmd->filter))
|
||||
return pvl;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name)
|
||||
{
|
||||
struct list *lvh;
|
||||
struct lv_list *lvl;
|
||||
const char *ptr;
|
||||
|
||||
/* Use last component */
|
||||
if ((ptr = strrchr(lv_name, '/')))
|
||||
ptr++;
|
||||
else
|
||||
ptr = lv_name;
|
||||
|
||||
list_iterate(lvh, &vg->lvs) {
|
||||
lvl = list_item(lvh, struct lv_list);
|
||||
if (!strcmp(lvl->lv->name, ptr))
|
||||
return lvl;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct logical_volume *find_lv(struct volume_group *vg, const char *lv_name)
|
||||
{
|
||||
struct lv_list *lvl = find_lv_in_vg(vg, lv_name);
|
||||
return lvl ? lvl->lv : NULL;
|
||||
}
|
||||
|
||||
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev)
|
||||
{
|
||||
struct list *pvh;
|
||||
struct physical_volume *pv;
|
||||
|
||||
list_iterate(pvh, &vg->pvs) {
|
||||
pv = list_item(pvh, struct pv_list)->pv;
|
||||
|
||||
if (dev == pv->dev)
|
||||
return pv;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
326
lib/metadata/metadata.h
Normal file
326
lib/metadata/metadata.h
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*
|
||||
* This is the in core representation of a volume group and its
|
||||
* associated physical and logical volumes.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_METADATA_H
|
||||
#define _LVM_METADATA_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <asm/page.h>
|
||||
#include "dev-cache.h"
|
||||
#include "list.h"
|
||||
#include "uuid.h"
|
||||
|
||||
#define NAME_LEN 128
|
||||
#define MAX_STRIPES 128
|
||||
#define SECTOR_SIZE 512
|
||||
#define STRIPE_SIZE_DEFAULT 16 /* 16KB */
|
||||
#define STRIPE_SIZE_MIN ( PAGE_SIZE/SECTOR_SIZE) /* PAGESIZE in sectors */
|
||||
#define STRIPE_SIZE_MAX ( 512L * 1024 / SECTOR_SIZE) /* 512 KB in sectors */
|
||||
|
||||
|
||||
/* Various flags */
|
||||
/* Note that the bits no longer necessarily correspond to LVM1 disk format */
|
||||
|
||||
#define EXPORTED_VG 0x00000002 /* VG PV */
|
||||
#define RESIZEABLE_VG 0x00000004 /* VG */
|
||||
#define PARTIAL_VG 0x00000040 /* VG */
|
||||
|
||||
/* May any free extents on this PV be used or must they be left free? */
|
||||
#define ALLOCATABLE_PV 0x00000008 /* PV */
|
||||
|
||||
#define SPINDOWN_LV 0x00000010 /* LV */
|
||||
#define BADBLOCK_ON 0x00000020 /* LV */
|
||||
|
||||
/* FIXME: do we really set read/write for a whole vg ? */
|
||||
#define LVM_READ 0x00000100 /* LV VG */
|
||||
#define LVM_WRITE 0x00000200 /* LV VG */
|
||||
#define CLUSTERED 0x00000400 /* VG */
|
||||
#define SHARED 0x00000800 /* VG */
|
||||
|
||||
/* FIXME: This should be an enum rather than a bitset,
|
||||
remove from status - EJT */
|
||||
#define ALLOC_SIMPLE 0x00001000 /* LV */
|
||||
#define ALLOC_STRICT 0x00002000 /* LV */
|
||||
#define ALLOC_CONTIGUOUS 0x00004000 /* LV */
|
||||
|
||||
#define SNAPSHOT 0x00010000 /* LV */
|
||||
#define SNAPSHOT_ORG 0x00020000 /* LV */
|
||||
|
||||
|
||||
struct physical_volume {
|
||||
struct id id;
|
||||
struct device *dev;
|
||||
char *vg_name;
|
||||
|
||||
uint32_t status;
|
||||
uint64_t size;
|
||||
|
||||
/* physical extents */
|
||||
uint64_t pe_size;
|
||||
uint64_t pe_start;
|
||||
uint32_t pe_count;
|
||||
uint32_t pe_allocated; /* FIXME: change the name to alloc_count ? */
|
||||
};
|
||||
|
||||
struct cmd_context;
|
||||
|
||||
struct volume_group {
|
||||
struct cmd_context *cmd;
|
||||
|
||||
struct id id;
|
||||
char *name;
|
||||
char *system_id;
|
||||
|
||||
uint32_t status;
|
||||
|
||||
uint32_t extent_size;
|
||||
uint32_t extent_count;
|
||||
uint32_t free_count;
|
||||
|
||||
uint32_t max_lv;
|
||||
uint32_t max_pv;
|
||||
|
||||
/* physical volumes */
|
||||
uint32_t pv_count;
|
||||
struct list pvs;
|
||||
|
||||
/* logical volumes */
|
||||
uint32_t lv_count;
|
||||
struct list lvs;
|
||||
};
|
||||
|
||||
struct stripe_segment {
|
||||
struct list list;
|
||||
|
||||
struct logical_volume *lv;
|
||||
uint32_t le;
|
||||
uint32_t len;
|
||||
uint32_t stripe_size;
|
||||
uint32_t stripes;
|
||||
|
||||
/* There will be one area for each stripe */
|
||||
struct {
|
||||
struct physical_volume *pv;
|
||||
uint32_t pe;
|
||||
} area[0];
|
||||
};
|
||||
|
||||
struct logical_volume {
|
||||
struct id id;
|
||||
char *name;
|
||||
|
||||
struct volume_group *vg;
|
||||
|
||||
uint32_t status;
|
||||
uint32_t read_ahead;
|
||||
|
||||
uint64_t size;
|
||||
uint32_t le_count;
|
||||
|
||||
struct list segments;
|
||||
};
|
||||
|
||||
struct name_list {
|
||||
struct list list;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct pv_list {
|
||||
struct list list;
|
||||
struct physical_volume *pv;
|
||||
};
|
||||
|
||||
struct lv_list {
|
||||
struct list list;
|
||||
struct logical_volume *lv;
|
||||
};
|
||||
|
||||
struct cmd_context {
|
||||
/* format handler allocates all objects from here */
|
||||
struct pool *mem;
|
||||
|
||||
/* misc. vars needed by format handler */
|
||||
char *dev_dir;
|
||||
struct dev_filter *filter;
|
||||
struct config_file *cf;
|
||||
};
|
||||
|
||||
struct format_instance {
|
||||
struct cmd_context *cmd;
|
||||
struct format_handler *ops;
|
||||
void *private;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Ownership of objects passes to caller.
|
||||
*/
|
||||
struct format_handler {
|
||||
/*
|
||||
* Returns a name_list of vg's.
|
||||
*/
|
||||
struct list *(*get_vgs)(struct format_instance *fi);
|
||||
|
||||
/*
|
||||
* Returns pv_list of fully-populated pv structures.
|
||||
*/
|
||||
struct list *(*get_pvs)(struct format_instance *fi);
|
||||
|
||||
/*
|
||||
* Return PV with given path.
|
||||
*/
|
||||
struct physical_volume *(*pv_read)(struct format_instance *fi,
|
||||
const char *pv_name);
|
||||
|
||||
/*
|
||||
* Tweak an already filled out a pv ready for importing into a
|
||||
* vg. eg. pe_count is format specific.
|
||||
*/
|
||||
int (*pv_setup)(struct format_instance *fi, struct physical_volume *pv,
|
||||
struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Write a PV structure to disk. Fails if the PV is in a VG ie
|
||||
* pv->vg_name must be null.
|
||||
*/
|
||||
int (*pv_write)(struct format_instance *fi,
|
||||
struct physical_volume *pv);
|
||||
|
||||
/*
|
||||
* Tweak an already filled out a lv eg, check there
|
||||
* aren't too many extents.
|
||||
*/
|
||||
int (*lv_setup)(struct format_instance *fi, struct logical_volume *lv);
|
||||
|
||||
/*
|
||||
* Tweak an already filled out vg. eg, max_pv is format
|
||||
* specific.
|
||||
*/
|
||||
int (*vg_setup)(struct format_instance *fi, struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* The name may be prefixed with the dev_dir from the
|
||||
* job_context.
|
||||
*/
|
||||
struct volume_group *(*vg_read)(struct format_instance *fi,
|
||||
const char *vg_name);
|
||||
|
||||
/*
|
||||
* Write out complete VG metadata. You must ensure internal
|
||||
* consistency before calling. eg. PEs can't refer to PVs not
|
||||
* part of the VG.
|
||||
*
|
||||
* It is also the responsibility of the caller to ensure external
|
||||
* consistency, eg by calling pv_write() if removing PVs from
|
||||
* a VG or calling vg_write() a second time if splitting a VG
|
||||
* into two.
|
||||
*
|
||||
* vg_write() must not read or write from any PVs not included
|
||||
* in the volume_group structure it is handed. Note: format1
|
||||
* does read all pv's currently.
|
||||
*/
|
||||
int (*vg_write)(struct format_instance *fi, struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Destructor for this object.
|
||||
*/
|
||||
void (*destroy)(struct format_instance *fi);
|
||||
};
|
||||
|
||||
/*
|
||||
* Utility functions
|
||||
*/
|
||||
struct physical_volume *pv_create(struct format_instance *fi,
|
||||
const char *name,
|
||||
struct id *id);
|
||||
|
||||
struct volume_group *vg_create(struct format_instance *fi, const char *name,
|
||||
uint32_t extent_size, int max_pv, int max_lv,
|
||||
int pv_count, char **pv_names);
|
||||
|
||||
/*
|
||||
* This needs the format instance to check the
|
||||
* pv's are orphaned.
|
||||
*/
|
||||
int vg_extend(struct format_instance *fi,
|
||||
struct volume_group *vg, int pv_count, char **pv_names);
|
||||
|
||||
|
||||
/*
|
||||
* Create a new LV within a given volume group.
|
||||
*
|
||||
*/
|
||||
struct logical_volume *lv_create(struct format_instance *fi,
|
||||
const char *name,
|
||||
uint32_t status,
|
||||
uint32_t stripes,
|
||||
uint32_t stripe_size,
|
||||
uint32_t extents,
|
||||
struct volume_group *vg,
|
||||
struct list *acceptable_pvs);
|
||||
|
||||
int lv_reduce(struct format_instance *fi,
|
||||
struct logical_volume *lv, uint32_t extents);
|
||||
|
||||
int lv_extend(struct format_instance *fi,
|
||||
struct logical_volume *lv,
|
||||
uint32_t stripes,
|
||||
uint32_t stripe_size,
|
||||
uint32_t extents,
|
||||
struct list *allocatable_pvs);
|
||||
|
||||
/* lv must be part of vg->lvs */
|
||||
int lv_remove(struct volume_group *vg, struct logical_volume *lv);
|
||||
|
||||
|
||||
/* FIXME: Move to other files */
|
||||
int id_eq(struct id *op1, struct id *op2);
|
||||
|
||||
/* Manipulate PV structures */
|
||||
int pv_add(struct volume_group *vg, struct physical_volume *pv);
|
||||
int pv_remove(struct volume_group *vg, struct physical_volume *pv);
|
||||
struct physical_volume *pv_find(struct volume_group *vg,
|
||||
const char *pv_name);
|
||||
|
||||
|
||||
/* Find a PV within a given VG */
|
||||
struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name);
|
||||
|
||||
/* Find an LV within a given VG */
|
||||
struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name);
|
||||
|
||||
/* Return the VG that contains a given LV (based on path given in lv_name) */
|
||||
/* or environment var */
|
||||
struct volume_group *find_vg_with_lv(const char *lv_name);
|
||||
|
||||
|
||||
/* FIXME Merge these functions with ones above */
|
||||
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev);
|
||||
struct logical_volume *find_lv(struct volume_group *vg, const char *lv_name);
|
||||
|
||||
/*
|
||||
* Remove a dev_dir if present.
|
||||
*/
|
||||
const char *strip_dir(const char *vg_name, const char *dir);
|
||||
|
||||
/*
|
||||
* Checks that an lv has no gaps or overlapping segments.
|
||||
*/
|
||||
int lv_check_segments(struct logical_volume *lv);
|
||||
|
||||
|
||||
/*
|
||||
* Sometimes (eg, after an lvextend), it is possible to merge two
|
||||
* adjacent segments into a single segment. This function trys
|
||||
* to merge as many segments as possible.
|
||||
*/
|
||||
int lv_merge_segments(struct logical_volume *lv);
|
||||
|
||||
|
||||
#endif
|
||||
255
lib/metadata/pv_map.c
Normal file
255
lib/metadata/pv_map.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "pv_map.h"
|
||||
#include "log.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static int _create_maps(struct pool *mem, struct list *pvs, struct list *maps)
|
||||
{
|
||||
struct list *tmp;
|
||||
struct physical_volume *pv;
|
||||
struct pv_map *pvm;
|
||||
|
||||
list_iterate(tmp, pvs) {
|
||||
pv = list_item(tmp, struct pv_list)->pv;
|
||||
|
||||
if (!(pv->status & ALLOCATABLE_PV))
|
||||
continue;
|
||||
|
||||
if (!(pvm = pool_zalloc(mem, sizeof(*pvm)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pvm->pv = pv;
|
||||
if (!(pvm->allocated_extents =
|
||||
bitset_create(mem, pv->pe_count))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_init(&pvm->areas);
|
||||
list_add(maps, &pvm->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _set_allocated(struct hash_table *hash,
|
||||
struct physical_volume *pv, int pe)
|
||||
{
|
||||
struct pv_map *pvm;
|
||||
|
||||
if (!(pvm = (struct pv_map *) hash_lookup(hash, dev_name(pv->dev)))) {
|
||||
/*
|
||||
* it doesn't matter that this fails, it just
|
||||
* means this part of the lv is on a pv that
|
||||
* we're not interested in allocating to.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (bit(pvm->allocated_extents, pe)) {
|
||||
log_error("Physical extent %d of %s referenced by more than "
|
||||
"one logical volume", pe, dev_name(pv->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
bit_set(pvm->allocated_extents, pe);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _fill_bitsets(struct volume_group *vg, struct list *maps)
|
||||
{
|
||||
struct list *lvh, *pvmh, *segh;
|
||||
struct logical_volume *lv;
|
||||
struct pv_map *pvm;
|
||||
uint32_t s, pe;
|
||||
struct hash_table *hash;
|
||||
struct stripe_segment *seg;
|
||||
int r = 0;
|
||||
|
||||
if (!(hash = hash_create(128))) {
|
||||
log_err("Couldn't create hash table for pv maps.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* populate the hash table */
|
||||
list_iterate (pvmh, maps) {
|
||||
pvm = list_item(pvmh, struct pv_map);
|
||||
if (!hash_insert(hash, dev_name(pvm->pv->dev), pvm)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* iterate through all the lv's setting bit's for used pe's */
|
||||
list_iterate (lvh, &vg->lvs) {
|
||||
lv = list_item(lvh, struct lv_list)->lv;
|
||||
|
||||
list_iterate (segh, &lv->segments) {
|
||||
seg = list_item(segh, struct stripe_segment);
|
||||
|
||||
for (s = 0; s < seg->stripes; s++) {
|
||||
for (pe = 0; pe < (seg->len / seg->stripes);
|
||||
pe++) {
|
||||
if (!_set_allocated(hash,
|
||||
seg->area[s].pv,
|
||||
seg->area[s].pe
|
||||
+ pe)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
hash_destroy(hash);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Areas are maintained in size order.
|
||||
*/
|
||||
static void _insert_area(struct list *head, struct pv_area *a)
|
||||
{
|
||||
struct list *pvah;
|
||||
struct pv_area *pva;
|
||||
|
||||
if (list_empty(head)) {
|
||||
list_add(head, &a->list);
|
||||
return;
|
||||
}
|
||||
|
||||
list_iterate (pvah, head) {
|
||||
pva = list_item(pvah, struct pv_area);
|
||||
|
||||
if (pva->count < a->count)
|
||||
break;
|
||||
}
|
||||
|
||||
list_add_h(&pva->list, &a->list);
|
||||
}
|
||||
|
||||
static int _create_single_area(struct pool *mem, struct pv_map *pvm,
|
||||
uint32_t *extent)
|
||||
{
|
||||
uint32_t e = *extent, b, count = pvm->pv->pe_count;
|
||||
struct pv_area *pva;
|
||||
|
||||
while (e < count && bit(pvm->allocated_extents, e))
|
||||
e++;
|
||||
|
||||
if (e == count) {
|
||||
*extent = e;
|
||||
return 1;
|
||||
}
|
||||
|
||||
b = e++;
|
||||
|
||||
while (e < count && !bit(pvm->allocated_extents, e))
|
||||
e++;
|
||||
|
||||
if (!(pva = pool_zalloc(mem, sizeof(*pva)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pva->map = pvm;
|
||||
pva->start = b;
|
||||
pva->count = e - b;
|
||||
_insert_area(&pvm->areas, pva);
|
||||
*extent = e;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _create_areas(struct pool *mem, struct pv_map *pvm)
|
||||
{
|
||||
uint32_t pe = 0;
|
||||
|
||||
while (pe < pvm->pv->pe_count)
|
||||
if (!_create_single_area(mem, pvm, &pe)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _create_all_areas(struct pool *mem, struct list *maps)
|
||||
{
|
||||
struct list *tmp;
|
||||
struct pv_map *pvm;
|
||||
|
||||
list_iterate(tmp, maps) {
|
||||
pvm = list_item(tmp, struct pv_map);
|
||||
|
||||
if (!_create_areas(mem, pvm)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct list *create_pv_maps(struct pool *mem, struct volume_group *vg,
|
||||
struct list *pvs)
|
||||
{
|
||||
struct list *maps = pool_zalloc(mem, sizeof(*maps));
|
||||
|
||||
if (!maps) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_init(maps);
|
||||
|
||||
if (!_create_maps(mem, pvs, maps)) {
|
||||
log_error("Couldn't create physical volume maps in %s",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_fill_bitsets(vg, maps)) {
|
||||
log_error("Couldn't fill extent allocation bitmaps in %s",
|
||||
vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_create_all_areas(mem, maps)) {
|
||||
log_error("Couldn't create area maps in %s", vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return maps;
|
||||
|
||||
bad:
|
||||
pool_free(mem, maps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void consume_pv_area(struct pv_area *pva, uint32_t to_go)
|
||||
{
|
||||
list_del(&pva->list);
|
||||
|
||||
assert(to_go <= pva->count);
|
||||
|
||||
if (to_go < pva->count) {
|
||||
/* split the area */
|
||||
pva->start += to_go;
|
||||
pva->count -= to_go;
|
||||
_insert_area(&pva->map->areas, pva);
|
||||
}
|
||||
}
|
||||
43
lib/metadata/pv_map.h
Normal file
43
lib/metadata/pv_map.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_PV_MAP_H
|
||||
#define _LVM_PV_MAP_H
|
||||
|
||||
#include "metadata.h"
|
||||
#include "bitset.h"
|
||||
#include "pool.h"
|
||||
|
||||
/*
|
||||
* The in core rep. only stores a mapping from
|
||||
* logical extents to physical extents against an
|
||||
* lv. Sometimes, when allocating a new lv for
|
||||
* instance, it is useful to have the inverse
|
||||
* mapping available.
|
||||
*/
|
||||
|
||||
struct pv_area {
|
||||
struct pv_map *map;
|
||||
uint32_t start;
|
||||
uint32_t count;
|
||||
|
||||
struct list list;
|
||||
};
|
||||
|
||||
struct pv_map {
|
||||
struct physical_volume *pv;
|
||||
bitset_t allocated_extents;
|
||||
struct list areas;
|
||||
|
||||
struct list list;
|
||||
};
|
||||
|
||||
struct list *create_pv_maps(struct pool *mem,
|
||||
struct volume_group *vg, struct list *pvs);
|
||||
|
||||
void consume_pv_area(struct pv_area *area, uint32_t to_go);
|
||||
|
||||
#endif
|
||||
101
lib/misc/lvm-file.c
Normal file
101
lib/misc/lvm-file.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "lvm-file.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/*
|
||||
* Creates a temporary filename, and opens a descriptor to the
|
||||
* file. Both the filename and descriptor are needed so we can
|
||||
* rename the file after successfully writing it. Grab
|
||||
* NFS-supported exclusive fcntl discretionary lock.
|
||||
*/
|
||||
int create_temp_name(const char *dir, char *buffer, size_t len,
|
||||
int *fd)
|
||||
{
|
||||
int i, num;
|
||||
pid_t pid;
|
||||
char hostname[255];
|
||||
struct flock lock = {
|
||||
l_type: F_WRLCK,
|
||||
l_whence: 0,
|
||||
l_start: 0,
|
||||
l_len: 0
|
||||
};
|
||||
|
||||
num = rand();
|
||||
pid = getpid();
|
||||
if (gethostname(hostname, sizeof(hostname)) < 0) {
|
||||
log_sys_error("gethostname", "");
|
||||
strcpy(hostname, "nohostname");
|
||||
}
|
||||
|
||||
for (i = 0; i < 20; i++, num++) {
|
||||
|
||||
if (lvm_snprintf(buffer, len, "%s/.lvm_%s_%d_%d",
|
||||
dir, hostname, pid, num) == -1) {
|
||||
log_err("Not enough space to build temporary file "
|
||||
"string.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*fd = open(buffer, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
|
||||
S_IRUSR | S_IRGRP | S_IROTH |
|
||||
S_IWUSR | S_IWGRP | S_IWOTH);
|
||||
if (*fd < 0)
|
||||
continue;
|
||||
|
||||
if (!fcntl(*fd, F_SETLK, &lock))
|
||||
return 1;
|
||||
|
||||
close(*fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NFS-safe rename of a temporary file to a common name, designed
|
||||
* to avoid race conditions and not overwrite the destination if
|
||||
* it exists.
|
||||
*
|
||||
* Try to create the new filename as a hard link to the original.
|
||||
* Check the link count of the original file to see if it worked.
|
||||
* (Assumes nothing else touches our temporary file!) If it
|
||||
* worked, unlink the old filename.
|
||||
*/
|
||||
int lvm_rename(const char *old, const char *new)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
link(old, new);
|
||||
|
||||
if (stat(old, &buf)) {
|
||||
log_sys_error("stat", old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buf.st_nlink != 2) {
|
||||
log_error("%s: rename to %s failed", old, new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (unlink(old)) {
|
||||
log_sys_error("unlink", old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
18
lib/misc/lvm-file.h
Normal file
18
lib/misc/lvm-file.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create a temporary filename, and opens a descriptor to the file.
|
||||
*/
|
||||
int create_temp_name(const char *dir, char *buffer, size_t len, int *fd);
|
||||
|
||||
/*
|
||||
* NFS-safe rename of a temporary file to a common name, designed
|
||||
* to avoid race conditions and not overwrite the destination if
|
||||
* it exists.
|
||||
*/
|
||||
int lvm_rename(const char *old, const char *new);
|
||||
|
||||
36
lib/misc/lvm-string.h
Normal file
36
lib/misc/lvm-string.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_STRING_H
|
||||
#define _LVM_STRING_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
* On error, up to glibc 2.0.6, snprintf returned -1 if buffer was too small;
|
||||
* From glibc 2.1 it returns number of chars (excl. trailing null) that would
|
||||
* have been written had there been room.
|
||||
*
|
||||
* lvm_snprintf reverts to the old behaviour.
|
||||
*/
|
||||
static inline int lvm_snprintf(char *buf, size_t bufsize,
|
||||
const char *format, ...)
|
||||
{
|
||||
int n;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
n = vsnprintf(buf, bufsize, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (n < 0 || (n > bufsize - 1))
|
||||
return -1;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif
|
||||
193
lib/mm/dbg_malloc.c
Normal file
193
lib/mm/dbg_malloc.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
struct memblock {
|
||||
struct memblock *prev, *next; /* All allocated blocks are linked */
|
||||
size_t length; /* Size of the requested block */
|
||||
int id; /* Index of the block */
|
||||
const char *file; /* File that allocated */
|
||||
int line; /* Line that allocated */
|
||||
void *magic; /* Address of this block */
|
||||
};
|
||||
|
||||
static struct {
|
||||
unsigned int blocks, mblocks;
|
||||
unsigned int bytes, mbytes;
|
||||
|
||||
} _mem_stats = {0, 0, 0, 0};
|
||||
|
||||
static struct memblock *_head = 0;
|
||||
static struct memblock *_tail = 0;
|
||||
|
||||
void *malloc_aux(size_t s, const char *file, int line)
|
||||
{
|
||||
struct memblock *nb;
|
||||
size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);
|
||||
|
||||
if (!(nb = malloc(tsize))) {
|
||||
log_error("couldn't allocate any memory, size = %" PRIuPTR, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set up the file and line info */
|
||||
nb->file = file;
|
||||
nb->line = line;
|
||||
|
||||
#ifdef BOUNDS_CHECK
|
||||
bounds_check();
|
||||
#endif
|
||||
|
||||
/* setup fields */
|
||||
nb->magic = nb + 1;
|
||||
nb->length = s;
|
||||
nb->id = ++_mem_stats.blocks;
|
||||
nb->next = 0;
|
||||
nb->prev = _tail;
|
||||
|
||||
/* link to tail of the list */
|
||||
if (!_head)
|
||||
_head = _tail = nb;
|
||||
else {
|
||||
_tail->next = nb;
|
||||
_tail = nb;
|
||||
}
|
||||
|
||||
/* stomp a pretty pattern across the new memory
|
||||
and fill in the boundary bytes */
|
||||
{
|
||||
char *ptr = (char *) (nb + 1);
|
||||
int i;
|
||||
for (i = 0; i < s; i++)
|
||||
*ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;
|
||||
|
||||
for (i = 0; i < sizeof(unsigned long); i++)
|
||||
*ptr++ = (char) nb->id;
|
||||
}
|
||||
|
||||
if (_mem_stats.blocks > _mem_stats.mblocks)
|
||||
_mem_stats.mblocks = _mem_stats.blocks;
|
||||
|
||||
_mem_stats.bytes += s;
|
||||
if (_mem_stats.bytes > _mem_stats.mbytes)
|
||||
_mem_stats.mbytes = _mem_stats.bytes;
|
||||
|
||||
return nb + 1;
|
||||
}
|
||||
|
||||
void free_aux(void *p)
|
||||
{
|
||||
char *ptr;
|
||||
int i;
|
||||
struct memblock *mb = ((struct memblock *) p) - 1;
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
#ifdef BOUNDS_CHECK
|
||||
bounds_check();
|
||||
#endif
|
||||
|
||||
/* sanity check */
|
||||
assert(mb->magic == p);
|
||||
|
||||
/* check data at the far boundary */
|
||||
ptr = ((char *) mb) + sizeof(struct memblock) + mb->length;
|
||||
for (i = 0; i < sizeof(unsigned long); i++)
|
||||
if(*ptr++ != (char) mb->id)
|
||||
assert(!"Damage at far end of block");
|
||||
|
||||
/* have we freed this before ? */
|
||||
assert(mb->id != 0);
|
||||
mb->id = 0;
|
||||
|
||||
/* stomp a different pattern across the memory */
|
||||
ptr = ((char *) mb) + sizeof(struct memblock);
|
||||
for (i = 0; i < mb->length; i++)
|
||||
*ptr++ = i & 1 ? (char) 0xde : (char) 0xad;
|
||||
|
||||
/* unlink */
|
||||
if (mb->prev)
|
||||
mb->prev->next = mb->next;
|
||||
else
|
||||
_head = mb->next;
|
||||
|
||||
if (mb->next)
|
||||
mb->next->prev = mb->prev;
|
||||
else
|
||||
_tail = mb->prev;
|
||||
|
||||
assert(_mem_stats.blocks);
|
||||
_mem_stats.blocks--;
|
||||
_mem_stats.bytes -= mb->length;
|
||||
|
||||
/* free the memory */
|
||||
free(mb);
|
||||
}
|
||||
|
||||
void *realloc_aux(void *p, unsigned int s, const char *file, int line)
|
||||
{
|
||||
void *r;
|
||||
struct memblock *mb = ((struct memblock *) p) - 1;
|
||||
|
||||
r = malloc_aux(s, file, line);
|
||||
|
||||
if (p) {
|
||||
memcpy(r, p, mb->length);
|
||||
free_aux(p);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MEM
|
||||
int dump_memory(void)
|
||||
{
|
||||
unsigned long tot = 0;
|
||||
struct memblock *mb;
|
||||
if (_head)
|
||||
log_very_verbose("You have a memory leak:");
|
||||
|
||||
for (mb = _head; mb; mb = mb->next) {
|
||||
print_log(_LOG_INFO, mb->file, mb->line,
|
||||
"block %d at %p, size %" PRIdPTR,
|
||||
mb->id, mb->magic, mb->length);
|
||||
tot += mb->length;
|
||||
}
|
||||
|
||||
if (_head)
|
||||
log_very_verbose("%ld bytes leaked in total", tot);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void bounds_check(void)
|
||||
{
|
||||
struct memblock *mb = _head;
|
||||
while (mb) {
|
||||
int i;
|
||||
char *ptr = ((char *) (mb + 1)) + mb->length;
|
||||
for (i = 0; i < sizeof(unsigned long); i++)
|
||||
if (*ptr++ != (char) mb->id)
|
||||
assert(!"Memory smash");
|
||||
|
||||
mb = mb->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
42
lib/mm/dbg_malloc.h
Normal file
42
lib/mm/dbg_malloc.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DBG_MALLOC_H
|
||||
#define _LVM_DBG_MALLOC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef DEBUG_MEM
|
||||
void *malloc_aux(size_t s, const char *file, int line);
|
||||
void free_aux(void *p);
|
||||
void *realloc_aux(void *p, unsigned int s, const char *file, int line);
|
||||
int dump_memory(void);
|
||||
void bounds_check(void);
|
||||
|
||||
#define dbg_malloc(s) malloc_aux((s), __FILE__, __LINE__)
|
||||
#define dbg_free(p) free_aux(p)
|
||||
#define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__)
|
||||
#else
|
||||
#define dbg_malloc(s) malloc(s)
|
||||
#define dbg_free(p) free(p)
|
||||
#define dbg_realloc(p, s) realloc(p, s)
|
||||
#define dump_memory()
|
||||
#define bounds_check()
|
||||
#endif
|
||||
|
||||
static inline char *dbg_strdup(const char *str)
|
||||
{
|
||||
char *ret = dbg_malloc(strlen(str) + 1);
|
||||
|
||||
if (ret)
|
||||
strcpy(ret, str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
210
lib/mm/pool-debug.c
Normal file
210
lib/mm/pool-debug.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "pool.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
struct block {
|
||||
struct block *next;
|
||||
size_t size;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct pool {
|
||||
int begun;
|
||||
struct block *object;
|
||||
|
||||
struct block *blocks;
|
||||
struct block *tail;
|
||||
};
|
||||
|
||||
/* by default things come out aligned for doubles */
|
||||
#define DEFAULT_ALIGNMENT __alignof__ (double)
|
||||
|
||||
struct pool *pool_create(size_t chunk_hint)
|
||||
{
|
||||
struct pool *mem = dbg_malloc(sizeof(*mem));
|
||||
|
||||
if (!mem) {
|
||||
log_error("Couldn't create memory pool (size %u)",
|
||||
sizeof(*mem));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mem->begun = 0;
|
||||
mem->object = 0;
|
||||
mem->blocks = mem->tail = NULL;
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void _free_blocks(struct block *b)
|
||||
{
|
||||
struct block *n;
|
||||
|
||||
while (b) {
|
||||
n = b->next;
|
||||
dbg_free(b->data);
|
||||
dbg_free(b);
|
||||
b = n;
|
||||
}
|
||||
}
|
||||
|
||||
void pool_destroy(struct pool *p)
|
||||
{
|
||||
_free_blocks(p->blocks);
|
||||
dbg_free(p);
|
||||
}
|
||||
|
||||
void *pool_alloc(struct pool *p, size_t s)
|
||||
{
|
||||
return pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
static void _append_block(struct pool *p, struct block *b)
|
||||
{
|
||||
if (p->tail) {
|
||||
p->tail->next = b;
|
||||
p->tail = b;
|
||||
} else
|
||||
p->blocks = p->tail = b;
|
||||
}
|
||||
|
||||
static struct block *_new_block(size_t s, unsigned alignment)
|
||||
{
|
||||
static char *_oom = "Out of memory";
|
||||
|
||||
/* FIXME: I'm currently ignoring the alignment arg. */
|
||||
size_t len = sizeof(struct block) + s;
|
||||
struct block *b = dbg_malloc(len);
|
||||
|
||||
/*
|
||||
* Too lazy to implement alignment for debug version, and
|
||||
* I don't think LVM will use anything but default
|
||||
* align.
|
||||
*/
|
||||
assert(alignment == DEFAULT_ALIGNMENT);
|
||||
|
||||
if (!b) {
|
||||
log_err(_oom);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(b->data = dbg_malloc(s))) {
|
||||
log_err(_oom);
|
||||
dbg_free(b);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b->next = NULL;
|
||||
b->size = s;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment)
|
||||
{
|
||||
struct block *b = _new_block(s, alignment);
|
||||
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
_append_block(p, b);
|
||||
return b->data;
|
||||
}
|
||||
|
||||
void pool_empty(struct pool *p)
|
||||
{
|
||||
_free_blocks(p->blocks);
|
||||
p->blocks = p->tail = NULL;
|
||||
}
|
||||
|
||||
void pool_free(struct pool *p, void *ptr)
|
||||
{
|
||||
struct block *b, *prev = NULL;
|
||||
|
||||
for (b = p->blocks; b; b = b->next) {
|
||||
if (b->data == ptr)
|
||||
break;
|
||||
prev = b;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this fires then you tried to free a
|
||||
* pointer that either wasn't from this
|
||||
* pool, or isn't the start of a block.
|
||||
*/
|
||||
assert(b);
|
||||
|
||||
_free_blocks(b);
|
||||
|
||||
if (prev) {
|
||||
p->tail = prev;
|
||||
prev->next = NULL;
|
||||
} else
|
||||
p->blocks = p->tail = NULL;
|
||||
}
|
||||
|
||||
int pool_begin_object(struct pool *p, size_t init_size)
|
||||
{
|
||||
assert(!p->begun);
|
||||
p->begun = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pool_grow_object(struct pool *p, const void *buffer, size_t delta)
|
||||
{
|
||||
struct block *new;
|
||||
size_t size = delta;
|
||||
|
||||
assert(p->begun);
|
||||
|
||||
if (p->object)
|
||||
size += p->object->size;
|
||||
|
||||
if (!(new = _new_block(size, DEFAULT_ALIGNMENT))) {
|
||||
log_err("Couldn't extend object.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (p->object) {
|
||||
memcpy(new->data, p->object->data, p->object->size);
|
||||
dbg_free(p->object);
|
||||
}
|
||||
p->object = new;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *pool_end_object(struct pool *p)
|
||||
{
|
||||
assert(p->begun);
|
||||
_append_block(p, p->object);
|
||||
|
||||
p->begun = 0;
|
||||
p->object = NULL;
|
||||
return p->tail->data;
|
||||
}
|
||||
|
||||
void pool_abandon_object(struct pool *p)
|
||||
{
|
||||
assert(p->begun);
|
||||
dbg_free(p->object);
|
||||
p->begun = 0;
|
||||
p->object = NULL;
|
||||
}
|
||||
|
||||
char *pool_strdup(struct pool *p, const char *str)
|
||||
{
|
||||
char *ret = pool_alloc(p, strlen(str) + 1);
|
||||
|
||||
if (ret)
|
||||
strcpy(ret, str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
240
lib/mm/pool-fast.c
Normal file
240
lib/mm/pool-fast.c
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pool.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
struct chunk {
|
||||
char *begin, *end;
|
||||
struct chunk *prev;
|
||||
};
|
||||
|
||||
struct pool {
|
||||
struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free
|
||||
list to stop 'bobbling' */
|
||||
size_t chunk_size;
|
||||
size_t object_len;
|
||||
unsigned object_alignment;
|
||||
};
|
||||
|
||||
void _align_chunk(struct chunk *c, unsigned alignment);
|
||||
struct chunk *_new_chunk(struct pool *p, size_t s);
|
||||
|
||||
/* by default things come out aligned for doubles */
|
||||
#define DEFAULT_ALIGNMENT __alignof__ (double)
|
||||
|
||||
struct pool *pool_create(size_t chunk_hint)
|
||||
{
|
||||
size_t new_size = 1024;
|
||||
struct pool *p = dbg_malloc(sizeof(*p));
|
||||
|
||||
if (!p) {
|
||||
log_error("Couldn't create memory pool (size %u)",
|
||||
sizeof(*p));
|
||||
return 0;
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
/* round chunk_hint up to the next power of 2 */
|
||||
p->chunk_size = chunk_hint + sizeof(struct chunk);
|
||||
while (new_size < p->chunk_size)
|
||||
new_size <<= 1;
|
||||
p->chunk_size = new_size;
|
||||
return p;
|
||||
}
|
||||
|
||||
void pool_destroy(struct pool *p)
|
||||
{
|
||||
struct chunk *c, *pr;
|
||||
dbg_free(p->spare_chunk);
|
||||
c = p->chunk;
|
||||
while (c) {
|
||||
pr = c->prev;
|
||||
dbg_free(c);
|
||||
c = pr;
|
||||
}
|
||||
|
||||
dbg_free(p);
|
||||
}
|
||||
|
||||
void *pool_alloc(struct pool *p, size_t s)
|
||||
{
|
||||
return pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
void *r;
|
||||
|
||||
/* realign begin */
|
||||
if (c)
|
||||
_align_chunk(c, alignment);
|
||||
|
||||
/* have we got room ? */
|
||||
if(!c || (c->begin > c->end) || (c->end - c->begin < s)) {
|
||||
/* allocate new chunk */
|
||||
int needed = s + alignment + sizeof(struct chunk);
|
||||
c = _new_chunk(p, (needed > p->chunk_size) ?
|
||||
needed : p->chunk_size);
|
||||
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
_align_chunk(c, alignment);
|
||||
}
|
||||
|
||||
r = c->begin;
|
||||
c->begin += s;
|
||||
return r;
|
||||
}
|
||||
|
||||
void pool_empty(struct pool *p)
|
||||
{
|
||||
struct chunk *c;
|
||||
|
||||
for (c = p->chunk; c && c->prev; c = c->prev)
|
||||
;
|
||||
|
||||
if (p->chunk)
|
||||
pool_free(p, (char *) (p->chunk + 1));
|
||||
}
|
||||
|
||||
void pool_free(struct pool *p, void *ptr)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
|
||||
while (c) {
|
||||
if (((char *) c < (char *) ptr) &&
|
||||
((char *) c->end > (char *) ptr)) {
|
||||
c->begin = ptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p->spare_chunk)
|
||||
dbg_free(p->spare_chunk);
|
||||
p->spare_chunk = c;
|
||||
c = c->prev;
|
||||
}
|
||||
|
||||
if (!c)
|
||||
log_error("Internal error: pool_free asked to free pointer "
|
||||
"not in pool");
|
||||
else
|
||||
p->chunk = c;
|
||||
}
|
||||
|
||||
int pool_begin_object(struct pool *p, size_t hint)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
const size_t align = DEFAULT_ALIGNMENT;
|
||||
|
||||
p->object_len = 0;
|
||||
p->object_alignment = align;
|
||||
|
||||
if (c)
|
||||
_align_chunk(c, align);
|
||||
|
||||
if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) {
|
||||
/* allocate a new chunk */
|
||||
c = _new_chunk(p,
|
||||
hint > (p->chunk_size - sizeof(struct chunk)) ?
|
||||
hint + sizeof(struct chunk) + align :
|
||||
p->chunk_size);
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
_align_chunk(c, align);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pool_grow_object(struct pool *p, const void *extra, size_t n)
|
||||
{
|
||||
struct chunk *c = p->chunk, *nc;
|
||||
|
||||
if (c->end - (c->begin + p->object_len) < n) {
|
||||
/* move into a new chunk */
|
||||
if (p->object_len + n > (p->chunk_size / 2))
|
||||
nc = _new_chunk(p, (p->object_len + n) * 2);
|
||||
else
|
||||
nc = _new_chunk(p, p->chunk_size);
|
||||
|
||||
if (!nc)
|
||||
return 0;
|
||||
|
||||
_align_chunk(p->chunk, p->object_alignment);
|
||||
memcpy(p->chunk->begin, c->begin, p->object_len);
|
||||
c = p->chunk;
|
||||
}
|
||||
|
||||
memcpy(c->begin + p->object_len, extra, n);
|
||||
p->object_len += n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *pool_end_object(struct pool *p)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
void *r = c->begin;
|
||||
c->begin += p->object_len;
|
||||
p->object_len = 0u;
|
||||
p->object_alignment = DEFAULT_ALIGNMENT;
|
||||
return r;
|
||||
}
|
||||
|
||||
void pool_abandon_object(struct pool *p)
|
||||
{
|
||||
p->object_len = 0;
|
||||
p->object_alignment = DEFAULT_ALIGNMENT;
|
||||
}
|
||||
|
||||
char *pool_strdup(struct pool *p, const char *str)
|
||||
{
|
||||
char *ret = pool_alloc(p, strlen(str) + 1);
|
||||
|
||||
if (ret)
|
||||
strcpy(ret, str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _align_chunk(struct chunk *c, unsigned alignment)
|
||||
{
|
||||
c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
|
||||
}
|
||||
|
||||
struct chunk *_new_chunk(struct pool *p, size_t s)
|
||||
{
|
||||
struct chunk *c;
|
||||
|
||||
if (p->spare_chunk &&
|
||||
((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) {
|
||||
/* reuse old chunk */
|
||||
c = p->spare_chunk;
|
||||
p->spare_chunk = 0;
|
||||
} else {
|
||||
if (!(c = dbg_malloc(s))) {
|
||||
log_err("Out of memory. Requested %u bytes.", s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c->end = (char *) c + s;
|
||||
}
|
||||
|
||||
c->prev = p->chunk;
|
||||
c->begin = (char *) (c + 1);
|
||||
p->chunk = c;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
11
lib/mm/pool.c
Normal file
11
lib/mm/pool.c
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_POOL
|
||||
#include "pool-debug.c"
|
||||
#else
|
||||
#include "pool-fast.c"
|
||||
#endif
|
||||
121
lib/mm/pool.h
Normal file
121
lib/mm/pool.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_POOL_H
|
||||
#define _LVM_POOL_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
/*
|
||||
* The pool allocator is useful when you are going to allocate
|
||||
* lots of memory, use the memory for a bit, and then free the
|
||||
* memory in one go. A surprising amount of code has this usage
|
||||
* profile.
|
||||
*
|
||||
* You should think of the pool as an infinite, contiguous chunk
|
||||
* of memory. The front of this chunk of memory contains
|
||||
* allocated objects, the second half is free. pool_alloc grabs
|
||||
* the next 'size' bytes from the free half, in effect moving it
|
||||
* into the allocated half. This operation is very efficient.
|
||||
*
|
||||
* pool_free frees the allocated object *and* all objects
|
||||
* allocated after it. It is important to note this semantic
|
||||
* difference from malloc/free. This is also extremely
|
||||
* efficient, since a single pool_free can dispose of a large
|
||||
* complex object.
|
||||
*
|
||||
* pool_destroy frees all allocated memory.
|
||||
*
|
||||
* eg, If you are building a binary tree in your program, and
|
||||
* know that you are only ever going to insert into your tree,
|
||||
* and not delete (eg, maintaining a symbol table for a
|
||||
* compiler). You can create yourself a pool, allocate the nodes
|
||||
* from it, and when the tree becomes redundant call pool_destroy
|
||||
* (no nasty iterating through the tree to free nodes).
|
||||
*
|
||||
* eg, On the other hand if you wanted to repeatedly insert and
|
||||
* remove objects into the tree, you would be better off
|
||||
* allocating the nodes from a free list; you cannot free a
|
||||
* single arbitrary node with pool.
|
||||
*/
|
||||
|
||||
struct pool;
|
||||
|
||||
/* constructor and destructor */
|
||||
struct pool *pool_create(size_t chunk_hint);
|
||||
void pool_destroy(struct pool *p);
|
||||
|
||||
/* simple allocation/free routines */
|
||||
void *pool_alloc(struct pool *p, size_t s);
|
||||
void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment);
|
||||
void pool_empty(struct pool *p);
|
||||
void pool_free(struct pool *p, void *ptr);
|
||||
|
||||
/*
|
||||
* Object building routines:
|
||||
*
|
||||
* These allow you to 'grow' an object, useful for
|
||||
* building strings, or filling in dynamic
|
||||
* arrays.
|
||||
*
|
||||
* It's probably best explained with an example:
|
||||
*
|
||||
* char *build_string(struct pool *mem)
|
||||
* {
|
||||
* int i;
|
||||
* char buffer[16];
|
||||
*
|
||||
* if (!pool_begin_object(mem, 128))
|
||||
* return NULL;
|
||||
*
|
||||
* for (i = 0; i < 50; i++) {
|
||||
* snprintf(buffer, sizeof(buffer), "%d, ", i);
|
||||
* if (!pool_grow_object(mem, buffer, strlen(buffer)))
|
||||
* goto bad;
|
||||
* }
|
||||
*
|
||||
* // add null
|
||||
* if (!pool_grow_object(mem, "\0", 1))
|
||||
* goto bad;
|
||||
*
|
||||
* return pool_end_object(mem);
|
||||
*
|
||||
* bad:
|
||||
*
|
||||
* pool_abandon_object(mem);
|
||||
* return NULL;
|
||||
*}
|
||||
*
|
||||
* So start an object by calling pool_begin_object
|
||||
* with a guess at the final object size - if in
|
||||
* doubt make the guess too small.
|
||||
*
|
||||
* Then append chunks of data to your object with
|
||||
* pool_grow_object. Finally get your object with
|
||||
* a call to pool_end_object.
|
||||
*
|
||||
*/
|
||||
int pool_begin_object(struct pool *p, size_t hint);
|
||||
int pool_grow_object(struct pool *p, const void *extra, size_t delta);
|
||||
void *pool_end_object(struct pool *p);
|
||||
void pool_abandon_object(struct pool *p);
|
||||
|
||||
/* utilities */
|
||||
char *pool_strdup(struct pool *p, const char *str);
|
||||
|
||||
static inline void *pool_zalloc(struct pool *p, size_t s) {
|
||||
void *ptr = pool_alloc(p, s);
|
||||
|
||||
if (ptr)
|
||||
memset(ptr, 0, s);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
32
lib/mm/xlate.h
Normal file
32
lib/mm/xlate.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_XLATE_H
|
||||
#define _LVM_XLATE_H
|
||||
|
||||
/* FIXME: finish these as inlines */
|
||||
|
||||
uint16_t shuffle16(uint16_t n);
|
||||
uint32_t shuffle32(uint32_t n);
|
||||
uint64_t shuffle64(uint64_t n);
|
||||
|
||||
/* xlate functions move data between core and disk */
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define xlate16(x) shuffle16(x)
|
||||
# define xlate32(x) shuffle32(x)
|
||||
# define xlate64(x) shuffle64(x)
|
||||
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define xlate16(x) (x)
|
||||
# define xlate32(x) (x)
|
||||
# define xlate64(x) (x)
|
||||
|
||||
#else
|
||||
# error "__BYTE_ORDER must be defined as __LITTLE_ENDIAN or __BIG_ENDIAN"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
349
lib/regex/matcher.c
Normal file
349
lib/regex/matcher.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "matcher.h"
|
||||
#include "parse_rx.h"
|
||||
#include "log.h"
|
||||
#include "ttree.h"
|
||||
#include "bitset.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct dfa_state {
|
||||
int final;
|
||||
struct dfa_state *lookup[256];
|
||||
};
|
||||
|
||||
struct state_queue {
|
||||
struct dfa_state *s;
|
||||
bitset_t bits;
|
||||
struct state_queue *next;
|
||||
};
|
||||
|
||||
struct matcher { /* Instance variables for the lexer */
|
||||
struct dfa_state *start;
|
||||
int num_nodes, nodes_entered;
|
||||
struct rx_node **nodes;
|
||||
struct pool *scratch, *mem;
|
||||
};
|
||||
|
||||
#define TARGET_TRANS '\0'
|
||||
|
||||
static int _count_nodes(struct rx_node *rx)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if(rx->left)
|
||||
r += _count_nodes(rx->left);
|
||||
|
||||
if(rx->right)
|
||||
r += _count_nodes(rx->right);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _fill_table(struct matcher *m, struct rx_node *rx)
|
||||
{
|
||||
assert((rx->type != OR) || (rx->left && rx->right));
|
||||
|
||||
if(rx->left)
|
||||
_fill_table(m, rx->left);
|
||||
|
||||
if(rx->right)
|
||||
_fill_table(m, rx->right);
|
||||
|
||||
m->nodes[m->nodes_entered++] = rx;
|
||||
}
|
||||
|
||||
static void _create_bitsets(struct matcher *m)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < m->num_nodes; i++) {
|
||||
struct rx_node *n = m->nodes[i];
|
||||
n->firstpos = bitset_create(m->scratch, m->num_nodes);
|
||||
n->lastpos = bitset_create(m->scratch, m->num_nodes);
|
||||
n->followpos = bitset_create(m->scratch, m->num_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
static void _calc_functions(struct matcher *m)
|
||||
{
|
||||
int i, j, final = 1;
|
||||
struct rx_node *rx, *c1, *c2;
|
||||
|
||||
for(i = 0; i < m->num_nodes; i++) {
|
||||
rx = m->nodes[i];
|
||||
c1 = rx->left;
|
||||
c2 = rx->right;
|
||||
|
||||
if(bit(rx->charset, TARGET_TRANS))
|
||||
rx->final = final++;
|
||||
|
||||
switch(rx->type) {
|
||||
case CAT:
|
||||
if(c1->nullable)
|
||||
bit_union(rx->firstpos,
|
||||
c1->firstpos, c2->firstpos);
|
||||
else
|
||||
bit_copy(rx->firstpos, c1->firstpos);
|
||||
|
||||
if(c2->nullable)
|
||||
bit_union(rx->lastpos,
|
||||
c1->lastpos, c2->lastpos);
|
||||
else
|
||||
bit_copy(rx->lastpos, c2->lastpos);
|
||||
|
||||
rx->nullable = c1->nullable && c2->nullable;
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
bit_copy(rx->firstpos, c1->firstpos);
|
||||
bit_copy(rx->lastpos, c1->lastpos);
|
||||
rx->nullable = c1->nullable;
|
||||
break;
|
||||
|
||||
case OR:
|
||||
bit_union(rx->firstpos, c1->firstpos, c2->firstpos);
|
||||
bit_union(rx->lastpos, c1->lastpos, c2->lastpos);
|
||||
rx->nullable = c1->nullable || c2->nullable;
|
||||
break;
|
||||
|
||||
case QUEST:
|
||||
case STAR:
|
||||
bit_copy(rx->firstpos, c1->firstpos);
|
||||
bit_copy(rx->lastpos, c1->lastpos);
|
||||
rx->nullable = 1;
|
||||
break;
|
||||
|
||||
case CHARSET:
|
||||
bit_set(rx->firstpos, i);
|
||||
bit_set(rx->lastpos, i);
|
||||
rx->nullable = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Internal error: Unknown calc node type");
|
||||
}
|
||||
|
||||
/*
|
||||
* followpos has it's own switch
|
||||
* because PLUS and STAR do the
|
||||
* same thing.
|
||||
*/
|
||||
switch(rx->type) {
|
||||
case CAT:
|
||||
for(j = 0; j < m->num_nodes; j++) {
|
||||
if(bit(c1->lastpos, j)) {
|
||||
struct rx_node *n = m->nodes[j];
|
||||
bit_union(n->followpos,
|
||||
n->followpos, c2->firstpos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
case STAR:
|
||||
for(j = 0; j < m->num_nodes; j++) {
|
||||
if(bit(rx->lastpos, j)) {
|
||||
struct rx_node *n = m->nodes[j];
|
||||
bit_union(n->followpos,
|
||||
n->followpos, rx->firstpos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct dfa_state *_create_dfa_state(struct pool *mem)
|
||||
{
|
||||
return pool_zalloc(mem, sizeof(struct dfa_state));
|
||||
}
|
||||
|
||||
static struct state_queue *_create_state_queue(struct pool *mem,
|
||||
struct dfa_state *dfa,
|
||||
bitset_t bits)
|
||||
{
|
||||
struct state_queue *r = pool_alloc(mem, sizeof(*r));
|
||||
|
||||
if (!r) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r->s = dfa;
|
||||
r->bits = bitset_create(mem, bits[0]); /* first element is the size */
|
||||
bit_copy(r->bits, bits);
|
||||
r->next = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _calc_states(struct matcher *m, struct rx_node *rx)
|
||||
{
|
||||
int iwidth = (m->num_nodes / BITS_PER_INT) + 1;
|
||||
struct ttree *tt = ttree_create(m->scratch, iwidth);
|
||||
struct state_queue *h, *t, *tmp;
|
||||
struct dfa_state *dfa, *ldfa;
|
||||
int i, a, set_bits = 0, count = 0;
|
||||
bitset_t bs = bitset_create(m->scratch, m->num_nodes), dfa_bits;
|
||||
|
||||
if (!tt) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!bs) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create first state */
|
||||
dfa = _create_dfa_state(m->mem);
|
||||
m->start = dfa;
|
||||
ttree_insert(tt, rx->firstpos + 1, dfa);
|
||||
|
||||
/* prime the queue */
|
||||
h = t = _create_state_queue(m->scratch, dfa, rx->firstpos);
|
||||
while (h) {
|
||||
/* pop state off front of the queue */
|
||||
dfa = h->s;
|
||||
dfa_bits = h->bits;
|
||||
h = h->next;
|
||||
|
||||
/* iterate through all the inputs for this state */
|
||||
bit_clear_all(bs);
|
||||
for(a = 0; a < 256; a++) {
|
||||
/* iterate through all the states in firstpos */
|
||||
for(i = bit_get_first(dfa_bits);
|
||||
i >=0;
|
||||
i = bit_get_next(dfa_bits, i)) {
|
||||
if(bit(m->nodes[i]->charset, a)) {
|
||||
if(a == TARGET_TRANS)
|
||||
dfa->final = m->nodes[i]->final;
|
||||
|
||||
bit_union(bs, bs, m->nodes[i]->followpos);
|
||||
set_bits = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(set_bits) {
|
||||
ldfa = ttree_lookup(tt, bs + 1);
|
||||
if(!ldfa) {
|
||||
/* push */
|
||||
ldfa = _create_dfa_state(m->mem);
|
||||
ttree_insert(tt, bs + 1, ldfa);
|
||||
tmp = _create_state_queue(m->scratch, ldfa, bs);
|
||||
if(!h)
|
||||
h = t = tmp;
|
||||
else {
|
||||
t->next = tmp;
|
||||
t = tmp;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
dfa->lookup[a] = ldfa;
|
||||
set_bits = 0;
|
||||
bit_clear_all(bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Matcher built with %d dfa states", count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct matcher *matcher_create(struct pool *mem,
|
||||
const char **patterns, int num)
|
||||
{
|
||||
char *all, *ptr;
|
||||
int i, len = 0;
|
||||
struct rx_node *rx;
|
||||
struct pool *scratch = pool_create(10 * 1024);
|
||||
struct matcher *m;
|
||||
|
||||
if (!scratch) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(m = pool_alloc(mem, sizeof(*m)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
|
||||
/* join the regexps together, delimiting with zero */
|
||||
for(i = 0; i < num; i++)
|
||||
len += strlen(patterns[i]) + 8;
|
||||
|
||||
ptr = all = pool_alloc(scratch, len + 1);
|
||||
|
||||
if (!all) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
for(i = 0; i < num; i++) {
|
||||
ptr += sprintf(ptr, "(.*(%s)%c)", patterns[i],
|
||||
TARGET_TRANS);
|
||||
if(i < (num - 1))
|
||||
*ptr++ = '|';
|
||||
}
|
||||
|
||||
/* parse this expression */
|
||||
if(!(rx = rx_parse_tok(scratch, all, ptr))) {
|
||||
log_error("Couldn't parse regex");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
m->mem = mem;
|
||||
m->scratch = scratch;
|
||||
m->num_nodes = _count_nodes(rx);
|
||||
m->nodes = pool_alloc(scratch, sizeof(*m->nodes) * m->num_nodes);
|
||||
|
||||
if (!m->nodes) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
_fill_table(m, rx);
|
||||
_create_bitsets(m);
|
||||
_calc_functions(m);
|
||||
_calc_states(m, rx);
|
||||
pool_destroy(scratch);
|
||||
m->scratch = NULL;
|
||||
|
||||
return m;
|
||||
|
||||
bad:
|
||||
pool_destroy(scratch);
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int matcher_run(struct matcher *m, const char *b)
|
||||
{
|
||||
struct dfa_state *cs = m->start;
|
||||
int r = 0;
|
||||
|
||||
for (; *b; b++) {
|
||||
|
||||
if (!(cs = cs->lookup[(int) (unsigned char) *b]))
|
||||
break;
|
||||
|
||||
if (cs->final && (cs->final > r))
|
||||
r = cs->final;
|
||||
}
|
||||
|
||||
/* subtract 1 to get back to zero index */
|
||||
return r - 1;
|
||||
}
|
||||
18
lib/regex/matcher.h
Normal file
18
lib/regex/matcher.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_MATCHER_H
|
||||
#define _LVM_MATCHER_H
|
||||
|
||||
#include "pool.h"
|
||||
|
||||
struct matcher;
|
||||
struct matcher *matcher_create(struct pool *mem,
|
||||
const char **patterns, int num);
|
||||
|
||||
int matcher_run(struct matcher *m, const char *begin);
|
||||
|
||||
#endif
|
||||
333
lib/regex/parse_rx.c
Normal file
333
lib/regex/parse_rx.c
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "parse_rx.h"
|
||||
#include "bitset.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
struct parse_sp { /* scratch pad for the parsing process */
|
||||
struct pool *mem;
|
||||
int type; /* token type, 0 indicates a charset */
|
||||
bitset_t charset; /* The current charset */
|
||||
const char *cursor; /* where we are in the regex */
|
||||
const char *rx_end; /* 1pte for the expression being parsed */
|
||||
};
|
||||
|
||||
|
||||
static struct rx_node *_or_term(struct parse_sp *ps);
|
||||
|
||||
|
||||
/*
|
||||
* Get the next token from the regular expression.
|
||||
* Returns: 1 success, 0 end of input, -1 error.
|
||||
*/
|
||||
static int _get_token(struct parse_sp *ps)
|
||||
{
|
||||
int neg = 0, range = 0;
|
||||
char c, lc = 0;
|
||||
const char *ptr = ps->cursor;
|
||||
if(ptr == ps->rx_end) { /* end of input ? */
|
||||
ps->type = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(*ptr) {
|
||||
/* charsets and ncharsets */
|
||||
case '[':
|
||||
ptr++;
|
||||
if(*ptr == '^') {
|
||||
bit_set_all(ps->charset);
|
||||
|
||||
/* never transition on zero */
|
||||
bit_clear(ps->charset, 0);
|
||||
neg = 1;
|
||||
ptr++;
|
||||
|
||||
} else
|
||||
bit_clear_all(ps->charset);
|
||||
|
||||
while((ptr < ps->rx_end) && (*ptr != ']')) {
|
||||
if(*ptr == '\\') {
|
||||
/* an escaped character */
|
||||
ptr++;
|
||||
switch(*ptr) {
|
||||
case 'n': c = '\n'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 't': c = '\t'; break;
|
||||
default:
|
||||
c = *ptr;
|
||||
}
|
||||
} else if(*ptr == '-' && lc) {
|
||||
/* we've got a range on our hands */
|
||||
range = 1;
|
||||
ptr++;
|
||||
if(ptr == ps->rx_end) {
|
||||
log_error("Incomplete range"
|
||||
"specification");
|
||||
return -1;
|
||||
}
|
||||
c = *ptr;
|
||||
} else
|
||||
c = *ptr;
|
||||
|
||||
if(range) {
|
||||
/* add lc - c into the bitset */
|
||||
if(lc > c) {
|
||||
char tmp = c;
|
||||
c = lc;
|
||||
lc = tmp;
|
||||
}
|
||||
|
||||
for(; lc <= c; lc++) {
|
||||
if(neg)
|
||||
bit_clear(ps->charset, lc);
|
||||
else
|
||||
bit_set(ps->charset, lc);
|
||||
}
|
||||
range = 0;
|
||||
} else {
|
||||
/* add c into the bitset */
|
||||
if(neg)
|
||||
bit_clear(ps->charset, c);
|
||||
else
|
||||
bit_set(ps->charset, c);
|
||||
}
|
||||
ptr++;
|
||||
lc = c;
|
||||
}
|
||||
|
||||
if(ptr >= ps->rx_end) {
|
||||
ps->type = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ps->type = 0;
|
||||
ps->cursor = ptr + 1;
|
||||
break;
|
||||
|
||||
/* These characters are special, we just return their ASCII
|
||||
codes as the type. Sorted into ascending order to help the
|
||||
compiler */
|
||||
case '(':
|
||||
case ')':
|
||||
case '*':
|
||||
case '+':
|
||||
case '?':
|
||||
case '|':
|
||||
case '^':
|
||||
case '$':
|
||||
ps->type = (int) *ptr;
|
||||
ps->cursor = ptr + 1;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
/* The 'all but newline' character set */
|
||||
ps->type = 0;
|
||||
ps->cursor = ptr + 1;
|
||||
bit_set_all(ps->charset);
|
||||
bit_clear(ps->charset, (int) '\n');
|
||||
bit_clear(ps->charset, (int) '\r');
|
||||
bit_clear(ps->charset, 0);
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
/* escaped character */
|
||||
ptr++;
|
||||
if(ptr >= ps->rx_end) {
|
||||
log_error("Badly quoted character at end "
|
||||
"of expression");
|
||||
ps->type = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ps->type = 0;
|
||||
ps->cursor = ptr + 1;
|
||||
bit_clear_all(ps->charset);
|
||||
switch(*ptr) {
|
||||
case 'n': bit_set(ps->charset, (int) '\n'); break;
|
||||
case 'r': bit_set(ps->charset, (int) '\r'); break;
|
||||
case 't': bit_set(ps->charset, (int) '\t'); break;
|
||||
default:
|
||||
bit_set(ps->charset, (int) *ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* add a single character to the bitset */
|
||||
ps->type = 0;
|
||||
ps->cursor = ptr + 1;
|
||||
bit_clear_all(ps->charset);
|
||||
bit_set(ps->charset, (int) *ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct rx_node *_node(struct pool *mem, int type,
|
||||
struct rx_node *l, struct rx_node *r)
|
||||
{
|
||||
struct rx_node *n = pool_zalloc(mem, sizeof(*n));
|
||||
|
||||
if (n) {
|
||||
if (!(n->charset = bitset_create(mem, 256))) {
|
||||
pool_free(mem, n);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n->type = type;
|
||||
n->left = l;
|
||||
n->right = r;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rx_node *_term(struct parse_sp *ps)
|
||||
{
|
||||
struct rx_node *n;
|
||||
|
||||
switch(ps->type) {
|
||||
case 0:
|
||||
if (!(n = _node(ps->mem, CHARSET, NULL, NULL))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bit_copy(n->charset, ps->charset);
|
||||
_get_token(ps); /* match charset */
|
||||
break;
|
||||
|
||||
case '(':
|
||||
_get_token(ps); /* match '(' */
|
||||
n = _or_term(ps);
|
||||
if(ps->type != ')') {
|
||||
log_error("missing ')' in regular expression");
|
||||
return 0;
|
||||
}
|
||||
_get_token(ps); /* match ')' */
|
||||
break;
|
||||
|
||||
default:
|
||||
n = 0;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rx_node *_closure_term(struct parse_sp *ps)
|
||||
{
|
||||
struct rx_node *l, *n;
|
||||
|
||||
if(!(l = _term(ps)))
|
||||
return NULL;
|
||||
|
||||
for (;;) {
|
||||
switch(ps->type) {
|
||||
case '*':
|
||||
n = _node(ps->mem, STAR, l, NULL);
|
||||
break;
|
||||
|
||||
case '+':
|
||||
n = _node(ps->mem, PLUS, l, NULL);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
n = _node(ps->mem, QUEST, l, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
return l;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_get_token(ps);
|
||||
l = n;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rx_node *_cat_term(struct parse_sp *ps)
|
||||
{
|
||||
struct rx_node *l, *r, *n;
|
||||
|
||||
if (!(l = _closure_term(ps)))
|
||||
return NULL;
|
||||
|
||||
if (ps->type == '|')
|
||||
return l;
|
||||
|
||||
if (!(r = _cat_term(ps)))
|
||||
return l;
|
||||
|
||||
if (!(n = _node(ps->mem, CAT, l, r)))
|
||||
stack;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rx_node *_or_term(struct parse_sp *ps)
|
||||
{
|
||||
struct rx_node *l, *r, *n;
|
||||
|
||||
if (!(l = _cat_term(ps)))
|
||||
return NULL;
|
||||
|
||||
if (ps->type != '|')
|
||||
return l;
|
||||
|
||||
_get_token(ps); /* match '|' */
|
||||
|
||||
if (!(r = _or_term(ps))) {
|
||||
log_error("Badly formed 'or' expression");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(n = _node(ps->mem, OR, l, r)))
|
||||
stack;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
struct rx_node *rx_parse_tok(struct pool *mem,
|
||||
const char *begin, const char *end)
|
||||
{
|
||||
struct rx_node *r;
|
||||
struct parse_sp *ps = pool_zalloc(mem, sizeof(*ps));
|
||||
|
||||
if (!ps) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ps->mem = mem;
|
||||
ps->charset = bitset_create(mem, 256);
|
||||
ps->cursor = begin;
|
||||
ps->rx_end = end;
|
||||
_get_token(ps); /* load the first token */
|
||||
|
||||
if (!(r = _or_term(ps))) {
|
||||
log_error("Parse error in regex");
|
||||
pool_free(mem, ps);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
struct rx_node *rx_parse_str(struct pool *mem, const char *str)
|
||||
{
|
||||
return rx_parse_tok(mem, str, str + strlen(str));
|
||||
}
|
||||
39
lib/regex/parse_rx.h
Normal file
39
lib/regex/parse_rx.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_PARSE_REGEX_H
|
||||
#define _LVM_PARSE_REGEX_H
|
||||
|
||||
#include "bitset.h"
|
||||
|
||||
enum {
|
||||
CAT,
|
||||
STAR,
|
||||
PLUS,
|
||||
OR,
|
||||
QUEST,
|
||||
CHARSET,
|
||||
HAT,
|
||||
DOLLAR
|
||||
};
|
||||
|
||||
struct rx_node {
|
||||
int type;
|
||||
bitset_t charset;
|
||||
struct rx_node *left, *right;
|
||||
|
||||
/* used to build the dfa for the toker */
|
||||
int nullable, final;
|
||||
bitset_t firstpos;
|
||||
bitset_t lastpos;
|
||||
bitset_t followpos;
|
||||
};
|
||||
|
||||
struct rx_node *rx_parse_str(struct pool *mem, const char *str);
|
||||
struct rx_node *rx_parse_tok(struct pool *mem,
|
||||
const char *begin, const char *end);
|
||||
|
||||
#endif
|
||||
110
lib/regex/ttree.c
Normal file
110
lib/regex/ttree.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the LGPL.
|
||||
*/
|
||||
|
||||
#include "ttree.h"
|
||||
#include "pool.h"
|
||||
#include "log.h"
|
||||
|
||||
struct node {
|
||||
unsigned k;
|
||||
struct node *l, *m, *r;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct ttree {
|
||||
int klen;
|
||||
struct pool *mem;
|
||||
struct node *root;
|
||||
};
|
||||
|
||||
struct node **_lookup_single(struct node **c, unsigned int k)
|
||||
{
|
||||
while (*c) {
|
||||
if (k < (*c)->k)
|
||||
c = &((*c)->l);
|
||||
|
||||
else if (k > (*c)->k)
|
||||
c = &((*c)->r);
|
||||
|
||||
else {
|
||||
c = &((*c)->m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void *ttree_lookup(struct ttree *tt, unsigned *key)
|
||||
{
|
||||
struct node **c = &tt->root;
|
||||
int count = tt->klen;
|
||||
|
||||
while (*c && count) {
|
||||
c = _lookup_single(c, *key++);
|
||||
count--;
|
||||
}
|
||||
|
||||
return *c ? (*c)->data : NULL;
|
||||
}
|
||||
|
||||
static struct node *_node(struct pool *mem, unsigned int k)
|
||||
{
|
||||
struct node *n = pool_zalloc(mem, sizeof(*n));
|
||||
|
||||
if (n)
|
||||
n->k = k;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int ttree_insert(struct ttree *tt, unsigned int *key, void *data)
|
||||
{
|
||||
struct node **c = &tt->root;
|
||||
int count = tt->klen;
|
||||
unsigned int k;
|
||||
|
||||
do {
|
||||
k = *key++;
|
||||
c = _lookup_single(c, k);
|
||||
count--;
|
||||
|
||||
} while (*c && count);
|
||||
|
||||
|
||||
if (!*c) {
|
||||
count++;
|
||||
|
||||
while (count--) {
|
||||
if (!(*c = _node(tt->mem, k))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
k = *key++;
|
||||
|
||||
if (count)
|
||||
c = &((*c)->m);
|
||||
}
|
||||
}
|
||||
(*c)->data = data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ttree *ttree_create(struct pool *mem, unsigned int klen)
|
||||
{
|
||||
struct ttree *tt;
|
||||
|
||||
if (!(tt = pool_zalloc(mem, sizeof(*tt)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tt->klen = klen;
|
||||
tt->mem = mem;
|
||||
return tt;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user