mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-30 20:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			807 Commits
		
	
	
		
			dev-dct-fi
			...
			beta2_1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | fff780035d | ||
|  | 46127e673d | ||
|  | 70df59b224 | ||
|  | 0fdbaa803f | ||
|  | 6af1830eff | ||
|  | 6f860e2bd5 | ||
|  | 20a492f7ee | ||
|  | 63875e7591 | ||
|  | 0ad98cabde | ||
|  | 668879d2e1 | ||
|  | 2e09783302 | ||
|  | 49734114b3 | ||
|  | 950d9d6ee7 | ||
|  | f7974aee2e | ||
|  | c870a82621 | ||
|  | 984929a001 | ||
|  | 7f9e2c1db8 | ||
|  | 324e8389dc | ||
|  | 6f448c5a38 | ||
|  | ec43efbb20 | ||
|  | 3ed065de37 | ||
|  | 8152f0d72c | ||
|  | f8047f4736 | ||
|  | b93c66dc2d | ||
|  | ac877b3065 | ||
|  | dee8abfdde | ||
|  | cef3841d73 | ||
|  | 3cd5e8a041 | ||
|  | 515b5f866e | ||
|  | 321f62bf92 | ||
|  | f94fa47b52 | ||
|  | c502f8a722 | ||
|  | 59b4868ac3 | ||
|  | 3634e12cce | ||
|  | 6d45445391 | ||
|  | 4f47e268cc | ||
|  | 0035b31cdb | ||
|  | f2565aee03 | ||
|  | 5bd85668dd | ||
|  | 20f990b6ce | ||
|  | 6821379586 | ||
|  | 73b040eb49 | ||
|  | 49aa4b2e1e | ||
|  | 972241c74c | ||
|  | 680750e3c2 | ||
|  | 5e7d4d9d15 | ||
|  | 1e57e60613 | ||
|  | 3ac7ce605a | ||
|  | b720dea9f0 | ||
|  | c80722aefe | ||
|  | a84fa69f28 | ||
|  | e33781e59f | ||
|  | 8824bc7ece | ||
|  | c2e3b0e448 | ||
|  | f61a38e85a | ||
|  | d23e948216 | ||
|  | 58bdaa31f0 | ||
|  | b6491d88a6 | ||
|  | f6061ba62e | ||
|  | 427899ddce | ||
|  | c4ab7d2dbd | ||
|  | 0fd2ba033f | ||
|  | ed38939a93 | ||
|  | c7ee26ce5a | ||
|  | 909b8cb303 | ||
|  | b0277370cf | ||
|  | 2ec8656bea | ||
|  | b2ef256910 | ||
|  | 63d6ce95db | ||
|  | a9532b189c | ||
|  | 844545411f | ||
|  | 4e23a2b9b8 | ||
|  | 5deba027eb | ||
|  | fc8b7efc6f | ||
|  | a1c2d9c0f3 | ||
|  | 4ca49a0501 | ||
|  | 493c53d090 | ||
|  | b27e956d35 | ||
|  | 35ebed75c6 | ||
|  | 7bfdb5f77f | ||
|  | 8d8c02317f | ||
|  | a34482feab | ||
|  | cbdc8fd4a6 | ||
|  | 8d3afaa53c | ||
|  | 7ced9ef3df | ||
|  | e8a9ae7e80 | ||
|  | 73a88ab3d3 | ||
|  | aaed82738a | ||
|  | de7f7b96db | ||
|  | 1a669b3e68 | ||
|  | 333af9b13a | ||
|  | a5bca5e240 | ||
|  | c885633e02 | ||
|  | ca7e20b7ca | ||
|  | 545e11a3d7 | ||
|  | 44f5287664 | ||
|  | cf510897f1 | ||
|  | 1d171345f8 | ||
|  | 4fa7e1cd49 | ||
|  | acd008298e | ||
|  | 83a8021515 | ||
|  | cf88dfb1db | ||
|  | 8937c4b481 | ||
|  | cc6af10a4d | ||
|  | 6d94578955 | ||
|  | 08442ab71e | ||
|  | 10d91d213f | ||
|  | b7a3b06994 | ||
|  | 5f12c37f23 | ||
|  | 585edebccd | ||
|  | 9921c62234 | ||
|  | 1ac76d2e16 | ||
|  | 6e983bf400 | ||
|  | 6a8fd4fa6e | ||
|  | 3698eaa2d2 | ||
|  | 8d97ca433c | ||
|  | 23cc65e537 | ||
|  | 2f5a3c2bbe | ||
|  | f6485616cd | ||
|  | 6544fb43d9 | ||
|  | a954b32dcc | ||
|  | 426dc7836c | ||
|  | ff941ffc16 | ||
|  | a063c201df | ||
|  | e2be3fa0aa | ||
|  | 609da6fb50 | ||
|  | fc9f3ccec3 | ||
|  | f7baa67a0a | ||
|  | e8d78c2cdb | ||
|  | 9d33366092 | ||
|  | f5d61515c2 | ||
|  | 711a04a972 | ||
|  | e5b470a3f1 | ||
|  | 31820e1e22 | ||
|  | 4849e8cd6d | ||
|  | 77e3b460aa | ||
|  | b5321001f8 | ||
|  | 38eba9f5ea | ||
|  | ea6f399454 | ||
|  | f8bf2d7b7d | ||
|  | c4856caebb | ||
|  | fc1030bb22 | ||
|  | 9eb6cad8dc | ||
|  | 0fa2a78dce | ||
|  | a56fa1558b | ||
|  | 261c73a997 | ||
|  | 929c1333ca | ||
|  | 286a79d94d | ||
|  | 8b951f99da | ||
|  | abb449bca0 | ||
|  | bd084028d1 | ||
|  | 138a27570b | ||
|  | c2ed40a74f | ||
|  | a7e7a00cab | ||
|  | 64a31ab3cd | ||
|  | 71958bc0f1 | ||
|  | 366ec35612 | ||
|  | 3738f6e8ae | ||
|  | 051a8e2af1 | ||
|  | 2f43f28d5e | ||
|  | b64769754b | ||
|  | a97daa18d1 | ||
|  | 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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| before 2.0 | ||||
| ----------- | ||||
|  | ||||
| vgexport | ||||
| vgimport | ||||
| snapshots | ||||
| pvmove | ||||
| device-mapper support for 2.5 kernel series | ||||
| review FIXMEs | ||||
| extra validation & full consistency checks in format1 with LVM1 | ||||
| partial activation (aka VG quorum) | ||||
| error message review | ||||
| locking during metadata changes | ||||
| format2 with atomic transactions | ||||
| bidirectional format1/format2 migration tool | ||||
| persistent minors | ||||
| statistics target and tool support | ||||
| review tool exit codes for LVM1 compatibility | ||||
|  | ||||
| before 2.1 | ||||
| ---------- | ||||
|  | ||||
| e2fsadm | ||||
| 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                                                   \ | ||||
| ) | ||||
							
								
								
									
										29
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| lvm2 (0.95.05-1) unstable; urgency=low | ||||
|  | ||||
|   * New release (Beta2). | ||||
|  | ||||
|  -- Andres Salomon <dilinger@mp3revolution.net>  Thu, 25 Apr 2002 00:37:41 -0500 | ||||
|  | ||||
| lvm2 (0.95.04cvs20020306-1) unstable; urgency=low | ||||
|  | ||||
|   * CVS updated. | ||||
|   * Convert from debian native package. | ||||
|  | ||||
|  -- Andres Salomon <dilinger@mp3revolution.net>  Wed,  6 Mar 2002 00:43:21 -0500 | ||||
|  | ||||
| lvm2 (0.95.04cvs20020304) unstable; urgency=low | ||||
|  | ||||
|   * CVS updated. | ||||
|   * Enhance init script; create devmapper control device, etc. | ||||
|   * Add dmsetup as a suggestion. | ||||
|   * Add /etc/lvm/lvm.conf conffile. | ||||
|   * Add undocumented(7) for the commands missing manpages. | ||||
|    | ||||
|  -- Andres Salomon <dilinger@mp3revolution.net>  Mon,  4 Mar 2002 04:51:26 -0500 | ||||
|  | ||||
| lvm2 (0.95.02cvs20020220) unstable; urgency=low | ||||
|  | ||||
|   * Initial Release. | ||||
|  | ||||
|  -- Andres Salomon <dilinger@mp3revolution.net>  Wed, 20 Feb 2002 03:17:25 -0500 | ||||
|  | ||||
							
								
								
									
										2
									
								
								debian/conffiles
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								debian/conffiles
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| /etc/lvm/lvm.conf | ||||
| /etc/init.d/lvm2 | ||||
							
								
								
									
										19
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| Source: lvm2 | ||||
| Section: admin | ||||
| Priority: optional | ||||
| Maintainer: Andres Salomon <dilinger@mp3revolution.net> | ||||
| Build-Depends: debhelper (>> 3.0.0), libdevmapper-dev, libreadline4-dev | ||||
| Standards-Version: 3.5.2 | ||||
|  | ||||
| Package: lvm2 | ||||
| Architecture: any | ||||
| Depends: libdevmapper0 | ||||
| Replaces: lvm10, lvm-common | ||||
| Provides: lvm-binaries | ||||
| Suggests: dmsetup | ||||
| Description: The Linux Logical Volume Manager | ||||
|  This is LVM2, the rewrite of The Linux Logical Volume Manager.  LVM | ||||
|  supports enterprise level volume management of disk and disk subsystems | ||||
|  by grouping arbitrary disks into volume groups. The total capacity of | ||||
|  volume groups can be allocated to logical volumes, which are accessed as | ||||
|  regular block devices. | ||||
							
								
								
									
										25
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| This package was debianized by Andres Salomon <dilinger@mp3revolution.net> on | ||||
| Wed, 20 Feb 2002 03:17:25 -0500. | ||||
|  | ||||
| It was downloaded from http://www.sistina.com/products_lvm.htm | ||||
|  | ||||
| Upstream Author(s): LVM Development Team | ||||
|  | ||||
| Copyright (c) 2001-2002 LVM Development Team | ||||
|  | ||||
| LVM2 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. | ||||
|  | ||||
| LVM2 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 | ||||
|  | ||||
| On Debian systems, the full text of the GPL can be found in | ||||
| /usr/share/common-licenses/GPL | ||||
							
								
								
									
										4
									
								
								debian/dirs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								debian/dirs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| etc/lvm | ||||
| usr/share/man/man5 | ||||
| usr/share/man/man8 | ||||
| sbin | ||||
							
								
								
									
										8
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| BUGS | ||||
| INTRO | ||||
| README | ||||
| TODO | ||||
| VERSION | ||||
| doc/example.conf | ||||
| doc/pvmove_outline.txt | ||||
| doc/testing.txt | ||||
							
								
								
									
										65
									
								
								debian/init.d
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								debian/init.d
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| #! /bin/sh | ||||
| # | ||||
| # lvm2		This script handles LVM2 initialization/shutdown. | ||||
| # | ||||
| #		Written by Andres Salomon <dilinger@mp3revolution.net>. | ||||
| # | ||||
|  | ||||
| PATH=/sbin:/bin:/usr/sbin:/usr/bin | ||||
| NAME=lvm2 | ||||
| DESC=LVM | ||||
|  | ||||
| test -x /sbin/vgchange || exit 0 | ||||
| modprobe dm-mod >/dev/null 2>&1 | ||||
|  | ||||
| # Create necessary files in /dev for device-mapper | ||||
| create_devfiles() { | ||||
| 	DIR="/dev/device-mapper" | ||||
| 	FILE="$DIR/control" | ||||
| 	major=$(awk '$2 ~ /^misc$/ {print $1}' /proc/devices) | ||||
| 	minor=$(awk "\$2 ~ /^$DM_NAME\$/ {print \$1}" /proc/misc) | ||||
|  | ||||
| 	if test ! -d $DIR; then | ||||
| 		mkdir --mode=755 $DIR >/dev/null 2>&1 | ||||
| 	fi | ||||
|  | ||||
| 	if test ! -c $FILE -a ! -z "$minor"; then | ||||
| 		mknod --mode=600 $FILE c $major $minor >/dev/null 2>&1 | ||||
| 	fi | ||||
| } | ||||
|  | ||||
| case "$1" in | ||||
|   start) | ||||
| 	echo -n "Initializing $DESC: " | ||||
| 	create_devfiles | ||||
| 	vgchange -a y | ||||
|  | ||||
| #	# Mount all LVM devices | ||||
| #	for vg in $( vgchange -a y 2>/dev/null | grep active | awk -F\" '{print $2}' ); do | ||||
| #		MTPT=$( grep $vg /etc/fstab | awk '{print $2}' ) | ||||
| #		mount $MTPT | ||||
| #	done | ||||
| 	echo "$NAME." | ||||
| 	;; | ||||
|   stop) | ||||
| 	echo -n "Shutting down $DESC: " | ||||
| 	# We don't really try all that hard to shut it down; far too many | ||||
| 	# things that can keep it from successfully shutting down. | ||||
| 	vgchange -a n | ||||
| 	echo "$NAME." | ||||
| 	;; | ||||
|   restart|force-reload) | ||||
| 	echo -n "Restarting $DESC: " | ||||
| 	vgchange -a n | ||||
| 	sleep 1 | ||||
| 	vgchange -a y | ||||
| 	echo "$NAME." | ||||
| 	;; | ||||
|   *) | ||||
| 	N=/etc/init.d/$NAME | ||||
| 	echo "Usage: $N {start|stop|restart|force-reload}" >&2 | ||||
| 	exit 1 | ||||
| 	;; | ||||
| esac | ||||
|  | ||||
| exit 0 | ||||
							
								
								
									
										26
									
								
								debian/manpages
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								debian/manpages
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| debian/lvm2/usr/share/man/man5/lvm.conf.5 | ||||
| debian/lvm2/usr/share/man/man8/lvchange.8 | ||||
| debian/lvm2/usr/share/man/man8/lvcreate.8 | ||||
| debian/lvm2/usr/share/man/man8/lvdisplay.8 | ||||
| debian/lvm2/usr/share/man/man8/lvextend.8 | ||||
| debian/lvm2/usr/share/man/man8/lvm.8 | ||||
| debian/lvm2/usr/share/man/man8/lvmchange.8 | ||||
| debian/lvm2/usr/share/man/man8/lvreduce.8 | ||||
| debian/lvm2/usr/share/man/man8/lvremove.8 | ||||
| debian/lvm2/usr/share/man/man8/lvrename.8 | ||||
| debian/lvm2/usr/share/man/man8/lvscan.8 | ||||
| debian/lvm2/usr/share/man/man8/pvchange.8 | ||||
| debian/lvm2/usr/share/man/man8/pvcreate.8 | ||||
| debian/lvm2/usr/share/man/man8/pvdisplay.8 | ||||
| debian/lvm2/usr/share/man/man8/pvscan.8 | ||||
| debian/lvm2/usr/share/man/man8/vgcfgbackup.8 | ||||
| debian/lvm2/usr/share/man/man8/vgchange.8 | ||||
| debian/lvm2/usr/share/man/man8/vgck.8 | ||||
| debian/lvm2/usr/share/man/man8/vgcreate.8 | ||||
| debian/lvm2/usr/share/man/man8/vgdisplay.8 | ||||
| debian/lvm2/usr/share/man/man8/vgextend.8 | ||||
| debian/lvm2/usr/share/man/man8/vgmerge.8 | ||||
| debian/lvm2/usr/share/man/man8/vgreduce.8 | ||||
| debian/lvm2/usr/share/man/man8/vgremove.8 | ||||
| debian/lvm2/usr/share/man/man8/vgrename.8 | ||||
| debian/lvm2/usr/share/man/man8/vgscan.8 | ||||
							
								
								
									
										49
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										49
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| #! /bin/sh | ||||
| # postinst script for lvm2 | ||||
| # | ||||
| # see: dh_installdeb(1) | ||||
|  | ||||
| set -e | ||||
|  | ||||
| # summary of how this script can be called: | ||||
| #        * <postinst> `configure' <most-recently-configured-version> | ||||
| #        * <old-postinst> `abort-upgrade' <new version> | ||||
| #        * <conflictor's-postinst> `abort-remove' `in-favour' <package> | ||||
| #          <new-version> | ||||
| #        * <deconfigured's-postinst> `abort-deconfigure' `in-favour' | ||||
| #          <failed-install-package> <version> `removing' | ||||
| #          <conflicting-package> <version> | ||||
| # for details, see http://www.debian.org/doc/debian-policy/ or | ||||
| # the debian-policy package | ||||
| # | ||||
| # quoting from the policy: | ||||
| #     Any necessary prompting should almost always be confined to the | ||||
| #     post-installation script, and should be protected with a conditional | ||||
| #     so that unnecessary prompting doesn't happen if a package's | ||||
| #     installation fails and the `postinst' is called with `abort-upgrade', | ||||
| #     `abort-remove' or `abort-deconfigure'. | ||||
|  | ||||
| case "$1" in | ||||
|     configure) | ||||
|     	update-rc.d lvm2 start 25 S . start 50 0 6 . >/dev/null | ||||
| 	/etc/init.d/lvm2 start | ||||
|     ;; | ||||
|  | ||||
|     abort-upgrade|abort-remove|abort-deconfigure) | ||||
|  | ||||
|     ;; | ||||
|  | ||||
|     *) | ||||
|         echo "postinst called with unknown argument \`$1'" >&2 | ||||
|         exit 1 | ||||
|     ;; | ||||
| esac | ||||
|  | ||||
| # dh_installdeb will replace this with shell code automatically | ||||
| # generated by other debhelper scripts. | ||||
|  | ||||
| #DEBHELPER# | ||||
|  | ||||
| exit 0 | ||||
|  | ||||
|  | ||||
							
								
								
									
										39
									
								
								debian/postrm
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										39
									
								
								debian/postrm
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #! /bin/sh | ||||
| # postrm script for lvm2 | ||||
| # | ||||
| # see: dh_installdeb(1) | ||||
|  | ||||
| set -e | ||||
|  | ||||
| # summary of how this script can be called: | ||||
| #        * <postrm> `remove' | ||||
| #        * <postrm> `purge' | ||||
| #        * <old-postrm> `upgrade' <new-version> | ||||
| #        * <new-postrm> `failed-upgrade' <old-version> | ||||
| #        * <new-postrm> `abort-install' | ||||
| #        * <new-postrm> `abort-install' <old-version> | ||||
| #        * <new-postrm> `abort-upgrade' <old-version> | ||||
| #        * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version> | ||||
| # for details, see http://www.debian.org/doc/debian-policy/ or | ||||
| # the debian-policy package | ||||
|  | ||||
|  | ||||
| case "$1" in | ||||
| 	purge) | ||||
| 		rm -f /etc/init.d/lvm2 | ||||
| 		update-rc.d lvm2 remove >/dev/null | ||||
| 	;; | ||||
| 	remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) | ||||
| 	;; | ||||
|  | ||||
| 	*) | ||||
| 		echo "postrm called with unknown argument \`$1'" >&2 | ||||
| 		exit 1 | ||||
| esac | ||||
|  | ||||
| # dh_installdeb will replace this with shell code automatically | ||||
| # generated by other debhelper scripts. | ||||
|  | ||||
| #DEBHELPER# | ||||
|  | ||||
| exit 0 | ||||
							
								
								
									
										120
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										120
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| #!/usr/bin/make -f | ||||
| # Sample debian/rules that uses debhelper.  | ||||
| # GNU copyright 1997 by Joey Hess. | ||||
| # | ||||
| # This version is for a hypothetical package that builds an | ||||
| # architecture-dependant package, as well as an architecture-independent | ||||
| # package. | ||||
|  | ||||
| # Uncomment this to turn on verbose mode.  | ||||
| #export DH_VERBOSE=1 | ||||
|  | ||||
| # This is the debhelper compatibility version to use. | ||||
| export DH_COMPAT=3 | ||||
|  | ||||
| # These are used for cross-compiling and for saving the configure script | ||||
| # from having to guess our platform (since we know it already) | ||||
| DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) | ||||
| DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) | ||||
|  | ||||
| ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) | ||||
| 	CFLAGS += -g | ||||
| endif | ||||
| ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) | ||||
| 	INSTALL_PROGRAM += -s | ||||
| endif | ||||
|  | ||||
| configure: configure-stamp | ||||
| configure-stamp: | ||||
| 	dh_testdir | ||||
| 	# Add here commands to configure the package. | ||||
| 	./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/ --mandir=\$${prefix}/usr/share/man --infodir=\$${prefix}/usr/share/info | ||||
|  | ||||
| 	touch configure-stamp | ||||
|  | ||||
| build-arch: configure-stamp build-arch-stamp | ||||
| build-arch-stamp: | ||||
| 	dh_testdir | ||||
|  | ||||
| 	# Add here command to compile/build the package. | ||||
| 	$(MAKE) | ||||
|  | ||||
| 	touch build-arch-stamp | ||||
|  | ||||
| build-indep: configure-stamp build-indep-stamp | ||||
| build-indep-stamp: | ||||
| 	dh_testdir | ||||
|  | ||||
| 	# Add here command to compile/build the arch indep package. | ||||
| 	# It's ok not to do anything here, if you don't need to build | ||||
| 	#  anything for this package. | ||||
| 	#/usr/bin/docbook-to-man debian/lvm2.sgml > lvm2.1 | ||||
|  | ||||
| 	touch build-indep-stamp | ||||
|  | ||||
| build: build-arch build-indep | ||||
|  | ||||
| clean: | ||||
| 	dh_testdir | ||||
| 	dh_testroot | ||||
| 	rm -f build-stamp configure-stamp | ||||
|  | ||||
| 	# Add here commands to clean up after the build process. | ||||
| 	-$(MAKE) distclean | ||||
| 	-test -r /usr/share/misc/config.sub && \ | ||||
| 	  cp -f /usr/share/misc/config.sub config.sub | ||||
| 	-test -r /usr/share/misc/config.guess && \ | ||||
| 	  cp -f /usr/share/misc/config.guess config.guess | ||||
|  | ||||
|  | ||||
| 	dh_clean | ||||
|  | ||||
| install: DH_OPTIONS= | ||||
| install: build | ||||
| 	dh_testdir | ||||
| 	dh_testroot | ||||
| 	dh_clean -k | ||||
| 	dh_installdirs | ||||
|  | ||||
| 	# Add here commands to install the package into debian/lvm2. | ||||
| 	$(MAKE) install prefix=$(CURDIR)/debian/lvm2 | ||||
| 	install -m 0644 doc/example.conf debian/lvm2/etc/lvm/lvm.conf | ||||
|  | ||||
|  | ||||
| # Build architecture-independent files here. | ||||
| # Pass -i to all debhelper commands in this target to reduce clutter. | ||||
| binary-indep: build install | ||||
| # nada. | ||||
|  | ||||
| # Build architecture-dependent files here. | ||||
| binary-arch: build install | ||||
| 	dh_testdir | ||||
| 	dh_testroot | ||||
|  | ||||
| #	dh_installdebconf | ||||
| 	dh_installdocs | ||||
| 	dh_installexamples | ||||
| #	dh_installlogrotate -a | ||||
| #	dh_installemacsen -a | ||||
| #	dh_installpam -a | ||||
| #	dh_installmime -a | ||||
| 	dh_installinit -n | ||||
| 	dh_installcron | ||||
| 	dh_installman | ||||
| 	dh_installinfo  | ||||
| 	dh_undocumented | ||||
| 	dh_installchangelogs | ||||
| 	dh_strip | ||||
| 	dh_link | ||||
| 	dh_compress | ||||
| 	dh_fixperms | ||||
| 	dh_makeshlibs | ||||
| 	dh_installdeb | ||||
| #	dh_perl -a | ||||
| 	dh_shlibdeps | ||||
| 	dh_gencontrol | ||||
| 	dh_md5sums | ||||
| 	dh_builddeb | ||||
|  | ||||
| binary: binary-indep binary-arch | ||||
| .PHONY: build clean binary-indep binary-arch binary install configure | ||||
							
								
								
									
										14
									
								
								debian/undocumented
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								debian/undocumented
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| e2fsadm.8 | ||||
| lvmdiskscan.8 | ||||
| lvmsadc.8 | ||||
| lvmsar.8 | ||||
| lvresize.8 | ||||
| pvdata.8 | ||||
| pvmove.8 | ||||
| pvresize.8 | ||||
| version.8 | ||||
| vgcfgrestore.8 | ||||
| vgexport.8 | ||||
| vgimport.8 | ||||
| vgmknodes.8 | ||||
| vgsplit.8 | ||||
| @@ -1 +0,0 @@ | ||||
| Wow!  This is really incredible documentation! | ||||
							
								
								
									
										152
									
								
								doc/example.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								doc/example.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| # 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. | ||||
|  | ||||
|     # Remember to run vgscan after you change this parameter. | ||||
|  | ||||
|     # 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 | ||||
| } | ||||
|  | ||||
| # Metadata settings | ||||
| metadata { | ||||
|     # List of directories holding copies of text format metadata | ||||
|     dirs = [ "/etc/lvm/metadata" ] | ||||
| } | ||||
|  | ||||
| # 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 | ||||
|  | ||||
|     # Default metadata format commands use - "lvm1" (default) or "text" | ||||
|     format = "lvm1" | ||||
|  | ||||
|     # Location of proc filesystem | ||||
|     proc = "/proc" | ||||
| } | ||||
|  | ||||
							
								
								
									
										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 | ||||
| +----------+---------+--------+ | ||||
							
								
								
									
										46
									
								
								doc/testing.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								doc/testing.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| Here's how I test new LVM2 builds without interfering with the stable | ||||
| LVM2 that is running the LV's on my development box. | ||||
|  | ||||
| 1) Create a set of loopback devices. | ||||
|  | ||||
| 2) Create a new directory to contain the LVM2 configuration files for | ||||
|    this setup.  (I use /etc/lvm_loops) | ||||
|  | ||||
| 3) Write a suitable lvm.conf file, this goes in the directory you just | ||||
|    created.  eg, my /etc/lvm_loops/lvm.conf looks like: | ||||
|  | ||||
|    log { | ||||
|         file="/tmp/lvm2_loop.log" | ||||
|         level=9 | ||||
|         verbose=0 | ||||
|         overwrite=1 | ||||
|    } | ||||
|  | ||||
|    devices { | ||||
|         scan = "/dev" | ||||
|         filter = ["a/loop/", "r/.*/"] | ||||
|    } | ||||
|  | ||||
|  | ||||
|    The important this to note is the devices section which makes sure that | ||||
|    only the loopback devices are considered for LVM2 operations. | ||||
|  | ||||
| 4) When you want to use this test setup just set the environment | ||||
|    variable LVM_SYSTEM_DIR to point to your config directory | ||||
|    (/etc/lvm_loops in my case). | ||||
|  | ||||
| 5) It's a good idea to do a vgscan to initialise the filters: | ||||
|  | ||||
|    export LVM_SYSTEM_DIR=/etc/lvm_loops | ||||
|    ./lvm vgscan | ||||
|  | ||||
|    where ./lvm is the new build of LVM2 that I'm trying out. | ||||
|  | ||||
| 7) Test away.  Make sure that you are explicit about which lvm | ||||
|    executable you want to execute (eg, ./lvm if you are in | ||||
|    LVM2/tools). | ||||
|  | ||||
|     | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| The driver directory | ||||
							
								
								
									
										33
									
								
								include/.symlinks
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								include/.symlinks
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| ../lib/activate/activate.h | ||||
| ../lib/commands/errors.h | ||||
| ../lib/commands/toolcontext.h | ||||
| ../lib/config/config.h | ||||
| ../lib/config/defaults.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/locking/locking.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 | ||||
|  | ||||
							
								
								
									
										68
									
								
								lib/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								lib/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| # | ||||
| # 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/dev_manager.c \ | ||||
| 	activate/fs.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 \ | ||||
| 	locking/external_locking.c \ | ||||
| 	locking/file_locking.c \ | ||||
| 	locking/locking.c \ | ||||
| 	locking/no_locking.c \ | ||||
| 	log/log.c \ | ||||
| 	metadata/lv_manip.c \ | ||||
| 	metadata/merge.c \ | ||||
| 	metadata/metadata.c \ | ||||
| 	metadata/pv_map.c \ | ||||
| 	metadata/snapshot_manip.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 | ||||
							
								
								
									
										310
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,310 @@ | ||||
| /* | ||||
|  * 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 "pool.h" | ||||
| #include "toolcontext.h" | ||||
| #include "dev_manager.h" | ||||
|  | ||||
| /* FIXME Temporary */ | ||||
| #include "vgcache.h" | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <linux/kdev_t.h> | ||||
| #include <fcntl.h> | ||||
|  | ||||
| #define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args) | ||||
|  | ||||
| int library_version(char *version, size_t size) | ||||
| { | ||||
| 	if (!dm_get_library_version(version, size)) | ||||
| 		return 0; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Returns 1 if info structure populated, else 0 on failure. | ||||
|  */ | ||||
| int lv_info(struct logical_volume *lv, struct dm_info *info) | ||||
| { | ||||
| 	int r; | ||||
| 	struct dev_manager *dm; | ||||
|  | ||||
| 	if (!(dm = dev_manager_create(lv->vg->name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(r = dev_manager_info(dm, lv, info))) | ||||
| 		stack; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _lv_active(struct logical_volume *lv) | ||||
| { | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return info.exists; | ||||
| } | ||||
|  | ||||
| static int _lv_open_count(struct logical_volume *lv) | ||||
| { | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return info.open_count; | ||||
| } | ||||
|  | ||||
| /* FIXME Need to detect and handle an lv rename */ | ||||
| static int _lv_activate(struct logical_volume *lv) | ||||
| { | ||||
| 	int r; | ||||
| 	struct dev_manager *dm; | ||||
|  | ||||
| 	if (!(dm = dev_manager_create(lv->vg->name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(r = dev_manager_activate(dm, lv))) | ||||
| 		stack; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _lv_deactivate(struct logical_volume *lv) | ||||
| { | ||||
| 	int r; | ||||
| 	struct dev_manager *dm; | ||||
|  | ||||
| 	if (!(dm = dev_manager_create(lv->vg->name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(r = dev_manager_deactivate(dm, lv))) | ||||
| 		stack; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _lv_suspend(struct logical_volume *lv) | ||||
| { | ||||
| 	int r; | ||||
| 	struct dev_manager *dm; | ||||
|  | ||||
| 	if (!(dm = dev_manager_create(lv->vg->name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(r = dev_manager_suspend(dm, lv))) | ||||
| 		stack; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * These two functions return the number of LVs in the state, | ||||
|  * or -1 on error. | ||||
|  */ | ||||
| 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; | ||||
| } | ||||
|  | ||||
| static struct logical_volume *_lv_from_lvid(struct cmd_context *cmd, | ||||
| 					    const char *lvid_s) | ||||
| { | ||||
| 	struct lv_list *lvl; | ||||
| 	struct volume_group *vg; | ||||
| 	union lvid *lvid; | ||||
|  | ||||
| 	lvid = (union lvid *) lvid_s; | ||||
|  | ||||
| 	log_very_verbose("Finding volume group for uuid %s", lvid_s); | ||||
| 	if (!(vg = vg_read_by_vgid(cmd, lvid->id[0].uuid))) { | ||||
| 		log_error("Volume group for uuid not found: %s", lvid_s); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	log_verbose("Found volume group \"%s\"", vg->name); | ||||
| 	if (vg->status & EXPORTED_VG) { | ||||
| 		log_error("Volume group \"%s\" is exported", vg->name); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(lvl = find_lv_in_vg_by_lvid(vg, lvid))) { | ||||
| 		log_very_verbose("Can't find logical volume id %s", lvid_s); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return lvl->lv; | ||||
| } | ||||
|  | ||||
| /* These return success if the device is not active */ | ||||
| int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	struct logical_volume *lv; | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!(lv = _lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Suspending '%s'.", lv->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (info.exists && !info.suspended) | ||||
| 		return _lv_suspend(lv); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	struct logical_volume *lv; | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!(lv = _lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Resuming '%s'.", lv->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (info.exists && info.suspended) | ||||
| 		return _lv_activate(lv); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lv_deactivate(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	struct logical_volume *lv; | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!(lv = _lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Deactivating '%s'.", lv->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (info.exists) | ||||
| 		return _lv_deactivate(lv); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lv_activate(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	struct logical_volume *lv; | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!(lv = _lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Activating '%s'.", lv->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!info.exists || info.suspended) | ||||
| 		return _lv_activate(lv); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										42
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef LVM_ACTIVATE_H | ||||
| #define LVM_ACTIVATE_H | ||||
|  | ||||
| #include <libdevmapper.h> | ||||
|  | ||||
| int driver_version(char *version, size_t size); | ||||
| int library_version(char *version, size_t size); | ||||
|  | ||||
| /* | ||||
|  * Returns 1 if info structure has been populated, else 0. | ||||
|  */ | ||||
| int lv_info(struct logical_volume *lv, struct dm_info *info); | ||||
|  | ||||
| /* | ||||
|  * These should eventually use config file | ||||
|  * to determine whether or not to activate | ||||
|  */ | ||||
| int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s); | ||||
| int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s); | ||||
| int lv_activate(struct cmd_context *cmd, const char *lvid_s); | ||||
| int lv_deactivate(struct cmd_context *cmd, const char *lvid_s); | ||||
|  | ||||
| /* | ||||
|  * FIXME: | ||||
|  * I don't like the *lvs_in_vg* function names. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * 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); | ||||
|  | ||||
| int lv_setup_cow_store(struct logical_volume *lv); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										1527
									
								
								lib/activate/dev_manager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1527
									
								
								lib/activate/dev_manager.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										40
									
								
								lib/activate/dev_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								lib/activate/dev_manager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_DEV_MANAGER_H | ||||
| #define _LVM_DEV_MANAGER_H | ||||
|  | ||||
| #include "metadata.h" | ||||
|  | ||||
| #include <libdevmapper.h> | ||||
|  | ||||
| struct dev_manager; | ||||
|  | ||||
| /* | ||||
|  * Constructor and destructor. | ||||
|  */ | ||||
| struct dev_manager *dev_manager_create(const char *vg_name); | ||||
| void dev_manager_destroy(struct dev_manager *dm); | ||||
|  | ||||
| /* | ||||
|  * The device handler is responsible for creating all the layered | ||||
|  * dm devices, and ensuring that all constraints are maintained | ||||
|  * (eg, an origin is created before its snapshot, but is not | ||||
|  * unsuspended until the snapshot is also created.) | ||||
|  */ | ||||
| int dev_manager_info(struct dev_manager *dm, struct logical_volume *lv, | ||||
| 		     struct dm_info *info); | ||||
| int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv); | ||||
| int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv); | ||||
| int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Put the desired changes into effect. | ||||
|  */ | ||||
| int dev_manager_execute(struct dev_manager *dm); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										162
									
								
								lib/activate/fs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								lib/activate/fs.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "fs.h" | ||||
| #include "log.h" | ||||
| #include "toolcontext.h" | ||||
| #include "lvm-string.h" | ||||
| #include "lvm-file.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
| #include <limits.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include <libdevmapper.h> | ||||
|  | ||||
| static int _mk_dir(struct volume_group *vg) | ||||
| { | ||||
| 	char vg_path[PATH_MAX]; | ||||
|  | ||||
| 	if (lvm_snprintf(vg_path, sizeof(vg_path), "%s%s", | ||||
| 			 vg->cmd->dev_dir, vg->name) == -1) { | ||||
| 		log_error("Couldn't construct name of volume " | ||||
| 			  "group directory."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (dir_exists(vg_path)) | ||||
| 		return 1; | ||||
|  | ||||
| 	log_very_verbose("Creating directory %s", vg_path); | ||||
| 	if (mkdir(vg_path, 0555)) { | ||||
| 		log_sys_error("mkdir", vg_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _rm_dir(struct volume_group *vg) | ||||
| { | ||||
| 	char vg_path[PATH_MAX]; | ||||
|  | ||||
| 	if (lvm_snprintf(vg_path, sizeof(vg_path), "%s%s", | ||||
| 			 vg->cmd->dev_dir, vg->name) == -1) { | ||||
| 		log_error("Couldn't construct name of volume " | ||||
| 			  "group directory."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_very_verbose("Removing directory %s", vg_path); | ||||
|  | ||||
| 	if (is_empty_dir(vg_path)) | ||||
| 		rmdir(vg_path); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _mk_link(struct logical_volume *lv, const char *dev) | ||||
| { | ||||
| 	char lv_path[PATH_MAX], link_path[PATH_MAX]; | ||||
| 	struct stat buf; | ||||
|  | ||||
| 	if (lvm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s", | ||||
| 			 lv->vg->cmd->dev_dir, lv->vg->name, lv->name) == -1) { | ||||
| 		log_error("Couldn't create source pathname for " | ||||
| 			  "logical volume link %s", lv->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lvm_snprintf(link_path, sizeof(link_path), "%s/%s", | ||||
| 			 dm_dir(), dev) == -1) { | ||||
| 		log_error("Couldn't create destination pathname for " | ||||
| 			  "logical volume link for %s", lv->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!lstat(lv_path, &buf)) { | ||||
| 		if (!S_ISLNK(buf.st_mode)) { | ||||
| 			log_error("Symbolic link %s not created: file exists", | ||||
| 				  link_path); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (unlink(lv_path) < 0) { | ||||
| 			log_sys_error("unlink", lv_path); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	log_very_verbose("Linking %s -> %s", lv_path, link_path); | ||||
| 	if (symlink(link_path, lv_path) < 0) { | ||||
| 		log_sys_error("symlink", lv_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _rm_link(struct logical_volume *lv, const char *lv_name) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	char lv_path[PATH_MAX]; | ||||
|  | ||||
| 	if (lvm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s", | ||||
| 			 lv->vg->cmd->dev_dir, lv->vg->name, lv_name) == -1) { | ||||
| 		log_error("Couldn't determine link pathname."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_very_verbose("Removing link %s", lv_path); | ||||
| 	if (lstat(lv_path, &buf) || !S_ISLNK(buf.st_mode)) { | ||||
| 		log_error("%s not symbolic link - not removing", lv_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (unlink(lv_path) < 0) { | ||||
| 		log_sys_error("unlink", lv_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int fs_add_lv(struct logical_volume *lv, const char *dev) | ||||
| { | ||||
| 	if (!_mk_dir(lv->vg) || !_mk_link(lv, dev)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int fs_del_lv(struct logical_volume *lv) | ||||
| { | ||||
| 	if (!_rm_link(lv, lv->name) || !_rm_dir(lv->vg)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* FIXME Use rename() */ | ||||
| int fs_rename_lv(struct logical_volume *lv, | ||||
| 		 const char *dev, const char *old_name) | ||||
| { | ||||
| 	if (old_name && !_rm_link(lv, old_name)) | ||||
| 		stack; | ||||
|  | ||||
| 	if (!_mk_link(lv, dev)) | ||||
| 		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, const char *dev); | ||||
| int fs_del_lv(struct logical_volume *lv); | ||||
| int fs_rename_lv(struct logical_volume *lv, | ||||
| 		 const char *dev, const char *old_name); | ||||
|  | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										16
									
								
								lib/commands/errors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/commands/errors.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_ERRORS_H | ||||
| #define _LVM_ERRORS_H | ||||
|  | ||||
| #define EINVALID_CMD_LINE	1 | ||||
| #define ENO_SUCH_CMD		3 | ||||
| #define ECMD_PROCESSED		4 | ||||
| #define ECMD_FAILED		5 | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										37
									
								
								lib/commands/toolcontext.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lib/commands/toolcontext.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_TOOLCONTEXT_H | ||||
| #define _LVM_TOOLCONTEXT_H | ||||
|  | ||||
| #include "dev-cache.h" | ||||
| #include "config.h" | ||||
| #include "pool.h" | ||||
| #include "metadata.h" | ||||
|  | ||||
| /* command-instance-related variables needed by library */ | ||||
| struct cmd_context { | ||||
| 	/* format handler allocates all objects from here */ | ||||
| 	struct pool *mem; | ||||
|  | ||||
| 	struct format_type *fmt;	/* Current format to use by default */ | ||||
|  | ||||
| 	/* FIXME Move into dynamic list */ | ||||
| 	struct format_type *fmt1;	/* Format1 */ | ||||
| 	struct format_type *fmtt;	/* Format_text */ | ||||
|  | ||||
| 	char *cmd_line; | ||||
| 	char *dev_dir; | ||||
| 	struct dev_filter *filter; | ||||
| 	struct config_file *cf; | ||||
|  | ||||
| 	struct command *command; | ||||
| 	struct uuid_map *um; | ||||
| 	struct arg *args; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										684
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										684
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,684 @@ | ||||
| /* | ||||
|  * 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 (info.st_size == 0) { | ||||
| 		log_verbose("%s is empty", file); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	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 == (caddr_t) (-1)) { | ||||
| 		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) { | ||||
| 		if (*n->v->v.str) | ||||
| 			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 | ||||
|  | ||||
							
								
								
									
										36
									
								
								lib/config/defaults.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								lib/config/defaults.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_DEFAULTS_H | ||||
| #define _LVM_DEFAULTS_H | ||||
|  | ||||
|  | ||||
| #define DEFAULT_SYS_DIR "/etc/lvm" | ||||
|  | ||||
| #define DEFAULT_ARCHIVE_ENABLED 1 | ||||
| #define DEFAULT_BACKUP_ENABLED 1 | ||||
|  | ||||
| #define DEFAULT_ARCHIVE_SUBDIR "archive" | ||||
| #define DEFAULT_BACKUP_SUBDIR "backup" | ||||
|  | ||||
| #define DEFAULT_ARCHIVE_DAYS 30 | ||||
| #define DEFAULT_ARCHIVE_NUMBER 10 | ||||
|  | ||||
| #define DEFAULT_DEV_DIR "/dev" | ||||
| #define DEFAULT_PROC_DIR "/proc" | ||||
|  | ||||
| #define DEFAULT_LOCK_DIR "/var/lock/lvm" | ||||
|  | ||||
| #define DEFAULT_UMASK 0077 | ||||
|  | ||||
| #define DEFAULT_FORMAT "lvm1" | ||||
|  | ||||
| #ifdef READLINE_SUPPORT | ||||
|   #define DEFAULT_MAX_HISTORY 100 | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #endif /* _LVM_DEFAULTS_H */ | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										129
									
								
								lib/datastruct/btree.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								lib/datastruct/btree.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										232
									
								
								lib/datastruct/hash.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								lib/datastruct/hash.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,232 @@ | ||||
| /* | ||||
|  * 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_iter(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); | ||||
| } | ||||
							
								
								
									
										36
									
								
								lib/datastruct/hash.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								lib/datastruct/hash.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * 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_iter(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); | ||||
|  | ||||
| #define hash_iterate(v, h) \ | ||||
| 	for (v = hash_get_first(h); v; \ | ||||
| 	     v = hash_get_next(h, v)) | ||||
|  | ||||
| #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 | ||||
							
								
								
									
										399
									
								
								lib/device/dev-cache.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								lib/device/dev-cache.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,399 @@ | ||||
| /* | ||||
|  * 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_iter(_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 | ||||
							
								
								
									
										249
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| /* | ||||
|  * 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; | ||||
| 	} | ||||
|  | ||||
| 	dev->flags = 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _flush(int fd) | ||||
| { | ||||
| 	ioctl(fd, BLKFLSBUF, 0); | ||||
| } | ||||
|  | ||||
| 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 (dev->flags & DEV_ACCESSED_W) | ||||
| 		_flush(dev->fd); | ||||
|  | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
| 	dev->flags |= DEV_ACCESSED_W; | ||||
|  | ||||
| 	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; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	dev->flags |= DEV_ACCESSED_W; | ||||
|  | ||||
| 	/* FIXME: Always display error */ | ||||
| 	return (len == 0); | ||||
| } | ||||
							
								
								
									
										212
									
								
								lib/device/device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								lib/device/device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										62
									
								
								lib/device/device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								lib/device/device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * 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" | ||||
|  | ||||
| #define DEV_ACCESSED_W		0x00000001	/* Device written to? */ | ||||
|  | ||||
| /* | ||||
|  * 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; | ||||
| 	uint32_t flags; | ||||
| }; | ||||
|  | ||||
| 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 (dev) ? list_item(dev->aliases.n, struct str_list)->str : | ||||
| 		       "unknown device"; | ||||
| } | ||||
|  | ||||
| /* 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 | ||||
|  | ||||
							
								
								
									
										624
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										624
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,624 @@ | ||||
| /* | ||||
|  * 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 "toolcontext.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #define SIZE_BUF 128 | ||||
|  | ||||
| char *display_size(uint64_t 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_alloc_count, | ||||
| 		  pv->pe_alloc_count, *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; | ||||
| 	} | ||||
|  | ||||
| 	/* Compat */ | ||||
| 	if(!pv->pe_size) { | ||||
| 		size = display_size((uint64_t) pv->size / 2, SIZE_SHORT); | ||||
| 		log_print("\"%s\" is a new physical volume of %s", dev_name(pv->dev), size); | ||||
| 		dbg_free(size); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	set_cmd_name(""); | ||||
| 	init_msg_prefix(""); | ||||
|  | ||||
| /****** FIXME Do we really need this conditional here? */ | ||||
| 	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((uint64_t) 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 - static for now... | ||||
| 		size2 = display_size(pv->size / 2, SIZE_SHORT); | ||||
| ********/ | ||||
|  | ||||
| 		log_print("PV Size               %s [%llu secs]" " / not " | ||||
| 			  "usable %s [LVM: %s]", | ||||
| 			  size, (uint64_t) pv->size, size1, "151 KB"); | ||||
| 	/* , size2);    */ | ||||
|  | ||||
| 		dbg_free(size1); | ||||
| 		/* dbg_free(size2); */ | ||||
| 	} else | ||||
| 		log_print("PV Size               %s", size); | ||||
| 	dbg_free(size); | ||||
|  | ||||
| /******** FIXME anytime this *isn't* available? */ | ||||
| 	log_print("PV Status             available"); | ||||
|  | ||||
| /*********FIXME Anything use this? | ||||
| 	log_print("PV#                   %u", pv->pv_number); | ||||
| **********/ | ||||
|  | ||||
| 	pe_free = pv->pe_count - pv->pe_alloc_count; | ||||
| 	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 Erm...where is this stored? | ||||
| 	log_print("Cur LV                %u", vg->lv_count); | ||||
| */ | ||||
| 	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_alloc_count); | ||||
|  | ||||
| #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 cmd_context *cmd, 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_alloc_count); | ||||
|  | ||||
| 	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 cmd_context *cmd, struct logical_volume *lv) | ||||
| { | ||||
| 	char *size; | ||||
| 	uint32_t alloc; | ||||
| 	struct dm_info info; | ||||
| 	int inkernel; | ||||
| 	char uuid[64]; | ||||
| 	struct snapshot *snap; | ||||
| 	struct stripe_segment *seg; | ||||
| 	struct list *lvseg; | ||||
| 	struct logical_volume *origin; | ||||
|  | ||||
| 	if (!id_write_format(&lv->lvid.id[1], uuid, sizeof(uuid))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	inkernel = lv_info(lv, &info) && info.exists; | ||||
|  | ||||
| 	set_cmd_name(""); | ||||
| 	init_msg_prefix(""); | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| /* Not in LVM1 format  | ||||
| 	log_print("LV UUID                %s", uuid); | ||||
| **/ | ||||
| 	log_print("LV Write Access        %s", | ||||
| 		  (lv->status & LVM_WRITE) ? "read/write" : "read only"); | ||||
|  | ||||
| 	/* see if this LV is an origina for a snapshot */ | ||||
| 	if ((snap = find_origin(lv))) { | ||||
| 		log_print("LV snapshot status     source of"); | ||||
| 		log_print("                       %s%s/%s [%s]", | ||||
| 			  lv->vg->cmd->dev_dir, lv->vg->name, snap->cow->name, | ||||
| 			  "active"); | ||||
| 		/* reset so we don't try to use this to display other snapshot | ||||
|  		 * related information. */ | ||||
| 		snap = NULL; | ||||
| 	} | ||||
| 	/* Check to see if this LV is a COW target for a snapshot */ | ||||
| 	else if ((snap = find_cow(lv))) | ||||
| 		log_print("LV snapshot status     %s destination for %s%s/%s", | ||||
| 		 	  "active", lv->vg->cmd->dev_dir, lv->vg->name, | ||||
| 			  snap->origin->name); | ||||
|  | ||||
|  | ||||
| /******* 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 - not sure that we're going to bother with this | ||||
|     log_print("LV #                   %u", lv->lv_number + 1); | ||||
| ************/ | ||||
|  | ||||
| /* LVM1 lists the number of LVs open in this field, therefore, so do we. */ | ||||
| 	log_print("# open                 %u", lvs_in_vg_opened(lv->vg)); | ||||
|  | ||||
| /* We're not going to use this count ATM, 'cause it's not what LVM1 does  | ||||
| 	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 | ||||
| ********/ | ||||
|  | ||||
| 	if(snap) | ||||
| 		origin = snap->origin; | ||||
| 	else | ||||
| 		origin = lv; | ||||
| 	 | ||||
| 	size = display_size(origin->size / 2, SIZE_SHORT); | ||||
| 	log_print("LV Size                %s", size); | ||||
| 	dbg_free(size); | ||||
|  | ||||
| 	log_print("Current LE             %u", origin->le_count); | ||||
| 	 | ||||
| /********** FIXME allocation - is there anytime the allocated LEs will not | ||||
|  * equal the current LEs? */ | ||||
| 	log_print("Allocated LE           %u", origin->le_count); | ||||
| /**********/ | ||||
| 	 | ||||
|  | ||||
| 	list_iterate(lvseg, &lv->segments) { | ||||
| 		seg = list_item(lvseg, struct stripe_segment); | ||||
| 		if(seg->stripes > 1) { | ||||
| 			log_print("Stripes                %u", seg->stripes); | ||||
| 			log_print("Stripe size (KByte)    %u", seg->stripe_size/2); | ||||
| 		} | ||||
| 		/* only want the first segment for LVM1 format output */ | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	if(snap) { | ||||
| 		/*char *s1, *s2;*/ | ||||
| 		size = display_size(snap->chunk_size / 2, SIZE_SHORT); | ||||
| 		log_print("snapshot chunk size    %s", size); | ||||
| 		dbg_free(size); | ||||
| 		size = display_size(lv->size / 2, SIZE_SHORT); | ||||
| /*		s1 = display_size();*/ | ||||
| 		log_print("Allocated to snapshot  %s [%s/%s]", "NA", "NA", size);  | ||||
| 		dbg_free(size); | ||||
| /*		size = display_size(snap->cow->size / 2, SIZE_SHORT); */ | ||||
| 		log_print("Allocated to COW-table %s", "NA"); | ||||
| /*		dbg_free(size); */ | ||||
| 	} | ||||
|  | ||||
| /********** 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); | ||||
| 	} | ||||
|     } | ||||
| ******************/ | ||||
|  | ||||
| /** Not in LVM1 format output ** | ||||
| 	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); | ||||
|  | ||||
| 	if (lv->status & FIXED_MINOR) | ||||
| 		log_print("Persistent minor       %d", lv->minor); | ||||
|  | ||||
| /**************** | ||||
| #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]; | ||||
| 	uint32_t active_pvs; | ||||
| 	struct list *pvlist; | ||||
|  | ||||
| 	set_cmd_name(""); | ||||
| 	init_msg_prefix(""); | ||||
|  | ||||
| 	/* get the number of active PVs */ | ||||
| 	if(vg->status & PARTIAL_VG) { | ||||
| 		active_pvs=0; | ||||
| 		list_iterate(pvlist, &(vg->pvs)) { | ||||
| 			active_pvs++; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 		active_pvs=vg->pv_count; | ||||
|  | ||||
| 	log_print("--- Volume group ---"); | ||||
| 	log_print("VG Name               %s", vg->name); | ||||
| /****** Not in LVM1 output, so we aren't outputing it here: | ||||
| 	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/" : "available/", | ||||
| 		  vg->status & RESIZEABLE_VG ? "" : "NOT "); | ||||
| 	if (vg->status & CLUSTERED) { | ||||
| 		log_print("Clustered             yes"); | ||||
| 		log_print("Shared                %s", | ||||
| 			  vg->status & SHARED ? "yes" : "no"); | ||||
| 	} | ||||
| /****** FIXME VG # - we aren't implementing this because people should | ||||
|  * use the UUID for this anyway  | ||||
| 	log_print("VG #                  %u", vg->vg_number); | ||||
| *******/ | ||||
| 	log_print("MAX LV                %u", vg->max_lv); | ||||
| 	log_print("Cur LV                %u", vg->lv_count); | ||||
|         log_print("Open LV               %u", lvs_in_vg_opened(vg)); | ||||
|         log_print("MAX LV Size           256 TB"); | ||||
| 	log_print("Max PV                %u", vg->max_pv); | ||||
| 	log_print("Cur PV                %u", vg->pv_count); | ||||
|       	log_print("Act PV                %u", active_pvs); | ||||
|  | ||||
| 	s1 = | ||||
| 	    display_size((uint64_t) 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(((uint64_t) | ||||
| 			   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((uint64_t) 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(uint64_t 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 cmd_context *cmd, 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 cmd_context *cmd, 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 | ||||
							
								
								
									
										70
									
								
								lib/filters/filter-composite.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								lib/filters/filter-composite.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										226
									
								
								lib/filters/filter-regex.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								lib/filters/filter-regex.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,226 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										188
									
								
								lib/filters/filter.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								lib/filters/filter.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| /* | ||||
|  * 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(const char *proc); | ||||
|  | ||||
| void lvm_type_filter_destroy(struct dev_filter *f); | ||||
|  | ||||
| int md_major(void); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										662
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										662
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,662 @@ | ||||
| /* | ||||
|  * 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 format_type *fmt, | ||||
| 				     struct device *dev, struct pool *mem, | ||||
| 				     const char *vg_name) | ||||
| { | ||||
| 	struct disk_list *dl = pool_alloc(mem, sizeof(*dl)); | ||||
| 	const char *name = dev_name(dev); | ||||
|  | ||||
| 	if (!dl) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	dl->dev = dev; | ||||
| 	dl->mem = mem; | ||||
| 	list_init(&dl->uuids); | ||||
| 	list_init(&dl->lvds); | ||||
|  | ||||
| 	if (!read_pvd(dev, &dl->pvd)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * is it an orphan ? | ||||
| 	 */ | ||||
| 	if (!*dl->pvd.vg_name) { | ||||
| 		log_very_verbose("%s is not a member of any format1 VG", name); | ||||
|  | ||||
| 		/* Update VG cache */ | ||||
| 		vgcache_add(dl->pvd.vg_name, NULL, dev, fmt); | ||||
|  | ||||
| 		return (vg_name) ? NULL : dl; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_vgd(dl)) { | ||||
| 		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(dl); | ||||
|  | ||||
| 	/* Update VG cache with what we found */ | ||||
| 	vgcache_add(dl->pvd.vg_name, dl->vgd.vg_uuid, dev, fmt); | ||||
|  | ||||
| 	if (vg_name && strcmp(vg_name, dl->pvd.vg_name)) { | ||||
| 		log_very_verbose("%s is not a member of the VG %s", | ||||
| 				 name, vg_name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_uuids(dl)) { | ||||
| 		log_error("Failed to read PV uuid list from %s", name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_lvs(dl)) { | ||||
| 		log_error("Failed to read LV's from %s", name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_extents(dl)) { | ||||
| 		log_error("Failed to read extents from %s", name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	log_very_verbose("Found %s in %sVG %s", name, | ||||
| 			 (dl->vgd.vg_status & VG_EXPORTED) ? "exported " : "", | ||||
| 			 dl->pvd.vg_name); | ||||
|  | ||||
| 	return dl; | ||||
|  | ||||
|       bad: | ||||
| 	pool_free(dl->mem, dl); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| struct disk_list *read_disk(struct format_type *fmt, 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(fmt, 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(struct format_type *fmt, 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(fmt, 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(fmt, 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 format_type *fmt, 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; | ||||
| 	} | ||||
|  | ||||
| 	vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev, fmt); | ||||
| 	/* | ||||
| 	 * Stop here for orphan pv's. | ||||
| 	 */ | ||||
| 	if (data->pvd.vg_name[0] == '\0') { | ||||
| 		if (!test_mode()) | ||||
| 			vgcache_add(data->pvd.vg_name, NULL, data->dev, fmt); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!test_mode()) | ||||
| 		vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev, | ||||
| 			    fmt); | ||||
|  | ||||
| 	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 format_type *fmt, struct disk_list *data) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	if (!dev_open(data->dev, O_WRONLY)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	r = __write_all_pvd(fmt, 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 format_type *fmt, 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(fmt, dl))) | ||||
| 			fail; | ||||
|  | ||||
| 		log_very_verbose("Successfully wrote data to %s", | ||||
| 				 dev_name(dl->dev)); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										251
									
								
								lib/format1/disk-rep.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								lib/format1/disk-rep.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| /* | ||||
|  * 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 MAX_PV_SIZE	((uint32_t) -1) /* 2TB in sectors - 1 */ | ||||
| #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_PERSISTENT_MINOR  0x04	/*     "     */ | ||||
|  | ||||
| #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	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 format_type *fmt, struct device *dev, | ||||
| 			    struct pool *mem, const char *vg_name); | ||||
|  | ||||
| int read_pvs_in_vg(struct format_type *fmt, const char *vg_name, | ||||
| 		   struct dev_filter *filter, | ||||
| 		   struct pool *mem, struct list *results); | ||||
|  | ||||
| int write_disks(struct format_type *fmt, 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 format_instance *fid, 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 import_snapshots(struct pool *mem, struct volume_group *vg, | ||||
| 		     struct list *pvds); | ||||
|  | ||||
| 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 format_instance *fid, struct dev_filter *filter, | ||||
| 		       const char *candidate_vg, int *result); | ||||
| int export_vg_number(struct format_instance *fid, struct list *pvds, | ||||
| 		     const char *vg_name, struct dev_filter *filter); | ||||
|  | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										613
									
								
								lib/format1/format1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										613
									
								
								lib/format1/format1.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,613 @@ | ||||
| /* | ||||
|  * 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 "limits.h" | ||||
| #include "list.h" | ||||
| #include "log.h" | ||||
| #include "display.h" | ||||
| #include "toolcontext.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 format_instance *fid, | ||||
| 				      struct list *pvs) | ||||
| { | ||||
| 	struct pool *mem = fid->fmt->cmd->mem; | ||||
| 	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)); | ||||
|  | ||||
| 	vg->cmd = fid->fmt->cmd; | ||||
| 	vg->fid = fid; | ||||
| 	vg->seqno = 0; | ||||
| 	list_init(&vg->pvs); | ||||
| 	list_init(&vg->lvs); | ||||
| 	list_init(&vg->snapshots); | ||||
|  | ||||
| 	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(fid, 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; | ||||
|  | ||||
| 	if (!import_snapshots(mem, vg, pvs)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	return vg; | ||||
|  | ||||
|       bad: | ||||
| 	stack; | ||||
| 	pool_free(mem, vg); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static struct volume_group *_vg_read(struct format_instance *fid, | ||||
| 				     const char *vg_name, void *mda) | ||||
| { | ||||
| 	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, fid->fmt->cmd->dev_dir); | ||||
|  | ||||
| 	if (!read_pvs_in_vg | ||||
| 	    (fid->fmt, vg_name, fid->fmt->cmd->filter, mem, &pvs)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!(vg = _build_vg(fid, &pvs))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
|       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 format_instance *fid, 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(fid, pvds, vg->name, filter)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _vg_write(struct format_instance *fid, struct volume_group *vg, | ||||
| 		     void *mdl) | ||||
| { | ||||
| 	struct pool *mem = pool_create(1024 * 10); | ||||
| 	struct list pvds; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	list_init(&pvds); | ||||
|  | ||||
| 	r = (_flatten_vg(fid, mem, vg, &pvds, fid->fmt->cmd->dev_dir, | ||||
| 			 fid->fmt->cmd->filter) && | ||||
| 	     write_disks(fid->fmt, &pvds)); | ||||
| 	pool_destroy(mem); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int _pv_read(struct format_type *fmt, const char *name, | ||||
| 	     struct physical_volume *pv) | ||||
| { | ||||
| 	struct pool *mem = pool_create(1024); | ||||
| 	struct disk_list *dl; | ||||
| 	struct device *dev; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	log_very_verbose("Reading physical volume data %s from disk", name); | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dev = dev_cache_get(name, fmt->cmd->filter))) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dl = read_disk(fmt, dev, mem, NULL))) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!import_pv(fmt->cmd->mem, dl->dev, NULL, pv, &dl->pvd)) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	pv->fid = fmt->ops->create_instance(fmt, NULL, NULL); | ||||
|  | ||||
| 	r = 1; | ||||
|  | ||||
|       out: | ||||
| 	pool_destroy(mem); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static struct list *_get_pvs(struct format_type *fmt, struct list *results) | ||||
| { | ||||
| 	struct pool *mem = pool_create(1024 * 10); | ||||
| 	struct list pvs; | ||||
| 	uint32_t count; | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_init(&pvs); | ||||
|  | ||||
| 	if (!read_pvs_in_vg(fmt, NULL, fmt->cmd->filter, mem, &pvs)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!import_pvs(NULL, fmt->cmd->mem, NULL, &pvs, results, &count)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	return results; | ||||
|  | ||||
|       bad: | ||||
| 	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_type *fmt, struct list *names) | ||||
| { | ||||
| 	struct list *pvh; | ||||
| 	struct list *pvs; | ||||
| 	struct name_list *nl; | ||||
|  | ||||
| 	if (!(pvs = pool_alloc(fmt->cmd->mem, sizeof(*pvs)))) { | ||||
| 		log_error("PV list allocation failed"); | ||||
| 		goto err; | ||||
| 	} | ||||
|  | ||||
| 	list_init(pvs); | ||||
|  | ||||
| 	if (!_get_pvs(fmt, pvs)) { | ||||
| 		stack; | ||||
| 		goto err; | ||||
| 	} | ||||
|  | ||||
| 	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(fmt->cmd->mem, sizeof(*nl)))) { | ||||
| 			stack; | ||||
| 			goto err; | ||||
| 		} | ||||
|  | ||||
| 		if (!(nl->name = pool_strdup(fmt->cmd->mem, pvl->pv->vg_name))) { | ||||
| 			stack; | ||||
| 			goto err; | ||||
| 		} | ||||
|  | ||||
| 		list_add(names, &nl->list); | ||||
| 	} | ||||
|  | ||||
| 	if (list_empty(names)) | ||||
| 		pool_free(fmt->cmd->mem, pvs); | ||||
|  | ||||
| 	return names; | ||||
|  | ||||
|       err: | ||||
| 	pool_free(fmt->cmd->mem, pvs); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int _pv_setup(struct format_instance *fid, struct physical_volume *pv, | ||||
| 		     struct volume_group *vg) | ||||
| { | ||||
| 	/* setup operations for the PV structure */ | ||||
| 	if (pv->size > MAX_PV_SIZE) | ||||
| 		pv->size--; | ||||
| 	if (pv->size > MAX_PV_SIZE) { | ||||
| 		/* FIXME Limit hardcoded */ | ||||
| 		log_error("Physical volumes cannot be bigger than 2TB"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Nothing more to do if pe_size isn't known */ | ||||
| 	if (!vg) | ||||
| 		return 1; | ||||
|  | ||||
| 	/* | ||||
| 	 * This works out pe_start and pe_count. | ||||
| 	 */ | ||||
| 	if (!calculate_extent_count(pv)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _find_free_lvnum(struct logical_volume *lv) | ||||
| { | ||||
| 	int lvnum_used[MAX_LV]; | ||||
| 	int i = 0; | ||||
| 	struct list *lvh; | ||||
| 	struct lv_list *lvl; | ||||
|  | ||||
| 	memset(&lvnum_used, 0, sizeof(lvnum_used)); | ||||
|  | ||||
| 	list_iterate(lvh, &lv->vg->lvs) { | ||||
| 		lvl = list_item(lvh, struct lv_list); | ||||
| 		lvnum_used[lvnum_from_lvid(&lvl->lv->lvid)] = 1; | ||||
| 	} | ||||
|  | ||||
| 	while (lvnum_used[i]) | ||||
| 		i++; | ||||
|  | ||||
| 	return i; | ||||
| } | ||||
|  | ||||
| static int _lv_setup(struct format_instance *fid, struct logical_volume *lv) | ||||
| { | ||||
| 	uint64_t max_size = UINT_MAX; | ||||
|  | ||||
| 	if (!*lv->lvid.s) | ||||
| 		lvid_from_lvnum(&lv->lvid, &lv->vg->id, _find_free_lvnum(lv)); | ||||
|  | ||||
| 	if (lv->le_count > MAX_LE_TOTAL) { | ||||
| 		log_error("logical volumes cannot contain more than " | ||||
| 			  "%d extents.", MAX_LE_TOTAL); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (lv->size > max_size) { | ||||
| 		char *dummy = display_size(max_size, SIZE_SHORT); | ||||
| 		log_error("logical volumes cannot be larger than %s", dummy); | ||||
| 		dbg_free(dummy); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _pv_write(struct format_instance *fid, struct physical_volume *pv, | ||||
| 		     void *mdl) | ||||
| { | ||||
| 	struct pool *mem; | ||||
| 	struct disk_list *dl; | ||||
| 	struct list pvs; | ||||
|  | ||||
| 	list_init(&pvs); | ||||
|  | ||||
| 	if (*pv->vg_name || pv->pe_alloc_count) { | ||||
| 		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 = 0; | ||||
| 	pv->pe_start = PE_ALIGN; | ||||
|  | ||||
| 	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; | ||||
| 	dl->pvd.pe_on_disk.base = PE_ALIGN * SECTOR_SIZE; | ||||
|  | ||||
| 	list_add(&pvs, &dl->list); | ||||
| 	if (!write_disks(fid->fmt, &pvs)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	return 1; | ||||
|  | ||||
|       bad: | ||||
| 	pool_destroy(mem); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int _vg_setup(struct format_instance *fid, 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; | ||||
| } | ||||
|  | ||||
| struct format_instance *_create_instance(struct format_type *fmt, | ||||
| 					 const char *vgname, void *private) | ||||
| { | ||||
| 	struct format_instance *fid; | ||||
| 	struct metadata_area *mda; | ||||
|  | ||||
| 	if (!(fid = pool_alloc(fmt->cmd->mem, sizeof(*fid)))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	fid->fmt = fmt; | ||||
| 	list_init(&fid->metadata_areas); | ||||
|  | ||||
| 	/* Define a NULL metadata area */ | ||||
| 	if (!(mda = pool_alloc(fmt->cmd->mem, sizeof(*mda)))) { | ||||
| 		stack; | ||||
| 		pool_free(fmt->cmd->mem, fid); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	mda->metadata_locn = NULL; | ||||
| 	list_add(&fid->metadata_areas, &mda->list); | ||||
|  | ||||
| 	return fid; | ||||
| } | ||||
|  | ||||
| void _destroy_instance(struct format_instance *fid) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| void _destroy(struct format_type *fmt) | ||||
| { | ||||
| 	dbg_free(fmt); | ||||
| } | ||||
|  | ||||
| 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, | ||||
| 	create_instance:_create_instance, | ||||
| 	destroy_instance:_destroy_instance, | ||||
| 	destroy:	_destroy, | ||||
| }; | ||||
|  | ||||
| struct format_type *create_lvm1_format(struct cmd_context *cmd) | ||||
| { | ||||
| 	struct format_type *fmt = dbg_malloc(sizeof(*fmt)); | ||||
|  | ||||
| 	if (!fmt) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	fmt->cmd = cmd; | ||||
| 	fmt->ops = &_format1_ops; | ||||
| 	fmt->name = FMT_LVM1_NAME; | ||||
| 	fmt->features = 0; | ||||
| 	fmt->private = NULL; | ||||
|  | ||||
| 	return fmt; | ||||
| } | ||||
							
								
								
									
										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_type *create_lvm1_format(struct cmd_context *cmd); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										705
									
								
								lib/format1/import-export.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										705
									
								
								lib/format1/import-export.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,705 @@ | ||||
| /* | ||||
|  * 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> | ||||
| #include <linux/kdev_t.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_alloc_count = 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_alloc_count; | ||||
| 	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) | ||||
| { | ||||
| 	lvid_from_lvnum(&lv->lvid, &lv->vg->id, lvd->lv_number); | ||||
|  | ||||
| 	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_status & LV_PERSISTENT_MINOR) { | ||||
| 		lv->status |= FIXED_MINOR; | ||||
| 		lv->minor = MINOR(lvd->lv_dev); | ||||
| 	} else | ||||
| 		lv->minor = -1; | ||||
|  | ||||
| 	if (lvd->lv_access & LV_READ) | ||||
| 		lv->status |= LVM_READ; | ||||
|  | ||||
| 	if (lvd->lv_access & LV_WRITE) | ||||
| 		lv->status |= LVM_WRITE; | ||||
|  | ||||
| 	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 & SPINDOWN_LV) | ||||
| 		lvd->lv_status |= LV_SPINDOWN; | ||||
|  | ||||
| 	if (lv->status & FIXED_MINOR) { | ||||
| 		lvd->lv_status |= LV_PERSISTENT_MINOR; | ||||
| 		lvd->lv_dev = MKDEV(0, lv->minor); | ||||
| 	} | ||||
|  | ||||
| 	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 format_instance *fid, 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; | ||||
| 		} | ||||
|  | ||||
| 		pvl->pv->fid = fid; | ||||
| 		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; | ||||
| 	lv->vg = vg; | ||||
|  | ||||
| 	if (!import_lv(mem, lv, lvd)) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_add(&vg->lvs, &ll->list); | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| /* FIXME: tidy */ | ||||
| int export_lvs(struct disk_list *dl, struct volume_group *vg, | ||||
| 	       struct physical_volume *pv, const char *dev_dir) | ||||
| { | ||||
| 	int r = 0; | ||||
| 	struct list *lvh, *sh; | ||||
| 	struct lv_list *ll; | ||||
| 	struct lvd_list *lvdl; | ||||
| 	int lv_num, len; | ||||
| 	struct hash_table *lvd_hash; | ||||
|  | ||||
| 	if (!(lvd_hash = hash_create(32))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * setup the pv's extents array | ||||
| 	 */ | ||||
| 	len = sizeof(struct pe_disk) * dl->pvd.pe_total; | ||||
| 	if (!(dl->extents = pool_alloc(dl->mem, len))) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	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; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		export_lv(&lvdl->lvd, vg, ll->lv, dev_dir); | ||||
|  | ||||
| 		lv_num = lvnum_from_lvid(&ll->lv->lvid); | ||||
|  | ||||
| 		lvdl->lvd.lv_number = lv_num; | ||||
|  | ||||
| 		if (!hash_insert(lvd_hash, ll->lv->name, &lvdl->lvd)) { | ||||
| 			stack; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		if (!export_extents(dl, lv_num + 1, ll->lv, pv)) { | ||||
| 			stack; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		list_add(&dl->lvds, &lvdl->list); | ||||
| 		dl->pvd.lv_cur++; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Now we need to run through the snapshots, exporting | ||||
| 	 * the SNAPSHOT_ORG flags etc. | ||||
| 	 */ | ||||
| 	list_iterate(sh, &vg->snapshots) { | ||||
| 		struct lv_disk *org, *cow; | ||||
| 		struct snapshot *s = list_item(sh, | ||||
| 					       struct snapshot_list)->snapshot; | ||||
|  | ||||
| 		if (!(org = hash_lookup(lvd_hash, s->origin->name))) { | ||||
| 			log_err("Couldn't find snapshot origin '%s'.", | ||||
| 				s->origin->name); | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		if (!(cow = hash_lookup(lvd_hash, s->cow->name))) { | ||||
| 			log_err("Couldn't find snapshot cow store '%s'.", | ||||
| 				s->cow->name); | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		org->lv_access |= LV_SNAPSHOT_ORG; | ||||
| 		cow->lv_access |= LV_SNAPSHOT; | ||||
| 		cow->lv_snapshot_minor = org->lv_number; | ||||
| 		cow->lv_chunk_size = s->chunk_size; | ||||
| 	} | ||||
|  | ||||
| 	r = 1; | ||||
|  | ||||
|       out: | ||||
| 	hash_destroy(lvd_hash); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * FIXME: More inefficient code. | ||||
|  */ | ||||
| int import_snapshots(struct pool *mem, struct volume_group *vg, | ||||
| 		     struct list *pvds) | ||||
| { | ||||
| 	struct logical_volume *lvs[MAX_LV]; | ||||
| 	struct list *pvdh, *lvdh; | ||||
| 	struct disk_list *dl; | ||||
| 	struct lv_disk *lvd; | ||||
| 	int lvnum; | ||||
| 	struct logical_volume *org, *cow; | ||||
|  | ||||
| 	/* build an index of lv numbers */ | ||||
| 	memset(lvs, 0, sizeof(lvs)); | ||||
| 	list_iterate(pvdh, pvds) { | ||||
| 		dl = list_item(pvdh, struct disk_list); | ||||
|  | ||||
| 		list_iterate(lvdh, &dl->lvds) { | ||||
| 			lvd = &(list_item(lvdh, struct lvd_list)->lvd); | ||||
|  | ||||
| 			lvnum = lvd->lv_number; | ||||
|  | ||||
| 			if (lvnum > MAX_LV) { | ||||
| 				log_err("Logical volume number " | ||||
| 					"out of bounds."); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			if (!lvs[lvnum] && | ||||
| 			    !(lvs[lvnum] = find_lv(vg, lvd->lv_name))) { | ||||
| 				log_err("Couldn't find logical volume '%s'.", | ||||
| 					lvd->lv_name); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Now iterate through yet again adding the snapshots. | ||||
| 	 */ | ||||
| 	list_iterate(pvdh, pvds) { | ||||
| 		dl = list_item(pvdh, struct disk_list); | ||||
|  | ||||
| 		list_iterate(lvdh, &dl->lvds) { | ||||
| 			lvd = &(list_item(lvdh, struct lvd_list)->lvd); | ||||
|  | ||||
| 			if (!(lvd->lv_access & LV_SNAPSHOT)) | ||||
| 				continue; | ||||
|  | ||||
| 			lvnum = lvd->lv_number; | ||||
| 			cow = lvs[lvnum]; | ||||
| 			if (!(org = lvs[lvd->lv_snapshot_minor])) { | ||||
| 				log_err("Couldn't find origin logical volume " | ||||
| 					"for snapshot '%s'.", lvd->lv_name); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			/* we may have already added this snapshot */ | ||||
| 			if (lv_is_cow(cow)) | ||||
| 				continue; | ||||
|  | ||||
| 			/* insert the snapshot */ | ||||
| 			if (!vg_add_snapshot(org, cow, 1, lvd->lv_chunk_size)) { | ||||
| 				log_err("Couldn't add snapshot."); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	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 field | ||||
|  * used by LVM1. | ||||
|  */ | ||||
| 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 format_instance *fid, 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(fid, 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; | ||||
| } | ||||
							
								
								
									
										368
									
								
								lib/format1/import-extents.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								lib/format1/import-extents.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,368 @@ | ||||
| /* | ||||
|  * 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; | ||||
| } | ||||
							
								
								
									
										156
									
								
								lib/format1/layout.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								lib/format1/layout.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| /* | ||||
|  * 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; | ||||
| 		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; | ||||
| } | ||||
							
								
								
									
										143
									
								
								lib/format1/lvm1_label.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								lib/format1/lvm1_label.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										60
									
								
								lib/format1/vg_number.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								lib/format1/vg_number.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| /* | ||||
|  * 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 format_instance *fid, 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(fid->fmt, 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; | ||||
| } | ||||
							
								
								
									
										312
									
								
								lib/format_text/archive.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								lib/format_text/archive.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,312 @@ | ||||
| /* | ||||
|  * 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 "toolcontext.h" | ||||
|  | ||||
| #include <dirent.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/file.h> | ||||
| #include <fcntl.h> | ||||
| #include <time.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. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * A list of these is built up for our volume group.  Ordered | ||||
|  * with the least recent at the head. | ||||
|  */ | ||||
| struct archive_file { | ||||
| 	struct list list; | ||||
|  | ||||
| 	char *path; | ||||
| 	int index; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * 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 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); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Returns a list of archive_files. | ||||
|  */ | ||||
| static struct list *_scan_archive(struct pool *mem, | ||||
| 				  const char *vg, const char *dir) | ||||
| { | ||||
| 	int i, count, index; | ||||
| 	char vg_name[64], *path; | ||||
| 	struct dirent **dirent; | ||||
| 	struct archive_file *af; | ||||
| 	struct list *results; | ||||
|  | ||||
| 	if (!(results = pool_alloc(mem, sizeof(*results)))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_init(results); | ||||
|  | ||||
| 	if ((count = scandir(dir, &dirent, NULL, alphasort)) < 0) { | ||||
| 		log_err("Couldn't scan archive directory."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < count; i++) { | ||||
| 		/* ignore dot files */ | ||||
| 		if (dirent[i]->d_name[0] == '.') | ||||
| 			continue; | ||||
|  | ||||
| 		/* check the name is the correct format */ | ||||
| 		if (!_split_vg(dirent[i]->d_name, vg_name, | ||||
| 			       sizeof(vg_name), &index)) | ||||
| 			continue; | ||||
|  | ||||
| 		/* is it the vg we're interested in ? */ | ||||
| 		if (strcmp(vg, vg_name)) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!(path = _join(mem, dir, dirent[i]->d_name))) { | ||||
| 			stack; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		 * Create a new archive_file. | ||||
| 		 */ | ||||
| 		if (!(af = pool_alloc(mem, sizeof(*af)))) { | ||||
| 			log_err("Couldn't create new archive file."); | ||||
| 			results = NULL; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		af->index = index; | ||||
| 		af->path = path; | ||||
|  | ||||
| 		/* | ||||
| 		 * Insert it to the correct part of the list. | ||||
| 		 */ | ||||
| 		_insert_file(results, af); | ||||
| 	} | ||||
|  | ||||
|       out: | ||||
| 	for (i = 0; i < count; i++) | ||||
| 		free(dirent[i]); | ||||
| 	free(dirent); | ||||
|  | ||||
| 	return results; | ||||
| } | ||||
|  | ||||
| int archive_vg(struct volume_group *vg, | ||||
| 	       const char *dir, const char *desc, | ||||
| 	       uint32_t retain_days, uint32_t min_archive) | ||||
| { | ||||
| 	int i, fd; | ||||
| 	unsigned int index = 0; | ||||
| 	struct archive_file *last; | ||||
| 	FILE *fp = NULL; | ||||
| 	char temp_file[PATH_MAX], archive_name[PATH_MAX]; | ||||
| 	struct list *archives; | ||||
|  | ||||
| 	/* | ||||
| 	 * Write the vg out to a temporary file. | ||||
| 	 */ | ||||
| 	if (!create_temp_name(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, desc)) { | ||||
| 		stack; | ||||
| 		fclose(fp); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	fclose(fp); | ||||
|  | ||||
| 	/* | ||||
| 	 * Now we want to rename this file to <vg>_index.vg. | ||||
| 	 */ | ||||
| 	if (!(archives = _scan_archive(vg->cmd->mem, vg->name, dir))) { | ||||
| 		log_err("Couldn't scan the archive directory (%s).", dir); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (list_empty(archives)) | ||||
| 		index = 0; | ||||
|  | ||||
| 	else { | ||||
| 		last = list_item(archives->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", dir, vg->name, index) < 0) { | ||||
| 			log_err("archive file name too long."); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (lvm_rename(temp_file, archive_name)) | ||||
| 			break; | ||||
|  | ||||
| 		index++; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _display_archive(struct cmd_context *cmd, struct uuid_map *um, | ||||
| 			     struct archive_file *af) | ||||
| { | ||||
| 	struct volume_group *vg = NULL; | ||||
| 	struct format_instance *tf; | ||||
| 	time_t when; | ||||
| 	char *desc; | ||||
| 	void *context; | ||||
|  | ||||
| 	log_print("path:\t\t%s", af->path); | ||||
|  | ||||
| 	if (!(context = create_text_context(cmd->fmtt, af->path, NULL)) || | ||||
| 	    !(tf = cmd->fmtt->ops->create_instance(cmd->fmtt, NULL, context))) { | ||||
| 		log_error("Couldn't create text instance object."); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Read the archive file to ensure that it is valid, and | ||||
| 	 * retrieve the archive time and description. | ||||
| 	 */ | ||||
| 	/* FIXME Use variation on _vg_read */ | ||||
| 	if (!(vg = text_vg_import(tf, af->path, um, &when, &desc))) { | ||||
| 		log_print("Unable to read archive file."); | ||||
| 		tf->fmt->ops->destroy_instance(tf); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	log_print("description:\t%s", desc ? desc : "<No description>"); | ||||
| 	log_print("time:\t\t%s", ctime(&when)); | ||||
|  | ||||
| 	pool_free(cmd->mem, vg); | ||||
| 	tf->fmt->ops->destroy_instance(tf); | ||||
| } | ||||
|  | ||||
| int archive_list(struct cmd_context *cmd, struct uuid_map *um, | ||||
| 		 const char *dir, const char *vg) | ||||
| { | ||||
| 	struct list *archives, *ah; | ||||
| 	struct archive_file *af; | ||||
|  | ||||
| 	if (!(archives = _scan_archive(cmd->mem, vg, dir))) { | ||||
| 		log_err("Couldn't scan the archive directory (%s).", dir); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (list_empty(archives)) | ||||
| 		log_print("No archives found."); | ||||
|  | ||||
| 	list_iterate(ah, archives) { | ||||
| 		af = list_item(ah, struct archive_file); | ||||
|  | ||||
| 		_display_archive(cmd, um, af); | ||||
| 		log_print(" "); | ||||
| 	} | ||||
|  | ||||
| 	pool_free(cmd->mem, archives); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										549
									
								
								lib/format_text/export.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										549
									
								
								lib/format_text/export.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,549 @@ | ||||
| /* | ||||
|  * 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, const char *desc) | ||||
| { | ||||
| 	time_t t; | ||||
|  | ||||
| 	t = time(NULL); | ||||
|  | ||||
| 	_out(f, | ||||
| 	     "# This file was originally generated by the LVM2 library\n" | ||||
| 	     "# Generated: %s\n", ctime(&t)); | ||||
|  | ||||
| 	_out(f, "description = \"%s\"", desc); | ||||
| 	_out(f, "creation_time = %lu\n", 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); | ||||
|  | ||||
| 	_out(f, "seqno = %u", vg->seqno); | ||||
| 	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; | ||||
|  | ||||
| 	/* | ||||
| 	 * Don't bother with an lv section if there are no lvs. | ||||
| 	 */ | ||||
| 	if (list_empty(&vg->lvs)) | ||||
| 		return 1; | ||||
|  | ||||
| 	_out(f, "logical_volumes {"); | ||||
| 	_inc_indent(f); | ||||
|  | ||||
| 	list_iterate(lvh, &vg->lvs) { | ||||
| 		lv = list_item(lvh, struct lv_list)->lv; | ||||
|  | ||||
| 		_nl(f); | ||||
| 		_out(f, "%s {", lv->name); | ||||
| 		_inc_indent(f); | ||||
|  | ||||
| 		/* FIXME: Write full lvid */ | ||||
| 		if (!id_write_format(&lv->lvid.id[1], buffer, sizeof(buffer))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		_out(f, "id = \"%s\"", buffer); | ||||
|  | ||||
| 		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); | ||||
| 		if (lv->minor >= 0) | ||||
| 			_out(f, "minor = %d", lv->minor); | ||||
| 		_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; | ||||
| } | ||||
|  | ||||
| static int _print_snapshot(struct formatter *f, struct snapshot *s, | ||||
| 			   unsigned int count) | ||||
| { | ||||
| 	_nl(f); | ||||
| 	_out(f, "snapshot%u {", count); | ||||
| 	_inc_indent(f); | ||||
|  | ||||
| 	_out(f, "chunk_size = %u", s->chunk_size); | ||||
| 	_out(f, "origin = \"%s\"", s->origin->name); | ||||
| 	_out(f, "cow_store = \"%s\"", s->cow->name); | ||||
|  | ||||
| 	_dec_indent(f); | ||||
| 	_out(f, "}"); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _print_snapshots(struct formatter *f, struct volume_group *vg) | ||||
| { | ||||
| 	struct list *sh; | ||||
| 	struct snapshot *s; | ||||
| 	unsigned int count = 0; | ||||
|  | ||||
| 	/* | ||||
| 	 * Don't bother with a snapshot section if there are no | ||||
| 	 * snapshots. | ||||
| 	 */ | ||||
| 	if (list_empty(&vg->snapshots)) | ||||
| 		return 1; | ||||
|  | ||||
| 	_out(f, "snapshots {"); | ||||
| 	_inc_indent(f); | ||||
|  | ||||
| 	list_iterate(sh, &vg->snapshots) { | ||||
| 		s = list_item(sh, struct snapshot_list)->snapshot; | ||||
|  | ||||
| 		if (!_print_snapshot(f, s, count++)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	_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, const char *desc) | ||||
| { | ||||
| 	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, desc)) | ||||
| 		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; | ||||
|  | ||||
| 	_nl(f); | ||||
| 	if (!_print_snapshots(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; | ||||
| } | ||||
							
								
								
									
										160
									
								
								lib/format_text/flags.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								lib/format_text/flags.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| /* | ||||
|  * 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"}, | ||||
| 	{FIXED_MINOR, "FIXED_MINOR"}, | ||||
| 	{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; | ||||
| } | ||||
							
								
								
									
										638
									
								
								lib/format_text/format-text.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										638
									
								
								lib/format_text/format-text.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,638 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001-2002 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 "display.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "toolcontext.h" | ||||
| #include "vgcache.h" | ||||
| #include "lvm-string.h" | ||||
|  | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/file.h> | ||||
| #include <limits.h> | ||||
| #include <dirent.h> | ||||
|  | ||||
| /* Arbitrary limits copied from format1/disk_rep.h */ | ||||
| #define MAX_PV 256 | ||||
| #define MAX_LV 256 | ||||
| #define MAX_VG 99 | ||||
| #define MAX_PV_SIZE	((uint32_t) -1)	/* 2TB in sectors - 1 */ | ||||
|  | ||||
| struct dir_list { | ||||
| 	struct list list; | ||||
| 	char dir[0]; | ||||
| }; | ||||
|  | ||||
| struct text_context { | ||||
| 	char *path_live;	/* Path to file holding live metadata */ | ||||
| 	char *path_edit;	/* Path to file holding edited metadata */ | ||||
| 	char *desc;		/* Description placed inside file */ | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * NOTE: Currently there can be only one vg per file. | ||||
|  */ | ||||
|  | ||||
| static int _pv_setup(struct format_instance *fi, struct physical_volume *pv, | ||||
| 		     struct volume_group *vg) | ||||
| { | ||||
| 	/* setup operations for the PV structure */ | ||||
| 	if (pv->size > MAX_PV_SIZE) | ||||
| 		pv->size--; | ||||
| 	if (pv->size > MAX_PV_SIZE) { | ||||
| 		/* FIXME Limit hardcoded */ | ||||
| 		log_error("Physical volumes cannot be bigger than 2TB"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static 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 & (vg->extent_size - 1)) { | ||||
| 		log_error("Extent size must be power of 2"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _lv_setup(struct format_instance *fi, struct logical_volume *lv) | ||||
| { | ||||
| 	uint64_t max_size = UINT_MAX; | ||||
|  | ||||
| 	if (!*lv->lvid.s) | ||||
| 		lvid_create(&lv->lvid, &lv->vg->id); | ||||
|  | ||||
| 	if (lv->size > max_size) { | ||||
| 		char *dummy = display_size(max_size, SIZE_SHORT); | ||||
| 		log_error("logical volumes cannot be larger than %s", dummy); | ||||
| 		dbg_free(dummy); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct volume_group *_vg_read(struct format_instance *fi, | ||||
| 				     const char *vgname, void *mdl) | ||||
| { | ||||
| 	struct text_context *tc = (struct text_context *) mdl; | ||||
| 	struct volume_group *vg; | ||||
| 	time_t when; | ||||
| 	char *desc; | ||||
|  | ||||
| 	if (!(vg = text_vg_import(fi, tc->path_live, fi->fmt->cmd->um, &when, | ||||
| 				  &desc))) { | ||||
| 		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(vgname, vg->name)) { | ||||
| 		pool_free(fi->fmt->cmd->mem, vg); | ||||
| 		log_err("'%s' does not contain volume group '%s'.", | ||||
| 			tc->path_live, vgname); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return vg; | ||||
| } | ||||
|  | ||||
| static int _vg_write(struct format_instance *fi, struct volume_group *vg, | ||||
| 		     void *mdl) | ||||
| { | ||||
| 	struct text_context *tc = (struct text_context *) mdl; | ||||
|  | ||||
| 	FILE *fp; | ||||
| 	int fd; | ||||
| 	char *slash; | ||||
| 	char temp_file[PATH_MAX], temp_dir[PATH_MAX]; | ||||
|  | ||||
| 	slash = rindex(tc->path_edit, '/'); | ||||
|  | ||||
| 	if (slash == 0) | ||||
| 		strcpy(temp_dir, "."); | ||||
| 	else if (slash - tc->path_edit < PATH_MAX) { | ||||
| 		strncpy(temp_dir, tc->path_edit, slash - tc->path_edit); | ||||
| 		temp_dir[slash - tc->path_edit] = '\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, tc->desc)) { | ||||
| 		log_error("Failed to write metadata to %s.", temp_file); | ||||
| 		fclose(fp); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (fsync(fd)) { | ||||
| 		log_sys_error("fsync", tc->path_edit); | ||||
| 		fclose(fp); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (fclose(fp)) { | ||||
| 		log_sys_error("fclose", tc->path_edit); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (rename(temp_file, tc->path_edit)) { | ||||
| 		log_error("%s: rename to %s failed: %s", temp_file, | ||||
| 			  tc->path_edit, strerror(errno)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _pv_commit(struct format_instance *fi, struct physical_volume *pv, | ||||
| 		      void *mdl) | ||||
| { | ||||
| 	// struct text_context *tc = (struct text_context *) mdl; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _vg_commit(struct format_instance *fi, struct volume_group *vg, | ||||
| 		      void *mdl) | ||||
| { | ||||
| 	struct text_context *tc = (struct text_context *) mdl; | ||||
|  | ||||
| 	if (rename(tc->path_edit, tc->path_live)) { | ||||
| 		log_error("%s: rename to %s failed: %s", tc->path_edit, | ||||
| 			  tc->path_edit, strerror(errno)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	sync(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _vg_remove(struct format_instance *fi, struct volume_group *vg, | ||||
| 		      void *mdl) | ||||
| { | ||||
| 	struct text_context *tc = (struct text_context *) mdl; | ||||
|  | ||||
| 	if (path_exists(tc->path_edit) && unlink(tc->path_edit)) { | ||||
| 		log_sys_error("unlink", tc->path_edit); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (path_exists(tc->path_live) && unlink(tc->path_live)) { | ||||
| 		log_sys_error("unlink", tc->path_live); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	sync(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* Add vgname to list if it's not already there */ | ||||
| static int _add_vgname(struct format_type *fmt, struct list *names, | ||||
| 		       char *vgname) | ||||
| { | ||||
| 	struct list *nlh; | ||||
| 	struct name_list *nl; | ||||
|  | ||||
| 	list_iterate(nlh, names) { | ||||
| 		nl = list_item(nlh, struct name_list); | ||||
| 		if (!strcmp(vgname, nl->name)) | ||||
| 			return 1; | ||||
| 	} | ||||
|  | ||||
| 	vgcache_add(vgname, NULL, NULL, fmt); | ||||
|  | ||||
| 	if (!(nl = pool_alloc(fmt->cmd->mem, sizeof(*nl)))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(nl->name = pool_strdup(fmt->cmd->mem, vgname))) { | ||||
| 		log_error("strdup %s failed", vgname); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	list_add(names, &nl->list); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct list *_get_vgs(struct format_type *fmt, struct list *names) | ||||
| { | ||||
| 	struct dirent *dirent; | ||||
| 	struct dir_list *dl; | ||||
| 	struct list *dlh, *dir_list; | ||||
| 	char *tmp; | ||||
| 	DIR *d; | ||||
|  | ||||
| 	dir_list = (struct list *) fmt->private; | ||||
|  | ||||
| 	list_iterate(dlh, dir_list) { | ||||
| 		dl = list_item(dlh, struct dir_list); | ||||
| 		if (!(d = opendir(dl->dir))) { | ||||
| 			log_sys_error("opendir", dl->dir); | ||||
| 			continue; | ||||
| 		} | ||||
| 		while ((dirent = readdir(d))) | ||||
| 			if (strcmp(dirent->d_name, ".") && | ||||
| 			    strcmp(dirent->d_name, "..") && | ||||
| 			    (!(tmp = strstr(dirent->d_name, ".tmp")) || | ||||
| 			     tmp != dirent->d_name + strlen(dirent->d_name) | ||||
| 			     - 4)) | ||||
| 				if (!_add_vgname(fmt, names, dirent->d_name)) | ||||
| 					return NULL; | ||||
|  | ||||
| 		if (closedir(d)) | ||||
| 			log_sys_error("closedir", dl->dir); | ||||
| 	} | ||||
|  | ||||
| 	return names; | ||||
| } | ||||
|  | ||||
| static struct list *_get_pvs(struct format_type *fmt, struct list *results) | ||||
| { | ||||
| 	struct pv_list *pvl, *rhl; | ||||
| 	struct list *vgh; | ||||
| 	struct list *pvh; | ||||
| 	struct list *names = pool_alloc(fmt->cmd->mem, sizeof(*names)); | ||||
| 	struct list *rh; | ||||
| 	struct name_list *nl; | ||||
| 	struct volume_group *vg; | ||||
|  | ||||
| 	list_init(names); | ||||
| 	if (!_get_vgs(fmt, names)) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_iterate(vgh, names) { | ||||
|  | ||||
| 		nl = list_item(vgh, struct name_list); | ||||
| 		if (!(vg = vg_read(fmt->cmd, nl->name))) { | ||||
| 			log_error("format_text: _get_pvs failed to read VG %s", | ||||
| 				  nl->name); | ||||
| 			continue; | ||||
| 		} | ||||
| 		/* FIXME Use temp hash! */ | ||||
| 		list_iterate(pvh, &vg->pvs) { | ||||
| 			pvl = list_item(pvh, struct pv_list); | ||||
|  | ||||
| 			/* If in use, remove from list of orphans */ | ||||
| 			list_iterate(rh, results) { | ||||
| 				rhl = list_item(rh, struct pv_list); | ||||
| 				if (id_equal(&rhl->pv->id, &pvl->pv->id)) { | ||||
| 					if (*rhl->pv->vg_name) | ||||
| 						log_err("PV %s in two VGs " | ||||
| 							"%s and %s", | ||||
| 							dev_name(rhl->pv->dev), | ||||
| 							rhl->pv->vg_name, | ||||
| 							vg->name); | ||||
| 					else | ||||
| 						memcpy(&rhl->pv, &pvl->pv, | ||||
| 						       sizeof(struct | ||||
| 							      physical_volume)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pool_free(fmt->cmd->mem, names); | ||||
| 	return results; | ||||
| } | ||||
|  | ||||
| static int _pv_write(struct format_instance *fi, struct physical_volume *pv, | ||||
| 		     void *mdl) | ||||
| { | ||||
| 	/* No on-disk PV structure change required! */ | ||||
| 	/* FIXME vgcache could be wrong */ | ||||
| 	return 1; | ||||
| 	//return (fi->fmt->cmd->fmt1->ops->pv_write(fi, pv, NULL)); | ||||
| /*** FIXME Not required? | ||||
| 	struct volume_group *vg; | ||||
| 	struct list *pvh; | ||||
|  | ||||
| 	vg = _vg_read(fi, pv->vg_name); | ||||
|  | ||||
| 	// Find the PV in this VG  | ||||
| 	if (vg) { | ||||
| 		list_iterate(pvh, &vg->pvs) { | ||||
| 			struct pv_list *vgpv = list_item(pvh, struct pv_list); | ||||
|  | ||||
| 			if (id_equal(&pv->id, &vgpv->pv->id)) { | ||||
| 				vgpv->pv->status = pv->status; | ||||
| 				vgpv->pv->size = pv->size; | ||||
|  | ||||
| 				// Not sure if it's worth doing these  | ||||
| 				vgpv->pv->pe_size = pv->pe_size; | ||||
| 				vgpv->pv->pe_count = pv->pe_count; | ||||
| 				vgpv->pv->pe_start = pv->pe_start; | ||||
| 				vgpv->pv->pe_alloc_count = pv->pe_alloc_count; | ||||
|  | ||||
| 				// Write it back  | ||||
| 				_vg_write(fi, vg); | ||||
| 				pool_free(fi->fmt->cmd->mem, vg); | ||||
| 				return 1; | ||||
| 			} | ||||
| 		} | ||||
| 		pool_free(fi->fmt->cmd->mem, vg); | ||||
| 	} | ||||
|  | ||||
| 	// Can't handle PVs not in a VG  | ||||
| 	return 0; | ||||
| ***/ | ||||
| } | ||||
|  | ||||
| static int _pv_read(struct format_type *fmt, const char *pv_name, | ||||
| 		    struct physical_volume *pv) | ||||
| { | ||||
| 	struct pv_list *pvl; | ||||
| 	struct list *vgh; | ||||
| 	struct list *pvh; | ||||
| 	struct list *names = pool_alloc(fmt->cmd->mem, sizeof(*names)); | ||||
| 	struct name_list *nl; | ||||
| 	struct volume_group *vg; | ||||
| 	struct id *id; | ||||
|  | ||||
| 	/* FIXME Push up to pv_read */ | ||||
| 	if (!(id = uuid_map_lookup_label(fmt->cmd->mem, fmt->cmd->um, pv_name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	list_init(names); | ||||
| 	if (!_get_vgs(fmt, names)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	list_iterate(vgh, names) { | ||||
|  | ||||
| 		nl = list_item(vgh, struct name_list); | ||||
| 		if (!(vg = vg_read(fmt->cmd, nl->name))) { | ||||
| 			log_error("format_text: _pv_read failed to read VG %s", | ||||
| 				  nl->name); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		list_iterate(pvh, &vg->pvs) { | ||||
| 			pvl = list_item(pvh, struct pv_list); | ||||
| 			if (id_equal(&pvl->pv->id, id)) { | ||||
| 				memcpy(pv, pvl->pv, sizeof(*pv)); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pool_free(fmt->cmd->mem, names); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _destroy_instance(struct format_instance *fid) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static void _free_dirs(struct list *dir_list) | ||||
| { | ||||
| 	struct list *dl, *tmp; | ||||
|  | ||||
| 	list_iterate_safe(dl, tmp, dir_list) { | ||||
| 		list_del(dl); | ||||
| 		dbg_free(dl); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void _destroy(struct format_type *fmt) | ||||
| { | ||||
| 	if (fmt->private) { | ||||
| 		_free_dirs((struct list *) fmt->private); | ||||
| 		dbg_free(fmt->private); | ||||
| 	} | ||||
|  | ||||
| 	dbg_free(fmt); | ||||
| } | ||||
|  | ||||
| static struct format_instance *_create_text_instance(struct format_type *fmt, | ||||
| 						     const char *vgname, | ||||
| 						     void *context) | ||||
| { | ||||
| 	struct format_instance *fid; | ||||
| 	struct metadata_area *mda; | ||||
| 	struct dir_list *dl; | ||||
| 	struct list *dlh, *dir_list; | ||||
| 	char path[PATH_MAX]; | ||||
|  | ||||
| 	if (!(fid = pool_alloc(fmt->cmd->mem, sizeof(*fid)))) { | ||||
| 		log_error("Couldn't allocate format instance object."); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	fid->fmt = fmt; | ||||
|  | ||||
| 	list_init(&fid->metadata_areas); | ||||
|  | ||||
| 	if (!vgname) { | ||||
| 		if (!(mda = pool_alloc(fmt->cmd->mem, sizeof(*mda)))) { | ||||
| 			stack; | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		mda->metadata_locn = context; | ||||
| 		list_add(&fid->metadata_areas, &mda->list); | ||||
| 	} else { | ||||
| 		dir_list = (struct list *) fmt->private; | ||||
|  | ||||
| 		list_iterate(dlh, dir_list) { | ||||
| 			dl = list_item(dlh, struct dir_list); | ||||
| 			if (lvm_snprintf(path, PATH_MAX, "%s/%s", | ||||
| 					 dl->dir, vgname) < 0) { | ||||
| 				log_error("Name too long %s/%s", dl->dir, | ||||
| 					  vgname); | ||||
| 				return NULL; | ||||
| 			} | ||||
|  | ||||
| 			context = create_text_context(fmt, path, NULL); | ||||
| 			if (!(mda = pool_alloc(fmt->cmd->mem, sizeof(*mda)))) { | ||||
| 				stack; | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			mda->metadata_locn = context; | ||||
| 			list_add(&fid->metadata_areas, &mda->list); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return fid; | ||||
|  | ||||
| } | ||||
|  | ||||
| void *create_text_context(struct format_type *fmt, const char *path, | ||||
| 			  const char *desc) | ||||
| { | ||||
| 	struct text_context *tc; | ||||
| 	char *tmp; | ||||
|  | ||||
| 	if ((tmp = strstr(path, ".tmp")) && (tmp == path + strlen(path) - 4)) { | ||||
| 		log_error("%s: Volume group filename may not end in .tmp", | ||||
| 			  path); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(tc = pool_alloc(fmt->cmd->mem, sizeof(*tc)))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(tc->path_live = pool_strdup(fmt->cmd->mem, path))) { | ||||
| 		stack; | ||||
| 		goto no_mem; | ||||
| 	} | ||||
|  | ||||
| 	if (!(tc->path_edit = pool_alloc(fmt->cmd->mem, strlen(path) + 5))) { | ||||
| 		stack; | ||||
| 		goto no_mem; | ||||
| 	} | ||||
| 	sprintf(tc->path_edit, "%s.tmp", path); | ||||
|  | ||||
| 	if (!desc) | ||||
| 		desc = ""; | ||||
|  | ||||
| 	if (!(tc->desc = pool_strdup(fmt->cmd->mem, desc))) { | ||||
| 		stack; | ||||
| 		goto no_mem; | ||||
| 	} | ||||
|  | ||||
| 	return (void *) tc; | ||||
|  | ||||
|       no_mem: | ||||
| 	pool_free(fmt->cmd->mem, tc); | ||||
|  | ||||
| 	log_err("Couldn't allocate text format context object."); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| 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, | ||||
| 	pv_commit:	_pv_commit, | ||||
| 	vg_setup:	_vg_setup, | ||||
| 	lv_setup:	_lv_setup, | ||||
| 	vg_read:	_vg_read, | ||||
| 	vg_write:	_vg_write, | ||||
| 	vg_remove:	_vg_remove, | ||||
| 	vg_commit:	_vg_commit, | ||||
| 	create_instance:_create_text_instance, | ||||
| 	destroy_instance:_destroy_instance, | ||||
| 	destroy:	_destroy | ||||
| }; | ||||
|  | ||||
| static int _add_dir(const char *dir, struct list *dir_list) | ||||
| { | ||||
| 	struct dir_list *dl; | ||||
|  | ||||
| 	if (create_dir(dir)) { | ||||
| 		if (!(dl = dbg_malloc(sizeof(struct list) + strlen(dir) + 1))) { | ||||
| 			log_error("_add_dir allocation failed"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		strcpy(dl->dir, dir); | ||||
| 		list_add(dir_list, &dl->list); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct format_type *create_text_format(struct cmd_context *cmd) | ||||
| { | ||||
| 	struct format_type *fmt; | ||||
| 	struct config_node *cn; | ||||
| 	struct config_value *cv; | ||||
| 	struct list *dir_list; | ||||
|  | ||||
| 	if (!(fmt = dbg_malloc(sizeof(*fmt)))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	fmt->cmd = cmd; | ||||
| 	fmt->ops = &_text_handler; | ||||
| 	fmt->name = FMT_TEXT_NAME; | ||||
| 	fmt->features = FMT_SEGMENTS; | ||||
|  | ||||
| 	if (!(dir_list = dbg_malloc(sizeof(struct list)))) { | ||||
| 		log_error("Failed to allocate dir_list"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_init(dir_list); | ||||
| 	fmt->private = (void *) dir_list; | ||||
|  | ||||
| 	if (!(cn = find_config_node(cmd->cf->root, "metadata/dirs", '/'))) { | ||||
| 		log_verbose("metadata/dirs not in config file: Defaulting " | ||||
| 			    "to /etc/lvm/metadata"); | ||||
| 		_add_dir("/etc/lvm/metadata", dir_list); | ||||
| 		return fmt; | ||||
| 	} | ||||
|  | ||||
| 	for (cv = cn->v; cv; cv = cv->next) { | ||||
| 		if (cv->type != CFG_STRING) { | ||||
| 			log_error("Invalid string in config file: " | ||||
| 				  "metadata/dirs"); | ||||
| 			goto err; | ||||
| 		} | ||||
|  | ||||
| 		if (!_add_dir(cv->v.str, dir_list)) { | ||||
| 			log_error("Failed to add %s to internal device cache", | ||||
| 				  cv->v.str); | ||||
| 			goto err; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return fmt; | ||||
|  | ||||
|       err: | ||||
| 	_free_dirs(dir_list); | ||||
|  | ||||
| 	dbg_free(fmt); | ||||
| 	return NULL; | ||||
| } | ||||
							
								
								
									
										39
									
								
								lib/format_text/format-text.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								lib/format_text/format-text.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| /* | ||||
|  * 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" | ||||
|  | ||||
| /* | ||||
|  * Archives a vg config.  '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. | ||||
|  */ | ||||
| int archive_vg(struct volume_group *vg, | ||||
| 	       const char *dir, | ||||
| 	       const char *desc, | ||||
| 	       uint32_t retain_days, | ||||
| 	       uint32_t min_archive); | ||||
|  | ||||
| /* | ||||
|  * Displays a list of vg backups in a particular archive directory. | ||||
|  */ | ||||
| int archive_list(struct cmd_context *cmd, struct uuid_map *um, | ||||
| 		 const char *dir, const char *vg); | ||||
|  | ||||
| /* | ||||
|  * The text format can read and write a volume_group to a file. | ||||
|  */ | ||||
| struct format_type *create_text_format(struct cmd_context *cmd); | ||||
| void *create_text_context(struct format_type *fmt, const char *path,  | ||||
| 			  const char *desc); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										33
									
								
								lib/format_text/import-export.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								lib/format_text/import-export.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| /* | ||||
|  * 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, const char *desc); | ||||
| struct volume_group *text_vg_import(struct format_instance *fid, | ||||
| 				    const char *file, | ||||
| 				    struct uuid_map *um, | ||||
| 				    time_t *when, char **desc); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										677
									
								
								lib/format_text/import.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										677
									
								
								lib/format_text/import.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,677 @@ | ||||
| /* | ||||
|  * 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" | ||||
| #include "toolcontext.h" | ||||
|  | ||||
| typedef int (*section_fn) (struct format_instance * fid, 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_uint32(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 format_instance *fid, 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."); | ||||
| 		else | ||||
| 			log_err("Couldn't find device with uuid '%s'.", buffer); | ||||
|  | ||||
| 		if (partial_mode()) | ||||
| 			vg->status |= PARTIAL_VG; | ||||
| 		else | ||||
| 			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_alloc_count = 0; | ||||
| 	pv->fid = fid; | ||||
|  | ||||
| 	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; | ||||
| 	seg->lv = lv; | ||||
|  | ||||
| 	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_alloc_count += 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 format_instance *fid, 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; | ||||
|  | ||||
| 	/* FIXME: read full lvid */ | ||||
| 	if (!_read_id(&lv->lvid.id[1], lvn, "id")) { | ||||
| 		log_err("Couldn't read uuid for logical volume %s.", lv->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	memcpy(&lv->lvid.id[0], &lv->vg->id, sizeof(lv->lvid.id[0])); | ||||
|  | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
| 	lv->minor = -1; | ||||
| 	if ((lv->status & FIXED_MINOR) && | ||||
| 	    !_read_int32(lvn, "minor", &lv->minor)) { | ||||
| 		log_error("Couldn't read 'minor' value 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_snapshot(struct format_instance *fid, struct pool *mem, | ||||
| 			  struct volume_group *vg, struct config_node *sn, | ||||
| 			  struct config_node *vgn, struct hash_table *pv_hash, | ||||
| 			  struct uuid_map *um) | ||||
| { | ||||
| 	uint32_t chunk_size; | ||||
| 	const char *org_name, *cow_name; | ||||
| 	struct logical_volume *org, *cow; | ||||
|  | ||||
| 	if (!(sn = sn->child)) { | ||||
| 		log_err("Empty snapshot section."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_uint32(sn, "chunk_size", &chunk_size)) { | ||||
| 		log_err("Couldn't read chunk size for snapshot."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(cow_name = find_config_str(sn, "cow_store", '/', NULL))) { | ||||
| 		log_err("Snapshot cow storage not specified."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(org_name = find_config_str(sn, "origin", '/', NULL))) { | ||||
| 		log_err("Snapshot origin not specified."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(cow = find_lv(vg, cow_name))) { | ||||
| 		log_err("Unknown logical volume specified for " | ||||
| 			"snapshot cow store."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(org = find_lv(vg, org_name))) { | ||||
| 		log_err("Unknown logical volume specified for " | ||||
| 			"snapshot origin."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!vg_add_snapshot(org, cow, 1, chunk_size)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_sections(struct format_instance *fid, | ||||
| 			  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, int optional) | ||||
| { | ||||
| 	struct config_node *n; | ||||
|  | ||||
| 	if (!(n = find_config_node(vgn, section, '/'))) { | ||||
| 		if (!optional) { | ||||
| 			log_err("Couldn't find section '%s'.", section); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	for (n = n->child; n; n = n->sib) { | ||||
| 		if (!fn(fid, mem, vg, n, vgn, pv_hash, um)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct volume_group *_read_vg(struct format_instance *fid, | ||||
| 				     struct config_file *cf, | ||||
| 				     struct uuid_map *um) | ||||
| { | ||||
| 	struct config_node *vgn, *cn; | ||||
| 	struct volume_group *vg; | ||||
| 	struct hash_table *pv_hash = NULL; | ||||
| 	struct pool *mem = fid->fmt->cmd->mem; | ||||
|  | ||||
| 	/* skip any top-level values */ | ||||
| 	for (vgn = cf->root; (vgn && vgn->v); vgn = vgn->sib) ; | ||||
|  | ||||
| 	if (!vgn) { | ||||
| 		log_err("Couldn't find volume group in file."); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(vg = pool_zalloc(mem, sizeof(*vg)))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	vg->cmd = fid->fmt->cmd; | ||||
|  | ||||
| 	/* FIXME Determine format type from file contents */ | ||||
| 	/* eg Set to instance of fmt1 here if reading a format1 backup? */ | ||||
| 	vg->fid = fid; | ||||
|  | ||||
| 	if (!(vg->name = pool_strdup(mem, vgn->key))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!(vg->system_id = pool_zalloc(mem, NAME_LEN))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	vgn = vgn->child; | ||||
|  | ||||
| 	if ((cn = find_config_node(vgn, "system_id", '/')) && cn->v) { | ||||
| 		if (!cn->v->v.str) { | ||||
| 			log_error("system_id must be a string"); | ||||
| 			goto bad; | ||||
| 		} | ||||
| 		strncpy(vg->system_id, cn->v->v.str, NAME_LEN); | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_id(&vg->id, vgn, "id")) { | ||||
| 		log_err("Couldn't read uuid for volume group %s.", vg->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_int32(vgn, "seqno", &vg->seqno)) { | ||||
| 		log_err("Couldn't read 'seqno' 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(fid, "physical_volumes", _read_pv, mem, vg, | ||||
| 			    vgn, pv_hash, um, 0)) { | ||||
| 		log_err("Couldn't find all physical volumes for volume " | ||||
| 			"group %s.", vg->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	list_init(&vg->lvs); | ||||
| 	if (!_read_sections(fid, "logical_volumes", _read_lv, mem, vg, | ||||
| 			    vgn, pv_hash, um, 1)) { | ||||
| 		log_err("Couldn't read all logical volumes for volume " | ||||
| 			"group %s.", vg->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	list_init(&vg->snapshots); | ||||
| 	if (!_read_sections(fid, "snapshots", _read_snapshot, mem, vg, | ||||
| 			    vgn, pv_hash, um, 1)) { | ||||
| 		log_err("Couldn't read all snapshots for volume group %s.", | ||||
| 			vg->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	hash_destroy(pv_hash); | ||||
|  | ||||
| 	if (vg->status & PARTIAL_VG) { | ||||
| 		vg->status &= ~LVM_WRITE; | ||||
| 		vg->status |= LVM_READ; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Finished. | ||||
| 	 */ | ||||
| 	return vg; | ||||
|  | ||||
|       bad: | ||||
| 	if (pv_hash) | ||||
| 		hash_destroy(pv_hash); | ||||
|  | ||||
| 	pool_free(mem, vg); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static void _read_desc(struct pool *mem, | ||||
| 		       struct config_file *cf, time_t * when, char **desc) | ||||
| { | ||||
| 	const char *d; | ||||
| 	unsigned int u = 0u; | ||||
|  | ||||
| 	d = find_config_str(cf->root, "description", '/', ""); | ||||
| 	*desc = pool_strdup(mem, d); | ||||
|  | ||||
| 	get_config_uint32(cf->root, "creation_time", '/', &u); | ||||
| 	*when = u; | ||||
| } | ||||
|  | ||||
| struct volume_group *text_vg_import(struct format_instance *fid, | ||||
| 				    const char *file, | ||||
| 				    struct uuid_map *um, | ||||
| 				    time_t * when, char **desc) | ||||
| { | ||||
| 	struct volume_group *vg = NULL; | ||||
| 	struct config_file *cf; | ||||
|  | ||||
| 	*desc = NULL; | ||||
| 	*when = 0; | ||||
|  | ||||
| 	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(fid, cf, um))) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	_read_desc(fid->fmt->cmd->mem, cf, when, desc); | ||||
|  | ||||
|       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 | ||||
| 				] | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										152
									
								
								lib/label/label.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								lib/label/label.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										569
									
								
								lib/label/lvm2_label.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										569
									
								
								lib/label/lvm2_label.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,569 @@ | ||||
| /* | ||||
|  * 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); | ||||
							
								
								
									
										99
									
								
								lib/label/uuid-map.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								lib/label/uuid-map.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| /* | ||||
|  * 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" | ||||
| #include "pool.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; | ||||
| } | ||||
|  | ||||
| struct id *uuid_map_lookup_label(struct pool *mem, struct uuid_map *um, | ||||
| 				 const char *name) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 	struct label *lab; | ||||
| 	struct id *id; | ||||
|  | ||||
| 	if (!(dev = dev_cache_get(name, um->filter))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!label_read(dev, &lab)) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(id = pool_alloc(mem, sizeof(*id)))) { | ||||
| 		stack; | ||||
| 		label_destroy(lab); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	memcpy(id, &lab->id, sizeof(*id)); | ||||
|  | ||||
| 	label_destroy(lab); | ||||
|  | ||||
| 	return id; | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										29
									
								
								lib/label/uuid-map.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								lib/label/uuid-map.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
|  * 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" | ||||
| #include "pool.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); | ||||
| struct id *uuid_map_lookup_label(struct pool *mem, struct uuid_map *um,  | ||||
| 				 const char *name); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										115
									
								
								lib/locking/external_locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								lib/locking/external_locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "locking.h" | ||||
| #include "locking_types.h" | ||||
| #include "activate.h" | ||||
| #include "config.h" | ||||
| #include "defaults.h" | ||||
| #include "lvm-file.h" | ||||
| #include "lvm-string.h" | ||||
| #include "dbg_malloc.h" | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/file.h> | ||||
| #include <fcntl.h> | ||||
| #include <dlfcn.h> | ||||
| #include <signal.h> | ||||
|  | ||||
| static void *locking_module = NULL; | ||||
| static void (*end_fn) (void) = NULL; | ||||
| static int (*lock_fn) (struct cmd_context * cmd, const char *resource, | ||||
| 		       int flags) = NULL; | ||||
| static int (*init_fn) (int type, struct config_file * cf) = NULL; | ||||
|  | ||||
| static int lock_resource(struct cmd_context *cmd, const char *resource, | ||||
| 			 int flags) | ||||
| { | ||||
| 	if (lock_fn) | ||||
| 		return lock_fn(cmd, resource, flags); | ||||
| 	else | ||||
| 		return 0; | ||||
| } | ||||
|  | ||||
| static void fin_external_locking(void) | ||||
| { | ||||
| 	if (end_fn) | ||||
| 		end_fn(); | ||||
|  | ||||
| 	dlclose(locking_module); | ||||
|  | ||||
| 	locking_module = NULL; | ||||
| 	end_fn = NULL; | ||||
| 	lock_fn = NULL; | ||||
| } | ||||
|  | ||||
| int init_external_locking(struct locking_type *locking, struct config_file *cf) | ||||
| { | ||||
| 	char _lock_lib[PATH_MAX]; | ||||
|  | ||||
| 	if (locking_module) { | ||||
| 		log_error("External locking already initialised"); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	locking->lock_resource = lock_resource; | ||||
| 	locking->fin_locking = fin_external_locking; | ||||
|  | ||||
| 	/* Get locking module name from config file */ | ||||
| 	strncpy(_lock_lib, find_config_str(cf->root, "global/locking_library", | ||||
| 					   '/', "lvm2_locking.so"), | ||||
| 		sizeof(_lock_lib)); | ||||
|  | ||||
| 	/* If there is a module_dir in the config file then | ||||
| 	   look for the locking module in there first and then | ||||
| 	   using the normal dlopen(3) mechanism of looking | ||||
| 	   down LD_LIBRARY_PATH and /lib, /usr/lib. | ||||
| 	   If course, if the library name starts with a slash then | ||||
| 	   just use the name... */ | ||||
| 	if (_lock_lib[0] != '/') { | ||||
| 		struct stat st; | ||||
| 		char _lock_lib1[PATH_MAX]; | ||||
|  | ||||
| 		lvm_snprintf(_lock_lib1, sizeof(_lock_lib1), | ||||
| 			     "%s/%s", | ||||
| 			     find_config_str(cf->root, "global/module_dir", | ||||
| 					     '/', "RUBBISH"), _lock_lib); | ||||
|  | ||||
| 		/* Does it exist ? */ | ||||
| 		if (stat(_lock_lib1, &st) == 0) { | ||||
| 			strcpy(_lock_lib, _lock_lib1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	log_very_verbose("Opening locking library %s", _lock_lib); | ||||
|  | ||||
| 	locking_module = dlopen(_lock_lib, RTLD_LAZY); | ||||
| 	if (!locking_module) { | ||||
| 		log_error("Unable to open external locking module %s", | ||||
| 			  _lock_lib); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Get the functions we need */ | ||||
| 	init_fn = dlsym(locking_module, "init_locking"); | ||||
| 	lock_fn = dlsym(locking_module, "lock_resource"); | ||||
| 	end_fn = dlsym(locking_module, "end_locking"); | ||||
|  | ||||
| 	/* Are they all there ? */ | ||||
| 	if (!end_fn || !init_fn || !lock_fn) { | ||||
| 		log_error ("Shared library %s does not contain locking " | ||||
| 			   "functions", _lock_lib); | ||||
| 		dlclose(locking_module); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_verbose("Opened external locking module %s", _lock_lib); | ||||
| 	return init_fn(2, cf); | ||||
| } | ||||
							
								
								
									
										260
									
								
								lib/locking/file_locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								lib/locking/file_locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,260 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "locking.h" | ||||
| #include "locking_types.h" | ||||
| #include "activate.h" | ||||
| #include "config.h" | ||||
| #include "defaults.h" | ||||
| #include "lvm-file.h" | ||||
| #include "lvm-string.h" | ||||
| #include "dbg_malloc.h" | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/file.h> | ||||
| #include <fcntl.h> | ||||
| #include <signal.h> | ||||
|  | ||||
| struct lock_list { | ||||
| 	struct list list; | ||||
| 	int lf; | ||||
| 	char *res; | ||||
| }; | ||||
|  | ||||
| static struct list _lock_list; | ||||
| static char _lock_dir[NAME_LEN]; | ||||
|  | ||||
| static sig_t _oldhandler; | ||||
| static sigset_t _fullsigset, _intsigset; | ||||
| static int _handler_installed; | ||||
|  | ||||
| static int _release_lock(const char *file) | ||||
| { | ||||
| 	struct lock_list *ll; | ||||
| 	struct list *llh, *llt; | ||||
|  | ||||
| 	struct stat buf1, buf2; | ||||
|  | ||||
| 	list_iterate_safe(llh, llt, &_lock_list) { | ||||
| 		ll = list_item(llh, struct lock_list); | ||||
|  | ||||
| 		if (!file || !strcmp(ll->res, file)) { | ||||
| 			list_del(llh); | ||||
| 			log_very_verbose("Unlocking %s", ll->res); | ||||
|  | ||||
| 			if (flock(ll->lf, LOCK_NB | LOCK_UN)) | ||||
| 				log_sys_error("flock", ll->res); | ||||
|  | ||||
| 			if (!flock(ll->lf, LOCK_NB | LOCK_EX) && | ||||
| 			    !stat(ll->res, &buf1) && | ||||
| 			    !fstat(ll->lf, &buf2) && | ||||
| 			    !memcmp(&buf1.st_ino, &buf2.st_ino, sizeof(ino_t))) | ||||
| 				if (unlink(ll->res)) | ||||
| 					log_sys_error("unlink", ll->res); | ||||
|  | ||||
| 			if (close(ll->lf) < 0) | ||||
| 				log_sys_error("close", ll->res); | ||||
|  | ||||
| 			dbg_free(ll->res); | ||||
| 			dbg_free(llh); | ||||
|  | ||||
| 			if (file) | ||||
| 				return 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void fin_file_locking(void) | ||||
| { | ||||
| 	_release_lock(NULL); | ||||
| } | ||||
|  | ||||
| static void _remove_ctrl_c_handler() | ||||
| { | ||||
| 	siginterrupt(SIGINT, 0); | ||||
| 	if (!_handler_installed || _oldhandler == SIG_ERR) | ||||
| 		return; | ||||
|  | ||||
| 	sigprocmask(SIG_SETMASK, &_fullsigset, NULL); | ||||
| 	if (signal(SIGINT, _oldhandler) == SIG_ERR) | ||||
| 		log_sys_error("signal", "_remove_ctrl_c_handler"); | ||||
|  | ||||
| 	_handler_installed = 0; | ||||
| } | ||||
|  | ||||
| void _trap_ctrl_c(int signal) | ||||
| { | ||||
| 	_remove_ctrl_c_handler(); | ||||
| 	log_error("CTRL-c detected: giving up waiting for lock"); | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static void _install_ctrl_c_handler() | ||||
| { | ||||
| 	if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) | ||||
| 		return; | ||||
|  | ||||
| 	sigprocmask(SIG_SETMASK, &_intsigset, NULL); | ||||
| 	siginterrupt(SIGINT, 1); | ||||
|  | ||||
| 	_handler_installed = 1; | ||||
| } | ||||
|  | ||||
| static int _lock_file(const char *file, int flags) | ||||
| { | ||||
| 	int operation; | ||||
| 	int r = 1; | ||||
|  | ||||
| 	struct lock_list *ll; | ||||
| 	struct stat buf1, buf2; | ||||
|  | ||||
| 	switch (flags & LCK_TYPE_MASK) { | ||||
| 	case LCK_READ: | ||||
| 		operation = LOCK_SH; | ||||
| 		break; | ||||
| 	case LCK_WRITE: | ||||
| 		operation = LOCK_EX; | ||||
| 		break; | ||||
| 	case LCK_UNLOCK: | ||||
| 		return _release_lock(file); | ||||
| 	default: | ||||
| 		log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(ll = dbg_malloc(sizeof(struct lock_list)))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(ll->res = dbg_strdup(file))) { | ||||
| 		dbg_free(ll); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	ll->lf = -1; | ||||
|  | ||||
| 	log_very_verbose("Locking %s", ll->res); | ||||
| 	do { | ||||
| 		if (ll->lf > -1) | ||||
| 			close(ll->lf); | ||||
|  | ||||
| 		if ((ll->lf = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) | ||||
| 		    < 0) { | ||||
| 			log_sys_error("open", file); | ||||
| 			goto err; | ||||
| 		} | ||||
|  | ||||
| 		if ((flags & LCK_NONBLOCK)) | ||||
| 			operation |= LOCK_NB; | ||||
| 		else | ||||
| 			_install_ctrl_c_handler(); | ||||
|  | ||||
| 		r = flock(ll->lf, operation); | ||||
| 		if (!(flags & LCK_NONBLOCK)) | ||||
| 			_remove_ctrl_c_handler(); | ||||
|  | ||||
| 		if (r) { | ||||
| 			log_sys_error("flock", ll->res); | ||||
| 			goto err; | ||||
| 		} | ||||
|  | ||||
| 		if (!stat(ll->res, &buf1) && !fstat(ll->lf, &buf2) && | ||||
| 		    !memcmp(&buf1.st_ino, &buf2.st_ino, sizeof(ino_t))) | ||||
| 			break; | ||||
| 	} while (!(flags & LCK_NONBLOCK)); | ||||
|  | ||||
| 	list_add(&_lock_list, &ll->list); | ||||
| 	return 1; | ||||
|  | ||||
|       err: | ||||
| 	dbg_free(ll->res); | ||||
| 	dbg_free(ll); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lock_resource(struct cmd_context *cmd, const char *resource, int flags) | ||||
| { | ||||
| 	char lockfile[PATH_MAX]; | ||||
|  | ||||
| 	switch (flags & LCK_SCOPE_MASK) { | ||||
| 	case LCK_VG: | ||||
| 		if (!resource || !*resource) | ||||
| 			lvm_snprintf(lockfile, sizeof(lockfile), | ||||
| 				     "%s/P_orphans", _lock_dir); | ||||
| 		else | ||||
| 			lvm_snprintf(lockfile, sizeof(lockfile), | ||||
| 				     "%s/V_%s", _lock_dir, resource); | ||||
| 		if (!_lock_file(lockfile, flags)) | ||||
| 			return 0; | ||||
| 		break; | ||||
| 	case LCK_LV: | ||||
| 		/* Skip if driver isn't loaded */ | ||||
| 		/* FIXME Use /proc/misc instead? */ | ||||
| 		if (!driver_version(NULL, 0)) | ||||
| 			return 1; | ||||
| 		switch (flags & LCK_TYPE_MASK) { | ||||
| 		case LCK_UNLOCK: | ||||
| 			if (!lv_resume_if_active(cmd, resource)) | ||||
| 				return 0; | ||||
| 			break; | ||||
| 		case LCK_READ: | ||||
| 			if (!lv_activate(cmd, resource)) | ||||
| 				return 0; | ||||
| 			break; | ||||
| 		case LCK_WRITE: | ||||
| 			if (!lv_suspend_if_active(cmd, resource)) | ||||
| 				return 0; | ||||
| 			break; | ||||
| 		case LCK_EXCL: | ||||
| 			if (!lv_deactivate(cmd, resource)) | ||||
| 				return 0; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		log_error("Unrecognised lock scope: %d", | ||||
| 			  flags & LCK_SCOPE_MASK); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int init_file_locking(struct locking_type *locking, struct config_file *cf) | ||||
| { | ||||
| 	locking->lock_resource = lock_resource; | ||||
| 	locking->fin_locking = fin_file_locking; | ||||
|  | ||||
| 	/* Get lockfile directory from config file */ | ||||
| 	strncpy(_lock_dir, find_config_str(cf->root, "global/locking_dir", | ||||
| 					   '/', DEFAULT_LOCK_DIR), | ||||
| 		sizeof(_lock_dir)); | ||||
|  | ||||
| 	if (!create_dir(_lock_dir)) | ||||
| 		return 0; | ||||
|  | ||||
| 	list_init(&_lock_list); | ||||
|  | ||||
| 	if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) { | ||||
| 		log_sys_error("sigfillset", "init_file_locking"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (sigdelset(&_intsigset, SIGINT)) { | ||||
| 		log_sys_error("sigdelset", "init_file_locking"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										149
									
								
								lib/locking/locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								lib/locking/locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "locking.h" | ||||
| #include "locking_types.h" | ||||
| #include "lvm-string.h" | ||||
| #include "activate.h" | ||||
|  | ||||
| #include <signal.h> | ||||
|  | ||||
| static struct locking_type _locking; | ||||
| static sigset_t _oldset; | ||||
|  | ||||
| static int _lock_count = 0;	/* Number of locks held */ | ||||
| static int _signals_blocked = 0; | ||||
|  | ||||
| static void _block_signals(void) | ||||
| { | ||||
| 	sigset_t set; | ||||
|  | ||||
| 	if (_signals_blocked) | ||||
| 		return; | ||||
|  | ||||
| 	if (sigfillset(&set)) { | ||||
| 		log_sys_error("sigfillset", "_block_signals"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (sigprocmask(SIG_SETMASK, &set, &_oldset)) { | ||||
| 		log_sys_error("sigprocmask", "_block_signals"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	_signals_blocked = 1; | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static void _unblock_signals(void) | ||||
| { | ||||
| 	/* Don't unblock signals while any locks are held */ | ||||
| 	if (!_signals_blocked || _lock_count) | ||||
| 		return; | ||||
|  | ||||
| 	if (sigprocmask(SIG_SETMASK, &_oldset, NULL)) { | ||||
| 		log_sys_error("sigprocmask", "_block_signals"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	_signals_blocked = 0; | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static inline void _update_lock_count(int flags) | ||||
| { | ||||
| 	if ((flags & LCK_TYPE_MASK) == LCK_UNLOCK) | ||||
| 		_lock_count--; | ||||
| 	else | ||||
| 		_lock_count++; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Select a locking type | ||||
|  */ | ||||
| int init_locking(int type, struct config_file *cf) | ||||
| { | ||||
| 	switch (type) { | ||||
| 	case 0: | ||||
| 		init_no_locking(&_locking, cf); | ||||
| 		log_print("WARNING: Locking disabled. Be careful! " | ||||
| 			  "This could corrupt your metadata."); | ||||
| 		break; | ||||
| 	case 1: | ||||
| 		if (!init_file_locking(&_locking, cf)) | ||||
| 			return 0; | ||||
| 		log_very_verbose("File-based locking enabled."); | ||||
| 		break; | ||||
|  | ||||
| 	case 2: | ||||
| 		if (!init_external_locking(&_locking, cf)) | ||||
| 			return 0; | ||||
| 		log_very_verbose("External locking enabled."); | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		log_error("Unknown locking type requested."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void fin_locking(void) | ||||
| { | ||||
| 	_locking.fin_locking(); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * VG locking is by VG name. | ||||
|  * FIXME This should become VG uuid. | ||||
|  */ | ||||
| int _lock_vol(struct cmd_context *cmd, const char *resource, int flags) | ||||
| { | ||||
| 	_block_signals(); | ||||
|  | ||||
| 	if (!(_locking.lock_resource(cmd, resource, flags))) { | ||||
| 		_unblock_signals(); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_update_lock_count(flags); | ||||
| 	_unblock_signals(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lock_vol(struct cmd_context *cmd, const char *vol, int flags) | ||||
| { | ||||
| 	char resource[258]; | ||||
|  | ||||
| 	switch (flags & LCK_SCOPE_MASK) { | ||||
| 	case LCK_VG:		/* Lock VG to change on-disk metadata. */ | ||||
| 	case LCK_LV:		/* Suspends LV if it's active. */ | ||||
| 		strncpy(resource, (char *) vol, sizeof(resource)); | ||||
| 		break; | ||||
| 	default: | ||||
| 		log_error("Unrecognised lock scope: %d", | ||||
| 			  flags & LCK_SCOPE_MASK); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!_lock_vol(cmd, resource, flags)) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* Perform immediate unlock unless LCK_HOLD set */ | ||||
| 	if (!(flags & LCK_HOLD) && ((flags & LCK_TYPE_MASK) != LCK_UNLOCK)) { | ||||
| 		if (!_lock_vol(cmd, resource, | ||||
| 			       (flags & ~LCK_TYPE_MASK) | LCK_UNLOCK)) | ||||
| 			return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										67
									
								
								lib/locking/locking.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								lib/locking/locking.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "metadata.h" | ||||
| #include "uuid.h" | ||||
| #include "config.h" | ||||
|  | ||||
| int init_locking(int type, struct config_file *cf); | ||||
| void fin_locking(void); | ||||
|  | ||||
| /* | ||||
|  * LCK_VG: | ||||
|  *   Lock/unlock on-disk volume group data | ||||
|  *   Use "" to lock orphan PVs | ||||
|  *   char *vol holds volume group name | ||||
|  * | ||||
|  * LCK_LV: | ||||
|  *   Lock/unlock an individual logical volume | ||||
|  *   char *vol holds lvid | ||||
|  */ | ||||
| int lock_vol(struct cmd_context *cmd, const char *vol, int flags); | ||||
|  | ||||
| /* | ||||
|  * Lock type - these numbers are the same as VMS and the IBM DLM | ||||
|  */ | ||||
| #define LCK_TYPE_MASK	0x000000FF | ||||
|  | ||||
| #define LCK_NULL	0x00000000 /* LCK$_NLMODE */ | ||||
| #define LCK_READ	0x00000001 /* LCK$_CRMODE */ | ||||
|                                    /* LCK$_CWMODE */ | ||||
|                                    /* LCK$_PRMODE */ | ||||
| #define LCK_WRITE	0x00000004 /* LCK$_PWMODE */ | ||||
| #define LCK_EXCL	0x00000005 /* LCK$_EXMODE */ | ||||
| #define LCK_UNLOCK      0x00000010 /* This is ours */ | ||||
|  | ||||
| /* | ||||
|  * Lock scope | ||||
|  */ | ||||
| #define LCK_SCOPE_MASK	0x0000FF00 | ||||
| #define LCK_VG		0x00000000 | ||||
| #define LCK_LV		0x00000100 | ||||
|  | ||||
| /* | ||||
|  * Lock bits | ||||
|  */ | ||||
| #define LCK_NONBLOCK	0x00010000	/* Don't block waiting for lock? */ | ||||
| #define LCK_HOLD	0x00020000	/* Hold lock when lock_vol returns? */ | ||||
|  | ||||
| /* | ||||
|  * Common combinations | ||||
|  */ | ||||
| #define LCK_VG_READ		(LCK_VG | LCK_READ | LCK_HOLD) | ||||
| #define LCK_VG_WRITE		(LCK_VG | LCK_WRITE | LCK_HOLD) | ||||
| #define LCK_VG_UNLOCK		(LCK_VG | LCK_UNLOCK) | ||||
|  | ||||
| #define LCK_LV_DEACTIVATE	(LCK_LV | LCK_EXCL) | ||||
| #define LCK_LV_SUSPEND		(LCK_LV | LCK_WRITE) | ||||
| #define LCK_LV_ACTIVATE		(LCK_LV | LCK_READ) | ||||
| #define LCK_LV_UNLOCK		(LCK_LV | LCK_UNLOCK) | ||||
|  | ||||
| #define unlock_lv(cmd, vol)	lock_vol(cmd, vol, LCK_LV_UNLOCK) | ||||
| #define unlock_vg(cmd, vol)	lock_vol(cmd, vol, LCK_VG_UNLOCK) | ||||
|  | ||||
							
								
								
									
										32
									
								
								lib/locking/locking_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								lib/locking/locking_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "metadata.h" | ||||
| #include "config.h" | ||||
|  | ||||
| typedef int (*lock_resource_fn)(struct cmd_context *cmd, const char *resource,  | ||||
| 				int flags); | ||||
|  | ||||
| typedef void (*fin_lock_fn)(void); | ||||
|  | ||||
|  | ||||
| struct locking_type { | ||||
| 	lock_resource_fn lock_resource; | ||||
|  | ||||
| 	fin_lock_fn fin_locking; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Locking types | ||||
|  */ | ||||
| int init_no_locking(struct locking_type *locking, struct config_file *cf); | ||||
|  | ||||
| int init_file_locking(struct locking_type *locking, struct config_file *cf); | ||||
|  | ||||
| int init_external_locking(struct locking_type *locking, struct config_file *cf); | ||||
|  | ||||
							
								
								
									
										60
									
								
								lib/locking/no_locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								lib/locking/no_locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "locking.h" | ||||
| #include "locking_types.h" | ||||
| #include "lvm-string.h" | ||||
| #include "activate.h" | ||||
|  | ||||
| #include <signal.h> | ||||
|  | ||||
| /* | ||||
|  * No locking | ||||
|  */ | ||||
|  | ||||
| static void _no_fin_locking(void) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static int _no_lock_resource(struct cmd_context *cmd, const char *resource, | ||||
| 			     int flags) | ||||
| { | ||||
| 	switch (flags & LCK_SCOPE_MASK) { | ||||
| 	case LCK_VG: | ||||
| 		break; | ||||
| 	case LCK_LV: | ||||
| 		switch (flags & LCK_TYPE_MASK) { | ||||
| 		case LCK_UNLOCK: | ||||
| 			return lv_resume_if_active(cmd, resource); | ||||
| 		case LCK_READ: | ||||
| 			return lv_activate(cmd, resource); | ||||
| 		case LCK_WRITE: | ||||
| 			return lv_suspend_if_active(cmd, resource); | ||||
| 		case LCK_EXCL: | ||||
| 			return lv_deactivate(cmd, resource); | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		log_error("Unrecognised lock scope: %d", | ||||
| 			  flags & LCK_SCOPE_MASK); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int init_no_locking(struct locking_type *locking, struct config_file *cf) | ||||
| { | ||||
| 	locking->lock_resource = _no_lock_resource; | ||||
| 	locking->fin_locking = _no_fin_locking; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										190
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | ||||
| /* | ||||
|  * 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 int _log_suppress = 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 log_suppress(int suppress) | ||||
| { | ||||
| 	_log_suppress = suppress; | ||||
| } | ||||
|  | ||||
| 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; | ||||
|  | ||||
| 	if (!_log_suppress) { | ||||
| 		va_start(ap, format); | ||||
| 		switch (level) { | ||||
| 		case _LOG_DEBUG: | ||||
| 			if (!strcmp("<backtrace>", format)) | ||||
| 				break; | ||||
| 			if (_verbose_level > 2) { | ||||
| 				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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										96
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| /* | ||||
|  * 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) | ||||
|  * | ||||
|  * 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); | ||||
|  | ||||
| /* Suppress messages to stdout/stderr */ | ||||
| void log_suppress(int suppress); | ||||
|  | ||||
| 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("<backtrace>")	/* 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: | ||||
|  */ | ||||
							
								
								
									
										542
									
								
								lib/metadata/lv_manip.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								lib/metadata/lv_manip.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,542 @@ | ||||
| /* | ||||
|  * 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 "toolcontext.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_alloc_count += 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_alloc_count >= count); | ||||
| 		pv->pe_alloc_count -= 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; | ||||
|  | ||||
| 	lv->vg = vg; | ||||
|  | ||||
| 	if (!(lv->name = pool_strdup(cmd->mem, name))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	lv->status = status; | ||||
| 	lv->read_ahead = 0; | ||||
| 	lv->minor = -1; | ||||
| 	lv->size = (uint64_t) extents *vg->extent_size; | ||||
| 	lv->le_count = extents; | ||||
| 	list_init(&lv->segments); | ||||
|  | ||||
| 	if (!_allocate(vg, lv, acceptable_pvs, 0u, stripes, stripe_size)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (fi->fmt->ops->lv_setup && !fi->fmt->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 = (uint64_t) lv->le_count * lv->vg->extent_size; | ||||
|  | ||||
| 	if (fi->fmt->ops->lv_setup && !fi->fmt->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 += (uint64_t) extents *lv->vg->extent_size; | ||||
|  | ||||
| 	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->fmt->ops->lv_setup && !fi->fmt->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; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user