mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-11-03 08:23:48 +03:00 
			
		
		
		
	Compare commits
	
		
			884 Commits
		
	
	
		
			dev-dct-se
			...
			beta6_1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e2adc28cff | ||
| 
						 | 
					0fef6a6ecc | ||
| 
						 | 
					f2fd4b8a1f | ||
| 
						 | 
					95bd5605a8 | ||
| 
						 | 
					497cca7eca | ||
| 
						 | 
					54f78feedd | ||
| 
						 | 
					76408e53ae | ||
| 
						 | 
					be19e74d30 | ||
| 
						 | 
					dac578a775 | ||
| 
						 | 
					04732ce74b | ||
| 
						 | 
					a6c95a2374 | ||
| 
						 | 
					f68622abe9 | ||
| 
						 | 
					83a9a7bdb2 | ||
| 
						 | 
					6500afc0ca | ||
| 
						 | 
					c69b7ecc96 | ||
| 
						 | 
					615ef1e2d2 | ||
| 
						 | 
					cf69a0cd7f | ||
| 
						 | 
					06e892fb33 | ||
| 
						 | 
					e6c5dd6865 | ||
| 
						 | 
					247efdebdb | ||
| 
						 | 
					76f3792287 | ||
| 
						 | 
					64d8e2c727 | ||
| 
						 | 
					ead0bd9cb0 | ||
| 
						 | 
					66fc13b2ec | ||
| 
						 | 
					f3af4128b0 | ||
| 
						 | 
					0e43107c87 | ||
| 
						 | 
					1ee3e7997e | ||
| 
						 | 
					50fd61d91f | ||
| 
						 | 
					e4cdc051a9 | ||
| 
						 | 
					778e846e96 | ||
| 
						 | 
					a27759b647 | ||
| 
						 | 
					b75eceab41 | ||
| 
						 | 
					e748a5d5f4 | ||
| 
						 | 
					99c5a3ae46 | ||
| 
						 | 
					51da710f5a | ||
| 
						 | 
					569d69b3d2 | ||
| 
						 | 
					059a6b1d90 | ||
| 
						 | 
					990af7548a | ||
| 
						 | 
					a38aefdfc8 | ||
| 
						 | 
					3bcb12e7d1 | ||
| 
						 | 
					7904ecb462 | ||
| 
						 | 
					9ba4d45109 | ||
| 
						 | 
					56b8afe19d | ||
| 
						 | 
					f7aed9a94c | ||
| 
						 | 
					e12a7e881d | ||
| 
						 | 
					5afb65325d | ||
| 
						 | 
					135f520f32 | ||
| 
						 | 
					bc251f4ff6 | ||
| 
						 | 
					b8769751f6 | ||
| 
						 | 
					eff96d839e | ||
| 
						 | 
					aa34c23807 | ||
| 
						 | 
					195acdac8c | ||
| 
						 | 
					903e03c56c | ||
| 
						 | 
					0892767b8a | ||
| 
						 | 
					83ebfa772c | ||
| 
						 | 
					1583641322 | ||
| 
						 | 
					9510e2c256 | ||
| 
						 | 
					a9dbabe07e | ||
| 
						 | 
					12884008fa | ||
| 
						 | 
					02543bad1c | ||
| 
						 | 
					a8c56a5251 | ||
| 
						 | 
					4e5a855f3f | ||
| 
						 | 
					7e497a951e | ||
| 
						 | 
					cd08eabbfa | ||
| 
						 | 
					f7e62d9f81 | ||
| 
						 | 
					9a3761e86e | ||
| 
						 | 
					3619a68693 | ||
| 
						 | 
					efaf3c3bf9 | ||
| 
						 | 
					0e4e6a6f67 | ||
| 
						 | 
					42458e6278 | ||
| 
						 | 
					41ec995377 | ||
| 
						 | 
					4f37599326 | ||
| 
						 | 
					4144520e5c | ||
| 
						 | 
					6c4800546c | ||
| 
						 | 
					733733c8a7 | ||
| 
						 | 
					2aa67cc946 | ||
| 
						 | 
					9385981a9d | ||
| 
						 | 
					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 | 
							
								
								
									
										2
									
								
								BUGS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								BUGS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
Snapshots under 2.4.18 can deadlock due to a bug in the VM system.
 | 
			
		||||
2.4.19-pre8 is fine.
 | 
			
		||||
							
								
								
									
										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                                                   \
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										59
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
lvm2 (1.95.10-2) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fix software raid problems by ensuring lvm init script runs after
 | 
			
		||||
    raidtools init script.  (Closes: #152569)
 | 
			
		||||
 | 
			
		||||
 -- Andres Salomon <dilinger@mp3revolution.net>  Tue,  3 Sep 2002 04:05:43 -0400
 | 
			
		||||
 | 
			
		||||
lvm2 (1.95.10-1) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * New upstream release (Beta 3.2).
 | 
			
		||||
  * Change all references to /dev/device-mapper/control to
 | 
			
		||||
    /dev/mapper/control.
 | 
			
		||||
 | 
			
		||||
 -- Andres Salomon <dilinger@mp3revolution.net>  Sun,  1 Sep 2002 18:55:12 -0400
 | 
			
		||||
 | 
			
		||||
lvm2 (0.95.05-3) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Get rid of awk dependency in init script.  (Closes: #146257)
 | 
			
		||||
 | 
			
		||||
 -- Andres Salomon <dilinger@mp3revolution.net>  Sun, 12 May 2002 04:39:06 -0500
 | 
			
		||||
 | 
			
		||||
lvm2 (0.95.05-2) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Use ${shlibs:Depends} in Depends.
 | 
			
		||||
  * Get rid of postinst/postrm scripts, use debhelper's init script instead.
 | 
			
		||||
  * Add Conflicts against lvm10, lvm-common.
 | 
			
		||||
  * Fix endian issues on big-endian machines.
 | 
			
		||||
 | 
			
		||||
 -- Andres Salomon <dilinger@mp3revolution.net>  Thu,  2 May 2002 23:53:53 -0500
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										20
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
Source: lvm2
 | 
			
		||||
Section: admin
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Andres Salomon <dilinger@mp3revolution.net>
 | 
			
		||||
Build-Depends: debhelper (>> 3.0.0), libdevmapper-dev (>= 0.96.04), libreadline4-dev
 | 
			
		||||
Standards-Version: 3.5.2
 | 
			
		||||
 | 
			
		||||
Package: lvm2
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends}
 | 
			
		||||
Conflicts: lvm10, lvm-common
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										6
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
BUGS
 | 
			
		||||
INTRO
 | 
			
		||||
README
 | 
			
		||||
TODO
 | 
			
		||||
VERSION
 | 
			
		||||
doc/*
 | 
			
		||||
							
								
								
									
										64
									
								
								debian/init.d
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								debian/init.d
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
#! /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/mapper"
 | 
			
		||||
	FILE="$DIR/control"
 | 
			
		||||
	major=$(grep "[0-9] misc$" /proc/devices | sed 's/[ ]\+misc//')
 | 
			
		||||
	minor=$(grep "[0-9] device-mapper$" /proc/misc | sed 's/[ ]\+device-mapper//')
 | 
			
		||||
 | 
			
		||||
	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."
 | 
			
		||||
	;;
 | 
			
		||||
  *)
 | 
			
		||||
	echo "Usage: /etc/init.d/$NAME {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
 | 
			
		||||
							
								
								
									
										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 --update-rcd-params="start 26 S . start 50 0 6 ."
 | 
			
		||||
	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!
 | 
			
		||||
							
								
								
									
										154
									
								
								doc/example.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								doc/example.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
			
		||||
# 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).
 | 
			
		||||
 | 
			
		||||
    # 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|.*|"]
 | 
			
		||||
 | 
			
		||||
    # Use anchors if you want to be really specific
 | 
			
		||||
    # filter = ["a|^/dev/hda8$|", "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
 | 
			
		||||
							
								
								
									
										331
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,331 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns 1 if percent set, else 0 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int lv_snapshot_percent(struct logical_volume *lv, float *percent)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	struct dev_manager *dm;
 | 
			
		||||
 | 
			
		||||
	if (!(dm = dev_manager_create(lv->vg->name))) {
 | 
			
		||||
		stack;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(r = dev_manager_snapshot_percent(dm, lv, percent)))
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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);
 | 
			
		||||
/*
 | 
			
		||||
 * Returns 1 if percent has been set, else 0.
 | 
			
		||||
 */
 | 
			
		||||
int lv_snapshot_percent(struct logical_volume *lv, float *percent);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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
 | 
			
		||||
							
								
								
									
										1640
									
								
								lib/activate/dev_manager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1640
									
								
								lib/activate/dev_manager.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										41
									
								
								lib/activate/dev_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/activate/dev_manager.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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_snapshot_percent(struct dev_manager *dm,
 | 
			
		||||
				 struct logical_volume *lv, float *percent);
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										718
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										718
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,718 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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;
 | 
			
		||||
 | 
			
		||||
	case CFG_EMPTY_ARRAY:
 | 
			
		||||
		fprintf(fp, "[]");
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		log_err("Unknown value type");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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] = '\t';
 | 
			
		||||
	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 = NULL, *l, *ll = NULL;
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Special case an empty array.
 | 
			
		||||
		 */
 | 
			
		||||
		if (!h) {
 | 
			
		||||
			if (!(h = _create_value(p)))
 | 
			
		||||
				return NULL;
 | 
			
		||||
 | 
			
		||||
			h->type = CFG_EMPTY_ARRAY;
 | 
			
		||||
		}
 | 
			
		||||
	} 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);
 | 
			
		||||
	if (!v)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	switch (p->t) {
 | 
			
		||||
	case TOK_INT:
 | 
			
		||||
		v->type = CFG_INT;
 | 
			
		||||
		v->v.i = strtol(p->tb, NULL, 0);	/* FIXME: check error */
 | 
			
		||||
		match(TOK_INT);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case TOK_FLOAT:
 | 
			
		||||
		v->type = CFG_FLOAT;
 | 
			
		||||
		v->v.r = strtod(p->tb, NULL);	/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int get_config_str(struct config_node *cn, const char *path,
 | 
			
		||||
		   char sep, char **result)
 | 
			
		||||
{
 | 
			
		||||
	struct config_node *n;
 | 
			
		||||
 | 
			
		||||
	n = find_config_node(cn, path, sep);
 | 
			
		||||
 | 
			
		||||
	if (!n || !n->v || n->v->type != CFG_STRING)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	*result = n->v->v.str;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										76
									
								
								lib/config/config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								lib/config/config.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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,
 | 
			
		||||
	CFG_EMPTY_ARRAY
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
int get_config_str(struct config_node *cn, const char *path,
 | 
			
		||||
		   char sep, char **result);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										40
									
								
								lib/config/defaults.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								lib/config/defaults.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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"
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_MSG_PREFIX "  "
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_CMD_NAME 0
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
							
								
								
									
										250
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,250 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void _flush(int fd)
 | 
			
		||||
{
 | 
			
		||||
	ioctl(fd, BLKFLSBUF, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	}
 | 
			
		||||
	_flush(dev->fd);
 | 
			
		||||
	dev->flags = 0;
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dev_close(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	if (dev->fd < 0) {
 | 
			
		||||
		log_error("Attempt to close device '%s' "
 | 
			
		||||
			  "which is not open.", dev_name(dev));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										594
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										594
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,594 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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->alloc == ALLOC_STRICT) +
 | 
			
		||||
		   (lv->alloc == ALLOC_CONTIGUOUS) * 2), lv->read_ahead,
 | 
			
		||||
		  inkernel ? info.major : -1, inkernel ? info.minor : -1);
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
	alloc_policy_t alloc;
 | 
			
		||||
	const char *str;
 | 
			
		||||
} _policies[] = {
 | 
			
		||||
	{ALLOC_NEXT_FREE, "next free"},
 | 
			
		||||
	{ALLOC_STRICT, "strict"},
 | 
			
		||||
	{ALLOC_CONTIGUOUS, "contiguous"}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int _num_policies = sizeof(_policies) / sizeof(*_policies);
 | 
			
		||||
 | 
			
		||||
const char *get_alloc_string(alloc_policy_t alloc)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < _num_policies; i++)
 | 
			
		||||
		if (_policies[i].alloc == alloc)
 | 
			
		||||
			return _policies[i].str;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
alloc_policy_t get_alloc_from_string(const char *str)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < _num_policies; i++)
 | 
			
		||||
		if (!strcmp(_policies[i].str, str))
 | 
			
		||||
			return _policies[i].alloc;
 | 
			
		||||
 | 
			
		||||
	log_warn("Unknown allocation policy, defaulting to next free");
 | 
			
		||||
	return ALLOC_NEXT_FREE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv)
 | 
			
		||||
{
 | 
			
		||||
	char *size;
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	int inkernel;
 | 
			
		||||
	char uuid[64];
 | 
			
		||||
	struct snapshot *snap;
 | 
			
		||||
	struct stripe_segment *seg;
 | 
			
		||||
	struct list *lvseg;
 | 
			
		||||
	struct logical_volume *origin;
 | 
			
		||||
	float snap_percent;
 | 
			
		||||
	int snap_active;
 | 
			
		||||
 | 
			
		||||
	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 origin for a snapshot */
 | 
			
		||||
	if ((snap = find_origin(lv))) {
 | 
			
		||||
		struct list *slh, *snaplist = find_snapshots(lv);
 | 
			
		||||
 | 
			
		||||
		log_print("LV snapshot status     source of");
 | 
			
		||||
		list_iterate(slh, snaplist) {
 | 
			
		||||
			snap = list_item(slh, struct snapshot_list)->snapshot;
 | 
			
		||||
			snap_active = lv_snapshot_percent(snap->cow,
 | 
			
		||||
							  &snap_percent);
 | 
			
		||||
			log_print("                       %s%s/%s [%s]",
 | 
			
		||||
				 lv->vg->cmd->dev_dir, lv->vg->name,
 | 
			
		||||
				 snap->cow->name,
 | 
			
		||||
				 (snap_active > 0) ? "active" : "INACTIVE");
 | 
			
		||||
		}
 | 
			
		||||
		/* reset so we don't try to use this to display other snapshot
 | 
			
		||||
 		 * related information. */
 | 
			
		||||
		snap = NULL;
 | 
			
		||||
		snap_active = 0;
 | 
			
		||||
	}
 | 
			
		||||
	/* Check to see if this LV is a COW target for a snapshot */
 | 
			
		||||
	else if ((snap = find_cow(lv))) {
 | 
			
		||||
		snap_active = lv_snapshot_percent(lv, &snap_percent);
 | 
			
		||||
		log_print("LV snapshot status     %s destination for %s%s/%s",
 | 
			
		||||
		 	  (snap_active > 0) ? "active" : "INACTIVE",
 | 
			
		||||
			  lv->vg->cmd->dev_dir, lv->vg->name,
 | 
			
		||||
			  snap->origin->name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (inkernel && info.suspended)
 | 
			
		||||
		log_print("LV Status              suspended");
 | 
			
		||||
	else
 | 
			
		||||
		log_print("LV Status              %savailable",
 | 
			
		||||
			  !inkernel || (snap && (snap_active < 1))
 | 
			
		||||
			    ?  "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) {
 | 
			
		||||
		float fused, fsize;
 | 
			
		||||
		if(snap_percent == -1)
 | 
			
		||||
			snap_percent=100;
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
		sscanf(size, "%f", &fsize);
 | 
			
		||||
		fused = fsize * ( snap_percent / 100 );
 | 
			
		||||
		log_print("Allocated to snapshot  %2.2f%% [%2.2f/%s]",
 | 
			
		||||
			  snap_percent, fused, size);
 | 
			
		||||
		dbg_free(size);
 | 
			
		||||
 | 
			
		||||
		/* FIXME: Think this'll make them wonder?? */
 | 
			
		||||
		log_print("Allocated to COW-table %s", "00.01 KB");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/** 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
 | 
			
		||||
***************/
 | 
			
		||||
 | 
			
		||||
	log_print("Allocation             %s", get_alloc_string(lv->alloc));
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								lib/display/display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								lib/display/display.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Retrieve a text description of the allocation policy.  Only
 | 
			
		||||
 * extern because it's used by lvscan.
 | 
			
		||||
 */
 | 
			
		||||
const char *get_alloc_string(alloc_policy_t alloc);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * FIXME: put this somewhere more sensible.
 | 
			
		||||
 */
 | 
			
		||||
alloc_policy_t get_alloc_from_string(const char *str);
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
							
								
								
									
										243
									
								
								lib/filters/filter-persistent.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								lib/filters/filter-persistent.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,243 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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) {
 | 
			
		||||
		if (errno != EROFS)
 | 
			
		||||
			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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										667
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										667
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,667 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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, offset;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
		offset = sizeof(struct lv_disk) * ll->lvd.lv_number;
 | 
			
		||||
		if (offset + sizeof(struct lv_disk) >
 | 
			
		||||
		    data->pvd.lv_on_disk.size) {
 | 
			
		||||
			log_error("lv_number %d too large", ll->lvd.lv_number);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!_write_lvd(data->dev, pos + offset, &ll->lvd))
 | 
			
		||||
			fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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->alloc = ALLOC_STRICT;
 | 
			
		||||
 | 
			
		||||
	if (lvd->lv_allocation & LV_CONTIGUOUS)
 | 
			
		||||
		lv->alloc = ALLOC_CONTIGUOUS;
 | 
			
		||||
	else
 | 
			
		||||
		lv->alloc |= ALLOC_NEXT_FREE;
 | 
			
		||||
 | 
			
		||||
	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->alloc == ALLOC_STRICT)
 | 
			
		||||
		lvd->lv_allocation |= LV_STRICT;
 | 
			
		||||
 | 
			
		||||
	if (lv->alloc == 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										370
									
								
								lib/format1/import-extents.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								lib/format1/import-extents.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,370 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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) &&
 | 
			
		||||
		       (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) ||
 | 
			
		||||
		    (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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										359
									
								
								lib/format_text/archive.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								lib/format_text/archive.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,359 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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>
 | 
			
		||||
 | 
			
		||||
#define SECS_PER_DAY 86400	/* 24*60*60 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
	/* Sort fails beyond 5-digit indexes */
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _remove_expired(struct list *archives, uint32_t archives_size,
 | 
			
		||||
			    uint32_t retain_days, uint32_t min_archive)
 | 
			
		||||
{
 | 
			
		||||
	struct list *bh;
 | 
			
		||||
	struct archive_file *bf;
 | 
			
		||||
	struct stat sb;
 | 
			
		||||
	time_t retain_time;
 | 
			
		||||
 | 
			
		||||
	/* Make sure there are enough archives to even bother looking for
 | 
			
		||||
	 * expired ones... */
 | 
			
		||||
	if (archives_size <= min_archive)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Convert retain_days into the time after which we must retain */
 | 
			
		||||
	retain_time = time(NULL) - (time_t) retain_days * SECS_PER_DAY;
 | 
			
		||||
 | 
			
		||||
	/* Assume list is ordered oldest first (by index) */
 | 
			
		||||
	list_iterate(bh, archives) {
 | 
			
		||||
		bf = list_item(bh, struct archive_file);
 | 
			
		||||
 | 
			
		||||
		/* Get the mtime of the file and unlink if too old */
 | 
			
		||||
		if (stat(bf->path, &sb)) {
 | 
			
		||||
			log_sys_error("stat", bf->path);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (sb.st_mtime > retain_time)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		log_very_verbose("Expiring archive %s", bf->path);
 | 
			
		||||
		if (unlink(bf->path))
 | 
			
		||||
			log_sys_error("unlink", bf->path);
 | 
			
		||||
 | 
			
		||||
		/* Don't delete any more if we've reached the minimum */
 | 
			
		||||
		if (--archives_size <= min_archive)
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int archive_vg(struct volume_group *vg,
 | 
			
		||||
	       const char *dir, const char *desc,
 | 
			
		||||
	       uint32_t retain_days, uint32_t min_archive)
 | 
			
		||||
{
 | 
			
		||||
	int i, fd, renamed = 0;
 | 
			
		||||
	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_error("Archive file name too long.");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ((renamed = lvm_rename(temp_file, archive_name)))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		index++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!renamed)
 | 
			
		||||
		log_error("Archive rename failed for %s", temp_file);
 | 
			
		||||
 | 
			
		||||
	_remove_expired(archives, list_size(archives) + renamed, retain_days,
 | 
			
		||||
			min_archive);
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										553
									
								
								lib/format_text/export.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										553
									
								
								lib/format_text/export.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,553 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 "display.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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", ctime(&t));
 | 
			
		||||
 | 
			
		||||
	_out(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\"");
 | 
			
		||||
	_out(f, FORMAT_VERSION_FIELD " = %d\n", FORMAT_VERSION_VALUE);
 | 
			
		||||
 | 
			
		||||
	_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, "allocation_policy = \"%s\"",
 | 
			
		||||
		     get_alloc_string(lv->alloc));
 | 
			
		||||
		_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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								lib/format_text/flags.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								lib/format_text/flags.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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"},
 | 
			
		||||
	{FIXED_MINOR, "FIXED_MINOR"},
 | 
			
		||||
	{VISIBLE_LV, "VISIBLE"},
 | 
			
		||||
	{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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Only scan the flags if it wasn't an empty array.
 | 
			
		||||
	 */
 | 
			
		||||
	if (cv->type != CFG_EMPTY_ARRAY) {
 | 
			
		||||
		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
 | 
			
		||||
							
								
								
									
										46
									
								
								lib/format_text/import-export.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/format_text/import-export.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Constants to identify files this code can parse.
 | 
			
		||||
 */
 | 
			
		||||
#define CONTENTS_FIELD "contents"
 | 
			
		||||
#define CONTENTS_VALUE "Text Format Volume Group"
 | 
			
		||||
 | 
			
		||||
#define FORMAT_VERSION_FIELD "version"
 | 
			
		||||
#define FORMAT_VERSION_VALUE 1
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * VGs, PVs and LVs all have status bitsets, we gather together
 | 
			
		||||
 * common code for reading and writing them.
 | 
			
		||||
 */
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										746
									
								
								lib/format_text/import.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										746
									
								
								lib/format_text/import.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,746 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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"
 | 
			
		||||
#include "display.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)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Logs an attempt to read an invalid format file.
 | 
			
		||||
 */
 | 
			
		||||
static void _invalid_format(const char *str)
 | 
			
		||||
{
 | 
			
		||||
	log_error("Can't process text format file (%s)", str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Checks that the config file contains vg metadata, and that it
 | 
			
		||||
 * we recognise the version number,
 | 
			
		||||
 */
 | 
			
		||||
static int _check_version(struct config_file *cf)
 | 
			
		||||
{
 | 
			
		||||
	struct config_node *cn;
 | 
			
		||||
	struct config_value *cv;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check the contents field.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(cn = find_config_node(cf->root, CONTENTS_FIELD, '/'))) {
 | 
			
		||||
		_invalid_format("missing contents field");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cv = cn->v;
 | 
			
		||||
	if (!cv || cv->type != CFG_STRING || strcmp(cv->v.str, CONTENTS_VALUE))
 | 
			
		||||
	{
 | 
			
		||||
		_invalid_format("unrecognised contents field");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check the version number.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(cn = find_config_node(cf->root, FORMAT_VERSION_FIELD, '/'))) {
 | 
			
		||||
		_invalid_format("missing version number");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cv = cn->v;
 | 
			
		||||
	if (!cv || cv->type != CFG_INT || cv->v.i != FORMAT_VERSION_VALUE) {
 | 
			
		||||
		_invalid_format("unrecognised version number");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_error("Couldn't find uuid.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cv = cn->v;
 | 
			
		||||
	if (!cv || !cv->v.str) {
 | 
			
		||||
		log_error("uuid must be a string.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!id_read_format(id, cv->v.str)) {
 | 
			
		||||
		log_error("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_error("Empty pv section.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_id(&pv->id, pvn, "id")) {
 | 
			
		||||
		log_error("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_error("Couldn't find device.");
 | 
			
		||||
		else
 | 
			
		||||
			log_error("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_error("Couldn't find status flags for physical volume.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(read_flags(&pv->status, PV_FLAGS, cn->v))) {
 | 
			
		||||
		log_error("Couldn't read status flags for physical volume.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int64(pvn, "pe_start", &pv->pe_start)) {
 | 
			
		||||
		log_error("Couldn't read extent size for volume group.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int32(pvn, "pe_count", &pv->pe_count)) {
 | 
			
		||||
		log_error("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_error("Empty segment section.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int32(sn, "stripes", &stripes)) {
 | 
			
		||||
		log_error("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_error("Couldn't read 'start_extent' for segment '%s'.",
 | 
			
		||||
			sn->key);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int32(sn, "extent_count", &seg->len)) {
 | 
			
		||||
		log_error("Couldn't read 'extent_count' for segment '%s'.",
 | 
			
		||||
			sn->key);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (seg->stripes == 0) {
 | 
			
		||||
		log_error("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_error("Couldn't read 'stripe_size' for segment '%s'.",
 | 
			
		||||
			sn->key);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(cn = find_config_node(sn, "areas", '/'))) {
 | 
			
		||||
		log_error("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_error(bad, sn->key);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!(pv = hash_lookup(pv_hash, cv->v.str))) {
 | 
			
		||||
			log_error("Couldn't find physical volume '%s' for "
 | 
			
		||||
				"segment '%s'.",
 | 
			
		||||
				cv->v.str ? cv->v.str : "NULL", seg_name);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		seg->area[s].pv = pv;
 | 
			
		||||
 | 
			
		||||
		if (!(cv = cv->next)) {
 | 
			
		||||
			log_error(bad, sn->key);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cv->type != CFG_INT) {
 | 
			
		||||
			log_error(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_error("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_error("Couldn't read segment count for logical volume.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (seg_count != count) {
 | 
			
		||||
		log_error("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_error("Empty logical volume section.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lv->vg = vg;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: read full lvid */
 | 
			
		||||
	if (!_read_id(&lv->lvid.id[1], lvn, "id")) {
 | 
			
		||||
		log_error("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_error("Couldn't find status flags for logical volume.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(read_flags(&lv->status, LV_FLAGS, cn->v))) {
 | 
			
		||||
		log_error("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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * allocation_policy is optional since it is meaning less
 | 
			
		||||
	 * for things like mirrors and snapshots.  Where it isn't
 | 
			
		||||
	 * specified we default to the next free policy.
 | 
			
		||||
	 */
 | 
			
		||||
	lv->alloc = ALLOC_NEXT_FREE;
 | 
			
		||||
	if ((cn = find_config_node(lvn, "allocation_policy", '/'))) {
 | 
			
		||||
		struct config_value *cv = cn->v;
 | 
			
		||||
		if (!cv || !cv->v.str) {
 | 
			
		||||
			log_err("allocation_policy must be a string.");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lv->alloc = get_alloc_from_string(cv->v.str);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int32(lvn, "read_ahead", &lv->read_ahead)) {
 | 
			
		||||
		log_error("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_error("Empty snapshot section.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_uint32(sn, "chunk_size", &chunk_size)) {
 | 
			
		||||
		log_error("Couldn't read chunk size for snapshot.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(cow_name = find_config_str(sn, "cow_store", '/', NULL))) {
 | 
			
		||||
		log_error("Snapshot cow storage not specified.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(org_name = find_config_str(sn, "origin", '/', NULL))) {
 | 
			
		||||
		log_error("Snapshot origin not specified.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(cow = find_lv(vg, cow_name))) {
 | 
			
		||||
		log_error("Unknown logical volume specified for "
 | 
			
		||||
			"snapshot cow store.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(org = find_lv(vg, org_name))) {
 | 
			
		||||
		log_error("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_error("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_error("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_error("Couldn't read uuid for volume group %s.", vg->name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int32(vgn, "seqno", &vg->seqno)) {
 | 
			
		||||
		log_error("Couldn't read 'seqno' for volume group %s.", vg->name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(cn = find_config_node(vgn, "status", '/'))) {
 | 
			
		||||
		log_error("Couldn't find status flags for volume group %s.",
 | 
			
		||||
			vg->name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(read_flags(&vg->status, VG_FLAGS, cn->v))) {
 | 
			
		||||
		log_error("Couldn't read status flags for volume group %s.",
 | 
			
		||||
			vg->name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int32(vgn, "extent_size", &vg->extent_size)) {
 | 
			
		||||
		log_error("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_error("Couldn't read 'max_lv' for volume group %s.",
 | 
			
		||||
			vg->name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_read_int32(vgn, "max_pv", &vg->max_pv)) {
 | 
			
		||||
		log_error("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_error("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_error("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_error("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_error("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 (!_check_version(cf))
 | 
			
		||||
		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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										264
									
								
								lib/locking/file_locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								lib/locking/file_locking.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 file_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 = file_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;
 | 
			
		||||
 | 
			
		||||
	/* Trap a read-only file system */
 | 
			
		||||
	if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								lib/locking/locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								lib/locking/locking.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 "toolcontext.h"
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <limits.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.");
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	case 1:
 | 
			
		||||
		if (!init_file_locking(&_locking, cf))
 | 
			
		||||
			break;
 | 
			
		||||
		log_very_verbose("File-based locking enabled.");
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	case 2:
 | 
			
		||||
		if (!init_external_locking(&_locking, cf))
 | 
			
		||||
			break;
 | 
			
		||||
		log_very_verbose("External locking enabled.");
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		log_error("Unknown locking type requested.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!ignorelockingfailure())
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* FIXME Ensure only read ops are permitted */
 | 
			
		||||
	log_verbose("Locking disabled - only read operations permitted.");
 | 
			
		||||
 | 
			
		||||
	init_no_locking(&_locking, cf);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void fin_locking(void)
 | 
			
		||||
{
 | 
			
		||||
	_locking.fin_locking();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Does the LVM1 driver know of this VG name?
 | 
			
		||||
 */
 | 
			
		||||
int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname)
 | 
			
		||||
{
 | 
			
		||||
	struct stat info;
 | 
			
		||||
	char path[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
	if (lvm_snprintf(path, sizeof(path), "%s/lvm/VGs/%s",
 | 
			
		||||
			 find_config_str(cmd->cf->root, "global/proc", '/',
 | 
			
		||||
					 DEFAULT_PROC_DIR), vgname) < 0) {
 | 
			
		||||
		log_error("LVM1 proc VG pathname too long for %s", vgname);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (stat(path, &info) == 0) {
 | 
			
		||||
		log_error("%s exists: Is the original LVM driver using "
 | 
			
		||||
			  "this volume group?", path);
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (errno != ENOENT && errno != ENOTDIR) {
 | 
			
		||||
		log_sys_error("stat", path);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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. */
 | 
			
		||||
		/* If LVM1 driver knows about the VG, it can't be accessed. */
 | 
			
		||||
		if (!check_lvm1_vg_inactive(cmd, vol))
 | 
			
		||||
			return 0;
 | 
			
		||||
	case LCK_LV:
 | 
			
		||||
		/* Suspend 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								lib/locking/locking.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lib/locking/locking.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Does the LVM1 driver have this VG active?
 | 
			
		||||
 */
 | 
			
		||||
int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										201
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,201 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 int _ignorelockingfailure = 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_ignorelockingfailure(int level)
 | 
			
		||||
{
 | 
			
		||||
	_ignorelockingfailure = 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ignorelockingfailure()
 | 
			
		||||
{
 | 
			
		||||
	return _ignorelockingfailure;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										98
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 init_ignorelockingfailure(int level);
 | 
			
		||||
 | 
			
		||||
void set_cmd_name(const char *cmd_name);
 | 
			
		||||
 | 
			
		||||
int test_mode(void);
 | 
			
		||||
int partial_mode(void);
 | 
			
		||||
int debug_level(void);
 | 
			
		||||
int ignorelockingfailure(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:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										545
									
								
								lib/metadata/lv_manip.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										545
									
								
								lib/metadata/lv_manip.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,545 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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->alloc == ALLOC_CONTIGUOUS)
 | 
			
		||||
		r = _alloc_contiguous(lv, pvms, allocated);
 | 
			
		||||
 | 
			
		||||
	else if (lv->alloc == ALLOC_NEXT_FREE)
 | 
			
		||||
		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,
 | 
			
		||||
				 alloc_policy_t alloc,
 | 
			
		||||
				 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->alloc = alloc;
 | 
			
		||||
	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;
 | 
			
		||||
	lv->vg->free_count += extents;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										584
									
								
								lib/metadata/metadata.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										584
									
								
								lib/metadata/metadata.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,584 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2001 Sistina Software (UK) Limited.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is released under the LGPL.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "pool.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "dev-cache.h"
 | 
			
		||||
#include "metadata.h"
 | 
			
		||||
#include "toolcontext.h"
 | 
			
		||||
#include "lvm-string.h"
 | 
			
		||||
#include "uuid.h"
 | 
			
		||||
#include "vgcache.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg,
 | 
			
		||||
		  const char *pv_name)
 | 
			
		||||
{
 | 
			
		||||
	struct pv_list *pvl;
 | 
			
		||||
	struct physical_volume *pv;
 | 
			
		||||
	struct pool *mem = fi->fmt->cmd->mem;
 | 
			
		||||
 | 
			
		||||
	log_verbose("Adding physical volume '%s' to volume group '%s'",
 | 
			
		||||
		    pv_name, vg->name);
 | 
			
		||||
 | 
			
		||||
	if (!(pvl = pool_alloc(mem, sizeof(*pvl)))) {
 | 
			
		||||
		log_error("pv_list allocation for '%s' failed", pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(pv = pv_read(fi->fmt->cmd, pv_name))) {
 | 
			
		||||
		log_error("Failed to read existing physical volume '%s'",
 | 
			
		||||
			  pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (*pv->vg_name) {
 | 
			
		||||
		log_error("Physical volume '%s' is already in volume group "
 | 
			
		||||
			  "'%s'", pv_name, pv->vg_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(pv->vg_name = pool_strdup(mem, vg->name))) {
 | 
			
		||||
		log_error("vg->name allocation failed for '%s'", pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Units of 512-byte sectors */
 | 
			
		||||
	pv->pe_size = vg->extent_size;
 | 
			
		||||
 | 
			
		||||
	/* FIXME Do proper rounding-up alignment? */
 | 
			
		||||
	/* Reserved space for label; this holds 0 for PVs created by LVM1 */
 | 
			
		||||
	if (pv->pe_start < PE_ALIGN)
 | 
			
		||||
		pv->pe_start = PE_ALIGN;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The next two fields should be corrected
 | 
			
		||||
	 * by fi->pv_setup.
 | 
			
		||||
	 */
 | 
			
		||||
	pv->pe_count = (pv->size - pv->pe_start) / pv->pe_size;
 | 
			
		||||
 | 
			
		||||
	pv->pe_alloc_count = 0;
 | 
			
		||||
 | 
			
		||||
	if (!fi->fmt->ops->pv_setup(fi, pv, vg)) {
 | 
			
		||||
		log_error("Format-specific setup of physical volume '%s' "
 | 
			
		||||
			  "failed.", pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (find_pv_in_vg(vg, pv_name)) {
 | 
			
		||||
		log_error("Physical volume '%s' listed more than once.",
 | 
			
		||||
			  pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (vg->pv_count == vg->max_pv) {
 | 
			
		||||
		log_error("No space for '%s' - volume group '%s' "
 | 
			
		||||
			  "holds max %d physical volume(s).", pv_name,
 | 
			
		||||
			  vg->name, vg->max_pv);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pvl->pv = pv;
 | 
			
		||||
 | 
			
		||||
	list_add(&vg->pvs, &pvl->list);
 | 
			
		||||
	vg->pv_count++;
 | 
			
		||||
	vg->extent_count += pv->pe_count;
 | 
			
		||||
	vg->free_count += pv->pe_count;
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int vg_extend(struct format_instance *fi,
 | 
			
		||||
	      struct volume_group *vg, int pv_count, char **pv_names)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* attach each pv */
 | 
			
		||||
	for (i = 0; i < pv_count; i++)
 | 
			
		||||
		if (!_add_pv_to_vg(fi, vg, pv_names[i])) {
 | 
			
		||||
			log_error("Unable to add physical volume '%s' to "
 | 
			
		||||
				  "volume group '%s'.", pv_names[i], vg->name);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *strip_dir(const char *vg_name, const char *dev_dir)
 | 
			
		||||
{
 | 
			
		||||
	int len = strlen(dev_dir);
 | 
			
		||||
	if (!strncmp(vg_name, dev_dir, len))
 | 
			
		||||
		vg_name += len;
 | 
			
		||||
 | 
			
		||||
	return vg_name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name,
 | 
			
		||||
			       uint32_t extent_size, int max_pv, int max_lv,
 | 
			
		||||
			       int pv_count, char **pv_names)
 | 
			
		||||
{
 | 
			
		||||
	struct volume_group *vg;
 | 
			
		||||
	struct pool *mem = cmd->mem;
 | 
			
		||||
 | 
			
		||||
	if (!(vg = pool_zalloc(mem, sizeof(*vg)))) {
 | 
			
		||||
		stack;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* is this vg name already in use ? */
 | 
			
		||||
	init_partial(1);
 | 
			
		||||
	if (vg_read(cmd, vg_name)) {
 | 
			
		||||
		log_err("A volume group called '%s' already exists.", vg_name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
	init_partial(0);
 | 
			
		||||
 | 
			
		||||
	if (!id_create(&vg->id)) {
 | 
			
		||||
		log_err("Couldn't create uuid for volume group '%s'.", vg_name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Strip dev_dir if present */
 | 
			
		||||
	vg_name = strip_dir(vg_name, cmd->dev_dir);
 | 
			
		||||
 | 
			
		||||
	vg->cmd = cmd;
 | 
			
		||||
 | 
			
		||||
	if (!(vg->name = pool_strdup(mem, vg_name))) {
 | 
			
		||||
		stack;
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vg->seqno = 0;
 | 
			
		||||
 | 
			
		||||
	vg->status = (RESIZEABLE_VG | LVM_READ | LVM_WRITE);
 | 
			
		||||
	vg->system_id = pool_alloc(mem, NAME_LEN);
 | 
			
		||||
	*vg->system_id = '\0';
 | 
			
		||||
 | 
			
		||||
	vg->extent_size = extent_size;
 | 
			
		||||
	vg->extent_count = 0;
 | 
			
		||||
	vg->free_count = 0;
 | 
			
		||||
 | 
			
		||||
	vg->max_lv = max_lv;
 | 
			
		||||
	vg->max_pv = max_pv;
 | 
			
		||||
 | 
			
		||||
	vg->pv_count = 0;
 | 
			
		||||
	list_init(&vg->pvs);
 | 
			
		||||
 | 
			
		||||
	vg->lv_count = 0;
 | 
			
		||||
	list_init(&vg->lvs);
 | 
			
		||||
 | 
			
		||||
	vg->snapshot_count = 0;
 | 
			
		||||
	list_init(&vg->snapshots);
 | 
			
		||||
 | 
			
		||||
	if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg_name,
 | 
			
		||||
						       NULL))) {
 | 
			
		||||
		log_error("Failed to create format instance");
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!vg->fid->fmt->ops->vg_setup(vg->fid, vg)) {
 | 
			
		||||
		log_error("Format specific setup of volume group '%s' failed.",
 | 
			
		||||
			  vg_name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* attach the pv's */
 | 
			
		||||
	if (!vg_extend(vg->fid, vg, pv_count, pv_names))
 | 
			
		||||
		goto bad;
 | 
			
		||||
 | 
			
		||||
	return vg;
 | 
			
		||||
 | 
			
		||||
      bad:
 | 
			
		||||
	pool_free(mem, vg);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct physical_volume *pv_create(struct format_instance *fid,
 | 
			
		||||
				  const char *name,
 | 
			
		||||
				  struct id *id, uint64_t size)
 | 
			
		||||
{
 | 
			
		||||
	struct pool *mem = fid->fmt->cmd->mem;
 | 
			
		||||
	struct physical_volume *pv = pool_alloc(mem, sizeof(*pv));
 | 
			
		||||
 | 
			
		||||
	if (!pv) {
 | 
			
		||||
		stack;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!id)
 | 
			
		||||
		id_create(&pv->id);
 | 
			
		||||
	else
 | 
			
		||||
		memcpy(&pv->id, id, sizeof(*id));
 | 
			
		||||
 | 
			
		||||
	if (!(pv->dev = dev_cache_get(name, fid->fmt->cmd->filter))) {
 | 
			
		||||
		log_error("%s: Couldn't find device.", name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(pv->vg_name = pool_alloc(mem, NAME_LEN))) {
 | 
			
		||||
		stack;
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*pv->vg_name = 0;
 | 
			
		||||
	pv->status = ALLOCATABLE_PV;
 | 
			
		||||
 | 
			
		||||
	if (!dev_get_size(pv->dev, &pv->size)) {
 | 
			
		||||
		log_error("%s: Couldn't get size.", name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (size) {
 | 
			
		||||
		if (size > pv->size)
 | 
			
		||||
			log_print("WARNING: %s: Overriding real size. "
 | 
			
		||||
				  "You could lose data.", name);
 | 
			
		||||
		log_verbose("%s: Pretending size is %" PRIu64 " sectors.",
 | 
			
		||||
			    name, size);
 | 
			
		||||
		pv->size = size;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pv->size < PV_MIN_SIZE) {
 | 
			
		||||
		log_error("%s: Size must exceed minimum of %lu sectors.",
 | 
			
		||||
			  name, PV_MIN_SIZE);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pv->pe_size = 0;
 | 
			
		||||
	pv->pe_start = 0;
 | 
			
		||||
	pv->pe_count = 0;
 | 
			
		||||
	pv->pe_alloc_count = 0;
 | 
			
		||||
	pv->fid = fid;
 | 
			
		||||
 | 
			
		||||
	if (!fid->fmt->ops->pv_setup(fid, pv, NULL)) {
 | 
			
		||||
		log_error("%s: Format-specific setup of physical volume "
 | 
			
		||||
			  "failed.", name);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
	return pv;
 | 
			
		||||
 | 
			
		||||
      bad:
 | 
			
		||||
	pool_free(mem, pv);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name)
 | 
			
		||||
{
 | 
			
		||||
	struct list *pvh;
 | 
			
		||||
	struct pv_list *pvl;
 | 
			
		||||
 | 
			
		||||
	list_iterate(pvh, &vg->pvs) {
 | 
			
		||||
		pvl = list_item(pvh, struct pv_list);
 | 
			
		||||
		if (pvl->pv->dev == dev_cache_get(pv_name, vg->cmd->filter))
 | 
			
		||||
			return pvl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name)
 | 
			
		||||
{
 | 
			
		||||
	struct list *lvh;
 | 
			
		||||
	struct lv_list *lvl;
 | 
			
		||||
	const char *ptr;
 | 
			
		||||
 | 
			
		||||
	/* Use last component */
 | 
			
		||||
	if ((ptr = strrchr(lv_name, '/')))
 | 
			
		||||
		ptr++;
 | 
			
		||||
	else
 | 
			
		||||
		ptr = lv_name;
 | 
			
		||||
 | 
			
		||||
	list_iterate(lvh, &vg->lvs) {
 | 
			
		||||
		lvl = list_item(lvh, struct lv_list);
 | 
			
		||||
		if (!strcmp(lvl->lv->name, ptr))
 | 
			
		||||
			return lvl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg, union lvid *lvid)
 | 
			
		||||
{
 | 
			
		||||
	struct list *lvh;
 | 
			
		||||
	struct lv_list *lvl;
 | 
			
		||||
 | 
			
		||||
	list_iterate(lvh, &vg->lvs) {
 | 
			
		||||
		lvl = list_item(lvh, struct lv_list);
 | 
			
		||||
		if (!strncmp(lvl->lv->lvid.s, lvid->s, sizeof(*lvid)))
 | 
			
		||||
			return lvl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct logical_volume *find_lv(struct volume_group *vg, const char *lv_name)
 | 
			
		||||
{
 | 
			
		||||
	struct lv_list *lvl = find_lv_in_vg(vg, lv_name);
 | 
			
		||||
	return lvl ? lvl->lv : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct list *pvh;
 | 
			
		||||
	struct physical_volume *pv;
 | 
			
		||||
 | 
			
		||||
	list_iterate(pvh, &vg->pvs) {
 | 
			
		||||
		pv = list_item(pvh, struct pv_list)->pv;
 | 
			
		||||
 | 
			
		||||
		if (dev == pv->dev)
 | 
			
		||||
			return pv;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int vg_remove(struct volume_group *vg)
 | 
			
		||||
{
 | 
			
		||||
	struct list *mdah;
 | 
			
		||||
	void *mdl;
 | 
			
		||||
 | 
			
		||||
	if (!vg->fid->fmt->ops->vg_remove)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/* FIXME Improve recovery situation? */
 | 
			
		||||
	/* Remove each copy of the metadata */
 | 
			
		||||
	list_iterate(mdah, &vg->fid->metadata_areas) {
 | 
			
		||||
		mdl = list_item(mdah, struct metadata_area)->metadata_locn;
 | 
			
		||||
		if (!vg->fid->fmt->ops->vg_remove(vg->fid, vg, mdl)) {
 | 
			
		||||
			stack;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int vg_write(struct volume_group *vg)
 | 
			
		||||
{
 | 
			
		||||
	struct list *mdah;
 | 
			
		||||
	void *mdl;
 | 
			
		||||
 | 
			
		||||
	if (vg->status & PARTIAL_VG) {
 | 
			
		||||
		log_error("Cannot change metadata for partial volume group %s",
 | 
			
		||||
			  vg->name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vg->seqno++;
 | 
			
		||||
 | 
			
		||||
	/* Write to each copy of the metadata area */
 | 
			
		||||
	list_iterate(mdah, &vg->fid->metadata_areas) {
 | 
			
		||||
		mdl = list_item(mdah, struct metadata_area)->metadata_locn;
 | 
			
		||||
		if (!vg->fid->fmt->ops->vg_write(vg->fid, vg, mdl)) {
 | 
			
		||||
			stack;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!vg->fid->fmt->ops->vg_commit)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/* Commit to each copy of the metadata area */
 | 
			
		||||
	list_iterate(mdah, &vg->fid->metadata_areas) {
 | 
			
		||||
		mdl = list_item(mdah, struct metadata_area)->metadata_locn;
 | 
			
		||||
		if (!vg->fid->fmt->ops->vg_commit(vg->fid, vg, mdl)) {
 | 
			
		||||
			stack;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name)
 | 
			
		||||
{
 | 
			
		||||
	struct format_instance *fid;
 | 
			
		||||
	struct format_type *fmt;
 | 
			
		||||
	struct volume_group *vg, *correct_vg;
 | 
			
		||||
	struct list *mdah, *names;
 | 
			
		||||
	void *mdl;
 | 
			
		||||
	int inconsistent = 0, first_time = 1;
 | 
			
		||||
 | 
			
		||||
	/* create format instance with appropriate metadata area */
 | 
			
		||||
	if (!(fmt = vgcache_find_format(vg_name))) {
 | 
			
		||||
		/* Do full scan */
 | 
			
		||||
		if (!(names = get_vgs(cmd))) {
 | 
			
		||||
			stack;
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		pool_free(cmd->mem, names);
 | 
			
		||||
		if (!(fmt = vgcache_find_format(vg_name))) {
 | 
			
		||||
			stack;
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(fid = fmt->ops->create_instance(fmt, vg_name, NULL))) {
 | 
			
		||||
		log_error("Failed to create format instance");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Ensure contents of all metadata areas match - else do recovery */
 | 
			
		||||
	list_iterate(mdah, &fid->metadata_areas) {
 | 
			
		||||
		mdl = list_item(mdah, struct metadata_area)->metadata_locn;
 | 
			
		||||
		if (!(vg = fid->fmt->ops->vg_read(fid, vg_name, mdl))) {
 | 
			
		||||
 			inconsistent = 1;
 | 
			
		||||
 			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (first_time) {
 | 
			
		||||
			correct_vg = vg;
 | 
			
		||||
			first_time = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (correct_vg->seqno != vg->seqno) {
 | 
			
		||||
			inconsistent = 1;
 | 
			
		||||
			if (vg->seqno > correct_vg->seqno)
 | 
			
		||||
				correct_vg = vg;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Failed to find VG */
 | 
			
		||||
	if (first_time) {
 | 
			
		||||
		stack;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (inconsistent) {
 | 
			
		||||
		log_print("Inconsistent metadata copies found - updating "
 | 
			
		||||
			  "to use version %u", correct_vg->seqno);
 | 
			
		||||
		if (!vg_write(correct_vg)) {
 | 
			
		||||
			log_error("Automatic metadata correction failed");
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vgcache_add(vg_name, correct_vg->id.uuid, NULL, fmt);
 | 
			
		||||
 | 
			
		||||
	return correct_vg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct volume_group *vg_read_by_vgid(struct cmd_context *cmd, const char *vgid)
 | 
			
		||||
{
 | 
			
		||||
	char *vgname;
 | 
			
		||||
	struct list *vgs, *vgh;
 | 
			
		||||
	struct volume_group *vg;
 | 
			
		||||
 | 
			
		||||
	if (!(vgs = get_vgs(cmd))) {
 | 
			
		||||
		log_error("vg_read_by_vgid: get_vgs failed");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list_iterate(vgh, vgs) {
 | 
			
		||||
		vgname = list_item(vgh, struct name_list)->name;
 | 
			
		||||
		if ((vg = vg_read(cmd, vgname)) &&
 | 
			
		||||
		    !strncmp(vg->id.uuid, vgid, ID_LEN)) return vg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pool_free(cmd->mem, vgs);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* FIXME Use label functions instead of PV functions? */
 | 
			
		||||
struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name)
 | 
			
		||||
{
 | 
			
		||||
	struct physical_volume *pv;
 | 
			
		||||
 | 
			
		||||
	if (!(pv = pool_zalloc(cmd->mem, sizeof(*pv)))) {
 | 
			
		||||
		log_error("pv_list allocation for '%s' failed", pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Member of a format1 VG? */
 | 
			
		||||
	if (!(cmd->fmt1->ops->pv_read(cmd->fmt1, pv_name, pv))) {
 | 
			
		||||
		log_error("Failed to read existing physical volume '%s'",
 | 
			
		||||
			  pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Member of a format_text VG? */
 | 
			
		||||
	if (!(cmd->fmtt->ops->pv_read(cmd->fmtt, pv_name, pv))) {
 | 
			
		||||
		log_error("Failed to read existing physical volume '%s'",
 | 
			
		||||
			  pv_name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!pv->size)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	else
 | 
			
		||||
		return pv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct list *get_vgs(struct cmd_context *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct list *names;
 | 
			
		||||
 | 
			
		||||
	if (!(names = pool_alloc(cmd->mem, sizeof(*names)))) {
 | 
			
		||||
		log_error("VG name list allocation failed");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list_init(names);
 | 
			
		||||
 | 
			
		||||
	if (!cmd->fmt1->ops->get_vgs(cmd->fmt1, names) ||
 | 
			
		||||
	    !cmd->fmtt->ops->get_vgs(cmd->fmtt, names) ||
 | 
			
		||||
	    list_empty(names)) {
 | 
			
		||||
		pool_free(cmd->mem, names);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return names;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct list *get_pvs(struct cmd_context *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct list *results;
 | 
			
		||||
 | 
			
		||||
	if (!(results = pool_alloc(cmd->mem, sizeof(*results)))) {
 | 
			
		||||
		log_error("PV list allocation failed");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list_init(results);
 | 
			
		||||
 | 
			
		||||
	/* fmtt modifies fmt1 output */
 | 
			
		||||
	if (!cmd->fmt1->ops->get_pvs(cmd->fmt1, results) ||
 | 
			
		||||
	    !cmd->fmtt->ops->get_pvs(cmd->fmtt, results)) {
 | 
			
		||||
		pool_free(cmd->mem, results);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return results;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pv_write(struct cmd_context *cmd, struct physical_volume *pv)
 | 
			
		||||
{
 | 
			
		||||
	struct list *mdah;
 | 
			
		||||
	void *mdl;
 | 
			
		||||
 | 
			
		||||
	/* Write to each copy of the metadata area */
 | 
			
		||||
	list_iterate(mdah, &pv->fid->metadata_areas) {
 | 
			
		||||
		mdl = list_item(mdah, struct metadata_area)->metadata_locn;
 | 
			
		||||
		if (!pv->fid->fmt->ops->pv_write(pv->fid, pv, mdl)) {
 | 
			
		||||
			stack;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!pv->fid->fmt->ops->pv_commit)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/* Commit to each copy of the metadata area */
 | 
			
		||||
	list_iterate(mdah, &pv->fid->metadata_areas) {
 | 
			
		||||
		mdl = list_item(mdah, struct metadata_area)->metadata_locn;
 | 
			
		||||
		if (!pv->fid->fmt->ops->pv_commit(pv->fid, pv, mdl)) {
 | 
			
		||||
			stack;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										418
									
								
								lib/metadata/metadata.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								lib/metadata/metadata.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,418 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2001 Sistina Software (UK) Limited.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is released under the LGPL.
 | 
			
		||||
 *
 | 
			
		||||
 * This is the in core representation of a volume group and its
 | 
			
		||||
 * associated physical and logical volumes.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _LVM_METADATA_H
 | 
			
		||||
#define _LVM_METADATA_H
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include "dev-cache.h"
 | 
			
		||||
#include "list.h"
 | 
			
		||||
#include "uuid.h"
 | 
			
		||||
 | 
			
		||||
#define NAME_LEN 128
 | 
			
		||||
#define MAX_STRIPES 128
 | 
			
		||||
#define SECTOR_SIZE 512
 | 
			
		||||
#define STRIPE_SIZE_DEFAULT 16    /* 16KB */
 | 
			
		||||
#define STRIPE_SIZE_MIN ( PAGE_SIZE/SECTOR_SIZE)     /* PAGESIZE in sectors */
 | 
			
		||||
#define STRIPE_SIZE_MAX ( 512L * 1024 / SECTOR_SIZE) /* 512 KB in sectors */
 | 
			
		||||
#define PV_MIN_SIZE ( 512L * 1024 / SECTOR_SIZE) /* 512 KB in sectors */
 | 
			
		||||
#define PE_ALIGN (65536UL / SECTOR_SIZE) /* PE alignment */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Various flags */
 | 
			
		||||
/* Note that the bits no longer necessarily correspond to LVM1 disk format */
 | 
			
		||||
 | 
			
		||||
#define BIT(x) (1 << x)
 | 
			
		||||
#define EXPORTED_VG    BIT(0)  /* VG PV */
 | 
			
		||||
#define RESIZEABLE_VG  BIT(1)  /* VG */
 | 
			
		||||
#define PARTIAL_VG     BIT(2)  /* VG */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * May any free extents on this PV be used or must they be left
 | 
			
		||||
 * free?
 | 
			
		||||
 */
 | 
			
		||||
#define ALLOCATABLE_PV BIT(3)  /* PV */
 | 
			
		||||
 | 
			
		||||
#define SPINDOWN_LV    BIT(4)  /* LV */
 | 
			
		||||
#define BADBLOCK_ON    BIT(5)  /* LV */
 | 
			
		||||
#define FIXED_MINOR    BIT(6)  /* LV */
 | 
			
		||||
#define VISIBLE_LV     BIT(7)  /* LV */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * FIXME: do we really set read/write for a whole vg ?
 | 
			
		||||
 */
 | 
			
		||||
#define LVM_READ       BIT(8)  /* LV VG */
 | 
			
		||||
#define LVM_WRITE      BIT(9)  /* LV VG */
 | 
			
		||||
#define CLUSTERED      BIT(10)  /* VG */
 | 
			
		||||
#define SHARED         BIT(11) /* VG */
 | 
			
		||||
 | 
			
		||||
#define FMT_SEGMENTS		0x00000001 /* Arbitrary segment parameters? */
 | 
			
		||||
 | 
			
		||||
#define FMT_TEXT_NAME		"text"
 | 
			
		||||
#define FMT_LVM1_NAME		"lvm1"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
	ALLOC_NEXT_FREE,
 | 
			
		||||
	ALLOC_STRICT,
 | 
			
		||||
	ALLOC_CONTIGUOUS
 | 
			
		||||
 | 
			
		||||
} alloc_policy_t;
 | 
			
		||||
 | 
			
		||||
struct physical_volume {
 | 
			
		||||
        struct id id;
 | 
			
		||||
	struct device *dev;
 | 
			
		||||
	struct format_instance *fid;
 | 
			
		||||
	char *vg_name;
 | 
			
		||||
 | 
			
		||||
        uint32_t status;
 | 
			
		||||
        uint64_t size;
 | 
			
		||||
 | 
			
		||||
        /* physical extents */
 | 
			
		||||
        uint64_t pe_size;
 | 
			
		||||
        uint64_t pe_start;
 | 
			
		||||
        uint32_t pe_count;
 | 
			
		||||
        uint32_t pe_alloc_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cmd_context;
 | 
			
		||||
 | 
			
		||||
struct format_type {
 | 
			
		||||
	struct cmd_context *cmd;
 | 
			
		||||
	struct format_handler *ops;
 | 
			
		||||
	const char *name;
 | 
			
		||||
	uint32_t features;
 | 
			
		||||
	void *private;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct metadata_area {
 | 
			
		||||
	struct list list;
 | 
			
		||||
	void *metadata_locn;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct format_instance {
 | 
			
		||||
	struct format_type *fmt;
 | 
			
		||||
	struct list metadata_areas;	/* e.g. metadata locations */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct volume_group {
 | 
			
		||||
	struct cmd_context *cmd;
 | 
			
		||||
	struct format_instance *fid;
 | 
			
		||||
	uint32_t seqno;			/* Metadata sequence number */
 | 
			
		||||
 | 
			
		||||
	struct id id;
 | 
			
		||||
	char *name;
 | 
			
		||||
	char *system_id;
 | 
			
		||||
 | 
			
		||||
        uint32_t status;
 | 
			
		||||
 | 
			
		||||
        uint32_t extent_size;
 | 
			
		||||
        uint32_t extent_count;
 | 
			
		||||
        uint32_t free_count;
 | 
			
		||||
 | 
			
		||||
        uint32_t max_lv;
 | 
			
		||||
        uint32_t max_pv;
 | 
			
		||||
 | 
			
		||||
        /* physical volumes */
 | 
			
		||||
        uint32_t pv_count;
 | 
			
		||||
	struct list pvs;
 | 
			
		||||
 | 
			
		||||
        /* logical volumes */
 | 
			
		||||
        uint32_t lv_count;
 | 
			
		||||
	struct list lvs;
 | 
			
		||||
 | 
			
		||||
	/* snapshots */
 | 
			
		||||
	uint32_t snapshot_count;
 | 
			
		||||
	struct list snapshots;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct stripe_segment {
 | 
			
		||||
	struct list list;
 | 
			
		||||
 | 
			
		||||
	struct logical_volume *lv;
 | 
			
		||||
	uint32_t le;
 | 
			
		||||
	uint32_t len;
 | 
			
		||||
	uint32_t stripe_size;
 | 
			
		||||
	uint32_t stripes;
 | 
			
		||||
 | 
			
		||||
	/* There will be one area for each stripe */
 | 
			
		||||
        struct {
 | 
			
		||||
		struct physical_volume *pv;
 | 
			
		||||
		uint32_t pe;
 | 
			
		||||
	} area[0];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct logical_volume {
 | 
			
		||||
	union lvid lvid;
 | 
			
		||||
        char *name;
 | 
			
		||||
 | 
			
		||||
	struct volume_group *vg;
 | 
			
		||||
 | 
			
		||||
        uint32_t status;
 | 
			
		||||
	alloc_policy_t alloc;
 | 
			
		||||
	uint32_t read_ahead;
 | 
			
		||||
	int32_t minor;
 | 
			
		||||
 | 
			
		||||
        uint64_t size;
 | 
			
		||||
        uint32_t le_count;
 | 
			
		||||
 | 
			
		||||
	struct list segments;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct snapshot {
 | 
			
		||||
	int persistent;		/* boolean */
 | 
			
		||||
	uint32_t chunk_size;	/* in 512 byte sectors */
 | 
			
		||||
 | 
			
		||||
	struct logical_volume *origin;
 | 
			
		||||
	struct logical_volume *cow;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct name_list {
 | 
			
		||||
	struct list list;
 | 
			
		||||
	char *name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pv_list {
 | 
			
		||||
	struct list list;
 | 
			
		||||
	struct physical_volume *pv;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lv_list {
 | 
			
		||||
	struct list list;
 | 
			
		||||
	struct logical_volume *lv;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct snapshot_list {
 | 
			
		||||
	struct list list;
 | 
			
		||||
 | 
			
		||||
	struct snapshot *snapshot;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Ownership of objects passes to caller.
 | 
			
		||||
 */
 | 
			
		||||
struct format_handler {
 | 
			
		||||
	/*
 | 
			
		||||
	 * Returns a name_list of vg's.
 | 
			
		||||
	 */
 | 
			
		||||
	struct list *(*get_vgs)(struct format_type *fmt, struct list *names);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Returns pv_list of fully-populated pv structures.
 | 
			
		||||
	 */
 | 
			
		||||
	struct list *(*get_pvs)(struct format_type *fmt, struct list *results);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Return PV with given path.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*pv_read)(struct format_type *fmt,
 | 
			
		||||
					   const char *pv_name,
 | 
			
		||||
					   struct physical_volume *pv);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Tweak an already filled out a pv ready for importing into a
 | 
			
		||||
	 * vg.  eg. pe_count is format specific.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*pv_setup)(struct format_instance *fi, struct physical_volume *pv,
 | 
			
		||||
			struct volume_group *vg);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Write a PV structure to disk. Fails if the PV is in a VG ie
 | 
			
		||||
	 * pv->vg_name must be null.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*pv_write)(struct format_instance *fi, struct physical_volume *pv,
 | 
			
		||||
			void *mdl);
 | 
			
		||||
	int (*pv_commit)(struct format_instance *fid,
 | 
			
		||||
			 struct physical_volume *pv, void *mdl);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Tweak an already filled out a lv eg, check there
 | 
			
		||||
	 * aren't too many extents.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*lv_setup)(struct format_instance *fi, struct logical_volume *lv);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Tweak an already filled out vg.  eg, max_pv is format
 | 
			
		||||
	 * specific.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*vg_setup)(struct format_instance *fi, struct volume_group *vg);
 | 
			
		||||
	int (*vg_remove)(struct format_instance *fi, struct volume_group *vg,
 | 
			
		||||
			 void *mdl);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The name may be prefixed with the dev_dir from the
 | 
			
		||||
	 * job_context.
 | 
			
		||||
	 * mdl is the metadata location to use
 | 
			
		||||
	 */
 | 
			
		||||
	struct volume_group *(*vg_read)(struct format_instance *fi,
 | 
			
		||||
					const char *vg_name, void *mdl);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Write out complete VG metadata.  You must ensure internal
 | 
			
		||||
	 * consistency before calling. eg. PEs can't refer to PVs not
 | 
			
		||||
	 * part of the VG.
 | 
			
		||||
	 *
 | 
			
		||||
	 * It is also the responsibility of the caller to ensure external
 | 
			
		||||
	 * consistency, eg by calling pv_write() if removing PVs from
 | 
			
		||||
	 * a VG or calling vg_write() a second time if splitting a VG
 | 
			
		||||
	 * into two.
 | 
			
		||||
	 *
 | 
			
		||||
	 * vg_write() must not read or write from any PVs not included
 | 
			
		||||
	 * in the volume_group structure it is handed. Note: format1
 | 
			
		||||
	 * does read all pv's currently.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*vg_write)(struct format_instance *fid, struct volume_group *vg,
 | 
			
		||||
			void *mdl);
 | 
			
		||||
 | 
			
		||||
	int (*vg_commit)(struct format_instance *fid, struct volume_group *vg,
 | 
			
		||||
			void *mdl);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Create format instance with a particular metadata area
 | 
			
		||||
	 */
 | 
			
		||||
	struct format_instance *(*create_instance)(struct format_type *fmt,
 | 
			
		||||
						   const char *vgname,
 | 
			
		||||
						   void *context);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Destructor for format instance
 | 
			
		||||
	 */
 | 
			
		||||
	void (*destroy_instance)(struct format_instance *fid);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Destructor for format type
 | 
			
		||||
	 */
 | 
			
		||||
	void (*destroy)(struct format_type *fmt);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Utility functions
 | 
			
		||||
 */
 | 
			
		||||
int vg_write(struct volume_group *vg);
 | 
			
		||||
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name);
 | 
			
		||||
struct volume_group *vg_read_by_vgid(struct cmd_context *cmd, const char *vgid);
 | 
			
		||||
struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name);
 | 
			
		||||
struct list *get_pvs(struct cmd_context *cmd);
 | 
			
		||||
struct list *get_vgs(struct cmd_context *cmd);
 | 
			
		||||
int pv_write(struct cmd_context *cmd, struct physical_volume *pv);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct physical_volume *pv_create(struct format_instance *fi,
 | 
			
		||||
				  const char *name,
 | 
			
		||||
				  struct id *id,
 | 
			
		||||
				  uint64_t size);
 | 
			
		||||
 | 
			
		||||
struct volume_group *vg_create(struct cmd_context *cmd, const char *name,
 | 
			
		||||
			       uint32_t extent_size, int max_pv, int max_lv,
 | 
			
		||||
			       int pv_count, char **pv_names);
 | 
			
		||||
int vg_remove(struct volume_group *vg);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This needs the format instance to check the
 | 
			
		||||
 * pv's are orphaned.
 | 
			
		||||
 */
 | 
			
		||||
int vg_extend(struct format_instance *fi,
 | 
			
		||||
	      struct volume_group *vg, int pv_count, char **pv_names);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Create a new LV within a given volume group.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
struct logical_volume *lv_create(struct format_instance *fi,
 | 
			
		||||
				 const char *name,
 | 
			
		||||
				 uint32_t status,
 | 
			
		||||
				 alloc_policy_t alloc,
 | 
			
		||||
				 uint32_t stripes,
 | 
			
		||||
				 uint32_t stripe_size,
 | 
			
		||||
				 uint32_t extents,
 | 
			
		||||
				 struct volume_group *vg,
 | 
			
		||||
				 struct list *acceptable_pvs);
 | 
			
		||||
 | 
			
		||||
int lv_reduce(struct format_instance *fi,
 | 
			
		||||
	      struct logical_volume *lv, uint32_t extents);
 | 
			
		||||
 | 
			
		||||
int lv_extend(struct format_instance *fi,
 | 
			
		||||
	      struct logical_volume *lv,
 | 
			
		||||
	      uint32_t stripes,
 | 
			
		||||
	      uint32_t stripe_size,
 | 
			
		||||
	      uint32_t extents,
 | 
			
		||||
	      struct list *allocatable_pvs);
 | 
			
		||||
 | 
			
		||||
/* lv must be part of vg->lvs */
 | 
			
		||||
int lv_remove(struct volume_group *vg, struct logical_volume *lv);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* FIXME: Move to other files */
 | 
			
		||||
int id_eq(struct id *op1, struct id *op2);
 | 
			
		||||
 | 
			
		||||
/* Manipulate PV structures */
 | 
			
		||||
int pv_add(struct volume_group *vg, struct physical_volume *pv);
 | 
			
		||||
int pv_remove(struct volume_group *vg, struct physical_volume *pv);
 | 
			
		||||
struct physical_volume *pv_find(struct volume_group *vg,
 | 
			
		||||
				const char *pv_name);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Find a PV within a given VG */
 | 
			
		||||
struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name);
 | 
			
		||||
 | 
			
		||||
/* Find an LV within a given VG */
 | 
			
		||||
struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name);
 | 
			
		||||
struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg, 
 | 
			
		||||
				      union lvid *lvid);
 | 
			
		||||
 | 
			
		||||
/* Return the VG that contains a given LV (based on path given in lv_name) */
 | 
			
		||||
/* or environment var */
 | 
			
		||||
struct volume_group *find_vg_with_lv(const char *lv_name);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* FIXME Merge these functions with ones above */
 | 
			
		||||
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev);
 | 
			
		||||
struct logical_volume *find_lv(struct volume_group *vg, const char *lv_name);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Remove a dev_dir if present.
 | 
			
		||||
 */
 | 
			
		||||
const char *strip_dir(const char *vg_name, const char *dir);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Checks that an lv has no gaps or overlapping segments.
 | 
			
		||||
 */
 | 
			
		||||
int lv_check_segments(struct logical_volume *lv);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Sometimes (eg, after an lvextend), it is possible to merge two
 | 
			
		||||
 * adjacent segments into a single segment.  This function trys
 | 
			
		||||
 * to merge as many segments as possible.
 | 
			
		||||
 */
 | 
			
		||||
int lv_merge_segments(struct logical_volume *lv);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Useful functions for managing snapshots.
 | 
			
		||||
 */
 | 
			
		||||
int lv_is_origin(struct logical_volume *lv);
 | 
			
		||||
int lv_is_cow(struct logical_volume *lv);
 | 
			
		||||
 | 
			
		||||
struct snapshot *find_cow(struct logical_volume *lv);
 | 
			
		||||
struct snapshot *find_origin(struct logical_volume *lv);
 | 
			
		||||
struct list *find_snapshots(struct logical_volume *lv);
 | 
			
		||||
 | 
			
		||||
int vg_add_snapshot(struct logical_volume *origin,
 | 
			
		||||
		    struct logical_volume *cow,
 | 
			
		||||
		    int persistent,
 | 
			
		||||
		    uint32_t chunk_size);
 | 
			
		||||
 | 
			
		||||
int vg_remove_snapshot(struct volume_group *vg, struct logical_volume *cow);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user