mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-22 19:33:16 +03:00 
			
		
		
		
	Compare commits
	
		
			1045 Commits
		
	
	
		
			dev-mcsont
			...
			v2_00_07
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 81318c7968 | ||
|  | 47a2c1c6e5 | ||
|  | 39cee65c6b | ||
|  | 8582ec724e | ||
|  | b0139682e8 | ||
|  | d39c475a6d | ||
|  | 48f38354c6 | ||
|  | cd5a920ed5 | ||
|  | 71bc1f378d | ||
|  | 0ee6c31cff | ||
|  | af89a9971e | ||
|  | c718a8ef72 | ||
|  | 8c8ad0faf0 | ||
|  | 314d5bbb7f | ||
|  | 102255757a | ||
|  | 914067a0d0 | ||
|  | 06e3ae2536 | ||
|  | 7f9b252556 | ||
|  | 3d700e243f | ||
|  | bcfc78ce11 | ||
|  | 09241765d5 | ||
|  | 671c83c265 | ||
|  | 772d28b766 | ||
|  | c26fcea58d | ||
|  | 1e5e26dbff | ||
|  | 742fc54864 | ||
|  | 49738f43c0 | ||
|  | 9f85f61010 | ||
|  | 239f422039 | ||
|  | 67af3c37be | ||
|  | a9442385c4 | ||
|  | 8c9cd10b8b | ||
|  | 72542059dd | ||
|  | a843fc6d40 | ||
|  | 4beed60c08 | ||
|  | 4049c1e480 | ||
|  | 8449314da2 | ||
|  | 63ad057028 | ||
|  | e720464330 | ||
|  | 24036afef9 | ||
|  | c78fa1a1bc | ||
|  | faa8b9022c | ||
|  | 729bafef7a | ||
|  | 590b028632 | ||
|  | 8150d00f36 | ||
|  | 060065926f | ||
|  | 70babe8a28 | ||
|  | c36e09664f | ||
|  | a9672246f3 | ||
|  | ff571884e9 | ||
|  | 475138bceb | ||
|  | 4a8af199c2 | ||
|  | bdabf5db72 | ||
|  | 6a5f21b34e | ||
|  | d608be103c | ||
|  | 374bb5d18a | ||
|  | 031d6c25ff | ||
|  | 223fb7b075 | ||
|  | a746741971 | ||
|  | 120faf2a58 | ||
|  | 990bca0dc6 | ||
|  | 3406472db7 | ||
|  | 1bd733c9f6 | ||
|  | 238c7f982e | ||
|  | fcb81147cb | ||
|  | 1915b73783 | ||
|  | ee79e621fb | ||
|  | d203275a3b | ||
|  | 9e8a996222 | ||
|  | 0126b0b3ed | ||
|  | 458928612c | ||
|  | e33f88e28d | ||
|  | be570bbf9e | ||
|  | f59b4be110 | ||
|  | 37336e41be | ||
|  | d24a1a3f0a | ||
|  | f7258955bd | ||
|  | 2a1eae5d6f | ||
|  | 50ee0a4adb | ||
|  | 955a26584e | ||
|  | 1d3e407c8f | ||
|  | cb809c4596 | ||
|  | 53bbe2888e | ||
|  | 7246f476a5 | ||
|  | 0785d1c390 | ||
|  | 85d2c49d14 | ||
|  | 8b77d62b7f | ||
|  | 373058a32a | ||
|  | e6293c2c8c | ||
|  | eff181c959 | ||
|  | 54752c2305 | ||
|  | b4753c044f | ||
|  | 26493424ae | ||
|  | 0282fd1332 | ||
|  | b9a019a08b | ||
|  | 66f6a0e687 | ||
|  | 2dd1b9f97d | ||
|  | 89615f3045 | ||
|  | 7e46192f67 | ||
|  | e78d985cdf | ||
|  | e8c4bf56fe | ||
|  | e6aa7d323d | ||
|  | fca8e25929 | ||
|  | 8e8ac286b4 | ||
|  | 7d9770b9a2 | ||
|  | 1996230460 | ||
|  | de7897a864 | ||
|  | e2884dcdb7 | ||
|  | 544a53a42b | ||
|  | 2e4787bfc8 | ||
|  | 1829eeb171 | ||
|  | c7488e3c4a | ||
|  | 3bf9606383 | ||
|  | 4f43f18f0a | ||
|  | 5b7f197397 | ||
|  | 018141c97f | ||
|  | 4d7813e57c | ||
|  | 605c60208f | ||
|  | 884fafcc30 | ||
|  | 56baa90320 | ||
|  | 6bb20ee09e | ||
|  | 541356430c | ||
|  | 2f4d91fd69 | ||
|  | f58c5e6b30 | ||
|  | 0311d0132c | ||
|  | c946c97402 | ||
|  | 84a6f51318 | ||
|  | 24a1501b0d | ||
|  | 383b6f5fcc | ||
|  | 633dd7ff9b | ||
|  | 580624fad6 | ||
|  | a8190f7efa | ||
|  | dd2157534b | ||
|  | 38a90e7669 | ||
|  | 6bfc526dcd | ||
|  | aadb8a7405 | ||
|  | 27082bf77e | ||
|  | a2903c80cd | ||
|  | 9a77c5369c | ||
|  | 3c30741a19 | ||
|  | 7028ad4ec0 | ||
|  | 8de750c6aa | ||
|  | 04f98de9ee | ||
|  | a1a019784b | ||
|  | 4aeeae77bd | ||
|  | 651cfc2b78 | ||
|  | 2ef8af25e2 | ||
|  | 0b13852a5b | ||
|  | 13427578c9 | ||
|  | d89ca2087e | ||
|  | 8b0ea9fba6 | ||
|  | 9f0b653d5a | ||
|  | 659a339233 | ||
|  | 4c29f177a0 | ||
|  | d664e63d55 | ||
|  | 2493509dbe | ||
|  | 1c8b27f554 | ||
|  | 68297b7186 | ||
|  | 34dd8d0a91 | ||
|  | 81c44790d5 | ||
|  | d557b335cf | ||
|  | 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! | ||||
							
								
								
									
										45
									
								
								INSTALL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								INSTALL
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| 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  | ||||
|    and libdevmapper.so 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 wish to use the built-in LVM2 shell and have GNU readline  | ||||
|    installed (http://www.gnu.org/directory/readline.html) use: | ||||
|      ./configure --enable-readline | ||||
|  | ||||
|    If you don't want to include the LVM1 backwards-compatibility code use: | ||||
|      ./configure --with-lvm1=none  | ||||
|  | ||||
|    To separate the LVM1 support into a shared library loaded by lvm.conf use: | ||||
|      ./configure --with-lvm1=shared | ||||
|  | ||||
|  | ||||
| 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 can | ||||
|    make the tools run more efficiently - and avoid scanning /dev/cdrom! | ||||
|  | ||||
| Please also refer to the WHATS_NEW file and the manual pages for the  | ||||
| individual commands. | ||||
|  | ||||
							
								
								
									
										35
									
								
								Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| # | ||||
| # 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 += lib/format1 \ | ||||
| 	     test/mm test/device test/format1 test/regex test/filters | ||||
| endif | ||||
|  | ||||
| include make.tmpl | ||||
|  | ||||
| lib: include | ||||
| tools: include lib | ||||
|  | ||||
|  | ||||
							
								
								
									
										25
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								README
									
									
									
									
									
								
							| @@ -1,2 +1,23 @@ | ||||
| This is pretty much empty so far...if you can't see subdirectories, | ||||
| try 'cvs -f update' | ||||
| This directory contains LVM2, 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 WHATS_NEW file. | ||||
| Installation instructions are in INSTALL. | ||||
|  | ||||
| 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. | ||||
|   linux-lvm@sistina.com | ||||
|   Subscribe from http://lists.sistina.com/mailman/listinfo/linux-lvm | ||||
|  | ||||
|   | ||||
							
								
								
									
										94
									
								
								WHATS_NEW
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								WHATS_NEW
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| Wednesday 30th April 2003 | ||||
| ========================= | ||||
| A pvmove implementation is now available for the new metadata format. | ||||
|  | ||||
| When running a command that allocates space (e.g. lvcreate), you can now | ||||
| restrict not only which disk(s) may be used but also the Physical Extents  | ||||
| on those disks.  e.g. lvcreate -L 10 vg1 /dev/hda6:1000-2000:3000-4000 | ||||
|  | ||||
|  | ||||
| Monday 18th November 2002 | ||||
| ======================== | ||||
|  | ||||
| The new format of LVM metadata is ready for you to test! | ||||
|   We expect it to be more efficient and more robust than the original format. | ||||
|   It's more compact and supports transactional changes and replication. | ||||
|   Should things go wrong on a system, it's human-readable (and editable). | ||||
|  | ||||
| Please report any problems you find to the mailing list,  | ||||
| linux-lvm@sistina.com.  The software has NOT yet been thoroughly | ||||
| tested and so quite possibly there'll still be some bugs in it. | ||||
| Be aware of the disclaimer in the COPYING file. | ||||
|  | ||||
| While testing, we recommend turning logging on in the configuration file  | ||||
| to provide us with diagnostic information: | ||||
|   log { | ||||
|         file="/tmp/lvm2.log" | ||||
| 	level=7 | ||||
| 	activation=1 | ||||
|   } | ||||
|  | ||||
| You should schedule regular backups of your configuration file and | ||||
| metadata backups and archives (normally kept under /etc/lvm). | ||||
|  | ||||
| Please read docs/example.conf and "man lvm.conf" to find out more about  | ||||
| the configuration file. | ||||
|  | ||||
| To convert an existing volume group called vg1 to the new format using | ||||
| the default settings, use "vgconvert -M2 vg1".  See "man vgconvert". | ||||
|  | ||||
| -M (or --metadatatype in its long form) is a new flag to indicate which | ||||
| format of metadata the command should use for anything it creates. | ||||
| Currently, the valid types are "lvm1" and "lvm2" and they can be | ||||
| abbreviated to "1" and "2" respectively.  The default value for this | ||||
| flag can be changed in the global section in the config file. | ||||
|  | ||||
| Backwards-compatible support for the original LVM1 metadata format is | ||||
| maintained, but it can be moved into a shared library or removed | ||||
| completely with configure's --with-lvm1 option. | ||||
|  | ||||
| Under LVM2, the basic unit of metadata is the volume group.  Different | ||||
| volume groups can use different formats of metadata - vg1 could use | ||||
| the original LVM1 format while vg2 used the new format - but you can't | ||||
| mix formats within a volume group.  So to add a PV to an LVM2-format | ||||
| volume group you must run "pvcreate -M2" on it, followed by "vgextend". | ||||
|  | ||||
| With LVM2-format metadata, lvextend will let you specify striping | ||||
| parameters.  So an LV could consist of two or more "segments" - the | ||||
| first segment could have 3 stripes while the second segment has just 2. | ||||
|  | ||||
| LVM2 maintains a backup of the current metadata for each volume group | ||||
| in /etc/lvm/backup, and puts copies of previous versions in | ||||
| /etc/lvm/archive.  "vgcfgbackup" and "vgcfgrestore" can be used to | ||||
| create and restore from these files.  If you fully understand what  | ||||
| you're doing, metadata can be changed by editing a copy of a current | ||||
| backup file and using vgcfgrestore to reload it. | ||||
|  | ||||
| Please read the pvcreate man page for more information on the new | ||||
| format for metadata. | ||||
|  | ||||
| All tools that can change things have a --test flag which can be used | ||||
| to check the effect  of a set of cmdline args without really making the | ||||
| changes. | ||||
|  | ||||
|  | ||||
| What's not finished? | ||||
| ==================== | ||||
| The internal cache.  If you turn on debugging output you'll see lots of | ||||
| repeated messages, many of which will eventually get optimised out. | ||||
|  | ||||
| --test sometimes causes a command to fail (e.g. vgconvert --test) even  | ||||
| though the real command would work: again, fixing this is waiting for  | ||||
| the work on the cache. | ||||
|  | ||||
| Several of the tools do not yet contain the logic to handle full | ||||
| recovery: combinations of pvcreate and vgcfgrestore may sometimes be | ||||
| needed to restore metadata if a tool gets interrupted or crashes or | ||||
| finds something unexpected.  This applies particularly to tools that | ||||
| work on more than one volume group at once (e.g. vgsplit). | ||||
|  | ||||
| Display output.  Some metadata information cannot yet be displayed. | ||||
|  | ||||
| Recovery tools to salvage "lost" metadata directly from the disks: | ||||
| but we hope the new format will mean such tools are hardly ever needed! | ||||
|  | ||||
							
								
								
									
										777
									
								
								autoconf/config.guess
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										777
									
								
								autoconf/config.guess
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										373
									
								
								autoconf/config.sub
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										373
									
								
								autoconf/config.sub
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,10 @@ | ||||
| #! /bin/sh | ||||
| # Configuration validation subroutine script, version 1.1. | ||||
| #   Copyright (C) 1991, 92-97, 1998, 1999 Free Software Foundation, Inc. | ||||
| # Configuration validation subroutine script. | ||||
| #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 | ||||
| #   Free Software Foundation, Inc. | ||||
|  | ||||
| timestamp='2001-09-07' | ||||
|  | ||||
| # This file is (in principle) common to ALL GNU software. | ||||
| # The presence of a machine in this file suggests that SOME GNU software | ||||
| # can handle that machine.  It does not imply ALL GNU software can. | ||||
| @@ -25,6 +29,8 @@ | ||||
| # configuration script generated by Autoconf, you may include it under | ||||
| # the same distribution terms that you use for the rest of that program. | ||||
|  | ||||
| # Please send patches to <config-patches@gnu.org>. | ||||
| # | ||||
| # Configuration subroutine to validate and canonicalize a configuration type. | ||||
| # Supply the specified configuration type as an argument. | ||||
| # If it is invalid, we print an error message on stderr and exit with code 1. | ||||
| @@ -45,30 +51,73 @@ | ||||
| #	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM | ||||
| # It is wrong to echo any other type of specification. | ||||
|  | ||||
| if [ x$1 = x ] | ||||
| then | ||||
| 	echo Configuration name missing. 1>&2 | ||||
| 	echo "Usage: $0 CPU-MFR-OPSYS" 1>&2 | ||||
| 	echo "or     $0 ALIAS" 1>&2 | ||||
| 	echo where ALIAS is a recognized configuration type. 1>&2 | ||||
| 	exit 1 | ||||
| fi | ||||
| me=`echo "$0" | sed -e 's,.*/,,'` | ||||
|  | ||||
| # First pass through any local machine types. | ||||
| case $1 in | ||||
| 	*local*) | ||||
| 		echo $1 | ||||
| 		exit 0 | ||||
| 		;; | ||||
| 	*) | ||||
| 	;; | ||||
| usage="\ | ||||
| Usage: $0 [OPTION] CPU-MFR-OPSYS | ||||
|        $0 [OPTION] ALIAS | ||||
|  | ||||
| Canonicalize a configuration name. | ||||
|  | ||||
| Operation modes: | ||||
|   -h, --help         print this help, then exit | ||||
|   -t, --time-stamp   print date of last modification, then exit | ||||
|   -v, --version      print version number, then exit | ||||
|  | ||||
| Report bugs and patches to <config-patches@gnu.org>." | ||||
|  | ||||
| version="\ | ||||
| GNU config.sub ($timestamp) | ||||
|  | ||||
| Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 | ||||
| Free Software Foundation, Inc. | ||||
|  | ||||
| This is free software; see the source for copying conditions.  There is NO | ||||
| warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." | ||||
|  | ||||
| help=" | ||||
| Try \`$me --help' for more information." | ||||
|  | ||||
| # Parse command line | ||||
| while test $# -gt 0 ; do | ||||
|   case $1 in | ||||
|     --time-stamp | --time* | -t ) | ||||
|        echo "$timestamp" ; exit 0 ;; | ||||
|     --version | -v ) | ||||
|        echo "$version" ; exit 0 ;; | ||||
|     --help | --h* | -h ) | ||||
|        echo "$usage"; exit 0 ;; | ||||
|     -- )     # Stop option processing | ||||
|        shift; break ;; | ||||
|     - )	# Use stdin as input. | ||||
|        break ;; | ||||
|     -* ) | ||||
|        echo "$me: invalid option $1$help" | ||||
|        exit 1 ;; | ||||
|  | ||||
|     *local*) | ||||
|        # First pass through any local machine types. | ||||
|        echo $1 | ||||
|        exit 0;; | ||||
|  | ||||
|     * ) | ||||
|        break ;; | ||||
|   esac | ||||
| done | ||||
|  | ||||
| case $# in | ||||
|  0) echo "$me: missing argument$help" >&2 | ||||
|     exit 1;; | ||||
|  1) ;; | ||||
|  *) echo "$me: too many arguments$help" >&2 | ||||
|     exit 1;; | ||||
| esac | ||||
|  | ||||
| # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). | ||||
| # Here we must recognize all the valid KERNEL-OS combinations. | ||||
| maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` | ||||
| case $maybe_os in | ||||
|   linux-gnu*) | ||||
|   nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*) | ||||
|     os=-$maybe_os | ||||
|     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` | ||||
|     ;; | ||||
| @@ -94,7 +143,7 @@ case $os in | ||||
| 	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ | ||||
| 	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ | ||||
| 	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ | ||||
| 	-apple) | ||||
| 	-apple | -axis) | ||||
| 		os= | ||||
| 		basic_machine=$1 | ||||
| 		;; | ||||
| @@ -105,9 +154,17 @@ case $os in | ||||
| 	-scout) | ||||
| 		;; | ||||
| 	-wrs) | ||||
| 		os=vxworks | ||||
| 		os=-vxworks | ||||
| 		basic_machine=$1 | ||||
| 		;; | ||||
| 	-chorusos*) | ||||
| 		os=-chorusos | ||||
| 		basic_machine=$1 | ||||
| 		;; | ||||
|  	-chorusrdb) | ||||
|  		os=-chorusrdb | ||||
| 		basic_machine=$1 | ||||
|  		;; | ||||
| 	-hiux*) | ||||
| 		os=-hiuxwe2 | ||||
| 		;; | ||||
| @@ -156,33 +213,60 @@ case $os in | ||||
| 	-psos*) | ||||
| 		os=-psos | ||||
| 		;; | ||||
| 	-mint | -mint[0-9]*) | ||||
| 		basic_machine=m68k-atari | ||||
| 		os=-mint | ||||
| 		;; | ||||
| esac | ||||
|  | ||||
| # Decode aliases for certain CPU-COMPANY combinations. | ||||
| case $basic_machine in | ||||
| 	# Recognize the basic CPU types without company name. | ||||
| 	# Some are omitted here because they have special meanings below. | ||||
| 	tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \ | ||||
| 		| arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \ | ||||
| 		| 580 | i960 | h8300 \ | ||||
| 		| hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \ | ||||
| 		| alpha | alphaev[4-7] | alphaev56 | alphapca5[67] \ | ||||
| 		| we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \ | ||||
| 		| 1750a | dsp16xx | pdp11 | mips16 | mips64 | mipsel | mips64el \ | ||||
| 		| mips64orion | mips64orionel | mipstx39 | mipstx39el \ | ||||
| 		| mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \ | ||||
| 		| mips64vr5000 | miprs64vr5000el | mcore \ | ||||
| 		| sparc | sparclet | sparclite | sparc64 | sparcv9 | v850 | c4x \ | ||||
| 		| thumb | d10v) | ||||
| 	1750a | 580 \ | ||||
| 	| a29k \ | ||||
| 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | ||||
| 	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ | ||||
| 	| c4x | clipper \ | ||||
| 	| d10v | d30v | dsp16xx \ | ||||
| 	| fr30 \ | ||||
| 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | ||||
| 	| i370 | i860 | i960 | ia64 \ | ||||
| 	| m32r | m68000 | m68k | m88k | mcore \ | ||||
| 	| mips16 | mips64 | mips64el | mips64orion | mips64orionel \ | ||||
| 	| mips64vr4100 | mips64vr4100el | mips64vr4300 \ | ||||
| 	| mips64vr4300el | mips64vr5000 | mips64vr5000el \ | ||||
| 	| mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \ | ||||
| 	| mipsisa32 \ | ||||
| 	| mn10200 | mn10300 \ | ||||
| 	| ns16k | ns32k \ | ||||
| 	| openrisc \ | ||||
| 	| pdp10 | pdp11 | pj | pjl \ | ||||
| 	| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | ||||
| 	| pyramid \ | ||||
| 	| s390 | s390x \ | ||||
| 	| sh | sh[34] | sh[34]eb | shbe | shle \ | ||||
| 	| sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \ | ||||
| 	| stormy16 | strongarm \ | ||||
| 	| tahoe | thumb | tic80 | tron \ | ||||
| 	| v850 \ | ||||
| 	| we32k \ | ||||
| 	| x86 | xscale \ | ||||
| 	| z8k) | ||||
| 		basic_machine=$basic_machine-unknown | ||||
| 		;; | ||||
| 	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | h8500 | w65) | ||||
| 	m6811 | m68hc11 | m6812 | m68hc12) | ||||
| 		# Motorola 68HC11/12. | ||||
| 		basic_machine=$basic_machine-unknown | ||||
| 		os=-none | ||||
| 		;; | ||||
| 	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) | ||||
| 		;; | ||||
|  | ||||
| 	# We use `pc' rather than `unknown' | ||||
| 	# because (1) that's what they normally are, and | ||||
| 	# (2) the word "unknown" tends to confuse beginning users. | ||||
| 	i[34567]86) | ||||
| 	i*86 | x86_64) | ||||
| 	  basic_machine=$basic_machine-pc | ||||
| 	  ;; | ||||
| 	# Object if more than one company name word. | ||||
| @@ -191,24 +275,43 @@ case $basic_machine in | ||||
| 		exit 1 | ||||
| 		;; | ||||
| 	# Recognize the basic CPU types with company name. | ||||
| 	# FIXME: clean up the formatting here. | ||||
| 	vax-* | tahoe-* | i[34567]86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \ | ||||
| 	      | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \ | ||||
| 	      | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \ | ||||
| 	      | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \ | ||||
| 	      | xmp-* | ymp-* \ | ||||
| 	      | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* | hppa2.0n-* \ | ||||
| 	      | alpha-* | alphaev[4-7]-* | alphaev56-* | alphapca5[67]-* \ | ||||
| 	      | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \ | ||||
| 	      | clipper-* | orion-* \ | ||||
| 	      | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \ | ||||
| 	      | sparc64-* | sparcv9-* | sparc86x-* | mips16-* | mips64-* | mipsel-* \ | ||||
| 	      | mips64el-* | mips64orion-* | mips64orionel-* \ | ||||
| 	      | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \ | ||||
| 	      | mipstx39-* | mipstx39el-* | mcore-* \ | ||||
| 	      | f301-* | armv*-* | t3e-* \ | ||||
| 	      | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \ | ||||
| 	      | thumb-* | v850-* | d30v-* | tic30-* | c30-* ) | ||||
| 	580-* \ | ||||
| 	| a29k-* \ | ||||
| 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | ||||
| 	| alphapca5[67]-* | arc-* \ | ||||
| 	| arm-*  | armbe-* | armle-* | armv*-* \ | ||||
| 	| bs2000-* \ | ||||
| 	| c[123]* | c30-* | [cjt]90-* | c54x-* \ | ||||
| 	| clipper-* | cray2-* | cydra-* \ | ||||
| 	| d10v-* | d30v-* \ | ||||
| 	| elxsi-* \ | ||||
| 	| f30[01]-* | f700-* | fr30-* | fx80-* \ | ||||
| 	| h8300-* | h8500-* \ | ||||
| 	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | ||||
| 	| i*86-* | i860-* | i960-* | ia64-* \ | ||||
| 	| m32r-* \ | ||||
| 	| m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \ | ||||
| 	| m88110-* | m88k-* | mcore-* \ | ||||
| 	| mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \ | ||||
| 	| mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \ | ||||
| 	| mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \ | ||||
| 	| mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \ | ||||
| 	| none-* | np1-* | ns16k-* | ns32k-* \ | ||||
| 	| orion-* \ | ||||
| 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | ||||
| 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | ||||
| 	| pyramid-* \ | ||||
| 	| romp-* | rs6000-* \ | ||||
| 	| s390-* | s390x-* \ | ||||
| 	| sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \ | ||||
| 	| sparc-* | sparc64-* | sparc86x-* | sparclite-* \ | ||||
| 	| sparcv9-* | sparcv9b-* | stormy16-* | strongarm-* | sv1-* \ | ||||
| 	| t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \ | ||||
| 	| v850-* | vax-* \ | ||||
| 	| we32k-* \ | ||||
| 	| x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \ | ||||
| 	| ymp-* \ | ||||
| 	| z8k-*) | ||||
| 		;; | ||||
| 	# Recognize the various machine names and aliases which stand | ||||
| 	# for a CPU type and a company and sometimes even an OS. | ||||
| @@ -245,14 +348,14 @@ case $basic_machine in | ||||
| 		os=-sysv | ||||
| 		;; | ||||
| 	amiga | amiga-*) | ||||
| 		basic_machine=m68k-cbm | ||||
| 		basic_machine=m68k-unknown | ||||
| 		;; | ||||
| 	amigaos | amigados) | ||||
| 		basic_machine=m68k-cbm | ||||
| 		basic_machine=m68k-unknown | ||||
| 		os=-amigaos | ||||
| 		;; | ||||
| 	amigaunix | amix) | ||||
| 		basic_machine=m68k-cbm | ||||
| 		basic_machine=m68k-unknown | ||||
| 		os=-sysv4 | ||||
| 		;; | ||||
| 	apollo68) | ||||
| @@ -299,13 +402,16 @@ case $basic_machine in | ||||
| 		basic_machine=cray2-cray | ||||
| 		os=-unicos | ||||
| 		;; | ||||
| 	[ctj]90-cray) | ||||
| 		basic_machine=c90-cray | ||||
| 	[cjt]90) | ||||
| 		basic_machine=${basic_machine}-cray | ||||
| 		os=-unicos | ||||
| 		;; | ||||
| 	crds | unos) | ||||
| 		basic_machine=m68k-crds | ||||
| 		;; | ||||
| 	cris | cris-* | etrax*) | ||||
| 		basic_machine=cris-axis | ||||
| 		;; | ||||
| 	da30 | da30-*) | ||||
| 		basic_machine=m68k-da30 | ||||
| 		;; | ||||
| @@ -353,6 +459,10 @@ case $basic_machine in | ||||
| 		basic_machine=tron-gmicro | ||||
| 		os=-sysv | ||||
| 		;; | ||||
| 	go32) | ||||
| 		basic_machine=i386-pc | ||||
| 		os=-go32 | ||||
| 		;; | ||||
| 	h3050r* | hiux*) | ||||
| 		basic_machine=hppa1.1-hitachi | ||||
| 		os=-hiuxwe2 | ||||
| @@ -426,22 +536,21 @@ case $basic_machine in | ||||
| 		;; | ||||
| 	i370-ibm* | ibm*) | ||||
| 		basic_machine=i370-ibm | ||||
| 		os=-mvs | ||||
| 		;; | ||||
| # I'm not sure what "Sysv32" means.  Should this be sysv3.2? | ||||
| 	i[34567]86v32) | ||||
| 	i*86v32) | ||||
| 		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` | ||||
| 		os=-sysv32 | ||||
| 		;; | ||||
| 	i[34567]86v4*) | ||||
| 	i*86v4*) | ||||
| 		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` | ||||
| 		os=-sysv4 | ||||
| 		;; | ||||
| 	i[34567]86v) | ||||
| 	i*86v) | ||||
| 		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` | ||||
| 		os=-sysv | ||||
| 		;; | ||||
| 	i[34567]86sol2) | ||||
| 	i*86sol2) | ||||
| 		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` | ||||
| 		os=-solaris2 | ||||
| 		;; | ||||
| @@ -453,14 +562,6 @@ case $basic_machine in | ||||
| 		basic_machine=i386-unknown | ||||
| 		os=-vsta | ||||
| 		;; | ||||
| 	i386-go32 | go32) | ||||
| 		basic_machine=i386-unknown | ||||
| 		os=-go32 | ||||
| 		;; | ||||
| 	i386-mingw32 | mingw32) | ||||
| 		basic_machine=i386-unknown | ||||
| 		os=-mingw32 | ||||
| 		;; | ||||
| 	iris | iris4d) | ||||
| 		basic_machine=mips-sgi | ||||
| 		case $os in | ||||
| @@ -486,10 +587,14 @@ case $basic_machine in | ||||
| 		basic_machine=ns32k-utek | ||||
| 		os=-sysv | ||||
| 		;; | ||||
| 	mingw32) | ||||
| 		basic_machine=i386-pc | ||||
| 		os=-mingw32 | ||||
| 		;; | ||||
| 	miniframe) | ||||
| 		basic_machine=m68000-convergent | ||||
| 		;; | ||||
| 	*mint | *MiNT) | ||||
| 	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) | ||||
| 		basic_machine=m68k-atari | ||||
| 		os=-mint | ||||
| 		;; | ||||
| @@ -507,14 +612,22 @@ case $basic_machine in | ||||
| 	mips3*) | ||||
| 		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown | ||||
| 		;; | ||||
| 	mmix*) | ||||
| 		basic_machine=mmix-knuth | ||||
| 		os=-mmixware | ||||
| 		;; | ||||
| 	monitor) | ||||
| 		basic_machine=m68k-rom68k | ||||
| 		os=-coff | ||||
| 		;; | ||||
| 	msdos) | ||||
| 		basic_machine=i386-unknown | ||||
| 		basic_machine=i386-pc | ||||
| 		os=-msdos | ||||
| 		;; | ||||
| 	mvs) | ||||
| 		basic_machine=i370-ibm | ||||
| 		os=-mvs | ||||
| 		;; | ||||
| 	ncr3000) | ||||
| 		basic_machine=i486-ncr | ||||
| 		os=-sysv4 | ||||
| @@ -524,7 +637,7 @@ case $basic_machine in | ||||
| 		os=-netbsd | ||||
| 		;; | ||||
| 	netwinder) | ||||
| 		basic_machine=armv4l-corel | ||||
| 		basic_machine=armv4l-rebel | ||||
| 		os=-linux | ||||
| 		;; | ||||
| 	news | news700 | news800 | news900) | ||||
| @@ -572,9 +685,16 @@ case $basic_machine in | ||||
| 		basic_machine=i960-intel | ||||
| 		os=-mon960 | ||||
| 		;; | ||||
| 	nonstopux) | ||||
| 		basic_machine=mips-compaq | ||||
| 		os=-nonstopux | ||||
| 		;; | ||||
| 	np1) | ||||
| 		basic_machine=np1-gould | ||||
| 		;; | ||||
| 	nsr-tandem) | ||||
| 		basic_machine=nsr-tandem | ||||
| 		;; | ||||
| 	op50n-* | op60c-*) | ||||
| 		basic_machine=hppa1.1-oki | ||||
| 		os=-proelf | ||||
| @@ -604,28 +724,28 @@ case $basic_machine in | ||||
|         pc532 | pc532-*) | ||||
| 		basic_machine=ns32k-pc532 | ||||
| 		;; | ||||
| 	pentium | p5 | k5 | k6 | nexen) | ||||
| 	pentium | p5 | k5 | k6 | nexgen) | ||||
| 		basic_machine=i586-pc | ||||
| 		;; | ||||
| 	pentiumpro | p6 | 6x86) | ||||
| 	pentiumpro | p6 | 6x86 | athlon) | ||||
| 		basic_machine=i686-pc | ||||
| 		;; | ||||
| 	pentiumii | pentium2) | ||||
| 		basic_machine=i786-pc | ||||
| 		basic_machine=i686-pc | ||||
| 		;; | ||||
| 	pentium-* | p5-* | k5-* | k6-* | nexen-*) | ||||
| 	pentium-* | p5-* | k5-* | k6-* | nexgen-*) | ||||
| 		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` | ||||
| 		;; | ||||
| 	pentiumpro-* | p6-* | 6x86-*) | ||||
| 	pentiumpro-* | p6-* | 6x86-* | athlon-*) | ||||
| 		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` | ||||
| 		;; | ||||
| 	pentiumii-* | pentium2-*) | ||||
| 		basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` | ||||
| 		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` | ||||
| 		;; | ||||
| 	pn) | ||||
| 		basic_machine=pn-gould | ||||
| 		;; | ||||
| 	power)	basic_machine=rs6000-ibm | ||||
| 	power)	basic_machine=power-ibm | ||||
| 		;; | ||||
| 	ppc)	basic_machine=powerpc-unknown | ||||
| 	        ;; | ||||
| @@ -637,9 +757,23 @@ case $basic_machine in | ||||
| 	ppcle-* | powerpclittle-*) | ||||
| 		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` | ||||
| 		;; | ||||
| 	ppc64)	basic_machine=powerpc64-unknown | ||||
| 	        ;; | ||||
| 	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` | ||||
| 		;; | ||||
| 	ppc64le | powerpc64little | ppc64-le | powerpc64-little) | ||||
| 		basic_machine=powerpc64le-unknown | ||||
| 	        ;; | ||||
| 	ppc64le-* | powerpc64little-*) | ||||
| 		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` | ||||
| 		;; | ||||
| 	ps2) | ||||
| 		basic_machine=i386-ibm | ||||
| 		;; | ||||
| 	pw32) | ||||
| 		basic_machine=i586-unknown | ||||
| 		os=-pw32 | ||||
| 		;; | ||||
| 	rom68k) | ||||
| 		basic_machine=m68k-rom68k | ||||
| 		os=-coff | ||||
| @@ -719,6 +853,10 @@ case $basic_machine in | ||||
| 	sun386 | sun386i | roadrunner) | ||||
| 		basic_machine=i386-sun | ||||
| 		;; | ||||
| 	sv1) | ||||
| 		basic_machine=sv1-cray | ||||
| 		os=-unicos | ||||
| 		;; | ||||
| 	symmetry) | ||||
| 		basic_machine=i386-sequent | ||||
| 		os=-dynix | ||||
| @@ -727,6 +865,10 @@ case $basic_machine in | ||||
| 		basic_machine=t3e-cray | ||||
| 		os=-unicos | ||||
| 		;; | ||||
| 	tic54x | c54x*) | ||||
| 		basic_machine=tic54x-unknown | ||||
| 		os=-coff | ||||
| 		;; | ||||
| 	tx39) | ||||
| 		basic_machine=mipstx39-unknown | ||||
| 		;; | ||||
| @@ -779,6 +921,10 @@ case $basic_machine in | ||||
| 		basic_machine=hppa1.1-winbond | ||||
| 		os=-proelf | ||||
| 		;; | ||||
| 	windows32) | ||||
| 		basic_machine=i386-pc | ||||
| 		os=-windows32-msvcrt | ||||
| 		;; | ||||
| 	xmp) | ||||
| 		basic_machine=xmp-cray | ||||
| 		os=-unicos | ||||
| @@ -822,13 +968,20 @@ case $basic_machine in | ||||
| 	vax) | ||||
| 		basic_machine=vax-dec | ||||
| 		;; | ||||
| 	pdp10) | ||||
| 		# there are many clones, so DEC is not a safe bet | ||||
| 		basic_machine=pdp10-unknown | ||||
| 		;; | ||||
| 	pdp11) | ||||
| 		basic_machine=pdp11-dec | ||||
| 		;; | ||||
| 	we32k) | ||||
| 		basic_machine=we32k-att | ||||
| 		;; | ||||
| 	sparc | sparcv9) | ||||
| 	sh3 | sh4 | sh3eb | sh4eb) | ||||
| 		basic_machine=sh-unknown | ||||
| 		;; | ||||
| 	sparc | sparcv9 | sparcv9b) | ||||
| 		basic_machine=sparc-sun | ||||
| 		;; | ||||
|         cydra) | ||||
| @@ -850,6 +1003,9 @@ case $basic_machine in | ||||
| 		basic_machine=c4x-none | ||||
| 		os=-coff | ||||
| 		;; | ||||
| 	*-unknown) | ||||
| 		# Make sure to match an already-canonicalized machine name. | ||||
| 		;; | ||||
| 	*) | ||||
| 		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 | ||||
| 		exit 1 | ||||
| @@ -906,14 +1062,30 @@ case $os in | ||||
| 	      | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | ||||
| 	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | ||||
| 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | ||||
| 	      | -chorusos* | -chorusrdb* \ | ||||
| 	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | ||||
| 	      | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ | ||||
| 	      | -interix* | -uwin* | -rhapsody* | -openstep* | -oskit*) | ||||
| 	      | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ | ||||
| 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | ||||
| 	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | ||||
| 	      | -os2* | -vos*) | ||||
| 	# Remember, each alternative MUST END IN *, to match a version number. | ||||
| 		;; | ||||
| 	-qnx*) | ||||
| 		case $basic_machine in | ||||
| 		    x86-* | i*86-*) | ||||
| 			;; | ||||
| 		    *) | ||||
| 			os=-nto$os | ||||
| 			;; | ||||
| 		esac | ||||
| 		;; | ||||
| 	-nto*) | ||||
| 		os=-nto-qnx | ||||
| 		;; | ||||
| 	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | ||||
| 	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ | ||||
| 	      | -macos* | -mpw* | -magic* | -mon960* | -lnews*) | ||||
| 	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) | ||||
| 		;; | ||||
| 	-mac*) | ||||
| 		os=`echo $os | sed -e 's|mac|macos|'` | ||||
| @@ -927,6 +1099,12 @@ case $os in | ||||
| 	-sunos6*) | ||||
| 		os=`echo $os | sed -e 's|sunos6|solaris3|'` | ||||
| 		;; | ||||
| 	-opened*) | ||||
| 		os=-openedition | ||||
| 		;; | ||||
| 	-wince*) | ||||
| 		os=-wince | ||||
| 		;; | ||||
| 	-osfrose*) | ||||
| 		os=-osfrose | ||||
| 		;; | ||||
| @@ -951,6 +1129,9 @@ case $os in | ||||
| 	-ns2 ) | ||||
| 	        os=-nextstep2 | ||||
| 		;; | ||||
| 	-nsk*) | ||||
| 		os=-nsk | ||||
| 		;; | ||||
| 	# Preserve the version number of sinix5. | ||||
| 	-sinix5.*) | ||||
| 		os=`echo $os | sed -e 's|sinix|sysv|'` | ||||
| @@ -985,7 +1166,7 @@ case $os in | ||||
| 	-xenix) | ||||
| 		os=-xenix | ||||
| 		;; | ||||
|         -*mint | -*MiNT) | ||||
|         -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) | ||||
| 	        os=-mint | ||||
| 		;; | ||||
| 	-none) | ||||
| @@ -1013,12 +1194,15 @@ case $basic_machine in | ||||
| 	*-acorn) | ||||
| 		os=-riscix1.2 | ||||
| 		;; | ||||
| 	arm*-corel) | ||||
| 	arm*-rebel) | ||||
| 		os=-linux | ||||
| 		;; | ||||
| 	arm*-semi) | ||||
| 		os=-aout | ||||
| 		;; | ||||
| 	pdp10-*) | ||||
| 		os=-tops20 | ||||
| 		;; | ||||
|         pdp11-*) | ||||
| 		os=-none | ||||
| 		;; | ||||
| @@ -1127,7 +1311,7 @@ case $basic_machine in | ||||
| 	*-masscomp) | ||||
| 		os=-rtu | ||||
| 		;; | ||||
| 	f301-fujitsu) | ||||
| 	f30[01]-fujitsu | f700-fujitsu) | ||||
| 		os=-uxpv | ||||
| 		;; | ||||
| 	*-rom68k) | ||||
| @@ -1187,7 +1371,7 @@ case $basic_machine in | ||||
| 			-genix*) | ||||
| 				vendor=ns | ||||
| 				;; | ||||
| 			-mvs*) | ||||
| 			-mvs* | -opened*) | ||||
| 				vendor=ibm | ||||
| 				;; | ||||
| 			-ptx*) | ||||
| @@ -1205,12 +1389,23 @@ case $basic_machine in | ||||
| 			-mpw* | -macos*) | ||||
| 				vendor=apple | ||||
| 				;; | ||||
| 			-*mint | -*MiNT) | ||||
| 			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) | ||||
| 				vendor=atari | ||||
| 				;; | ||||
| 			-vos*) | ||||
| 				vendor=stratus | ||||
| 				;; | ||||
| 		esac | ||||
| 		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` | ||||
| 		;; | ||||
| esac | ||||
|  | ||||
| echo $basic_machine$os | ||||
| exit 0 | ||||
|  | ||||
| # Local variables: | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "timestamp='" | ||||
| # time-stamp-format: "%:y-%02m-%02d" | ||||
| # time-stamp-end: "'" | ||||
| # End: | ||||
|   | ||||
							
								
								
									
										205
									
								
								configure.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								configure.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| ################################################################################ | ||||
| ## | ||||
| ##    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 Get system type | ||||
| AC_CANONICAL_SYSTEM | ||||
|  | ||||
| case "$host_os" in | ||||
| 	linux*) | ||||
| 		CFLAGS= ;; | ||||
| esac | ||||
|  | ||||
| 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" ]) | ||||
|  | ||||
| dnl -- format1 inclusion type | ||||
| AC_ARG_WITH(lvm1, | ||||
|   [  --with-lvm1=TYPE        LVM1 metadata support: internal/shared/none | ||||
|                           [TYPE=internal] ], | ||||
|   [ LVM1="$withval" ], | ||||
|   [ LVM1="internal" ]) | ||||
|  | ||||
| if [[ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ]]; | ||||
|  then  AC_MSG_ERROR( | ||||
| --with-lvm1 parameter invalid | ||||
| ) | ||||
|  exit | ||||
| fi; | ||||
|  | ||||
| if test x$LVM1 = xinternal; then | ||||
| 	CFLAGS="$CFLAGS -DLVM1_INTERNAL" | ||||
| fi | ||||
|  | ||||
| AC_ARG_ENABLE(jobs, [  --enable-jobs=NUM       Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=-j2) | ||||
|  | ||||
| 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 Enable readline | ||||
| AC_ARG_ENABLE(readline, [  --enable-readline       Enable readline support],  \ | ||||
| READLINE=$enableval, READLINE=no) | ||||
|  | ||||
| if test x$READLINE = xyes; then | ||||
| 	CFLAGS="$CFLAGS -DREADLINE_SUPPORT" | ||||
| fi | ||||
|  | ||||
| dnl Enable Debugging | ||||
| AC_ARG_ENABLE(debug,    [  --enable-debug          Enable debugging],  \ | ||||
| DEBUG=yes, DEBUG=no) | ||||
|  | ||||
| dnl Disable devmapper | ||||
| AC_ARG_ENABLE(devmapper, [  --disable-devmapper     Disable device-mapper interaction],  \ | ||||
| DEVMAPPER=no, DEVMAPPER=yes) | ||||
|  | ||||
| if test x$DEVMAPPER = xyes; then | ||||
| 	CFLAGS="$CFLAGS -DDEVMAPPER_SUPPORT" | ||||
| fi | ||||
|  | ||||
| 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 dlopen | ||||
| AC_CHECK_LIB(dl, dlopen, HAVE_LIBDL=yes, HAVE_LIBDL=no) | ||||
|  | ||||
| if test x$HAVE_LIBDL = xyes; then | ||||
| 	CFLAGS="$CFLAGS -DHAVE_LIBDL" | ||||
| 	LIBS="-ldl $LIBS" | ||||
| fi | ||||
|  | ||||
| dnl Check for getopt | ||||
| AC_CHECK_HEADERS(getopt.h, CFLAGS="$CFLAGS -DHAVE_GETOPTLONG") | ||||
|  | ||||
| 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, CFLAGS="$CFLAGS -DHAVE_RL_COMPLETION_MATCHES") | ||||
| 		 | ||||
| fi | ||||
|  | ||||
| if test "-f VERSION"; then | ||||
|   LVM_VERSION="\"`cat VERSION`\"" | ||||
| else | ||||
|   LVM_VERSION="Unknown" | ||||
| fi | ||||
|  | ||||
| AC_SUBST(JOBS) | ||||
| AC_SUBST(STATIC_LINK) | ||||
| AC_SUBST(LVM1) | ||||
| AC_SUBST(OWNER) | ||||
| AC_SUBST(GROUP) | ||||
| AC_SUBST(CFLAGS) | ||||
| AC_SUBST(LIBS) | ||||
| AC_SUBST(LVM_VERSION) | ||||
| AC_SUBST(DEBUG) | ||||
| AC_SUBST(DEVMAPPER) | ||||
| AC_SUBST(HAVE_LIBDL) | ||||
| 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							 	\ | ||||
| lib/format1/Makefile						 	\ | ||||
| man/Makefile							 	\ | ||||
| tools/Makefile							 	\ | ||||
| tools/version.h								\ | ||||
| test/mm/Makefile							\ | ||||
| test/device/Makefile							\ | ||||
| test/format1/Makefile							\ | ||||
| test/regex/Makefile                                                     \ | ||||
| test/filters/Makefile                                                   \ | ||||
| ) | ||||
							
								
								
									
										6
									
								
								debian/README.Debian
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								debian/README.Debian
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| LVM2 requires the device-mapper kernel module (dm-mod).  This is | ||||
| available as a kernel patch for 2.4 (in kernel-patch-device-mapper), and | ||||
| is distributed with linux 2.5 and above.  The LVM1 kernel module (lvm-mod) | ||||
| will not work with lvm2 packages.  dm-mod and lvm-mod may both be loaded | ||||
| in the kernel at the same time with no problems.  Without dm-mod, this | ||||
| package is pretty useless. | ||||
							
								
								
									
										82
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| lvm2 (1.95.15-1) unstable; urgency=low | ||||
|    | ||||
|   * New upstream release. | ||||
|   * Remove undocumented manpage symlinks. | ||||
|   * Update description to be more informative.  (Closes: #173499) | ||||
|   * Add kernel-patch-device-mapper suggestion. | ||||
|   * Update standards version. | ||||
|  | ||||
|  -- Andres Salomon <dilinger@mp3revolution.net>  Sun, 16 Feb 2002 04:21:26 -0400 | ||||
|  | ||||
| lvm2 (1.95.11-1) unstable; urgency=low | ||||
|  | ||||
|   * New upstream release.  (Closes: #171436) | ||||
|   * Removed TODO and INTRO from debian/docs; added WHATS_NEW. | ||||
|   * Remove vgcfgrestore.8 undocumented symlink. | ||||
|   * Added a README.Debian, mentioning the device-mapper kernel module | ||||
|     requirement that lvm2 has.  (Closes: #171674, #163020) | ||||
|   * Get rid of debian/conffiles (debhelper's smart enough to figure that out). | ||||
|   * debian/copyright fix to appease lintian. | ||||
|   * Fix typo in tools/commands.h that caused /usr/sbin/; to be created. | ||||
|  | ||||
|  -- Andres Salomon <dilinger@mp3revolution.net>  Mon,  9 Dec 2002 02:51:02 -0400 | ||||
|  | ||||
| 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 | ||||
|  | ||||
							
								
								
									
										24
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| 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.8.0 | ||||
|  | ||||
| Package: lvm2 | ||||
| Architecture: any | ||||
| Depends: ${shlibs:Depends} | ||||
| Conflicts: lvm10, lvm-common | ||||
| Replaces: lvm10, lvm-common | ||||
| Provides: lvm-binaries | ||||
| Suggests: dmsetup, kernel-patch-device-mapper | ||||
| 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. | ||||
|  . | ||||
|  LVM2 is currently stable, but has some unimplemented features (most notably, | ||||
|  pvmove and e2fsadm).  It is not yet recommended for production use.  It is | ||||
|  backwards-compatible with LVM1 (lvm10), and requires Linux kernel 2.4. | ||||
							
								
								
									
										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: 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 | ||||
							
								
								
									
										5
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| BUGS | ||||
| README | ||||
| VERSION | ||||
| WHATS_NEW | ||||
| 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 | ||||
							
								
								
									
										119
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										119
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| #!/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_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 | ||||
| @@ -1 +0,0 @@ | ||||
| Wow!  This is really incredible documentation! | ||||
							
								
								
									
										257
									
								
								doc/example.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								doc/example.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,257 @@ | ||||
| # 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 including the file layout. | ||||
| # | ||||
| # To put this file in a different directory and override /etc/lvm set | ||||
| # the environment variable LVM_SYSTEM_DIR before running the tools. | ||||
|  | ||||
|  | ||||
| # This section allows you 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 filter that tells LVM2 to only use a restricted set of devices. | ||||
|     # 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). | ||||
|     # The first expression found to match a device name determines if | ||||
|     # the device will be accepted or rejected (ignored).  Devices that | ||||
|     # don't match any patterns are accepted. | ||||
|  | ||||
|     # If using RAID md devices as physical volumes, you should | ||||
|     # set up a filter here to reject the constituent devices. | ||||
|  | ||||
|     # Remember to run vgscan after you change this parameter to ensure  | ||||
|     # that the cache file gets regenerated (see below). | ||||
|  | ||||
|     # By default we accept every block device: | ||||
|     filter = [ "a/.*/" ] | ||||
|  | ||||
|     # Exclude the cdrom drive | ||||
|     # filter = [ "r|/dev/cdrom|" ] | ||||
|  | ||||
|     # 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 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 safe to delete this file: the tools regenerate it. | ||||
|     cache = "/etc/lvm/.cache" | ||||
|  | ||||
|     # You can turn off writing this cache file by setting this to 0. | ||||
|     write_cache_state = 1 | ||||
|  | ||||
|     # An advanced setting. | ||||
|     # List of pairs of additional acceptable block device types found  | ||||
|     # in /proc/devices with maximum (non-zero) number of partitions. | ||||
|     # types = [ "fd", 16 ] | ||||
| } | ||||
|  | ||||
| # This section that allows you to configure the nature of the | ||||
| # information that LVM2 reports. | ||||
| log { | ||||
|  | ||||
|     # Controls the messages sent to stdout or stderr. | ||||
|     # 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 | ||||
|  | ||||
|     # Should we log error and debug messages to a file? | ||||
|     # By default there is no log file. | ||||
|     #file = "/var/log/lvm2.log" | ||||
|  | ||||
|     # Should we overwrite the log file each time the program is run? | ||||
|     # By default we append. | ||||
|     overwrite = 0 | ||||
|  | ||||
|     # What level of log messages should we send to the log file and/or syslog? | ||||
|     # There are 6 syslog-like log levels currently in use - 2 to 7 inclusive. | ||||
|     # 7 is the most verbose (LOG_DEBUG). | ||||
|     level = 0 | ||||
|      | ||||
|     # 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).  Default is two spaces, so you can see/grep the severity | ||||
|     # of each message. | ||||
|     prefix = "  " | ||||
|  | ||||
|     # To make the messages look similar to the original LVM tools use: | ||||
|     #   indent = 0 | ||||
|     #   command_names = 1 | ||||
|     #   prefix = " -- " | ||||
|  | ||||
|     # Set this if you want log messages during activation. | ||||
|     # Don't use this in low memory situations (can deadlock). | ||||
|     # activation = 0 | ||||
| } | ||||
|  | ||||
| # 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 ? | ||||
|     # Remember to back up this directory regularly! | ||||
|     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 ? | ||||
|     # Remember to back up this directory regularly! | ||||
|     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 (readline) mode. | ||||
| shell { | ||||
|  | ||||
|     # Number of lines of history to store in ~/.lvm_history | ||||
|     history_size = 100 | ||||
| } | ||||
|  | ||||
|  | ||||
| # Miscellaneous global LVM2 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 | ||||
|  | ||||
|     # Whether or not to communicate with the kernel device-mapper. | ||||
|     # Set to 0 if you want to use the tools to manipulate LVM metadata  | ||||
|     # without activating any logical volumes. | ||||
|     # If the device-mapper kernel driver is not present in your kernel | ||||
|     # setting this to 0 should suppress the error messages. | ||||
|     activation = 1 | ||||
|  | ||||
|     # The default metadata format that commands should use - "lvm1" or "lvm2". | ||||
|     # The command line override is -M1 or -M2. | ||||
|     # Defaults to "lvm1" if compiled in, else "lvm2". | ||||
|     # format = "lvm1" | ||||
|  | ||||
|     # Location of proc filesystem | ||||
|     proc = "/proc" | ||||
|  | ||||
|     # Type of locking to use. Defaults to file-based locking (1). | ||||
|     # Turn locking off by setting to 0 (dangerous: risks metadata corruption | ||||
|     # if LVM2 commands get run concurrently). | ||||
|     locking_type = 1 | ||||
|  | ||||
|     # Local non-LV directory that holds file-based locks while commands are | ||||
|     # in progress.  A directory like /tmp that may get wiped on reboot is OK. | ||||
|     locking_dir = "/var/lock/lvm" | ||||
|  | ||||
|     # Other entries can go here to allow you to load shared libraries | ||||
|     # e.g. if support for LVM1 metadata was compiled as a shared library use | ||||
|     #   format_libraries = "liblvm2format1.so"  | ||||
|     # Full pathnames can be given. | ||||
|  | ||||
|     # Search this directory first for shared libraries. | ||||
|     #   library_dir = "/lib" | ||||
| } | ||||
|  | ||||
| activation { | ||||
|     # Device used in place of missing stripes if activating incomplete volume. | ||||
|     # For now, you need to set this up yourself first (e.g. with 'dmsetup') | ||||
|     # For example, you could make it return I/O errors using the 'error'  | ||||
|     # target or make it return zeros. | ||||
|     missing_stripe_filler = "/dev/ioerror" | ||||
|  | ||||
|     # Size (in KB) of each copy operation when mirroring | ||||
|     mirror_region_size = 512 | ||||
|  | ||||
|     # How much stack (in KB) to reserve for use while devices suspended | ||||
|     reserved_stack = 256 | ||||
|  | ||||
|     # How much memory (in KB) to reserve for use while devices suspended | ||||
|     reserved_memory = 8192 | ||||
|  | ||||
|     # Nice value used while devices suspended | ||||
|     process_priority = -18 | ||||
| } | ||||
|  | ||||
|  | ||||
| #################### | ||||
| # Advanced section # | ||||
| #################### | ||||
|  | ||||
| # Metadata settings | ||||
| # | ||||
| # metadata { | ||||
|     # Default number of copies of metadata to hold on each PV.  0, 1 or 2. | ||||
|     # It's best to leave this at 2. | ||||
|     # You might want to override it from the command line with 0 or 1  | ||||
|     # when running pvcreate on new PVs which are to be added to large VGs. | ||||
|  | ||||
|     # pvmetadatacopies = 2 | ||||
|  | ||||
|     # Approximate default size of on-disk metadata areas in sectors. | ||||
|     # You should increase this if you have large volume groups or | ||||
|     # you want to retain a large on-disk history of your metadata changes. | ||||
|  | ||||
|     # pvmetadatasize = 255 | ||||
|  | ||||
|     # List of directories holding live copies of text format metadata. | ||||
|     # These directories must not be on logical volumes! | ||||
|     # It's possible to use LVM2 with a couple of directories here, | ||||
|     # preferably on different (non-LV) filesystems, and with no other  | ||||
|     # on-disk metadata (pvmetadatacopies = 0). Or this can be in | ||||
|     # addition to on-disk metadata areas. | ||||
|     # The feature was originally added to simplify testing and is not | ||||
|     # supported under low memory situations - the machine could lock up. | ||||
|     # | ||||
|     # Never edit any files in these directories by hand unless you | ||||
|     # you are absolutely sure you know what you are doing! Use | ||||
|     # the supplied toolset to make changes (e.g. vgcfgrestore). | ||||
|  | ||||
|     # dirs = [ "/etc/lvm/metadata", "/mnt/disk2/lvm/metadata2" ] | ||||
| #} | ||||
|  | ||||
|  | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										36
									
								
								include/.symlinks
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								include/.symlinks
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| ../lib/activate/activate.h | ||||
| ../lib/cache/lvmcache.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/format_text/format-text.h | ||||
| ../lib/label/label.h | ||||
| ../lib/locking/locking.h | ||||
| ../lib/log/log.h | ||||
| ../lib/metadata/metadata.h | ||||
| ../lib/mm/dbg_malloc.h | ||||
| ../lib/mm/memlock.h | ||||
| ../lib/mm/pool.h | ||||
| ../lib/mm/xlate.h | ||||
| ../lib/misc/crc.h | ||||
| ../lib/misc/lib.h | ||||
| ../lib/misc/lvm-file.h | ||||
| ../lib/misc/lvm-string.h | ||||
| ../lib/misc/sharedlib.h | ||||
| ../lib/regex/matcher.h | ||||
| ../lib/report/report.h | ||||
| ../lib/uuid/uuid.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 | ||||
|  | ||||
							
								
								
									
										96
									
								
								lib/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								lib/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| # | ||||
| # Copyright (C) 2001 Sistina Software (UK) Limited | ||||
| # | ||||
| # This file is released under the GPL. | ||||
| # | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| VPATH = @srcdir@ | ||||
|  | ||||
| ifeq ("@LVM1@", "shared") | ||||
|   SUBDIRS = format1 | ||||
| endif | ||||
|  | ||||
| SOURCES=\ | ||||
| 	activate/activate.c \ | ||||
| 	cache/lvmcache.c \ | ||||
| 	commands/toolcontext.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 \ | ||||
| 	format_text/archive.c \ | ||||
| 	format_text/export.c \ | ||||
| 	format_text/flags.c \ | ||||
| 	format_text/format-text.c \ | ||||
| 	format_text/import.c \ | ||||
| 	format_text/import_vsn1.c \ | ||||
| 	format_text/text_label.c \ | ||||
| 	label/label.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/mirror.c \ | ||||
| 	metadata/pv_map.c \ | ||||
| 	metadata/snapshot_manip.c \ | ||||
| 	misc/crc.c \ | ||||
| 	misc/lvm-file.c \ | ||||
| 	misc/lvm-string.c \ | ||||
| 	mm/memlock.c \ | ||||
| 	mm/pool.c \ | ||||
| 	regex/matcher.c \ | ||||
| 	regex/parse_rx.c \ | ||||
| 	regex/ttree.c \ | ||||
| 	report/report.c \ | ||||
| 	uuid/uuid.c  | ||||
|  | ||||
| ifeq ("@LVM1@", "internal") | ||||
|   SOURCES+=\ | ||||
| 	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 | ||||
| endif | ||||
|  | ||||
| ifeq ("@DEBUG@", "yes") | ||||
|   SOURCES+=\ | ||||
| 	mm/dbg_malloc.c | ||||
| endif | ||||
|  | ||||
| ifeq ("@DEVMAPPER@", "yes") | ||||
|   SOURCES+=\ | ||||
| 	activate/dev_manager.c \ | ||||
| 	activate/fs.c | ||||
| endif | ||||
|  | ||||
| ifeq ("@HAVE_LIBDL@", "yes") | ||||
|   SOURCES+=\ | ||||
| 	locking/external_locking.c \ | ||||
| 	misc/sharedlib.c | ||||
| endif | ||||
|  | ||||
| TARGETS=liblvm.a | ||||
|  | ||||
| include ../make.tmpl | ||||
|  | ||||
| liblvm.a: $(OBJECTS) | ||||
| 	$(RM) $@ | ||||
| 	$(AR) r $@ $(OBJECTS) | ||||
| 	$(RANLIB) $@ | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| Base library directory | ||||
							
								
								
									
										476
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										476
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,476 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "metadata.h" | ||||
| #include "activate.h" | ||||
| #include "memlock.h" | ||||
| #include "display.h" | ||||
| #include "fs.h" | ||||
| #include "lvm-string.h" | ||||
| #include "pool.h" | ||||
| #include "toolcontext.h" | ||||
| #include "dev_manager.h" | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args) | ||||
|  | ||||
| #ifndef DEVMAPPER_SUPPORT | ||||
| void set_activation(int act) | ||||
| { | ||||
| 	if (act) | ||||
| 		log_error("Compiled without libdevmapper support. " | ||||
| 			  "Can't enable activation."); | ||||
| } | ||||
| int activation(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int library_version(char *version, size_t size) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int driver_version(char *version, size_t size) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_info(const struct logical_volume *lv, struct lvinfo *info) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_snapshot_percent(struct logical_volume *lv, float *percent) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_mirror_percent(struct logical_volume *lv, int wait, float *percent, | ||||
| 		      uint32_t *event_nr) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lvs_in_vg_activated(struct volume_group *vg) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lvs_in_vg_opened(struct volume_group *vg) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| int lv_deactivate(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| int lv_activate(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| void activation_exit(void) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| #else				/* DEVMAPPER_SUPPORT */ | ||||
|  | ||||
| static int _activation = 1; | ||||
|  | ||||
| void set_activation(int act) | ||||
| { | ||||
| 	if (act == _activation) | ||||
| 		return; | ||||
|  | ||||
| 	_activation = act; | ||||
| 	if (_activation) | ||||
| 		log_verbose("Activation enabled. Device-mapper kernel " | ||||
| 			    "driver will be used."); | ||||
| 	else | ||||
| 		log_verbose("Activation disabled. No device-mapper " | ||||
| 			    "interaction will be attempted."); | ||||
| } | ||||
|  | ||||
| int activation(void) | ||||
| { | ||||
| 	return _activation; | ||||
| } | ||||
|  | ||||
| int library_version(char *version, size_t size) | ||||
| { | ||||
| 	if (!activation()) | ||||
| 		return 0; | ||||
|  | ||||
| 	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; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 0; | ||||
|  | ||||
| 	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(const struct logical_volume *lv, struct lvinfo *info) | ||||
| { | ||||
| 	int r; | ||||
| 	struct dev_manager *dm; | ||||
| 	struct dm_info dminfo; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(dm = dev_manager_create(lv->vg->name, lv->vg->cmd->cf))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(r = dev_manager_info(dm, lv, &dminfo))) | ||||
| 		stack; | ||||
|  | ||||
| 	info->exists = dminfo.exists; | ||||
| 	info->suspended = dminfo.suspended; | ||||
| 	info->open_count = dminfo.open_count; | ||||
| 	info->major = dminfo.major; | ||||
| 	info->minor = dminfo.minor; | ||||
| 	info->read_only = dminfo.read_only; | ||||
|  | ||||
| 	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 (!activation()) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(dm = dev_manager_create(lv->vg->name, lv->vg->cmd->cf))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(r = dev_manager_snapshot_percent(dm, lv, percent))) | ||||
| 		stack; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* FIXME Merge with snapshot_percent */ | ||||
| int lv_mirror_percent(struct logical_volume *lv, int wait, float *percent, | ||||
| 		      uint32_t *event_nr) | ||||
| { | ||||
| 	int r; | ||||
| 	struct dev_manager *dm; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(dm = dev_manager_create(lv->vg->name, lv->vg->cmd->cf))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(r = dev_manager_mirror_percent(dm, lv, wait, percent, event_nr))) | ||||
| 		stack; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _lv_active(struct logical_volume *lv) | ||||
| { | ||||
| 	struct lvinfo info; | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return info.exists; | ||||
| } | ||||
|  | ||||
| static int _lv_open_count(struct logical_volume *lv) | ||||
| { | ||||
| 	struct lvinfo 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, lv->vg->cmd->cf))) { | ||||
| 		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, lv->vg->cmd->cf))) { | ||||
| 		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, lv->vg->cmd->cf))) { | ||||
| 		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; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 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; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 0; | ||||
|  | ||||
| 	list_iterate(lvh, &vg->lvs) { | ||||
| 		lv = list_item(lvh, struct lv_list)->lv; | ||||
| 		count += (_lv_open_count(lv) == 1); | ||||
| 	} | ||||
|  | ||||
| 	return count; | ||||
| } | ||||
|  | ||||
| /* 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 lvinfo info; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!(lv = lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Suspending '%s'.", lv->name); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!info.exists || info.suspended) | ||||
| 		return 1; | ||||
|  | ||||
| 	memlock_inc(); | ||||
| 	if (!_lv_suspend(lv)) { | ||||
| 		memlock_dec(); | ||||
| 		fs_unlock(); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	struct logical_volume *lv; | ||||
| 	struct lvinfo info; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!(lv = lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Resuming '%s'.", lv->name); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!info.exists || !info.suspended) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!_lv_activate(lv)) | ||||
| 		return 0; | ||||
|  | ||||
| 	memlock_dec(); | ||||
| 	fs_unlock(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lv_deactivate(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	struct logical_volume *lv; | ||||
| 	struct lvinfo info; | ||||
| 	int r; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!(lv = lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Deactivating '%s'.", lv->name); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!info.exists) | ||||
| 		return 1; | ||||
|  | ||||
| 	memlock_inc(); | ||||
| 	r = _lv_deactivate(lv); | ||||
| 	memlock_dec(); | ||||
| 	fs_unlock(); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int lv_activate(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	struct logical_volume *lv; | ||||
| 	struct lvinfo info; | ||||
| 	int r; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!(lv = lv_from_lvid(cmd, lvid_s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (test_mode()) { | ||||
| 		_skip("Activating '%s'.", lv->name); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_info(lv, &info)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (info.exists && !info.suspended) | ||||
| 		return 1; | ||||
|  | ||||
| 	memlock_inc(); | ||||
| 	r = _lv_activate(lv); | ||||
| 	memlock_dec(); | ||||
| 	fs_unlock(); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| void activation_exit(void) | ||||
| { | ||||
| 	dev_manager_exit(); | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										57
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef LVM_ACTIVATE_H | ||||
| #define LVM_ACTIVATE_H | ||||
|  | ||||
| #include "metadata.h" | ||||
|  | ||||
| #ifdef DEVMAPPER_SUPPORT | ||||
| #  include <libdevmapper.h> | ||||
| #endif | ||||
|  | ||||
| struct lvinfo { | ||||
| 	int exists; | ||||
| 	int suspended; | ||||
| 	unsigned int open_count; | ||||
| 	int major; | ||||
| 	int minor; | ||||
| 	int read_only; | ||||
| }; | ||||
|  | ||||
| void set_activation(int activation); | ||||
| int activation(void); | ||||
|  | ||||
| int driver_version(char *version, size_t size); | ||||
| int library_version(char *version, size_t size); | ||||
|  | ||||
| void activation_exit(void); | ||||
|  | ||||
| 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); | ||||
|  | ||||
| /* | ||||
|  * Returns 1 if info structure has been populated, else 0. | ||||
|  */ | ||||
| int lv_info(const struct logical_volume *lv, struct lvinfo *info); | ||||
| /* | ||||
|  * Returns 1 if percent has been set, else 0. | ||||
|  */ | ||||
| int lv_snapshot_percent(struct logical_volume *lv, float *percent); | ||||
| int lv_mirror_percent(struct logical_volume *lv, int wait, float *percent, | ||||
| 		      uint32_t *event_nr); | ||||
|  | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										2212
									
								
								lib/activate/dev_manager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2212
									
								
								lib/activate/dev_manager.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										46
									
								
								lib/activate/dev_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/activate/dev_manager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * 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 "config.h" | ||||
|  | ||||
| struct dev_manager; | ||||
| struct dm_info; | ||||
|  | ||||
| /* | ||||
|  * Constructor and destructor. | ||||
|  */ | ||||
| struct dev_manager *dev_manager_create(const char *vg_name, | ||||
| 				       struct config_tree *cf); | ||||
| void dev_manager_destroy(struct dev_manager *dm); | ||||
| void dev_manager_exit(void); | ||||
|  | ||||
| /* | ||||
|  * 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, const 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_mirror_percent(struct dev_manager *dm, | ||||
| 			       struct logical_volume *lv, int wait, | ||||
| 			       float *percent, uint32_t *event_nr); | ||||
| 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 | ||||
							
								
								
									
										337
									
								
								lib/activate/fs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								lib/activate/fs.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,337 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "fs.h" | ||||
| #include "toolcontext.h" | ||||
| #include "lvm-string.h" | ||||
| #include "lvm-file.h" | ||||
| #include "memlock.h" | ||||
|  | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
| #include <limits.h> | ||||
| #include <dirent.h> | ||||
| #include <libdevmapper.h> | ||||
|  | ||||
| static int _mk_dir(const char *dev_dir, const char *vg_name) | ||||
| { | ||||
| 	char vg_path[PATH_MAX]; | ||||
|  | ||||
| 	if (lvm_snprintf(vg_path, sizeof(vg_path), "%s%s", | ||||
| 			 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(const char *dev_dir, const char *vg_name) | ||||
| { | ||||
| 	char vg_path[PATH_MAX]; | ||||
|  | ||||
| 	if (lvm_snprintf(vg_path, sizeof(vg_path), "%s%s", | ||||
| 			 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 void _rm_blks(const char *dir) | ||||
| { | ||||
| 	const char *name; | ||||
| 	char path[PATH_MAX]; | ||||
| 	struct dirent *dirent; | ||||
| 	struct stat buf; | ||||
| 	DIR *d; | ||||
|  | ||||
| 	if (!(d = opendir(dir))) { | ||||
| 		log_sys_error("opendir", dir); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	while ((dirent = readdir(d))) { | ||||
| 		name = dirent->d_name; | ||||
|  | ||||
| 		if (!strcmp(name, ".") || !strcmp(name, "..")) | ||||
| 			continue; | ||||
|  | ||||
| 		if (lvm_snprintf(path, sizeof(path), "%s/%s", dir, name) == -1) { | ||||
| 			log_error("Couldn't create path for %s", name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (!lstat(path, &buf)) { | ||||
| 			if (!S_ISBLK(buf.st_mode)) | ||||
| 				continue; | ||||
| 			log_very_verbose("Removing %s", path); | ||||
| 			if (unlink(path) < 0) | ||||
| 				log_sys_error("unlink", path); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int _mk_link(const char *dev_dir, const char *vg_name, | ||||
| 		    const char *lv_name, const char *dev) | ||||
| { | ||||
| 	char lv_path[PATH_MAX], link_path[PATH_MAX], lvm1_group_path[PATH_MAX]; | ||||
| 	char vg_path[PATH_MAX]; | ||||
| 	struct stat buf; | ||||
|  | ||||
| 	if (lvm_snprintf(vg_path, sizeof(vg_path), "%s%s", | ||||
| 			 dev_dir, vg_name) == -1) { | ||||
| 		log_error("Couldn't create path for volume group dir %s", | ||||
| 			  vg_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lvm_snprintf(lv_path, sizeof(lv_path), "%s/%s", vg_path, | ||||
| 			 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 (lvm_snprintf(lvm1_group_path, sizeof(lvm1_group_path), "%s/group", | ||||
| 			 vg_path) == -1) { | ||||
| 		log_error("Couldn't create pathname for LVM1 group file for %s", | ||||
| 			  vg_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* To reach this point, the VG must have been locked. | ||||
| 	 * As locking fails if the VG is active under LVM1, it's  | ||||
| 	 * now safe to remove any LVM1 devices we find here | ||||
| 	 * (as well as any existing LVM2 symlink). */ | ||||
| 	if (!lstat(lvm1_group_path, &buf)) { | ||||
| 		if (!S_ISCHR(buf.st_mode)) { | ||||
| 			log_error("Non-LVM1 character device found at %s", | ||||
| 				  lvm1_group_path); | ||||
| 		} else { | ||||
| 			_rm_blks(vg_path); | ||||
|  | ||||
| 			log_very_verbose("Removing %s", lvm1_group_path); | ||||
| 			if (unlink(lvm1_group_path) < 0) | ||||
| 				log_sys_error("unlink", lvm1_group_path); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!lstat(lv_path, &buf)) { | ||||
| 		if (!S_ISLNK(buf.st_mode) && !S_ISBLK(buf.st_mode)) { | ||||
| 			log_error("Symbolic link %s not created: file exists", | ||||
| 				  link_path); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		log_very_verbose("Removing %s", lv_path); | ||||
| 		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(const char *dev_dir, const char *vg_name, | ||||
| 		    const char *lv_name) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	char lv_path[PATH_MAX]; | ||||
|  | ||||
| 	if (lvm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s", | ||||
| 			 dev_dir, vg_name, lv_name) == -1) { | ||||
| 		log_error("Couldn't determine link pathname."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lstat(lv_path, &buf) || !S_ISLNK(buf.st_mode)) { | ||||
| 		log_error("%s not symbolic link - not removing", lv_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_very_verbose("Removing link %s", lv_path); | ||||
| 	if (unlink(lv_path) < 0) { | ||||
| 		log_sys_error("unlink", lv_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| typedef enum { | ||||
| 	FS_ADD, | ||||
| 	FS_DEL, | ||||
| 	FS_RENAME | ||||
| } fs_op_t; | ||||
|  | ||||
| static int _do_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name, | ||||
| 		     const char *lv_name, const char *dev, | ||||
| 		     const char *old_lv_name) | ||||
| { | ||||
| 	switch (type) { | ||||
| 	case FS_ADD: | ||||
| 		if (!_mk_dir(dev_dir, vg_name) || | ||||
| 		    !_mk_link(dev_dir, vg_name, lv_name, dev)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		break; | ||||
| 	case FS_DEL: | ||||
| 		if (!_rm_link(dev_dir, vg_name, lv_name) || | ||||
| 		    !_rm_dir(dev_dir, vg_name)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		break; | ||||
| 		/* FIXME Use rename() */ | ||||
| 	case FS_RENAME: | ||||
| 		if (old_lv_name && !_rm_link(dev_dir, vg_name, old_lv_name)) | ||||
| 			stack; | ||||
|  | ||||
| 		if (!_mk_link(dev_dir, vg_name, lv_name, dev)) | ||||
| 			stack; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static LIST_INIT(_fs_ops); | ||||
|  | ||||
| struct fs_op_parms { | ||||
| 	struct list list; | ||||
| 	fs_op_t type; | ||||
| 	char *dev_dir; | ||||
| 	char *vg_name; | ||||
| 	char *lv_name; | ||||
| 	char *dev; | ||||
| 	char *old_lv_name; | ||||
| 	char names[0]; | ||||
| }; | ||||
|  | ||||
| static void _store_str(char **pos, char **ptr, const char *str) | ||||
| { | ||||
| 	strcpy(*pos, str); | ||||
| 	*ptr = *pos; | ||||
| 	*pos += strlen(*ptr) + 1; | ||||
| } | ||||
|  | ||||
| static int _stack_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name, | ||||
| 			const char *lv_name, const char *dev, | ||||
| 			const char *old_lv_name) | ||||
| { | ||||
| 	struct fs_op_parms *fsp; | ||||
| 	size_t len = strlen(dev_dir) + strlen(vg_name) + strlen(lv_name) + | ||||
| 	    strlen(dev) + strlen(old_lv_name) + 5; | ||||
| 	char *pos; | ||||
|  | ||||
| 	if (!(fsp = dbg_malloc(sizeof(*fsp) + len))) { | ||||
| 		log_error("No space to stack fs operation"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	pos = fsp->names; | ||||
| 	fsp->type = type; | ||||
|  | ||||
| 	_store_str(&pos, &fsp->dev_dir, dev_dir); | ||||
| 	_store_str(&pos, &fsp->vg_name, vg_name); | ||||
| 	_store_str(&pos, &fsp->lv_name, lv_name); | ||||
| 	_store_str(&pos, &fsp->dev, dev); | ||||
| 	_store_str(&pos, &fsp->old_lv_name, old_lv_name); | ||||
|  | ||||
| 	list_add(&_fs_ops, &fsp->list); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _pop_fs_ops(void) | ||||
| { | ||||
| 	struct list *fsph, *fspht; | ||||
| 	struct fs_op_parms *fsp; | ||||
|  | ||||
| 	list_iterate_safe(fsph, fspht, &_fs_ops) { | ||||
| 		fsp = list_item(fsph, struct fs_op_parms); | ||||
| 		_do_fs_op(fsp->type, fsp->dev_dir, fsp->vg_name, fsp->lv_name, | ||||
| 			  fsp->dev, fsp->old_lv_name); | ||||
| 		list_del(&fsp->list); | ||||
| 		dbg_free(fsp); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int _fs_op(fs_op_t type, const char *dev_dir, const char *vg_name, | ||||
| 		  const char *lv_name, const char *dev, const char *old_lv_name) | ||||
| { | ||||
| 	if (memlock()) { | ||||
| 		if (!_stack_fs_op(type, dev_dir, vg_name, lv_name, dev, | ||||
| 				  old_lv_name)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	return _do_fs_op(type, dev_dir, vg_name, lv_name, dev, old_lv_name); | ||||
| } | ||||
|  | ||||
| int fs_add_lv(struct logical_volume *lv, const char *dev) | ||||
| { | ||||
| 	return _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name, lv->name, | ||||
| 		      dev, ""); | ||||
| } | ||||
|  | ||||
| int fs_del_lv(struct logical_volume *lv) | ||||
| { | ||||
| 	return _fs_op(FS_DEL, lv->vg->cmd->dev_dir, lv->vg->name, lv->name, | ||||
| 		      "", ""); | ||||
| } | ||||
|  | ||||
| int fs_rename_lv(struct logical_volume *lv, | ||||
| 		 const char *dev, const char *old_name) | ||||
| { | ||||
| 	return _fs_op(FS_RENAME, lv->vg->cmd->dev_dir, lv->vg->name, lv->name, | ||||
| 		      dev, old_name); | ||||
| } | ||||
|  | ||||
| void fs_unlock(void) | ||||
| { | ||||
| 	if (!memlock()) { | ||||
| 		dm_lib_release(); | ||||
| 		_pop_fs_ops(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										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); | ||||
| void fs_unlock(void); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										580
									
								
								lib/cache/lvmcache.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										580
									
								
								lib/cache/lvmcache.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,580 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "lvmcache.h" | ||||
| #include "hash.h" | ||||
| #include "toolcontext.h" | ||||
| #include "dev-cache.h" | ||||
| #include "metadata.h" | ||||
| #include "filter.h" | ||||
| #include "memlock.h" | ||||
|  | ||||
| static struct hash_table *_pvid_hash = NULL; | ||||
| static struct hash_table *_vgid_hash = NULL; | ||||
| static struct hash_table *_vgname_hash = NULL; | ||||
| static struct hash_table *_lock_hash = NULL; | ||||
| static struct list _vginfos; | ||||
| static int _has_scanned = 0; | ||||
| static int _vgs_locked = 0; | ||||
|  | ||||
| int lvmcache_init(void) | ||||
| { | ||||
| 	list_init(&_vginfos); | ||||
|  | ||||
| 	if (!(_vgname_hash = hash_create(128))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(_vgid_hash = hash_create(128))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(_pvid_hash = hash_create(128))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(_lock_hash = hash_create(128))) | ||||
| 		return 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void lvmcache_lock_vgname(const char *vgname, int read_only) | ||||
| { | ||||
| 	if (!_lock_hash && !lvmcache_init()) { | ||||
| 		log_error("Internal cache initialisation failed"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!hash_insert(_lock_hash, vgname, (void *) 1)) | ||||
| 		log_error("Cache locking failure for %s", vgname); | ||||
|  | ||||
| 	_vgs_locked++; | ||||
| } | ||||
|  | ||||
| static int _vgname_is_locked(const char *vgname) __attribute__ ((unused)); | ||||
| static int _vgname_is_locked(const char *vgname) | ||||
| { | ||||
| 	if (!_lock_hash) | ||||
| 		return 0; | ||||
|  | ||||
| 	return hash_lookup(_lock_hash, vgname) ? 1 : 0; | ||||
| } | ||||
|  | ||||
| void lvmcache_unlock_vgname(const char *vgname) | ||||
| { | ||||
| 	/* FIXME: Clear all CACHE_LOCKED flags in this vg */ | ||||
| 	hash_remove(_lock_hash, vgname); | ||||
|  | ||||
| 	/* FIXME Do this per-VG */ | ||||
| 	if (!--_vgs_locked) | ||||
| 		dev_close_all(); | ||||
| } | ||||
|  | ||||
| int vgs_locked(void) | ||||
| { | ||||
| 	return _vgs_locked; | ||||
| } | ||||
|  | ||||
| struct lvmcache_vginfo *vginfo_from_vgname(const char *vgname) | ||||
| { | ||||
| 	struct lvmcache_vginfo *vginfo; | ||||
|  | ||||
| 	if (!_vgname_hash) | ||||
| 		return NULL; | ||||
|  | ||||
| 	if (!(vginfo = hash_lookup(_vgname_hash, vgname))) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return vginfo; | ||||
| } | ||||
|  | ||||
| const struct format_type *fmt_from_vgname(const char *vgname) | ||||
| { | ||||
| 	struct lvmcache_vginfo *vginfo; | ||||
|  | ||||
| 	if (!(vginfo = vginfo_from_vgname(vgname))) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return vginfo->fmt; | ||||
| } | ||||
|  | ||||
| struct lvmcache_vginfo *vginfo_from_vgid(const char *vgid) | ||||
| { | ||||
| 	struct lvmcache_vginfo *vginfo; | ||||
| 	char id[ID_LEN + 1]; | ||||
|  | ||||
| 	if (!_vgid_hash || !vgid) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* vgid not necessarily NULL-terminated */ | ||||
| 	strncpy(&id[0], vgid, ID_LEN); | ||||
| 	id[ID_LEN] = '\0'; | ||||
|  | ||||
| 	if (!(vginfo = hash_lookup(_vgid_hash, id))) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return vginfo; | ||||
| } | ||||
|  | ||||
| struct lvmcache_info *info_from_pvid(const char *pvid) | ||||
| { | ||||
| 	struct lvmcache_info *info; | ||||
| 	char id[ID_LEN + 1]; | ||||
|  | ||||
| 	if (!_pvid_hash || !pvid) | ||||
| 		return NULL; | ||||
|  | ||||
| 	strncpy(&id[0], pvid, ID_LEN); | ||||
| 	id[ID_LEN] = '\0'; | ||||
|  | ||||
| 	if (!(info = hash_lookup(_pvid_hash, id))) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return info; | ||||
| } | ||||
|  | ||||
| static void _rescan_entry(struct lvmcache_info *info) | ||||
| { | ||||
| 	struct label *label; | ||||
|  | ||||
| 	if (info->status & CACHE_INVALID) | ||||
| 		label_read(info->dev, &label); | ||||
| } | ||||
|  | ||||
| static int _scan_invalid(void) | ||||
| { | ||||
| 	hash_iter(_pvid_hash, (iterate_fn) _rescan_entry); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lvmcache_label_scan(struct cmd_context *cmd, int full_scan) | ||||
| { | ||||
| 	struct label *label; | ||||
| 	struct dev_iter *iter; | ||||
| 	struct device *dev; | ||||
| 	struct list *fmth; | ||||
| 	struct format_type *fmt; | ||||
|  | ||||
| 	static int _scanning_in_progress = 0; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	/* Avoid recursion when a PVID can't be found! */ | ||||
| 	if (_scanning_in_progress) | ||||
| 		return 0; | ||||
|  | ||||
| 	_scanning_in_progress = 1; | ||||
|  | ||||
| 	if (!_vgname_hash && !lvmcache_init()) { | ||||
| 		log_error("Internal cache initialisation failed"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (_has_scanned && !full_scan) { | ||||
| 		r = _scan_invalid(); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(iter = dev_iter_create(cmd->filter))) { | ||||
| 		log_error("dev_iter creation failed"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	while ((dev = dev_iter_get(iter))) | ||||
| 		label_read(dev, &label); | ||||
|  | ||||
| 	dev_iter_destroy(iter); | ||||
|  | ||||
| 	_has_scanned = 1; | ||||
|  | ||||
| 	/* Perform any format-specific scanning e.g. text files */ | ||||
| 	list_iterate(fmth, &cmd->formats) { | ||||
| 		fmt = list_item(fmth, struct format_type); | ||||
| 		if (fmt->ops->scan && !fmt->ops->scan(fmt)) | ||||
| 			goto out; | ||||
| 	} | ||||
|  | ||||
| 	r = 1; | ||||
|  | ||||
|       out: | ||||
| 	_scanning_in_progress = 0; | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| struct list *lvmcache_get_vgnames(struct cmd_context *cmd, int full_scan) | ||||
| { | ||||
| 	struct list *vgih, *vgnames; | ||||
| 	struct str_list *sl; | ||||
|  | ||||
| 	lvmcache_label_scan(cmd, full_scan); | ||||
|  | ||||
| 	if (!(vgnames = pool_alloc(cmd->mem, sizeof(struct list)))) { | ||||
| 		log_error("vgnames list allocation failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_init(vgnames); | ||||
|  | ||||
| 	list_iterate(vgih, &_vginfos) { | ||||
| 		if (!(sl = pool_alloc(cmd->mem, sizeof(*sl)))) { | ||||
| 			log_error("strlist allocation failed"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		if (!(sl->str = pool_strdup(cmd->mem, | ||||
| 					    list_item(vgih, | ||||
| 						      struct lvmcache_vginfo)-> | ||||
| 					    vgname))) { | ||||
| 			log_error("vgname allocation failed"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		list_add(vgnames, &sl->list); | ||||
| 	} | ||||
|  | ||||
| 	return vgnames; | ||||
| } | ||||
|  | ||||
| struct device *device_from_pvid(struct cmd_context *cmd, struct id *pvid) | ||||
| { | ||||
| 	struct label *label; | ||||
| 	struct lvmcache_info *info; | ||||
|  | ||||
| 	/* Already cached ? */ | ||||
| 	if ((info = info_from_pvid((char *) pvid))) { | ||||
| 		if (label_read(info->dev, &label)) { | ||||
| 			info = (struct lvmcache_info *) label->info; | ||||
| 			if (id_equal(pvid, (struct id *) &info->dev->pvid)) | ||||
| 				return info->dev; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	lvmcache_label_scan(cmd, 0); | ||||
|  | ||||
| 	/* Try again */ | ||||
| 	if ((info = info_from_pvid((char *) pvid))) { | ||||
| 		if (label_read(info->dev, &label)) { | ||||
| 			info = (struct lvmcache_info *) label->info; | ||||
| 			if (id_equal(pvid, (struct id *) &info->dev->pvid)) | ||||
| 				return info->dev; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (memlock()) | ||||
| 		return NULL; | ||||
|  | ||||
| 	lvmcache_label_scan(cmd, 1); | ||||
|  | ||||
| 	/* Try again */ | ||||
| 	if ((info = info_from_pvid((char *) pvid))) { | ||||
| 		if (label_read(info->dev, &label)) { | ||||
| 			info = (struct lvmcache_info *) label->info; | ||||
| 			if (id_equal(pvid, (struct id *) &info->dev->pvid)) | ||||
| 				return info->dev; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static void _drop_vginfo(struct lvmcache_info *info) | ||||
| { | ||||
| 	if (!list_empty(&info->list)) { | ||||
| 		list_del(&info->list); | ||||
| 		list_init(&info->list); | ||||
| 	} | ||||
|  | ||||
| 	if (info->vginfo && list_empty(&info->vginfo->infos)) { | ||||
| 		hash_remove(_vgname_hash, info->vginfo->vgname); | ||||
| 		if (info->vginfo->vgname) | ||||
| 			dbg_free(info->vginfo->vgname); | ||||
| 		if (*info->vginfo->vgid) | ||||
| 			hash_remove(_vgid_hash, info->vginfo->vgid); | ||||
| 		list_del(&info->vginfo->list); | ||||
| 		dbg_free(info->vginfo); | ||||
| 	} | ||||
|  | ||||
| 	info->vginfo = NULL; | ||||
| } | ||||
|  | ||||
| /* Unused | ||||
| void lvmcache_del(struct lvmcache_info *info) | ||||
| { | ||||
| 	if (info->dev->pvid[0] && _pvid_hash) | ||||
| 		hash_remove(_pvid_hash, info->dev->pvid); | ||||
|  | ||||
| 	_drop_vginfo(info); | ||||
|  | ||||
| 	info->label->labeller->ops->destroy_label(info->label->labeller, | ||||
| 						info->label);  | ||||
| 	dbg_free(info); | ||||
|  | ||||
| 	return; | ||||
| } */ | ||||
|  | ||||
| static int _lvmcache_update_pvid(struct lvmcache_info *info, const char *pvid) | ||||
| { | ||||
| 	if (!strcmp(info->dev->pvid, pvid)) | ||||
| 		return 1; | ||||
| 	if (*info->dev->pvid) { | ||||
| 		hash_remove(_pvid_hash, info->dev->pvid); | ||||
| 	} | ||||
| 	strncpy(info->dev->pvid, pvid, sizeof(info->dev->pvid)); | ||||
| 	if (!hash_insert(_pvid_hash, pvid, info)) { | ||||
| 		log_error("_lvmcache_update: pvid insertion failed: %s", pvid); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _lvmcache_update_vgid(struct lvmcache_info *info, const char *vgid) | ||||
| { | ||||
| 	if (!vgid || !info->vginfo || !strncmp(info->vginfo->vgid, vgid, | ||||
| 					       sizeof(info->vginfo->vgid))) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (info->vginfo && *info->vginfo->vgid) | ||||
| 		hash_remove(_vgid_hash, info->vginfo->vgid); | ||||
| 	if (!vgid) | ||||
| 		return 1; | ||||
|  | ||||
| 	strncpy(info->vginfo->vgid, vgid, sizeof(info->vginfo->vgid)); | ||||
| 	info->vginfo->vgid[sizeof(info->vginfo->vgid) - 1] = '\0'; | ||||
| 	if (!hash_insert(_vgid_hash, info->vginfo->vgid, info->vginfo)) { | ||||
| 		log_error("_lvmcache_update: vgid hash insertion failed: %s", | ||||
| 			  info->vginfo->vgid); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lvmcache_update_vgname(struct lvmcache_info *info, const char *vgname) | ||||
| { | ||||
| 	struct lvmcache_vginfo *vginfo; | ||||
|  | ||||
| 	/* If vgname is NULL and we don't already have a vgname,  | ||||
| 	 * assume ORPHAN - we want every entry to have a vginfo | ||||
| 	 * attached for scanning reasons. | ||||
| 	 */ | ||||
| 	if (!vgname && !info->vginfo) | ||||
| 		vgname = ORPHAN; | ||||
|  | ||||
| 	if (!vgname || (info->vginfo && !strcmp(info->vginfo->vgname, vgname))) | ||||
| 		return 1; | ||||
|  | ||||
| 	/* Remove existing vginfo entry */ | ||||
| 	_drop_vginfo(info); | ||||
|  | ||||
| 	/* Get existing vginfo or create new one */ | ||||
| 	if (!(vginfo = vginfo_from_vgname(vgname))) { | ||||
| 		if (!(vginfo = dbg_malloc(sizeof(*vginfo)))) { | ||||
| 			log_error("lvmcache_update_vgname: list alloc failed"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		memset(vginfo, 0, sizeof(*vginfo)); | ||||
| 		if (!(vginfo->vgname = dbg_strdup(vgname))) { | ||||
| 			dbg_free(vginfo); | ||||
| 			log_error("cache vgname alloc failed for %s", vgname); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		list_init(&vginfo->infos); | ||||
| 		if (!hash_insert(_vgname_hash, vginfo->vgname, vginfo)) { | ||||
| 			log_error("cache_update: vg hash insertion failed: %s", | ||||
| 				  vginfo->vgname); | ||||
| 			dbg_free(vginfo->vgname); | ||||
| 			dbg_free(vginfo); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		/* Ensure orphans appear last on list_iterate */ | ||||
| 		if (!*vgname) | ||||
| 			list_add(&_vginfos, &vginfo->list); | ||||
| 		else | ||||
| 			list_add_h(&_vginfos, &vginfo->list); | ||||
| 	} | ||||
|  | ||||
| 	info->vginfo = vginfo; | ||||
| 	list_add(&vginfo->infos, &info->list); | ||||
|  | ||||
| 	/* FIXME Check consistency of list! */ | ||||
| 	vginfo->fmt = info->fmt; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lvmcache_update_vg(struct volume_group *vg) | ||||
| { | ||||
| 	struct list *pvh; | ||||
| 	struct physical_volume *pv; | ||||
| 	struct lvmcache_info *info; | ||||
| 	char pvid_s[ID_LEN + 1]; | ||||
| 	int vgid_updated = 0; | ||||
|  | ||||
| 	pvid_s[sizeof(pvid_s) - 1] = '\0'; | ||||
|  | ||||
| 	list_iterate(pvh, &vg->pvs) { | ||||
| 		pv = list_item(pvh, struct pv_list)->pv; | ||||
| 		strncpy(pvid_s, (char *) &pv->id, sizeof(pvid_s) - 1); | ||||
| 		/* FIXME Could pv->dev->pvid ever be different? */ | ||||
| 		if ((info = info_from_pvid(pvid_s))) { | ||||
| 			lvmcache_update_vgname(info, vg->name); | ||||
| 			if (!vgid_updated) { | ||||
| 				_lvmcache_update_vgid(info, (char *) &vg->id); | ||||
| 				vgid_updated = 1; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid, | ||||
| 				   struct device *dev, | ||||
| 				   const char *vgname, const char *vgid) | ||||
| { | ||||
| 	struct label *label; | ||||
| 	struct lvmcache_info *existing, *info; | ||||
| 	char pvid_s[ID_LEN + 1]; | ||||
|  | ||||
| 	if (!_vgname_hash && !lvmcache_init()) { | ||||
| 		log_error("Internal cache initialisation failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	strncpy(pvid_s, pvid, sizeof(pvid_s)); | ||||
| 	pvid_s[sizeof(pvid_s) - 1] = '\0'; | ||||
|  | ||||
| 	if (!(existing = info_from_pvid(pvid_s)) && | ||||
| 	    !(existing = info_from_pvid(dev->pvid))) { | ||||
| 		if (!(label = label_create(labeller))) { | ||||
| 			stack; | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		if (!(info = dbg_malloc(sizeof(*info)))) { | ||||
| 			log_error("lvmcache_info allocation failed"); | ||||
| 			label_destroy(label); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		memset(info, 0, sizeof(*info)); | ||||
|  | ||||
| 		label->info = info; | ||||
| 		info->label = label; | ||||
| 		list_init(&info->list); | ||||
| 		info->dev = dev; | ||||
| 	} else { | ||||
| 		if (existing->dev != dev) { | ||||
| 			/* Is the existing entry a duplicate pvid e.g. md ? */ | ||||
| 			if (MAJOR(existing->dev->dev) == md_major() && | ||||
| 			    MAJOR(dev->dev) != md_major()) { | ||||
| 				log_very_verbose("Ignoring duplicate PV %s on " | ||||
| 						 "%s - using md %s", | ||||
| 						 pvid, dev_name(dev), | ||||
| 						 dev_name(existing->dev)); | ||||
| 				return NULL; | ||||
| 			} else if (MAJOR(existing->dev->dev) != md_major() && | ||||
| 				   MAJOR(dev->dev) == md_major()) | ||||
| 				log_very_verbose("Duplicate PV %s on %s - " | ||||
| 						 "using md %s", pvid, | ||||
| 						 dev_name(existing->dev), | ||||
| 						 dev_name(dev)); | ||||
| 			else | ||||
| 				log_error("Found duplicate PV %s: using %s not " | ||||
| 					  "%s", pvid, dev_name(dev), | ||||
| 					  dev_name(existing->dev)); | ||||
| 		} | ||||
| 		info = existing; | ||||
| 		/* Has labeller changed? */ | ||||
| 		if (info->label->labeller != labeller) { | ||||
| 			label_destroy(info->label); | ||||
| 			if (!(info->label = label_create(labeller))) { | ||||
| 				/* FIXME leaves info without label! */ | ||||
| 				stack; | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			info->label->info = info; | ||||
| 		} | ||||
| 		label = info->label; | ||||
| 	} | ||||
|  | ||||
| 	info->fmt = (const struct format_type *) labeller->private; | ||||
| 	info->status |= CACHE_INVALID; | ||||
|  | ||||
| 	if (!_lvmcache_update_pvid(info, pvid_s)) { | ||||
| 		if (!existing) { | ||||
| 			dbg_free(info); | ||||
| 			label_destroy(label); | ||||
| 		} | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!lvmcache_update_vgname(info, vgname)) { | ||||
| 		if (!existing) { | ||||
| 			hash_remove(_pvid_hash, pvid_s); | ||||
| 			strcpy(info->dev->pvid, ""); | ||||
| 			dbg_free(info); | ||||
| 			label_destroy(label); | ||||
| 		} | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!_lvmcache_update_vgid(info, vgid)) | ||||
| 		/* Non-critical */ | ||||
| 		stack; | ||||
|  | ||||
| 	return info; | ||||
| } | ||||
|  | ||||
| static void _lvmcache_destroy_entry(struct lvmcache_info *info) | ||||
| { | ||||
| 	if (!list_empty(&info->list)) | ||||
| 		list_del(&info->list); | ||||
| 	strcpy(info->dev->pvid, ""); | ||||
| 	label_destroy(info->label); | ||||
| 	dbg_free(info); | ||||
| } | ||||
|  | ||||
| static void _lvmcache_destroy_vgnamelist(struct lvmcache_vginfo *vginfo) | ||||
| { | ||||
| 	if (vginfo->vgname) | ||||
| 		dbg_free(vginfo->vgname); | ||||
| 	dbg_free(vginfo); | ||||
| } | ||||
|  | ||||
| static void _lvmcache_destroy_lockname(int present) | ||||
| { | ||||
| 	/* Nothing to do */ | ||||
| } | ||||
|  | ||||
| void lvmcache_destroy(void) | ||||
| { | ||||
| 	_has_scanned = 0; | ||||
|  | ||||
| 	if (_vgid_hash) { | ||||
| 		hash_destroy(_vgid_hash); | ||||
| 		_vgid_hash = NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (_pvid_hash) { | ||||
| 		hash_iter(_pvid_hash, (iterate_fn) _lvmcache_destroy_entry); | ||||
| 		hash_destroy(_pvid_hash); | ||||
| 		_pvid_hash = NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (_vgname_hash) { | ||||
| 		hash_iter(_vgname_hash, | ||||
| 			  (iterate_fn) _lvmcache_destroy_vgnamelist); | ||||
| 		hash_destroy(_vgname_hash); | ||||
| 		_vgname_hash = NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (_lock_hash) { | ||||
| 		hash_iter(_lock_hash, (iterate_fn) _lvmcache_destroy_lockname); | ||||
| 		hash_destroy(_lock_hash); | ||||
| 		_lock_hash = NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_init(&_vginfos); | ||||
| } | ||||
							
								
								
									
										75
									
								
								lib/cache/lvmcache.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								lib/cache/lvmcache.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_CACHE_H | ||||
| #define _LVM_CACHE_H | ||||
|  | ||||
| #include "dev-cache.h" | ||||
| #include "uuid.h" | ||||
| #include "label.h" | ||||
| #include "metadata.h" | ||||
|  | ||||
| #define ORPHAN "" | ||||
|  | ||||
| #define CACHE_INVALID	0x00000001 | ||||
| #define CACHE_LOCKED	0x00000002 | ||||
|  | ||||
| /* LVM specific per-volume info */ | ||||
| /* Eventual replacement for struct physical_volume perhaps? */ | ||||
|  | ||||
| struct lvmcache_vginfo { | ||||
| 	struct list list;	/* Join these vginfos together */ | ||||
| 	struct list infos;	/* List head for lvmcache_infos */ | ||||
| 	char *vgname;		/* "" == orphan */ | ||||
| 	char vgid[ID_LEN + 1]; | ||||
| 	const struct format_type *fmt; | ||||
| }; | ||||
|  | ||||
| struct lvmcache_info { | ||||
| 	struct list list;	/* Join VG members together */ | ||||
| 	struct list mdas;	/* list head for metadata areas */ | ||||
| 	struct list das;	/* list head for data areas */ | ||||
| 	struct lvmcache_vginfo *vginfo;	/* NULL == unknown */ | ||||
| 	struct label *label; | ||||
| 	const struct format_type *fmt; | ||||
| 	struct device *dev; | ||||
| 	uint64_t device_size;	/* Bytes */ | ||||
| 	uint32_t status; | ||||
| }; | ||||
|  | ||||
| int lvmcache_init(void); | ||||
| void lvmcache_destroy(void); | ||||
|  | ||||
| /* Set full_scan to 1 to reread every filtered device label */ | ||||
| int lvmcache_label_scan(struct cmd_context *cmd, int full_scan); | ||||
|  | ||||
| /* Add/delete a device */ | ||||
| struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid, | ||||
| 				   struct device *dev, | ||||
| 				   const char *vgname, const char *vgid); | ||||
| void lvmcache_del(struct lvmcache_info *info); | ||||
|  | ||||
| /* Update things */ | ||||
| int lvmcache_update_vgname(struct lvmcache_info *info, const char *vgname); | ||||
| int lvmcache_update_vg(struct volume_group *vg); | ||||
|  | ||||
| void lvmcache_lock_vgname(const char *vgname, int read_only); | ||||
| void lvmcache_unlock_vgname(const char *vgname); | ||||
|  | ||||
| /* Queries */ | ||||
| const struct format_type *fmt_from_vgname(const char *vgname); | ||||
| struct lvmcache_vginfo *vginfo_from_vgname(const char *vgname); | ||||
| struct lvmcache_vginfo *vginfo_from_vgid(const char *vgid); | ||||
| struct lvmcache_info *info_from_pvid(const char *pvid); | ||||
| struct device *device_from_pvid(struct cmd_context *cmd, struct id *pvid); | ||||
| int vgs_locked(void); | ||||
|  | ||||
| /* Returns list of struct str_lists containing pool-allocated copy of vgnames */ | ||||
| /* Set full_scan to 1 to reread every filtered device label */ | ||||
| struct list *lvmcache_get_vgnames(struct cmd_context *cmd, int full_scan); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										15
									
								
								lib/commands/errors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/commands/errors.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										521
									
								
								lib/commands/toolcontext.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								lib/commands/toolcontext.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,521 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "toolcontext.h" | ||||
| #include "pool.h" | ||||
| #include "metadata.h" | ||||
| #include "defaults.h" | ||||
| #include "lvm-string.h" | ||||
| #include "activate.h" | ||||
| #include "filter.h" | ||||
| #include "filter-composite.h" | ||||
| #include "filter-persistent.h" | ||||
| #include "filter-regex.h" | ||||
| #include "label.h" | ||||
| #include "lvm-file.h" | ||||
| #include "format-text.h" | ||||
| #include "display.h" | ||||
| #include "memlock.h" | ||||
|  | ||||
| #ifdef HAVE_LIBDL | ||||
| #include "sharedlib.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef LVM1_INTERNAL | ||||
| #include "format1.h" | ||||
| #endif | ||||
|  | ||||
| #include <locale.h> | ||||
| #include <sys/stat.h> | ||||
| #include <syslog.h> | ||||
| #include <time.h> | ||||
|  | ||||
| #ifdef linux | ||||
| #  include <malloc.h> | ||||
| #endif | ||||
|  | ||||
| static FILE *_log; | ||||
|  | ||||
| static int _get_env_vars(struct cmd_context *cmd) | ||||
| { | ||||
| 	const char *e; | ||||
|  | ||||
| 	/* Set to "" to avoid using any system directory */ | ||||
| 	if ((e = getenv("LVM_SYSTEM_DIR"))) { | ||||
| 		if (lvm_snprintf(cmd->sys_dir, sizeof(cmd->sys_dir), | ||||
| 				 "%s", e) < 0) { | ||||
| 			log_error("LVM_SYSTEM_DIR environment variable " | ||||
| 				  "is too long."); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _init_logging(struct cmd_context *cmd) | ||||
| { | ||||
| 	int append = 1; | ||||
| 	time_t t; | ||||
|  | ||||
| 	const char *log_file; | ||||
|  | ||||
| 	/* Syslog */ | ||||
| 	cmd->default_settings.syslog = | ||||
| 	    find_config_int(cmd->cf->root, "log/syslog", '/', DEFAULT_SYSLOG); | ||||
| 	if (cmd->default_settings.syslog != 1) | ||||
| 		fin_syslog(); | ||||
|  | ||||
| 	if (cmd->default_settings.syslog > 1) | ||||
| 		init_syslog(cmd->default_settings.syslog); | ||||
|  | ||||
| 	/* Debug level for log file output */ | ||||
| 	cmd->default_settings.debug = | ||||
| 	    find_config_int(cmd->cf->root, "log/level", '/', DEFAULT_LOGLEVEL); | ||||
| 	init_debug(cmd->default_settings.debug); | ||||
|  | ||||
| 	/* Verbose level for tty output */ | ||||
| 	cmd->default_settings.verbose = | ||||
| 	    find_config_int(cmd->cf->root, "log/verbose", '/', DEFAULT_VERBOSE); | ||||
| 	init_verbose(cmd->default_settings.verbose); | ||||
|  | ||||
| 	/* Log message formatting */ | ||||
| 	init_indent(find_config_int(cmd->cf->root, "log/indent", '/', | ||||
| 				    DEFAULT_INDENT)); | ||||
|  | ||||
| 	cmd->default_settings.msg_prefix = find_config_str(cmd->cf->root, | ||||
| 							   "log/prefix", '/', | ||||
| 							   DEFAULT_MSG_PREFIX); | ||||
| 	init_msg_prefix(cmd->default_settings.msg_prefix); | ||||
|  | ||||
| 	cmd->default_settings.cmd_name = find_config_int(cmd->cf->root, | ||||
| 							 "log/command_names", | ||||
| 							 '/', DEFAULT_CMD_NAME); | ||||
| 	init_cmd_name(cmd->default_settings.cmd_name); | ||||
|  | ||||
| 	/* Test mode */ | ||||
| 	cmd->default_settings.test = | ||||
| 	    find_config_int(cmd->cf->root, "global/test", '/', 0); | ||||
|  | ||||
| 	/* Settings for logging to file */ | ||||
| 	if (find_config_int(cmd->cf->root, "log/overwrite", '/', | ||||
| 			    DEFAULT_OVERWRITE)) | ||||
| 		append = 0; | ||||
|  | ||||
| 	log_file = find_config_str(cmd->cf->root, "log/file", '/', 0); | ||||
| 	if (log_file) | ||||
| 		init_log_file(log_file, append); | ||||
|  | ||||
| 	log_file = find_config_str(cmd->cf->root, "log/activate_file", '/', 0); | ||||
| 	if (log_file) | ||||
| 		init_log_direct(log_file, append); | ||||
|  | ||||
| 	init_log_while_suspended(find_config_int(cmd->cf->root, | ||||
| 						 "log/activation", '/', 0)); | ||||
|  | ||||
| 	t = time(NULL); | ||||
| 	log_verbose("Logging initialised at %s", ctime(&t)); | ||||
|  | ||||
| 	/* Tell device-mapper about our logging */ | ||||
| #ifdef DEVMAPPER_SUPPORT | ||||
| 	dm_log_init(print_log); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int _process_config(struct cmd_context *cmd) | ||||
| { | ||||
| 	mode_t old_umask; | ||||
|  | ||||
| 	/* umask */ | ||||
| 	cmd->default_settings.umask = find_config_int(cmd->cf->root, | ||||
| 						      "global/umask", '/', | ||||
| 						      DEFAULT_UMASK); | ||||
|  | ||||
| 	if ((old_umask = umask((mode_t) cmd->default_settings.umask)) != | ||||
| 	    (mode_t) cmd->default_settings.umask) | ||||
| 		log_verbose("Set umask to %04o", cmd->default_settings.umask); | ||||
|  | ||||
| 	/* dev dir */ | ||||
| 	if (lvm_snprintf(cmd->dev_dir, sizeof(cmd->dev_dir), "%s/", | ||||
| 			 find_config_str(cmd->cf->root, "devices/dir", | ||||
| 					 '/', DEFAULT_DEV_DIR)) < 0) { | ||||
| 		log_error("Device directory given in config file too long"); | ||||
| 		return 0; | ||||
| 	} | ||||
| #ifdef DEVMAPPER_SUPPORT | ||||
| 	dm_set_dev_dir(cmd->dev_dir); | ||||
| #endif | ||||
|  | ||||
| 	/* proc dir */ | ||||
| 	if (lvm_snprintf(cmd->proc_dir, sizeof(cmd->proc_dir), "%s", | ||||
| 			 find_config_str(cmd->cf->root, "global/proc", | ||||
| 					 '/', DEFAULT_PROC_DIR)) < 0) { | ||||
| 		log_error("Device directory given in config file too long"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* activation? */ | ||||
| 	cmd->default_settings.activation = find_config_int(cmd->cf->root, | ||||
| 							   "global/activation", | ||||
| 							   '/', | ||||
| 							   DEFAULT_ACTIVATION); | ||||
| 	set_activation(cmd->default_settings.activation); | ||||
|  | ||||
| 	cmd->default_settings.suffix = find_config_int(cmd->cf->root, | ||||
| 						       "global/suffix", | ||||
| 						       '/', DEFAULT_SUFFIX); | ||||
|  | ||||
| 	if (!(cmd->default_settings.unit_factor = | ||||
| 	      units_to_bytes(find_config_str(cmd->cf->root, | ||||
| 					     "global/units", | ||||
| 					     '/', | ||||
| 					     DEFAULT_UNITS), | ||||
| 			     &cmd->default_settings.unit_type))) { | ||||
| 		log_error("Invalid units specification"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* Find and read config file */ | ||||
| static int _init_config(struct cmd_context *cmd) | ||||
| { | ||||
| 	struct stat info; | ||||
| 	char config_file[PATH_MAX] = ""; | ||||
|  | ||||
| 	if (!(cmd->cf = create_config_tree())) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* No config file if LVM_SYSTEM_DIR is empty */ | ||||
| 	if (!*cmd->sys_dir) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (lvm_snprintf(config_file, sizeof(config_file), | ||||
| 			 "%s/lvm.conf", cmd->sys_dir) < 0) { | ||||
| 		log_error("LVM_SYSTEM_DIR was too long"); | ||||
| 		destroy_config_tree(cmd->cf); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Is there a config file? */ | ||||
| 	if (stat(config_file, &info) == -1) { | ||||
| 		if (errno == ENOENT) | ||||
| 			return 1; | ||||
| 		log_sys_error("stat", config_file); | ||||
| 		destroy_config_tree(cmd->cf); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!read_config_file(cmd->cf, config_file)) { | ||||
| 		log_error("Failed to load config file %s", config_file); | ||||
| 		destroy_config_tree(cmd->cf); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _init_dev_cache(struct cmd_context *cmd) | ||||
| { | ||||
| 	struct config_node *cn; | ||||
| 	struct config_value *cv; | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(cn = find_config_node(cmd->cf->root, "devices/scan", '/'))) { | ||||
| 		if (!dev_cache_add_dir("/dev")) { | ||||
| 			log_error("Failed to add /dev to internal " | ||||
| 				  "device cache"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		log_verbose("device/scan not in config file: " | ||||
| 			    "Defaulting to /dev"); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	for (cv = cn->v; cv; cv = cv->next) { | ||||
| 		if (cv->type != CFG_STRING) { | ||||
| 			log_error("Invalid string in config file: " | ||||
| 				  "devices/scan"); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (!dev_cache_add_dir(cv->v.str)) { | ||||
| 			log_error("Failed to add %s to internal device cache", | ||||
| 				  cv->v.str); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct dev_filter *_init_filter_components(struct cmd_context *cmd) | ||||
| { | ||||
| 	struct config_node *cn; | ||||
| 	struct dev_filter *f1, *f2, *f3; | ||||
|  | ||||
| 	cn = find_config_node(cmd->cf->root, "devices/types", '/'); | ||||
|  | ||||
| 	if (!(f2 = lvm_type_filter_create(cmd->proc_dir, cn))) | ||||
| 		return NULL; | ||||
|  | ||||
| 	if (!(cn = find_config_node(cmd->cf->root, "devices/filter", '/'))) { | ||||
| 		log_debug("devices/filter not found in config file: no regex " | ||||
| 			  "filter installed"); | ||||
| 		return f2; | ||||
| 	} | ||||
|  | ||||
| 	if (!(f1 = regex_filter_create(cn->v))) { | ||||
| 		log_error("Failed to create regex device filter"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(f3 = composite_filter_create(2, f1, f2))) { | ||||
| 		log_error("Failed to create composite device filter"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return f3; | ||||
| } | ||||
|  | ||||
| static int _init_filters(struct cmd_context *cmd) | ||||
| { | ||||
| 	const char *dev_cache; | ||||
| 	struct dev_filter *f3, *f4; | ||||
| 	struct stat st; | ||||
| 	char cache_file[PATH_MAX]; | ||||
|  | ||||
| 	cmd->dump_filter = 0; | ||||
|  | ||||
| 	if (!(f3 = _init_filter_components(cmd))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (lvm_snprintf(cache_file, sizeof(cache_file), | ||||
| 			 "%s/.cache", cmd->sys_dir) < 0) { | ||||
| 		log_error("Persistent cache filename too long ('%s/.cache').", | ||||
| 			  cmd->sys_dir); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	dev_cache = | ||||
| 	    find_config_str(cmd->cf->root, "devices/cache", '/', cache_file); | ||||
| 	if (!(f4 = persistent_filter_create(f3, dev_cache))) { | ||||
| 		log_error("Failed to create persistent device filter"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Should we ever dump persistent filter state? */ | ||||
| 	if (find_config_int(cmd->cf->root, "devices/write_cache_state", '/', 1)) | ||||
| 		cmd->dump_filter = 1; | ||||
|  | ||||
| 	if (!*cmd->sys_dir) | ||||
| 		cmd->dump_filter = 0; | ||||
|  | ||||
| 	if (!stat(dev_cache, &st) && | ||||
| 	    (st.st_mtime > config_file_timestamp(cmd->cf)) && | ||||
| 	    !persistent_filter_load(f4)) | ||||
| 		log_verbose("Failed to load existing device cache from %s", | ||||
| 			    dev_cache); | ||||
|  | ||||
| 	cmd->filter = f4; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _init_formats(struct cmd_context *cmd) | ||||
| { | ||||
| 	const char *format; | ||||
|  | ||||
| 	struct format_type *fmt; | ||||
| 	struct list *fmth; | ||||
|  | ||||
| #ifdef HAVE_LIBDL | ||||
| 	struct config_node *cn; | ||||
| #endif | ||||
|  | ||||
| 	label_init(); | ||||
|  | ||||
| #ifdef LVM1_INTERNAL | ||||
| 	if (!(fmt = init_lvm1_format(cmd))) | ||||
| 		return 0; | ||||
| 	fmt->library = NULL; | ||||
| 	list_add(&cmd->formats, &fmt->list); | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_LIBDL | ||||
| 	/* Load any formats in shared libs */ | ||||
| 	if ((cn = find_config_node(cmd->cf->root, "global/format_libraries", | ||||
| 				   '/'))) { | ||||
|  | ||||
| 		struct config_value *cv; | ||||
| 		struct format_type *(*init_format_fn) (struct cmd_context *); | ||||
| 		void *lib; | ||||
|  | ||||
| 		for (cv = cn->v; cv; cv = cv->next) { | ||||
| 			if (cv->type != CFG_STRING) { | ||||
| 				log_error("Invalid string in config file: " | ||||
| 					  "global/format_libraries"); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			if (!(lib = load_shared_library(cmd->cf, cv->v.str, | ||||
| 							"format"))) { | ||||
| 				stack; | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			if (!(init_format_fn = dlsym(lib, "init_format"))) { | ||||
| 				log_error("Shared library %s does not contain " | ||||
| 					  "format functions", cv->v.str); | ||||
| 				dlclose(lib); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			if (!(fmt = init_format_fn(cmd))) | ||||
| 				return 0; | ||||
| 			fmt->library = lib; | ||||
| 			list_add(&cmd->formats, &fmt->list); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	if (!(fmt = create_text_format(cmd))) | ||||
| 		return 0; | ||||
| 	fmt->library = NULL; | ||||
| 	list_add(&cmd->formats, &fmt->list); | ||||
|  | ||||
| 	cmd->fmt_backup = fmt; | ||||
|  | ||||
| 	format = find_config_str(cmd->cf->root, "global/format", '/', | ||||
| 				 DEFAULT_FORMAT); | ||||
|  | ||||
| 	list_iterate(fmth, &cmd->formats) { | ||||
| 		fmt = list_item(fmth, struct format_type); | ||||
| 		if (!strcasecmp(fmt->name, format) || | ||||
| 		    (fmt->alias && !strcasecmp(fmt->alias, format))) { | ||||
| 			cmd->default_settings.fmt = fmt; | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	log_error("_init_formats: Default format (%s) not found", format); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Entry point */ | ||||
| struct cmd_context *create_toolcontext(struct arg *the_args) | ||||
| { | ||||
| 	struct cmd_context *cmd; | ||||
|  | ||||
| #ifdef M_MMAP_MAX | ||||
| 	mallopt(M_MMAP_MAX, 0); | ||||
| #endif | ||||
|  | ||||
| 	if (!setlocale(LC_ALL, "")) | ||||
| 		log_error("setlocale failed"); | ||||
|  | ||||
| 	init_syslog(DEFAULT_LOG_FACILITY); | ||||
|  | ||||
| 	if (!(cmd = dbg_malloc(sizeof(*cmd)))) { | ||||
| 		log_error("Failed to allocate command context"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	memset(cmd, 0, sizeof(*cmd)); | ||||
| 	cmd->args = the_args; | ||||
| 	list_init(&cmd->formats); | ||||
|  | ||||
| 	strcpy(cmd->sys_dir, DEFAULT_SYS_DIR); | ||||
|  | ||||
| 	if (!_get_env_vars(cmd)) | ||||
| 		goto error; | ||||
|  | ||||
| 	/* Create system directory if it doesn't already exist */ | ||||
| 	if (*cmd->sys_dir && !create_dir(cmd->sys_dir)) | ||||
| 		goto error; | ||||
|  | ||||
| 	if (!_init_config(cmd)) | ||||
| 		goto error; | ||||
|  | ||||
| 	_init_logging(cmd); | ||||
|  | ||||
| 	if (!_process_config(cmd)) | ||||
| 		goto error; | ||||
|  | ||||
| 	if (!_init_dev_cache(cmd)) | ||||
| 		goto error; | ||||
|  | ||||
| 	if (!_init_filters(cmd)) | ||||
| 		goto error; | ||||
|  | ||||
| 	if (!(cmd->mem = pool_create(4 * 1024))) { | ||||
| 		log_error("Command memory pool creation failed"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	memlock_init(cmd); | ||||
|  | ||||
| 	if (!_init_formats(cmd)) | ||||
| 		goto error; | ||||
|  | ||||
| 	cmd->current_settings = cmd->default_settings; | ||||
|  | ||||
| 	return cmd; | ||||
|  | ||||
|       error: | ||||
| 	dbg_free(cmd); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static void _destroy_formats(struct list *formats) | ||||
| { | ||||
| 	struct list *fmtl, *tmp; | ||||
| 	struct format_type *fmt; | ||||
| 	void *lib; | ||||
|  | ||||
| 	list_iterate_safe(fmtl, tmp, formats) { | ||||
| 		fmt = list_item(fmtl, struct format_type); | ||||
| 		list_del(&fmt->list); | ||||
| 		lib = fmt->library; | ||||
| 		fmt->ops->destroy(fmt); | ||||
| #ifdef HAVE_LIBDL | ||||
| 		if (lib) | ||||
| 			dlclose(lib); | ||||
| #endif | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void destroy_toolcontext(struct cmd_context *cmd) | ||||
| { | ||||
| 	if (cmd->dump_filter) | ||||
| 		persistent_filter_dump(cmd->filter); | ||||
|  | ||||
| 	activation_exit(); | ||||
| 	lvmcache_destroy(); | ||||
| 	label_exit(); | ||||
| 	_destroy_formats(&cmd->formats); | ||||
| 	cmd->filter->destroy(cmd->filter); | ||||
| 	pool_destroy(cmd->mem); | ||||
| 	dev_cache_exit(); | ||||
| 	destroy_config_tree(cmd->cf); | ||||
| 	dbg_free(cmd); | ||||
|  | ||||
| 	release_log_memory(); | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	fin_syslog(); | ||||
|  | ||||
| 	if (_log) | ||||
| 		fclose(_log); | ||||
|  | ||||
| } | ||||
							
								
								
									
										73
									
								
								lib/commands/toolcontext.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								lib/commands/toolcontext.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| /* | ||||
|  * 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" | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| /* | ||||
|  * Config options that can be changed while commands are processed | ||||
|  */ | ||||
| struct config_info { | ||||
| 	int debug; | ||||
| 	int verbose; | ||||
| 	int test; | ||||
| 	int syslog; | ||||
| 	int activation; | ||||
| 	int suffix; | ||||
| 	uint64_t unit_factor; | ||||
| 	char unit_type; | ||||
| 	const char *msg_prefix; | ||||
| 	int cmd_name;		/* Show command name? */ | ||||
|  | ||||
| 	int archive;		/* should we archive ? */ | ||||
| 	int backup;		/* should we backup ? */ | ||||
|  | ||||
| 	struct format_type *fmt; | ||||
|  | ||||
| 	mode_t umask; | ||||
| }; | ||||
|  | ||||
| /* FIXME Split into tool & library contexts */ | ||||
| /* command-instance-related variables needed by library */ | ||||
| struct cmd_context { | ||||
| 	/* format handler allocates all objects from here */ | ||||
| 	struct pool *mem; | ||||
|  | ||||
| 	const struct format_type *fmt;	/* Current format to use by default */ | ||||
| 	struct format_type *fmt_backup;	/* Format to use for backups */ | ||||
|  | ||||
| 	struct list formats;	/* Available formats */ | ||||
|  | ||||
| 	char *cmd_line; | ||||
| 	struct command *command; | ||||
| 	struct arg *args; | ||||
| 	char **argv; | ||||
|  | ||||
| 	struct dev_filter *filter; | ||||
| 	int dump_filter;	/* Dump filter when exiting? */ | ||||
|  | ||||
| 	struct config_tree *cf; | ||||
| 	struct config_info default_settings; | ||||
| 	struct config_info current_settings; | ||||
|  | ||||
| 	char sys_dir[PATH_MAX]; | ||||
| 	char dev_dir[PATH_MAX]; | ||||
| 	char proc_dir[PATH_MAX]; | ||||
| }; | ||||
|  | ||||
| struct cmd_context *create_toolcontext(struct arg *the_args); | ||||
| void destroy_toolcontext(struct cmd_context *cmd); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										893
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										893
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,893 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "config.h" | ||||
| #include "crc.h" | ||||
| #include "pool.h" | ||||
| #include "device.h" | ||||
|  | ||||
| #include <sys/stat.h> | ||||
| #include <sys/mman.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <ctype.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 { | ||||
| 	char *fb, *fe;		/* file limits */ | ||||
|  | ||||
| 	int t;			/* token limits and type */ | ||||
| 	char *tb, *te; | ||||
|  | ||||
| 	int fd;			/* descriptor for file being parsed */ | ||||
| 	int line;		/* line number we are on */ | ||||
|  | ||||
| 	struct pool *mem; | ||||
| }; | ||||
|  | ||||
| struct cs { | ||||
| 	struct config_tree cf; | ||||
| 	struct pool *mem; | ||||
| 	time_t timestamp; | ||||
| 	char *filename; | ||||
| }; | ||||
|  | ||||
| static void _get_token(struct parser *p, int tok_prev); | ||||
| 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_tree *create_config_tree(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; | ||||
| 	c->timestamp = 0; | ||||
| 	c->filename = NULL; | ||||
| 	return &c->cf; | ||||
| } | ||||
|  | ||||
| void destroy_config_tree(struct config_tree *cf) | ||||
| { | ||||
| 	pool_destroy(((struct cs *) cf)->mem); | ||||
| } | ||||
|  | ||||
| int read_config_fd(struct config_tree *cf, struct device *dev, | ||||
| 		   off_t offset, size_t size, off_t offset2, size_t size2, | ||||
| 		   checksum_fn_t checksum_fn, uint32_t checksum) | ||||
| { | ||||
| 	struct cs *c = (struct cs *) cf; | ||||
| 	struct parser *p; | ||||
| 	int r = 0; | ||||
| 	int use_mmap = 1; | ||||
| 	off_t mmap_offset = 0; | ||||
|  | ||||
| 	if (!(p = pool_alloc(c->mem, sizeof(*p)))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	p->mem = c->mem; | ||||
|  | ||||
| 	/* Only use mmap with regular files */ | ||||
| 	if (!(dev->flags & DEV_REGULAR) || size2) | ||||
| 		use_mmap = 0; | ||||
|  | ||||
| 	if (use_mmap) { | ||||
| 		mmap_offset = offset % getpagesize(); | ||||
| 		/* memory map the file */ | ||||
| 		p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ, | ||||
| 			     MAP_PRIVATE, dev_fd(dev), offset - mmap_offset); | ||||
| 		if (p->fb == (caddr_t) (-1)) { | ||||
| 			log_sys_error("mmap", dev_name(dev)); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		p->fb = p->fb + mmap_offset; | ||||
| 	} else { | ||||
| 		if (!(p->fb = dbg_malloc(size + size2))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		if (!dev_read(dev, (uint64_t) offset, size, p->fb)) { | ||||
| 			log_error("Read from %s failed", dev_name(dev)); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		if (size2) { | ||||
| 			if (!dev_read(dev, (uint64_t) offset2, size2, | ||||
| 				      p->fb + size)) { | ||||
| 				log_error("Circular read from %s failed", | ||||
| 					  dev_name(dev)); | ||||
| 				goto out; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (checksum_fn && checksum != | ||||
| 	    (checksum_fn(checksum_fn(INITIAL_CRC, p->fb, size), | ||||
| 			 p->fb + size, size2))) { | ||||
| 		log_error("%s: Checksum error", dev_name(dev)); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	p->fe = p->fb + size + size2; | ||||
|  | ||||
| 	/* parse */ | ||||
| 	p->tb = p->te = p->fb; | ||||
| 	p->line = 1; | ||||
| 	_get_token(p, TOK_SECTION_E); | ||||
| 	if (!(cf->root = _file(p))) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	r = 1; | ||||
|  | ||||
|       out: | ||||
| 	if (!use_mmap) | ||||
| 		dbg_free(p->fb); | ||||
| 	else { | ||||
| 		/* unmap the file */ | ||||
| 		if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) { | ||||
| 			log_sys_error("munmap", dev_name(dev)); | ||||
| 			r = 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int read_config_file(struct config_tree *cf, const char *file) | ||||
| { | ||||
| 	struct cs *c = (struct cs *) cf; | ||||
| 	struct stat info; | ||||
| 	struct device *dev; | ||||
| 	int r = 1; | ||||
|  | ||||
| 	if (stat(file, &info)) { | ||||
| 		log_sys_error("stat", file); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!S_ISREG(info.st_mode)) { | ||||
| 		log_error("%s is not a regular file", file); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (info.st_size == 0) { | ||||
| 		log_verbose("%s is empty", file); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dev = dev_create_file(file, NULL, NULL))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_open_flags(dev, O_RDONLY, 0, 0)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	r = read_config_fd(cf, dev, 0, (size_t) info.st_size, 0, 0, | ||||
| 			   (checksum_fn_t) NULL, 0); | ||||
|  | ||||
| 	dev_close(dev); | ||||
|  | ||||
| 	c->timestamp = info.st_mtime; | ||||
| 	c->filename = pool_strdup(c->mem, file); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| time_t config_file_timestamp(struct config_tree *cf) | ||||
| { | ||||
| 	struct cs *c = (struct cs *) cf; | ||||
|  | ||||
| 	return c->timestamp; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Returns 1 if config file reloaded | ||||
|  */ | ||||
| int reload_config_file(struct config_tree **cf) | ||||
| { | ||||
| 	struct config_tree *new_cf; | ||||
| 	struct cs *c = (struct cs *) *cf; | ||||
| 	struct cs *new_cs; | ||||
| 	struct stat info; | ||||
| 	struct device *dev; | ||||
| 	int r; | ||||
|  | ||||
| 	if (!c->filename) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (stat(c->filename, &info) == -1) { | ||||
| 		if (errno == ENOENT) | ||||
| 			return 1; | ||||
| 		log_sys_error("stat", c->filename); | ||||
| 		log_error("Failed to reload configuration file"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!S_ISREG(info.st_mode)) { | ||||
| 		log_error("Configuration file %s is not a regular file", | ||||
| 			  c->filename); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Unchanged? */ | ||||
| 	if (c->timestamp == info.st_mtime) | ||||
| 		return 0; | ||||
|  | ||||
| 	log_verbose("Detected config file change: Reloading %s", c->filename); | ||||
|  | ||||
| 	if (info.st_size == 0) { | ||||
| 		log_verbose("Config file reload: %s is empty", c->filename); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(new_cf = create_config_tree())) { | ||||
| 		log_error("Allocation of new config_tree failed"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dev = dev_create_file(c->filename, NULL, NULL))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_open_flags(dev, O_RDONLY, 0, 0)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	r = read_config_fd(new_cf, dev, 0, (size_t) info.st_size, | ||||
| 			   0, 0, (checksum_fn_t) NULL, 0); | ||||
|  | ||||
| 	dev_close(dev); | ||||
|  | ||||
| 	if (r) { | ||||
| 		new_cs = (struct cs *) new_cf; | ||||
| 		new_cs->filename = pool_strdup(new_cs->mem, c->filename); | ||||
| 		new_cs->timestamp = info.st_mtime; | ||||
| 		destroy_config_tree(*cf); | ||||
| 		*cf = new_cf; | ||||
| 	} | ||||
|  | ||||
| 	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_error("_write_value: Unknown value type: %d", v->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_file(struct config_tree *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 for 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,1}[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, t); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * tokeniser | ||||
|  */ | ||||
| static void _get_token(struct parser *p, int tok_prev) | ||||
| { | ||||
| 	int values_allowed = 0; | ||||
|  | ||||
| 	p->tb = p->te; | ||||
| 	_eat_space(p); | ||||
| 	if (p->tb == p->fe || !*p->tb) { | ||||
| 		p->t = TOK_EOF; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Should next token be interpreted as value instead of identifier? */ | ||||
| 	if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B || | ||||
| 	    tok_prev == TOK_COMMA) | ||||
| 		values_allowed = 1; | ||||
|  | ||||
| 	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) && (*p->te != '"')) { | ||||
| 			if ((*p->te == '\\') && (p->te + 1 != p->fe) && | ||||
| 			    *(p->te + 1)) | ||||
| 				p->te++; | ||||
| 			p->te++; | ||||
| 		} | ||||
|  | ||||
| 		if ((p->te != p->fe) && (*p->te)) | ||||
| 			p->te++; | ||||
| 		break; | ||||
|  | ||||
| 	case '\'': | ||||
| 		p->t = TOK_STRING; | ||||
| 		p->te++; | ||||
| 		while ((p->te != p->fe) && (*p->te) && (*p->te != '\'')) | ||||
| 			p->te++; | ||||
|  | ||||
| 		if ((p->te != p->fe) && (*p->te)) | ||||
| 			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': | ||||
| 	case '+': | ||||
| 	case '-': | ||||
| 		if (values_allowed) { | ||||
| 			p->te++; | ||||
| 			while ((p->te != p->fe) && (*p->te)) { | ||||
| 				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) && (*p->te) && !isspace(*p->te) && | ||||
| 		       (*p->te != '#') && (*p->te != '=') && (*p->te != '{') && | ||||
| 		       (*p->te != '}')) | ||||
| 			p->te++; | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void _eat_space(struct parser *p) | ||||
| { | ||||
| 	while ((p->tb != p->fe) && (*p->tb)) { | ||||
| 		if (*p->te == '#') { | ||||
| 			while ((p->te != p->fe) && (*p->te) && (*p->te != '\n')) | ||||
| 				p->te++; | ||||
| 			p->line++; | ||||
| 		} | ||||
|  | ||||
| 		else if (isspace(*p->te)) { | ||||
| 			while ((p->te != p->fe) && (*p->te) && 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) | ||||
| { | ||||
| 	size_t 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, const int 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, const int 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, | ||||
| 		    const int 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, | ||||
| 			const int 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, | ||||
| 		     const int 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, | ||||
| 		      const int 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, | ||||
| 		      const int 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, | ||||
| 		   const int 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; | ||||
| } | ||||
							
								
								
									
										81
									
								
								lib/config/config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								lib/config/config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_CONFIG_H | ||||
| #define _LVM_CONFIG_H | ||||
|  | ||||
| #include "device.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_tree { | ||||
| 	struct config_node *root; | ||||
| }; | ||||
|  | ||||
| struct config_tree *create_config_tree(void); | ||||
| void destroy_config_tree(struct config_tree *cf); | ||||
|  | ||||
| typedef uint32_t (*checksum_fn_t) (uint32_t initial, void *buf, uint32_t size); | ||||
|  | ||||
| int read_config_fd(struct config_tree *cf, struct device *dev, | ||||
| 		   off_t offset, size_t size, off_t offset2, size_t size2, | ||||
| 		   checksum_fn_t checksum_fn, uint32_t checksum); | ||||
|  | ||||
| int read_config_file(struct config_tree *cf, const char *file); | ||||
| int write_config_file(struct config_tree *cf, const char *file); | ||||
| int reload_config_file(struct config_tree **cf); | ||||
| time_t config_file_timestamp(struct config_tree *cf); | ||||
|  | ||||
| struct config_node *find_config_node(struct config_node *cn, | ||||
| 				     const char *path, const int separator); | ||||
|  | ||||
| const char *find_config_str(struct config_node *cn, | ||||
| 			    const char *path, const int sep, const char *fail); | ||||
|  | ||||
| int find_config_int(struct config_node *cn, const char *path, | ||||
| 		    const int sep, int fail); | ||||
|  | ||||
| float find_config_float(struct config_node *cn, const char *path, | ||||
| 			const int 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, | ||||
| 		     const int sep, int fail); | ||||
|  | ||||
| int get_config_uint32(struct config_node *cn, const char *path, | ||||
| 		      const int sep, uint32_t *result); | ||||
|  | ||||
| int get_config_uint64(struct config_node *cn, const char *path, | ||||
| 		      const int sep, uint64_t *result); | ||||
|  | ||||
| int get_config_str(struct config_node *cn, const char *path, | ||||
| 		   const int sep, char **result); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										91
									
								
								lib/config/defaults.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								lib/config/defaults.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_DEFAULTS_H | ||||
| #define _LVM_DEFAULTS_H | ||||
|  | ||||
| #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_SYS_DIR "/etc/lvm" | ||||
| #define DEFAULT_DEV_DIR "/dev" | ||||
| #define DEFAULT_PROC_DIR "/proc" | ||||
|  | ||||
| #define DEFAULT_LOCK_DIR "/var/lock/lvm" | ||||
| #define DEFAULT_LOCKING_LIB "lvm2_locking.so" | ||||
|  | ||||
| #define DEFAULT_UMASK 0077 | ||||
|  | ||||
| #ifdef LVM1_SUPPORT | ||||
| #  define DEFAULT_FORMAT "lvm1" | ||||
| #else | ||||
| #  define DEFAULT_FORMAT "lvm2" | ||||
| #endif | ||||
|  | ||||
| #define DEFAULT_STRIPESIZE 64	/* KB */ | ||||
| #define DEFAULT_PVMETADATASIZE 255 | ||||
| #define DEFAULT_PVMETADATACOPIES 1 | ||||
| #define DEFAULT_LABELSECTOR UINT64_C(1) | ||||
|  | ||||
| #define DEFAULT_MSG_PREFIX "  " | ||||
| #define DEFAULT_CMD_NAME 0 | ||||
| #define DEFAULT_OVERWRITE 0 | ||||
|  | ||||
| #ifndef DEFAULT_LOG_FACILITY | ||||
| #  define DEFAULT_LOG_FACILITY LOG_USER | ||||
| #endif | ||||
|  | ||||
| #define DEFAULT_SYSLOG 1 | ||||
| #define DEFAULT_VERBOSE 0 | ||||
| #define DEFAULT_LOGLEVEL 0 | ||||
| #define DEFAULT_INDENT 1 | ||||
| #define DEFAULT_UNITS "h" | ||||
| #define DEFAULT_SUFFIX 1 | ||||
|  | ||||
| #ifdef DEVMAPPER_SUPPORT | ||||
| #  define DEFAULT_ACTIVATION 1 | ||||
| #  define DEFAULT_RESERVED_MEMORY 8192 | ||||
| #  define DEFAULT_RESERVED_STACK 256 | ||||
| #  define DEFAULT_PROCESS_PRIORITY -18 | ||||
| #else | ||||
| #  define DEFAULT_ACTIVATION 0 | ||||
| #endif | ||||
|  | ||||
| #define DEFAULT_STRIPE_FILLER "/dev/ioerror" | ||||
| #define DEFAULT_MIRROR_REGION_SIZE 512	/* KB */ | ||||
| #define DEFAULT_INTERVAL 15 | ||||
|  | ||||
| #ifdef READLINE_SUPPORT | ||||
| #  define DEFAULT_MAX_HISTORY 100 | ||||
| #endif | ||||
|  | ||||
| #define DEFAULT_REP_ALIGNED 1 | ||||
| #define DEFAULT_REP_BUFFERED 1 | ||||
| #define DEFAULT_REP_HEADINGS 1 | ||||
| #define DEFAULT_REP_SEPARATOR " " | ||||
|  | ||||
| #define DEFAULT_LVS_COLS "lv_name,vg_name,lv_attr,lv_size,origin,snap_percent,move_pv,move_percent" | ||||
| #define DEFAULT_VGS_COLS "vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free" | ||||
| #define DEFAULT_PVS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free" | ||||
| #define DEFAULT_SEGS_COLS "lv_name,vg_name,lv_attr,stripes,segtype,seg_size" | ||||
|  | ||||
| #define DEFAULT_LVS_COLS_VERB "lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,origin,snap_percent,move_pv,move_percent,lv_uuid" | ||||
| #define DEFAULT_VGS_COLS_VERB "vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid" | ||||
| #define DEFAULT_PVS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pv_uuid" | ||||
| #define DEFAULT_SEGS_COLS_VERB "lv_name,vg_name,lv_attr,seg_start,seg_size,stripes,segtype,stripesize,chunksize" | ||||
|  | ||||
| #define DEFAULT_LVS_SORT "vg_name,lv_name" | ||||
| #define DEFAULT_VGS_SORT "vg_name" | ||||
| #define DEFAULT_PVS_SORT "pv_name" | ||||
| #define DEFAULT_SEGS_SORT "vg_name,lv_name,seg_start" | ||||
|  | ||||
| #endif				/* _LVM_DEFAULTS_H */ | ||||
							
								
								
									
										77
									
								
								lib/datastruct/bitset.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								lib/datastruct/bitset.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "bitset.h" | ||||
|  | ||||
| /* FIXME: calculate this. */ | ||||
| #define INT_SHIFT 5 | ||||
|  | ||||
| bitset_t bitset_create(struct pool *mem, unsigned num_bits) | ||||
| { | ||||
| 	unsigned n = (num_bits / BITS_PER_INT) + 2; | ||||
| 	size_t 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); | ||||
| } | ||||
							
								
								
									
										43
									
								
								lib/datastruct/bitset.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								lib/datastruct/bitset.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_BITSET_H | ||||
| #define _LVM_BITSET_H | ||||
|  | ||||
| #include "pool.h" | ||||
|  | ||||
| #include <limits.h> | ||||
|  | ||||
| typedef uint32_t *bitset_t; | ||||
|  | ||||
| bitset_t bitset_create(struct pool *mem, unsigned num_bits); | ||||
| void bitset_destroy(bitset_t bs); | ||||
|  | ||||
| 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 "lib.h" | ||||
| #include "btree.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 | ||||
| } | ||||
|  | ||||
| static 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; | ||||
| } | ||||
							
								
								
									
										25
									
								
								lib/datastruct/btree.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/datastruct/btree.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_BTREE_H | ||||
| #define _LVM_BTREE_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 | ||||
							
								
								
									
										231
									
								
								lib/datastruct/hash.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								lib/datastruct/hash.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "hash.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 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 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 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); | ||||
| void *hash_lookup_fixed(struct hash_table *t, const char *key, uint32_t len); | ||||
| 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 | ||||
							
								
								
									
										103
									
								
								lib/datastruct/list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								lib/datastruct/list.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| /* | ||||
|  * 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; | ||||
| }; | ||||
|  | ||||
| #define LIST_INIT(name)	struct list name = { &(name), &(name) } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| static inline int list_end(struct list *head, struct list *elem) | ||||
| { | ||||
| 	return elem->n == head; | ||||
| } | ||||
|  | ||||
| static inline struct list *list_next(struct list *head, struct list *elem) | ||||
| { | ||||
| 	return (list_end(head, elem) ? NULL : elem->n); | ||||
| } | ||||
|  | ||||
| #define list_item(v, t) \ | ||||
|     ((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->list)) | ||||
|  | ||||
| #define list_struct_base(v, t, h) \ | ||||
|     ((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->h)) | ||||
|  | ||||
| /* Given a known element in a known structure, locate another */ | ||||
| #define struct_field(v, t, e, f) \ | ||||
|     (((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->f) | ||||
|  | ||||
| /* Given a known element in a known structure, locate the list head */ | ||||
| #define list_head(v, t, e) struct_field(v, t, e, list) | ||||
|  | ||||
| #define list_iterate(v, head) \ | ||||
| 	for (v = (head)->n; v != head; v = v->n) | ||||
|  | ||||
| #define list_uniterate(v, head, start) \ | ||||
| 	for (v = (start)->p; v != head; v = v->p) | ||||
|  | ||||
| #define list_iterate_safe(v, t, head) \ | ||||
| 	for (v = (head)->n, t = v->n; v != head; v = t, t = v->n) | ||||
|  | ||||
| #define list_iterate_items(v, head) \ | ||||
| 	for (v = list_item((head)->n, typeof(*v)); &v->list != (head); \ | ||||
| 	     v = list_item(v->list.n, typeof(*v))) | ||||
|  | ||||
| static inline unsigned int list_size(const struct list *head) | ||||
| { | ||||
| 	unsigned int s = 0; | ||||
| 	const struct list *v; | ||||
|  | ||||
| 	list_iterate(v, head) | ||||
| 	    s++; | ||||
|  | ||||
| 	return s; | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										23
									
								
								lib/datastruct/lvm-types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								lib/datastruct/lvm-types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| /* | ||||
|  * 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> | ||||
|  | ||||
| /* Define some portable printing types */ | ||||
| #define PRIsize_t "Zu" | ||||
|  | ||||
| struct str_list { | ||||
| 	struct list list; | ||||
| 	const char *str; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										548
									
								
								lib/device/dev-cache.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										548
									
								
								lib/device/dev-cache.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,548 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "dev-cache.h" | ||||
| #include "pool.h" | ||||
| #include "hash.h" | ||||
| #include "list.h" | ||||
| #include "lvm-types.h" | ||||
| #include "btree.h" | ||||
| #include "filter.h" | ||||
|  | ||||
| #include <unistd.h> | ||||
| #include <sys/param.h> | ||||
| #include <dirent.h> | ||||
|  | ||||
| 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); | ||||
|  | ||||
| struct device *dev_create_file(const char *filename, struct device *dev, | ||||
| 			       struct str_list *alias) | ||||
| { | ||||
| 	int allocate = !dev; | ||||
|  | ||||
| 	if (allocate && !(dev = dbg_malloc(sizeof(*dev)))) { | ||||
| 		log_error("struct device allocation failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (allocate && !(alias = dbg_malloc(sizeof(*alias)))) { | ||||
| 		log_error("struct str_list allocation failed"); | ||||
| 		dbg_free(dev); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (!(alias->str = dbg_strdup(filename))) { | ||||
| 		log_error("filename strdup failed"); | ||||
| 		if (allocate) { | ||||
| 			dbg_free(dev); | ||||
| 			dbg_free(alias); | ||||
| 		} | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	dev->flags = DEV_REGULAR; | ||||
| 	if (allocate) | ||||
| 		dev->flags |= DEV_ALLOCED; | ||||
| 	list_init(&dev->aliases); | ||||
| 	list_add(&dev->aliases, &alias->list); | ||||
| 	dev->end = UINT64_C(0); | ||||
| 	dev->dev = 0; | ||||
| 	dev->fd = -1; | ||||
| 	dev->open_count = 0; | ||||
| 	memset(dev->pvid, 0, sizeof(dev->pvid)); | ||||
| 	list_init(&dev->open_list); | ||||
|  | ||||
| 	return dev; | ||||
| } | ||||
|  | ||||
| static struct device *_dev_create(dev_t d) | ||||
| { | ||||
| 	struct device *dev; | ||||
|  | ||||
| 	if (!(dev = _alloc(sizeof(*dev)))) { | ||||
| 		log_error("struct device allocation failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	dev->flags = 0; | ||||
| 	list_init(&dev->aliases); | ||||
| 	dev->dev = d; | ||||
| 	dev->fd = -1; | ||||
| 	dev->open_count = 0; | ||||
| 	dev->end = UINT64_C(0); | ||||
| 	memset(dev->pvid, 0, sizeof(dev->pvid)); | ||||
| 	list_init(&dev->open_list); | ||||
|  | ||||
| 	return dev; | ||||
| } | ||||
|  | ||||
| /* Return 1 if we prefer path1 else return 0 */ | ||||
| static int _compare_paths(const char *path0, const char *path1) | ||||
| { | ||||
| 	int slash0 = 0, slash1 = 0; | ||||
| 	const char *p; | ||||
| 	char p0[PATH_MAX], p1[PATH_MAX]; | ||||
| 	char *s0, *s1; | ||||
| 	struct stat stat0, stat1; | ||||
|  | ||||
| 	/* Return the path with fewer slashes */ | ||||
| 	for (p = path0; p++; p = (const char *) strchr(p, '/')) | ||||
| 		slash0++; | ||||
|  | ||||
| 	for (p = path1; p++; p = (const char *) strchr(p, '/')) | ||||
| 		slash1++; | ||||
|  | ||||
| 	if (slash0 < slash1) | ||||
| 		return 0; | ||||
| 	if (slash1 < slash0) | ||||
| 		return 1; | ||||
|  | ||||
| 	strncpy(p0, path0, PATH_MAX); | ||||
| 	strncpy(p1, path1, PATH_MAX); | ||||
| 	s0 = &p0[0] + 1; | ||||
| 	s1 = &p1[0] + 1; | ||||
|  | ||||
| 	/* We prefer symlinks - they exist for a reason! | ||||
| 	 * So we prefer a shorter path before the first symlink in the name. | ||||
| 	 * FIXME Configuration option to invert this? */ | ||||
| 	while (s0) { | ||||
| 		s0 = strchr(s0, '/'); | ||||
| 		s1 = strchr(s1, '/'); | ||||
| 		if (s0) { | ||||
| 			*s0 = '\0'; | ||||
| 			*s1 = '\0'; | ||||
| 		} | ||||
| 		if (lstat(p0, &stat0)) { | ||||
| 			log_sys_error("lstat", p0); | ||||
| 			return 1; | ||||
| 		} | ||||
| 		if (lstat(p1, &stat1)) { | ||||
| 			log_sys_error("lstat", p1); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		if (S_ISLNK(stat0.st_mode) && !S_ISLNK(stat1.st_mode)) | ||||
| 			return 0; | ||||
| 		if (!S_ISLNK(stat0.st_mode) && S_ISLNK(stat1.st_mode)) | ||||
| 			return 1; | ||||
| 		if (s0) { | ||||
| 			*s0++ = '/'; | ||||
| 			*s1++ = '/'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* ASCII comparison */ | ||||
| 	if (strcmp(path0, path1) < 0) | ||||
| 		return 0; | ||||
| 	else | ||||
| 		return 1; | ||||
| } | ||||
|  | ||||
| static int _add_alias(struct device *dev, const char *path) | ||||
| { | ||||
| 	struct str_list *sl = _alloc(sizeof(*sl)); | ||||
| 	struct list *ah; | ||||
| 	const char *oldpath; | ||||
| 	int prefer_old = 1; | ||||
|  | ||||
| 	if (!sl) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Is name already there? */ | ||||
| 	list_iterate(ah, &dev->aliases) { | ||||
| 		if (!strcmp(list_item(ah, struct str_list)->str, path)) { | ||||
| 			stack; | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(sl->str = pool_strdup(_cache.mem, path))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!list_empty(&dev->aliases)) { | ||||
| 		oldpath = list_item(dev->aliases.n, struct str_list)->str; | ||||
| 		prefer_old = _compare_paths(path, oldpath); | ||||
| 		log_debug("%s: Aliased to %s in device cache%s", | ||||
| 			  path, oldpath, prefer_old ? "" : " (preferred name)"); | ||||
|  | ||||
| 	} else | ||||
| 		log_debug("%s: Added to device cache", path); | ||||
|  | ||||
| 	if (prefer_old) | ||||
| 		list_add(&dev->aliases, &sl->list); | ||||
| 	else | ||||
| 		list_add_h(&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, | ||||
| 						   (uint32_t) d))) { | ||||
| 		/* create new device */ | ||||
| 		if (!(dev = _dev_create(d))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (!(btree_insert(_cache.devices, (uint32_t) 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) | ||||
| { | ||||
| 	size_t 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_has_scanned(void) | ||||
| { | ||||
| 	return _cache.has_scanned; | ||||
| } | ||||
|  | ||||
| void dev_cache_scan(int do_scan) | ||||
| { | ||||
| 	if (!do_scan) | ||||
| 		_cache.has_scanned = 1; | ||||
| 	else { | ||||
| 		_cache.has_scanned = 0; | ||||
| 		_full_scan(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| static 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))) { | ||||
| 		log_error("dir_list allocation failed"); | ||||
| 		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 */ | ||||
| /* set quiet if the cache is expected to be out-of-date */ | ||||
| /* FIXME Make rest of code pass/cache struct device instead of dev_name */ | ||||
| const char *dev_name_confirmed(struct device *dev, int quiet) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	const 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) { | ||||
| 			if (quiet) | ||||
| 				log_sys_debug("stat", name); | ||||
| 			else | ||||
| 				log_sys_error("stat", name); | ||||
| 		} | ||||
| 		if (quiet) | ||||
| 			log_debug("Path %s no longer valid for device(%d,%d)", | ||||
| 			  	  name, (int) MAJOR(dev->dev), | ||||
| 				  (int) MINOR(dev->dev)); | ||||
| 		else | ||||
| 			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) { | ||||
| 		log_error("dev_iter allocation failed"); | ||||
| 		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; | ||||
| } | ||||
							
								
								
									
										43
									
								
								lib/device/dev-cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								lib/device/dev-cache.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /* | ||||
|  * 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 "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); | ||||
|  | ||||
| /* Trigger(1) or avoid(0) a scan */ | ||||
| void dev_cache_scan(int do_scan); | ||||
| int dev_cache_has_scanned(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 | ||||
							
								
								
									
										467
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										467
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,467 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "lvm-types.h" | ||||
| #include "device.h" | ||||
| #include "metadata.h" | ||||
| #include "lvmcache.h" | ||||
| #include "memlock.h" | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/ioctl.h> | ||||
|  | ||||
| #ifdef linux | ||||
| #  define u64 uint64_t		/* Missing without __KERNEL__ */ | ||||
| #  undef WNOHANG		/* Avoid redefinition */ | ||||
| #  undef WUNTRACED		/* Avoid redefinition */ | ||||
| #  include <linux/fs.h>		/* For block ioctl definitions */ | ||||
| #  define BLKSIZE_SHIFT SECTOR_SHIFT | ||||
| #else | ||||
| #  include <sys/disk.h> | ||||
| #  define BLKBSZGET DKIOCGETBLOCKSIZE | ||||
| #  define BLKSSZGET DKIOCGETBLOCKSIZE | ||||
| #  define BLKGETSIZE64 DKIOCGETBLOCKCOUNT | ||||
| #  define BLKFLSBUF DKIOCSYNCHRONIZECACHE | ||||
| #  define BLKSIZE_SHIFT 0 | ||||
| #  ifndef O_DIRECT | ||||
| #    define O_DIRECT	0 | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| /* FIXME Use _llseek for 64-bit | ||||
| _syscall5(int,  _llseek,  uint,  fd, ulong, hi, ulong, lo, loff_t *, res, uint, wh); | ||||
|  if (_llseek((unsigned) fd, (ulong) (offset >> 32), (ulong) (offset & 0xFFFFFFFF), &pos, SEEK_SET) < 0) {  | ||||
| */ | ||||
|  | ||||
| static LIST_INIT(_open_devices); | ||||
|  | ||||
| /*----------------------------------------------------------------- | ||||
|  * The standard io loop that keeps submitting an io until it's | ||||
|  * all gone. | ||||
|  *---------------------------------------------------------------*/ | ||||
| static int _io(struct device_area *where, void *buffer, int should_write) | ||||
| { | ||||
| 	int fd = dev_fd(where->dev); | ||||
| 	ssize_t n = 0; | ||||
| 	size_t total = 0; | ||||
|  | ||||
| 	if (fd < 0) { | ||||
| 		log_error("Attempt to read an unopened device (%s).", | ||||
| 			  dev_name(where->dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Skip all writes in test mode. | ||||
| 	 */ | ||||
| 	if (should_write && test_mode()) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (where->size > SSIZE_MAX) { | ||||
| 		log_error("Read size too large: %" PRIu64, where->size); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lseek(fd, (off_t) where->start, SEEK_SET) < 0) { | ||||
| 		log_sys_error("lseek", dev_name(where->dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	while (total < (size_t) where->size) { | ||||
| 		do | ||||
| 			n = should_write ? | ||||
| 			    write(fd, buffer, (size_t) where->size - total) : | ||||
| 			    read(fd, buffer, (size_t) where->size - total); | ||||
| 		while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN))); | ||||
|  | ||||
| 		if (n <= 0) | ||||
| 			break; | ||||
|  | ||||
| 		total += n; | ||||
| 		buffer += n; | ||||
| 	} | ||||
|  | ||||
| 	return (total == (size_t) where->size); | ||||
| } | ||||
|  | ||||
| /*----------------------------------------------------------------- | ||||
|  * LVM2 uses O_DIRECT when performing metadata io, which requires | ||||
|  * block size aligned accesses.  If any io is not aligned we have | ||||
|  * to perform the io via a bounce buffer, obviously this is quite | ||||
|  * inefficient. | ||||
|  *---------------------------------------------------------------*/ | ||||
|  | ||||
| /* | ||||
|  * Get the sector size from an _open_ device. | ||||
|  */ | ||||
| static int _get_block_size(struct device *dev, unsigned int *size) | ||||
| { | ||||
| 	int s; | ||||
|  | ||||
| 	if (ioctl(dev_fd(dev), BLKBSZGET, &s) < 0) { | ||||
| 		log_sys_error("ioctl BLKBSZGET", dev_name(dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	*size = (unsigned int) s; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Widens a region to be an aligned region. | ||||
|  */ | ||||
| static void _widen_region(unsigned int block_size, struct device_area *region, | ||||
| 			  struct device_area *result) | ||||
| { | ||||
| 	uint64_t mask = block_size - 1, delta; | ||||
| 	memcpy(result, region, sizeof(*result)); | ||||
|  | ||||
| 	/* adjust the start */ | ||||
| 	delta = result->start & mask; | ||||
| 	if (delta) { | ||||
| 		result->start -= delta; | ||||
| 		result->size += delta; | ||||
| 	} | ||||
|  | ||||
| 	/* adjust the end */ | ||||
| 	delta = (result->start + result->size) & mask; | ||||
| 	if (delta) | ||||
| 		result->size += block_size - delta; | ||||
| } | ||||
|  | ||||
| static int _aligned_io(struct device_area *where, void *buffer, | ||||
| 		       int should_write) | ||||
| { | ||||
| 	void *bounce; | ||||
| 	unsigned int block_size = 0; | ||||
| 	uintptr_t mask; | ||||
| 	struct device_area widened; | ||||
|  | ||||
| 	if (!(where->dev->flags & DEV_REGULAR) && | ||||
| 	    !_get_block_size(where->dev, &block_size)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!block_size) | ||||
| 		block_size = SECTOR_SIZE * 2; | ||||
|  | ||||
| 	_widen_region(block_size, where, &widened); | ||||
|  | ||||
| 	/* Do we need to use a bounce buffer? */ | ||||
| 	mask = block_size - 1; | ||||
| 	if (!memcmp(where, &widened, sizeof(widened)) && | ||||
| 	    !((uintptr_t) buffer & mask)) | ||||
| 		return _io(where, buffer, should_write); | ||||
|  | ||||
| 	/* Allocate a bounce buffer with an extra block */ | ||||
| 	if (!(bounce = alloca((size_t) widened.size + block_size))) { | ||||
| 		log_error("Bounce buffer alloca failed"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Realign start of bounce buffer (using the extra sector) | ||||
| 	 */ | ||||
| 	if (((uintptr_t) bounce) & mask) | ||||
| 		bounce = (void *) ((((uintptr_t) bounce) + mask) & ~mask); | ||||
|  | ||||
| 	/* channel the io through the bounce buffer */ | ||||
| 	if (!_io(&widened, bounce, 0)) { | ||||
| 		if (!should_write) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		/* FIXME pre-extend the file */ | ||||
| 		memset(bounce, '\n', widened.size); | ||||
| 	} | ||||
|  | ||||
| 	if (should_write) { | ||||
| 		memcpy(bounce + (where->start - widened.start), buffer, | ||||
| 		       (size_t) where->size); | ||||
|  | ||||
| 		/* ... then we write */ | ||||
| 		return _io(&widened, bounce, 1); | ||||
| 	} | ||||
|  | ||||
| 	memcpy(buffer, bounce + (where->start - widened.start), | ||||
| 	       (size_t) where->size); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /*----------------------------------------------------------------- | ||||
|  * Public functions | ||||
|  *---------------------------------------------------------------*/ | ||||
|  | ||||
| int dev_get_size(struct device *dev, uint64_t *size) | ||||
| { | ||||
| 	int fd; | ||||
| 	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, BLKGETSIZE64, size) < 0) { | ||||
| 		log_sys_error("ioctl BLKGETSIZE64", name); | ||||
| 		close(fd); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	*size >>= BLKSIZE_SHIFT;	/* Convert to sectors */ | ||||
| 	close(fd); | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| void dev_flush(struct device *dev) | ||||
| { | ||||
| 	if (!(dev->flags & DEV_REGULAR) && ioctl(dev->fd, BLKFLSBUF, 0) >= 0) | ||||
| 		return; | ||||
|  | ||||
| 	if (fsync(dev->fd) >= 0) | ||||
| 		return; | ||||
|  | ||||
| 	sync(); | ||||
| } | ||||
|  | ||||
| int dev_open_flags(struct device *dev, int flags, int direct, int quiet) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	const char *name; | ||||
|  | ||||
| 	if (dev->fd >= 0) { | ||||
| 		dev->open_count++; | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (memlock()) | ||||
| 		log_error("WARNING: dev_open(%s) called while suspended", | ||||
| 			  dev_name(dev)); | ||||
|  | ||||
| 	if (dev->flags & DEV_REGULAR) | ||||
| 		name = dev_name(dev); | ||||
| 	else if (!(name = dev_name_confirmed(dev, quiet))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dev->flags & DEV_REGULAR) && | ||||
| 	    ((stat(name, &buf) < 0) || (buf.st_rdev != dev->dev))) { | ||||
| 		log_error("%s: stat failed: Has device name changed?", name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (direct) | ||||
| 		flags |= O_DIRECT; | ||||
|  | ||||
| 	if ((dev->fd = open(name, flags, 0777)) < 0) { | ||||
| 		log_sys_error("open", name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	dev->open_count = 1; | ||||
| 	dev->flags &= ~DEV_ACCESSED_W; | ||||
|  | ||||
| 	if (!(dev->flags & DEV_REGULAR) && | ||||
| 	    ((fstat(dev->fd, &buf) < 0) || (buf.st_rdev != dev->dev))) { | ||||
| 		log_error("%s: fstat failed: Has device name changed?", name); | ||||
| 		dev_close(dev); | ||||
| 		dev->fd = -1; | ||||
| 		return 0; | ||||
| 	} | ||||
| #if !O_DIRECT | ||||
| 	if (!(dev->flags & DEV_REGULAR)) | ||||
| 		dev_flush(dev); | ||||
| #endif | ||||
|  | ||||
| 	if ((flags & O_CREAT) && !(flags & O_TRUNC)) { | ||||
| 		dev->end = lseek(dev->fd, (off_t) 0, SEEK_END); | ||||
| 	} | ||||
|  | ||||
| 	list_add(&_open_devices, &dev->open_list); | ||||
| 	log_debug("Opened %s", dev_name(dev)); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int dev_open_quiet(struct device *dev) | ||||
| { | ||||
| 	/* FIXME Open O_RDONLY if vg read lock? */ | ||||
| 	return dev_open_flags(dev, O_RDWR, 1, 1); | ||||
| } | ||||
|  | ||||
| int dev_open(struct device *dev) | ||||
| { | ||||
| 	/* FIXME Open O_RDONLY if vg read lock? */ | ||||
| 	return dev_open_flags(dev, O_RDWR, 1, 0); | ||||
| } | ||||
|  | ||||
| static void _close(struct device *dev) | ||||
| { | ||||
| 	if (close(dev->fd)) | ||||
| 		log_sys_error("close", dev_name(dev)); | ||||
| 	dev->fd = -1; | ||||
| 	list_del(&dev->open_list); | ||||
|  | ||||
| 	log_debug("Closed %s", dev_name(dev)); | ||||
|  | ||||
| 	if (dev->flags & DEV_ALLOCED) { | ||||
| 		dbg_free((void *) list_item(dev->aliases.n, struct str_list)-> | ||||
| 			 str); | ||||
| 		dbg_free(dev->aliases.n); | ||||
| 		dbg_free(dev); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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 !O_DIRECT | ||||
| 	if (dev->flags & DEV_ACCESSED_W) | ||||
| 		dev_flush(dev); | ||||
| #endif | ||||
|  | ||||
| 	/* FIXME lookup device in cache to get vgname and see if it's locked? */ | ||||
| 	if (--dev->open_count < 1 && !vgs_locked()) | ||||
| 		_close(dev); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void dev_close_all(void) | ||||
| { | ||||
| 	struct list *doh, *doht; | ||||
| 	struct device *dev; | ||||
|  | ||||
| 	list_iterate_safe(doh, doht, &_open_devices) { | ||||
| 		dev = list_struct_base(doh, struct device, open_list); | ||||
| 		if (dev->open_count < 1) | ||||
| 			_close(dev); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer) | ||||
| { | ||||
| 	struct device_area where; | ||||
|  | ||||
| 	if (!dev->open_count) | ||||
| 		return 0; | ||||
|  | ||||
| 	where.dev = dev; | ||||
| 	where.start = offset; | ||||
| 	where.size = len; | ||||
|  | ||||
| 	return _aligned_io(&where, buffer, 0); | ||||
| } | ||||
|  | ||||
| /* FIXME If O_DIRECT can't extend file, dev_extend first; dev_truncate after. | ||||
|  *       But fails if concurrent processes writing | ||||
|  */ | ||||
|  | ||||
| /* FIXME pre-extend the file */ | ||||
| int dev_append(struct device *dev, size_t len, void *buffer) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	if (!dev->open_count) | ||||
| 		return 0; | ||||
|  | ||||
| 	r = dev_write(dev, dev->end, len, buffer); | ||||
| 	dev->end += (uint64_t) len; | ||||
|  | ||||
| #if !O_DIRECT | ||||
| 	dev_flush(dev); | ||||
| #endif | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer) | ||||
| { | ||||
| 	struct device_area where; | ||||
|  | ||||
| 	if (!dev->open_count) | ||||
| 		return 0; | ||||
|  | ||||
| 	where.dev = dev; | ||||
| 	where.start = offset; | ||||
| 	where.size = len; | ||||
|  | ||||
| 	dev->flags |= DEV_ACCESSED_W; | ||||
|  | ||||
| 	return _aligned_io(&where, buffer, 1); | ||||
| } | ||||
|  | ||||
| int dev_zero(struct device *dev, uint64_t offset, size_t len) | ||||
| { | ||||
| 	size_t s; | ||||
| 	char buffer[4096]; | ||||
|  | ||||
| 	if (!dev_open(dev)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if ((offset % SECTOR_SIZE) || (len % SECTOR_SIZE)) | ||||
| 		log_debug("Wiping %s at %" PRIu64 " length %" PRIsize_t, | ||||
| 			  dev_name(dev), offset, len); | ||||
| 	else | ||||
| 		log_debug("Wiping %s at sector %" PRIu64 " length %" PRIsize_t | ||||
| 			  " sectors", dev_name(dev), offset >> SECTOR_SHIFT, | ||||
| 			  len >> SECTOR_SHIFT); | ||||
|  | ||||
| 	memset(buffer, 0, sizeof(buffer)); | ||||
| 	while (1) { | ||||
| 		s = len > sizeof(buffer) ? sizeof(buffer) : len; | ||||
| 		if (!dev_write(dev, offset, s, buffer)) | ||||
| 			break; | ||||
|  | ||||
| 		len -= s; | ||||
| 		if (!len) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	dev->flags |= DEV_ACCESSED_W; | ||||
|  | ||||
| 	if (!dev_close(dev)) | ||||
| 		stack; | ||||
|  | ||||
| 	/* FIXME: Always display error */ | ||||
| 	return (len == 0); | ||||
| } | ||||
							
								
								
									
										210
									
								
								lib/device/device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								lib/device/device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| /* | ||||
|  * 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 | ||||
|  */ | ||||
|  | ||||
| #if 0 | ||||
| #include <sys/stat.h> | ||||
| #include <sys/mman.h> | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <ctype.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/major.h> | ||||
| #include <linux/genhd.h> | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
| #include "dev-cache.h" | ||||
| #include "metadata.h" | ||||
| #include "device.h" | ||||
|  | ||||
| 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 | ||||
							
								
								
									
										93
									
								
								lib/device/device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								lib/device/device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_DEVICE_H | ||||
| #define _LVM_DEVICE_H | ||||
|  | ||||
| #include "uuid.h" | ||||
| #include <fcntl.h> | ||||
|  | ||||
| #define DEV_ACCESSED_W		0x00000001	/* Device written to? */ | ||||
| #define DEV_REGULAR		0x00000002	/* Regular file? */ | ||||
| #define DEV_ALLOCED		0x00000004	/* dbg_malloc used */ | ||||
|  | ||||
| /* | ||||
|  * 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; | ||||
| 	int open_count; | ||||
| 	uint32_t flags; | ||||
| 	uint64_t end; | ||||
| 	struct list open_list; | ||||
|  | ||||
| 	char pvid[ID_LEN + 1]; | ||||
| }; | ||||
|  | ||||
| struct device_list { | ||||
| 	struct list list; | ||||
| 	struct device *dev; | ||||
| }; | ||||
|  | ||||
| struct device_area { | ||||
| 	struct device *dev; | ||||
| 	uint64_t start;		/* Bytes */ | ||||
| 	uint64_t size;		/* Bytes */ | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * 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); | ||||
|  | ||||
| /* Use quiet version if device number could change e.g. when opening LV */ | ||||
| int dev_open(struct device *dev); | ||||
| int dev_open_quiet(struct device *dev); | ||||
| int dev_open_flags(struct device *dev, int flags, int append, int quiet); | ||||
| int dev_close(struct device *dev); | ||||
| void dev_close_all(void); | ||||
|  | ||||
| static inline int dev_fd(struct device *dev) | ||||
| { | ||||
| 	return dev->fd; | ||||
| } | ||||
|  | ||||
| int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer); | ||||
| int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer); | ||||
| int dev_append(struct device *dev, size_t len, void *buffer); | ||||
| int dev_zero(struct device *dev, uint64_t offset, size_t len); | ||||
| void dev_flush(struct device *dev); | ||||
|  | ||||
| struct device *dev_create_file(const char *filename, struct device *dev, | ||||
| 			       struct str_list *alias); | ||||
|  | ||||
| static inline const char *dev_name(const 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, int quiet); | ||||
|  | ||||
| /* FIXME Check partition type if appropriate */ | ||||
|  | ||||
| #define is_lvm_partition(a) 1 | ||||
|  | ||||
| /* | ||||
| static inline int is_lvm_partition(const char *name) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| */ | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										680
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										680
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,680 @@ | ||||
| /* | ||||
|  * 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 "lib.h" | ||||
| #include "metadata.h" | ||||
| #include "display.h" | ||||
| #include "activate.h" | ||||
| #include "toolcontext.h" | ||||
|  | ||||
| #define SIZE_BUF 128 | ||||
|  | ||||
| static struct { | ||||
| 	alloc_policy_t alloc; | ||||
| 	const char *str; | ||||
| } _policies[] = { | ||||
| 	{ | ||||
| 	ALLOC_NEXT_FREE, "next free"}, { | ||||
| 	ALLOC_CONTIGUOUS, "contiguous"}, { | ||||
| 	ALLOC_DEFAULT, "next free (default)"} | ||||
| }; | ||||
|  | ||||
| static struct { | ||||
| 	segment_type_t segtype; | ||||
| 	const char *str; | ||||
| } _segtypes[] = { | ||||
| 	{ | ||||
| 	SEG_STRIPED, "striped"}, { | ||||
| 	SEG_MIRRORED, "mirror"}, { | ||||
| 	SEG_SNAPSHOT, "snapshot"} | ||||
| }; | ||||
|  | ||||
| static int _num_policies = sizeof(_policies) / sizeof(*_policies); | ||||
| static int _num_segtypes = sizeof(_segtypes) / sizeof(*_segtypes); | ||||
|  | ||||
| uint64_t units_to_bytes(const char *units, char *unit_type) | ||||
| { | ||||
| 	char *ptr = NULL; | ||||
| 	uint64_t v; | ||||
|  | ||||
| 	if (isdigit(*units)) { | ||||
| 		v = (uint64_t) strtod(units, &ptr); | ||||
| 		if (ptr == units) | ||||
| 			return 0; | ||||
| 		units = ptr; | ||||
| 	} else | ||||
| 		v = 1; | ||||
|  | ||||
| 	if (v == 1) | ||||
| 		*unit_type = *units; | ||||
| 	else | ||||
| 		*unit_type = 'U'; | ||||
|  | ||||
| 	switch (*units) { | ||||
| 	case 'h': | ||||
| 	case 'H': | ||||
| 		v = UINT64_C(1); | ||||
| 		*unit_type = *units; | ||||
| 		break; | ||||
| 	case 's': | ||||
| 		v *= SECTOR_SIZE; | ||||
| 		break; | ||||
| 	case 'b': | ||||
| 	case 'B': | ||||
| 		v *= UINT64_C(1); | ||||
| 		break; | ||||
| #define KILO UINT64_C(1024) | ||||
| 	case 'k': | ||||
| 		v *= KILO; | ||||
| 		break; | ||||
| 	case 'm': | ||||
| 		v *= KILO * KILO; | ||||
| 		break; | ||||
| 	case 'g': | ||||
| 		v *= KILO * KILO * KILO; | ||||
| 		break; | ||||
| 	case 't': | ||||
| 		v *= KILO * KILO * KILO * KILO; | ||||
| 		break; | ||||
| #undef KILO | ||||
| #define KILO UINT64_C(1000) | ||||
| 	case 'K': | ||||
| 		v *= KILO; | ||||
| 		break; | ||||
| 	case 'M': | ||||
| 		v *= KILO * KILO; | ||||
| 		break; | ||||
| 	case 'G': | ||||
| 		v *= KILO * KILO * KILO; | ||||
| 		break; | ||||
| 	case 'T': | ||||
| 		v *= KILO * KILO * KILO * KILO; | ||||
| 		break; | ||||
| #undef KILO | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (*(units + 1)) | ||||
| 		return 0; | ||||
|  | ||||
| 	return v; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| const char *get_segtype_string(segment_type_t segtype) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < _num_segtypes; i++) | ||||
| 		if (_segtypes[i].segtype == segtype) | ||||
| 			return _segtypes[i].str; | ||||
|  | ||||
| 	return "unknown"; | ||||
| } | ||||
|  | ||||
| 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_error("Unrecognised allocation policy - using default"); | ||||
| 	return ALLOC_DEFAULT; | ||||
| } | ||||
|  | ||||
| segment_type_t get_segtype_from_string(const char *str) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < _num_segtypes; i++) | ||||
| 		if (!strcmp(_segtypes[i].str, str)) | ||||
| 			return _segtypes[i].segtype; | ||||
|  | ||||
| 	log_error("Unrecognised segment type - using default (striped)"); | ||||
| 	return SEG_STRIPED; | ||||
| } | ||||
|  | ||||
| const char *display_size(struct cmd_context *cmd, uint64_t size, size_len_t sl) | ||||
| { | ||||
| 	int s; | ||||
| 	int suffix = 1; | ||||
| 	uint64_t byte = UINT64_C(0); | ||||
| 	uint64_t units = UINT64_C(1024); | ||||
| 	char *size_buf = NULL; | ||||
| 	const char *size_str[][3] = { | ||||
| 		{" Terabyte", " TB", "T"}, | ||||
| 		{" Gigabyte", " GB", "G"}, | ||||
| 		{" Megabyte", " MB", "M"}, | ||||
| 		{" Kilobyte", " KB", "K"}, | ||||
| 		{"", "", ""}, | ||||
| 		{" Byte    ", " B ", "B"}, | ||||
| 		{" Units   ", " Un", "U"}, | ||||
| 		{" Sectors ", " Se", "S"}, | ||||
| 		{"         ", "   ", " "}, | ||||
| 	}; | ||||
|  | ||||
| 	if (!(size_buf = pool_alloc(cmd->mem, SIZE_BUF))) { | ||||
| 		log_error("no memory for size display buffer"); | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	suffix = cmd->current_settings.suffix; | ||||
|  | ||||
| 	for (s = 0; s < 8; s++) | ||||
| 		if (toupper((int) cmd->current_settings.unit_type) == | ||||
| 		    *size_str[s][2]) | ||||
| 			break; | ||||
|  | ||||
| 	if (size == UINT64_C(0)) { | ||||
| 		sprintf(size_buf, "0%s", suffix ? size_str[s][sl] : ""); | ||||
| 		return size_buf; | ||||
| 	} | ||||
|  | ||||
| 	if (s < 8) { | ||||
| 		byte = cmd->current_settings.unit_factor; | ||||
| 		size *= UINT64_C(1024); | ||||
| 	} else { | ||||
| 		suffix = 1; | ||||
| 		if (cmd->current_settings.unit_type == 'H') | ||||
| 			units = UINT64_C(1000); | ||||
| 		else | ||||
| 			units = UINT64_C(1024); | ||||
| 		byte = units * units * units; | ||||
| 		s = 0; | ||||
| 		while (size_str[s] && size < byte) | ||||
| 			s++, byte /= units; | ||||
| 	} | ||||
|  | ||||
| 	snprintf(size_buf, SIZE_BUF - 1, "%.2f%s", (float) size / byte, | ||||
| 		 suffix ? size_str[s][sl] : ""); | ||||
|  | ||||
| 	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:%" PRIu32 ":%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; | ||||
| } | ||||
|  | ||||
| /* FIXME Include label fields */ | ||||
| void pvdisplay_full(struct cmd_context *cmd, struct physical_volume *pv, | ||||
| 		    void *handle) | ||||
| { | ||||
| 	char uuid[64]; | ||||
| 	const char *size; | ||||
|  | ||||
| 	uint32_t pe_free; | ||||
|  | ||||
| 	if (!pv) | ||||
| 		return; | ||||
|  | ||||
| 	if (!id_write_format(&pv->id, uuid, sizeof(uuid))) { | ||||
| 		stack; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	log_print("--- %sPhysical volume ---", pv->pe_size ? "" : "NEW "); | ||||
| 	log_print("PV Name               %s", dev_name(pv->dev)); | ||||
| 	log_print("VG Name               %s%s", pv->vg_name, | ||||
| 		  pv->status & EXPORTED_VG ? " (exported)" : ""); | ||||
|  | ||||
| 	size = display_size(cmd, (uint64_t) pv->size / 2, SIZE_SHORT); | ||||
| 	if (pv->pe_size && pv->pe_count) { | ||||
|  | ||||
| /******** FIXME display LVM on-disk data size | ||||
| 		size2 = display_size(pv->size / 2, SIZE_SHORT); | ||||
| ********/ | ||||
|  | ||||
| 		log_print("PV Size               %s" " / not usable %s",	/*  [LVM: %s]", */ | ||||
| 			  size, display_size(cmd, | ||||
| 					     (pv->size - | ||||
| 					      pv->pe_count * pv->pe_size) / 2, | ||||
| 					     SIZE_SHORT)); | ||||
|  | ||||
| 	} else | ||||
| 		log_print("PV Size               %s", size); | ||||
|  | ||||
| 	/* PV number not part of LVM2 design | ||||
| 	   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"); | ||||
|  | ||||
| 	/* LV count is no longer available when displaying PV | ||||
| 	   log_print("Cur LV                %u", vg->lv_count); | ||||
| 	 */ | ||||
| 	log_print("PE Size (KByte)       %" PRIu32, pv->pe_size / 2); | ||||
| 	log_print("Total PE              %u", pv->pe_count); | ||||
| 	log_print("Free PE               %" PRIu32, pe_free); | ||||
| 	log_print("Allocated PE          %u", pv->pe_alloc_count); | ||||
| 	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, void *handle) | ||||
| { | ||||
| 	char uuid[64]; | ||||
|  | ||||
| 	if (!pv) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!id_write_format(&pv->id, uuid, sizeof(uuid))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_print("PV Name               %s     ", dev_name(pv->dev)); | ||||
| 	/* FIXME  pv->pv_number); */ | ||||
| 	log_print("PV UUID               %s", *uuid ? uuid : "none"); | ||||
| 	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 lvinfo 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_CONTIGUOUS ? 2 : 0), lv->read_ahead, | ||||
| 		  inkernel ? info.major : -1, inkernel ? info.minor : -1); | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv, | ||||
| 		   void *handle) | ||||
| { | ||||
| 	struct lvinfo info; | ||||
| 	int inkernel, snap_active; | ||||
| 	char uuid[64]; | ||||
| 	struct snapshot *snap = NULL; | ||||
| 	struct list *slh, *snaplist; | ||||
| 	float snap_percent;	/* fused, fsize; */ | ||||
|  | ||||
| 	if (!id_write_format(&lv->lvid.id[1], uuid, sizeof(uuid))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	inkernel = lv_info(lv, &info) && info.exists; | ||||
|  | ||||
| 	log_print("--- Logical volume ---"); | ||||
|  | ||||
| 	log_print("LV Name                %s%s/%s", lv->vg->cmd->dev_dir, | ||||
| 		  lv->vg->name, lv->name); | ||||
| 	log_print("VG Name                %s", lv->vg->name); | ||||
|  | ||||
| 	log_print("LV UUID                %s", uuid); | ||||
|  | ||||
| 	log_print("LV Write Access        %s", | ||||
| 		  (lv->status & LVM_WRITE) ? "read/write" : "read only"); | ||||
|  | ||||
| 	if (lv_is_origin(lv)) { | ||||
| 		log_print("LV snapshot status     source of"); | ||||
|  | ||||
| 		snaplist = find_snapshots(lv); | ||||
| 		list_iterate(slh, snaplist) { | ||||
| 			snap = list_item(slh, struct snapshot_list)->snapshot; | ||||
| 			snap_active = lv_snapshot_percent(snap->cow, | ||||
| 							  &snap_percent); | ||||
| 			if (!snap_active || snap_percent < 0 || | ||||
| 			    snap_percent >= 100) | ||||
| 				snap_active = 0; | ||||
| 			log_print("                       %s%s/%s [%s]", | ||||
| 				  lv->vg->cmd->dev_dir, lv->vg->name, | ||||
| 				  snap->cow->name, | ||||
| 				  (snap_active > 0) ? "active" : "INACTIVE"); | ||||
| 		} | ||||
| 		snap = NULL; | ||||
| 	} else if ((snap = find_cow(lv))) { | ||||
| 		snap_active = lv_snapshot_percent(lv, &snap_percent); | ||||
| 		if (!snap_active || snap_percent < 0 || snap_percent >= 100) | ||||
| 			snap_active = 0; | ||||
| 		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 ? "" : "NOT "); | ||||
|  | ||||
| /********* FIXME lv_number | ||||
|     log_print("LV #                   %u", lv->lv_number + 1); | ||||
| ************/ | ||||
|  | ||||
| 	if (inkernel) | ||||
| 		log_print("# open                 %u", info.open_count); | ||||
|  | ||||
| 	log_print("LV Size                %s", | ||||
| 		  display_size(cmd, | ||||
| 			       snap ? snap->origin->size / 2 : lv->size / 2, | ||||
| 			       SIZE_SHORT)); | ||||
|  | ||||
| 	log_print("Current LE             %u", | ||||
| 		  snap ? snap->origin->le_count : lv->le_count); | ||||
|  | ||||
| /********** FIXME allocation | ||||
|     log_print("Allocated LE           %u", lv->allocated_le); | ||||
| **********/ | ||||
|  | ||||
| 	log_print("Segments               %u", list_size(&lv->segments)); | ||||
|  | ||||
| /********* FIXME Stripes & stripesize for each segment | ||||
| 	log_print("Stripe size (KByte)    %u", lv->stripesize / 2); | ||||
| ***********/ | ||||
|  | ||||
| 	if (snap) { | ||||
| 		if (snap_percent == -1) | ||||
| 			snap_percent = 100; | ||||
|  | ||||
| 		log_print("Snapshot chunk size    %s", | ||||
| 			  display_size(cmd, (uint64_t) snap->chunk_size / 2, | ||||
| 				       SIZE_SHORT)); | ||||
|  | ||||
| /* | ||||
| 	size = display_size(lv->size / 2, SIZE_SHORT); | ||||
| 	sscanf(size, "%f", &fsize); | ||||
| 	fused = fsize * snap_percent / 100; | ||||
| */ | ||||
| 		log_print("Allocated to snapshot  %.2f%% ",	/* [%.2f/%s]", */ | ||||
| 			  snap_percent);	/*, fused, size); */ | ||||
| 		/* dbg_free(size); */ | ||||
| 	} | ||||
|  | ||||
| /********** FIXME Snapshot | ||||
| 	size = ??? | ||||
| 	log_print("Allocated to COW-table %s", size); | ||||
| 	dbg_free(size); | ||||
|     } | ||||
| ******************/ | ||||
|  | ||||
| 	log_print("Allocation             %s", get_alloc_string(lv->alloc)); | ||||
| 	log_print("Read ahead sectors     %u", lv->read_ahead); | ||||
|  | ||||
| 	if (lv->status & FIXED_MINOR) { | ||||
| 		if (lv->major >= 0) | ||||
| 			log_print("Persistent major       %d", lv->major); | ||||
| 		log_print("Persistent minor       %d", lv->minor); | ||||
| 	} | ||||
|  | ||||
| 	if (inkernel) | ||||
| 		log_print("Block device           %d:%d", info.major, | ||||
| 			  info.minor); | ||||
|  | ||||
| 	log_print(" "); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void _display_stripe(struct lv_segment *seg, uint32_t s, const char *pre) | ||||
| { | ||||
| 	switch (seg->area[s].type) { | ||||
| 	case AREA_PV: | ||||
| 		log_print("%sPhysical volume\t%s", pre, | ||||
| 			  seg->area[s].u.pv.pv ? | ||||
| 			  dev_name(seg->area[s].u.pv.pv->dev) : "Missing"); | ||||
|  | ||||
| 		if (seg->area[s].u.pv.pv) | ||||
| 			log_print("%sPhysical extents\t%d to %d", pre, | ||||
| 				  seg->area[s].u.pv.pe, | ||||
| 				  seg->area[s].u.pv.pe + seg->area_len - 1); | ||||
| 		break; | ||||
| 	case AREA_LV: | ||||
| 		log_print("%sLogical volume\t%s", pre, | ||||
| 			  seg->area[s].u.lv.lv ? | ||||
| 			  seg->area[s].u.lv.lv->name : "Missing"); | ||||
|  | ||||
| 		if (seg->area[s].u.lv.lv) | ||||
| 			log_print("%sLogical extents\t%d to %d", pre, | ||||
| 				  seg->area[s].u.lv.le, | ||||
| 				  seg->area[s].u.lv.le + seg->area_len - 1); | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int lvdisplay_segments(struct logical_volume *lv) | ||||
| { | ||||
| 	uint32_t s; | ||||
| 	struct list *segh; | ||||
| 	struct lv_segment *seg; | ||||
|  | ||||
| 	log_print("--- Segments ---"); | ||||
|  | ||||
| 	list_iterate(segh, &lv->segments) { | ||||
| 		seg = list_item(segh, struct lv_segment); | ||||
|  | ||||
| 		log_print("Logical extent %u to %u:", | ||||
| 			  seg->le, seg->le + seg->len - 1); | ||||
|  | ||||
| 		if (seg->type == SEG_STRIPED && seg->area_count == 1) | ||||
| 			log_print("  Type\t\tlinear"); | ||||
| 		else | ||||
| 			log_print("  Type\t\t%s", | ||||
| 				  get_segtype_string(seg->type)); | ||||
|  | ||||
| 		switch (seg->type) { | ||||
| 		case SEG_STRIPED: | ||||
| 			if (seg->area_count == 1) | ||||
| 				_display_stripe(seg, 0, "  "); | ||||
| 			else { | ||||
| 				log_print("  Stripes\t\t%u", seg->area_count); | ||||
| 				log_print("  Stripe size\t\t%u KB", | ||||
| 					  seg->stripe_size / 2); | ||||
|  | ||||
| 				for (s = 0; s < seg->area_count; s++) { | ||||
| 					log_print("  Stripe %d:", s); | ||||
| 					_display_stripe(seg, s, "    "); | ||||
| 				} | ||||
| 			} | ||||
| 			log_print(" "); | ||||
| 			break; | ||||
| 		case SEG_SNAPSHOT: | ||||
| 			break; | ||||
| 		case SEG_MIRRORED: | ||||
| 			log_print("  Mirrors\t\t%u", seg->area_count); | ||||
| 			log_print("  Mirror size\t\t%u", seg->area_len); | ||||
| 			log_print("  Mirror original:"); | ||||
| 			_display_stripe(seg, 0, "    "); | ||||
| 			log_print("  Mirror destination:"); | ||||
| 			_display_stripe(seg, 1, "    "); | ||||
| 			log_print(" "); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	log_print(" "); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void vgdisplay_extents(struct volume_group *vg) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| void vgdisplay_full(struct volume_group *vg) | ||||
| { | ||||
| 	uint32_t access; | ||||
| 	uint32_t active_pvs; | ||||
| 	char uuid[64]; | ||||
|  | ||||
| 	if (vg->status & PARTIAL_VG) | ||||
| 		active_pvs = list_size(&vg->pvs); | ||||
| 	else | ||||
| 		active_pvs = vg->pv_count; | ||||
|  | ||||
| 	log_print("--- Volume group ---"); | ||||
| 	log_print("VG Name               %s", vg->name); | ||||
| 	log_print("System ID             %s", vg->system_id); | ||||
| 	log_print("Format                %s", vg->fid->fmt->name); | ||||
| 	if (vg->fid->fmt->features & FMT_MDAS) { | ||||
| 		log_print("Metadata Areas        %d", | ||||
| 			  list_size(&vg->fid->metadata_areas)); | ||||
| 		log_print("Metadata Sequence No  %d", vg->seqno); | ||||
| 	} | ||||
| 	access = vg->status & (LVM_READ | LVM_WRITE); | ||||
| 	log_print("VG Access             %s%s%s%s", | ||||
| 		  access == (LVM_READ | LVM_WRITE) ? "read/write" : "", | ||||
| 		  access == LVM_READ ? "read" : "", | ||||
| 		  access == LVM_WRITE ? "write" : "", | ||||
| 		  access == 0 ? "error" : ""); | ||||
| 	log_print("VG Status             %s%sresizable", | ||||
| 		  vg->status & EXPORTED_VG ? "exported/" : "", | ||||
| 		  vg->status & RESIZEABLE_VG ? "" : "NOT "); | ||||
| 	/* vg number not part of LVM2 design | ||||
| 	   log_print ("VG #                  %u\n", vg->vg_number); | ||||
| 	 */ | ||||
| 	if (vg->status & CLUSTERED) { | ||||
| 		log_print("Clustered             yes"); | ||||
| 		log_print("Shared                %s", | ||||
| 			  vg->status & SHARED ? "yes" : "no"); | ||||
| 	} | ||||
| 	log_print("MAX LV                %u", vg->max_lv); | ||||
| 	log_print("Cur LV                %u", vg->lv_count); | ||||
| 	log_print("Open LV               %u", lvs_in_vg_opened(vg)); | ||||
| /****** FIXME Max LV Size | ||||
|       log_print ( "MAX LV Size           %s", | ||||
|                ( s1 = display_size ( LVM_LV_SIZE_MAX(vg) / 2, SIZE_SHORT))); | ||||
|       free ( s1); | ||||
| *********/ | ||||
| 	log_print("Max PV                %u", vg->max_pv); | ||||
| 	log_print("Cur PV                %u", vg->pv_count); | ||||
| 	log_print("Act PV                %u", active_pvs); | ||||
|  | ||||
| 	log_print("VG Size               %s", | ||||
| 		  display_size(vg->cmd, | ||||
| 			       (uint64_t) vg->extent_count * (vg->extent_size / | ||||
| 							      2), SIZE_SHORT)); | ||||
|  | ||||
| 	log_print("PE Size               %s", | ||||
| 		  display_size(vg->cmd, (uint64_t) vg->extent_size / 2, | ||||
| 			       SIZE_SHORT)); | ||||
|  | ||||
| 	log_print("Total PE              %u", vg->extent_count); | ||||
|  | ||||
| 	log_print("Alloc PE / Size       %u / %s", | ||||
| 		  vg->extent_count - vg->free_count, display_size(vg->cmd, | ||||
| 								  ((uint64_t) | ||||
| 								   vg-> | ||||
| 								   extent_count | ||||
| 								   - | ||||
| 								   vg-> | ||||
| 								   free_count) * | ||||
| 								  (vg-> | ||||
| 								   extent_size / | ||||
| 								   2), | ||||
| 								  SIZE_SHORT)); | ||||
|  | ||||
| 	log_print("Free  PE / Size       %u / %s", vg->free_count, | ||||
| 		  display_size(vg->cmd, | ||||
| 			       (uint64_t) vg->free_count * (vg->extent_size / | ||||
| 							    2), SIZE_SHORT)); | ||||
|  | ||||
| 	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) | ||||
| { | ||||
| 	log_print("\"%s\" %-9s [%-9s used / %s free]", vg->name, | ||||
| /********* FIXME if "open" print "/used" else print "/idle"???  ******/ | ||||
| 		  display_size(vg->cmd, (uint64_t) vg->extent_count * | ||||
| 			       vg->extent_size / 2, SIZE_SHORT), | ||||
| 		  display_size(vg->cmd, | ||||
| 			       ((uint64_t) vg->extent_count - | ||||
| 				vg->free_count) * vg->extent_size / 2, | ||||
| 			       SIZE_SHORT), display_size(vg->cmd, | ||||
| 							 (uint64_t) vg-> | ||||
| 							 free_count * | ||||
| 							 vg->extent_size / 2, | ||||
| 							 SIZE_SHORT)); | ||||
| 	return; | ||||
| } | ||||
							
								
								
									
										64
									
								
								lib/display/display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								lib/display/display.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
|  * 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_UNIT = 2 } size_len_t; | ||||
|  | ||||
| uint64_t units_to_bytes(const char *units, char *unit_type); | ||||
|  | ||||
| /* Specify size in KB */ | ||||
| const char *display_size(struct cmd_context *cmd, uint64_t size, size_len_t sl); | ||||
| char *display_uuid(char *uuidstr); | ||||
|  | ||||
| void pvdisplay_colons(struct physical_volume *pv); | ||||
| void pvdisplay_full(struct cmd_context *cmd, struct physical_volume *pv, | ||||
| 		    void *handle); | ||||
| int pvdisplay_short(struct cmd_context *cmd, struct volume_group *vg, | ||||
| 		    struct physical_volume *pv, void *handle); | ||||
|  | ||||
| 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 *handle); | ||||
|  | ||||
| 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); | ||||
|  | ||||
| /* | ||||
|  * Allocation policy display conversion routines. | ||||
|  */ | ||||
| const char *get_alloc_string(alloc_policy_t alloc); | ||||
| alloc_policy_t get_alloc_from_string(const char *str); | ||||
|  | ||||
| /* | ||||
|  * Segment type display conversion routines. | ||||
|  */ | ||||
| segment_type_t get_segtype_from_string(const char *str); | ||||
| const char *get_segtype_string(segment_type_t segtype); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										71
									
								
								lib/filters/filter-composite.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lib/filters/filter-composite.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "filter-composite.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++; | ||||
| 	} | ||||
|  | ||||
| 	log_debug("Using %s", dev_name(dev)); | ||||
|  | ||||
| 	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 | ||||
							
								
								
									
										268
									
								
								lib/filters/filter-persistent.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								lib/filters/filter-persistent.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,268 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "config.h" | ||||
| #include "dev-cache.h" | ||||
| #include "hash.h" | ||||
| #include "filter-persistent.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); | ||||
|  | ||||
| 	if (!(pf->devices = hash_create(128))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int persistent_filter_wipe(struct dev_filter *f) | ||||
| { | ||||
| 	struct pfilter *pf = (struct pfilter *) f->private; | ||||
|  | ||||
| 	hash_wipe(pf->devices); | ||||
| 	/* Trigger complete device scan */ | ||||
| 	dev_cache_scan(1); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_array(struct pfilter *pf, struct config_tree *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); | ||||
| 		/* Populate dev_cache ourselves */ | ||||
| 		dev_cache_get(cv->v.str, NULL); | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int persistent_filter_load(struct dev_filter *f) | ||||
| { | ||||
| 	struct pfilter *pf = (struct pfilter *) f->private; | ||||
|  | ||||
| 	int r = 0; | ||||
| 	struct config_tree *cf; | ||||
|  | ||||
| 	if (!(cf = create_config_tree())) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!read_config_file(cf, pf->file)) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	_read_array(pf, cf, "persistent_filter_cache/valid_devices", | ||||
| 		    PF_GOOD_DEVICE); | ||||
| 	/* We don't gain anything by holding invalid devices */ | ||||
| 	/* _read_array(pf, cf, "persistent_filter_cache/invalid_devices", | ||||
| 	   PF_BAD_DEVICE); */ | ||||
|  | ||||
| 	/* Did we find anything? */ | ||||
| 	if (hash_get_num_entries(pf->devices)) { | ||||
| 		/* We populated dev_cache ourselves */ | ||||
| 		dev_cache_scan(0); | ||||
| 		r = 1; | ||||
| 	} | ||||
|  | ||||
| 	log_very_verbose("Loaded persistent filter cache from %s", pf->file); | ||||
|  | ||||
|       out: | ||||
| 	destroy_config_tree(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; | ||||
| 	} | ||||
| 	if (!dev_cache_has_scanned()) { | ||||
| 		log_very_verbose("Device cache incomplete - 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); | ||||
| 	/* We don't gain anything by remembering invalid devices */ | ||||
| 	/* _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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (l == PF_BAD_DEVICE) { | ||||
| 		log_debug("%s: Skipping (cached)", dev_name(dev)); | ||||
| 		return 0; | ||||
| 	} else | ||||
| 		return 1; | ||||
| } | ||||
|  | ||||
| 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 | ||||
							
								
								
									
										229
									
								
								lib/filters/filter-regex.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								lib/filters/filter-regex.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "pool.h" | ||||
| #include "filter-regex.h" | ||||
| #include "matcher.h" | ||||
| #include "device.h" | ||||
| #include "bitset.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 ix) | ||||
| { | ||||
| 	char sep, *r, *ptr; | ||||
|  | ||||
| 	/* | ||||
| 	 * is this an accept or reject pattern | ||||
| 	 */ | ||||
| 	switch (*pat) { | ||||
| 	case 'a': | ||||
| 		bit_set(accept, ix); | ||||
| 		break; | ||||
|  | ||||
| 	case 'r': | ||||
| 		bit_clear(accept, ix); | ||||
| 		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[ix] = r; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _build_matcher(struct rfilter *rf, struct config_value *val) | ||||
| { | ||||
| 	struct pool *scratch; | ||||
| 	struct config_value *v; | ||||
| 	char **regex; | ||||
| 	unsigned count = 0; | ||||
| 	int 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) { | ||||
| 					log_debug("%s: New preferred name", | ||||
| 						  sl->str); | ||||
| 					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 | ||||
							
								
								
									
										230
									
								
								lib/filters/filter.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								lib/filters/filter.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,230 @@ | ||||
| /* | ||||
|  * 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 "lib.h" | ||||
| #include "dev-cache.h" | ||||
| #include "filter.h" | ||||
| #include "lvm-string.h" | ||||
| #include "config.h" | ||||
|  | ||||
| #include <dirent.h> | ||||
| #include <unistd.h> | ||||
| #include <ctype.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| #define NUMBER_OF_MAJORS 256 | ||||
|  | ||||
| typedef struct { | ||||
| 	const char *name; | ||||
| 	const int max_partitions; | ||||
| } device_info_t; | ||||
|  | ||||
| static int _md_major = -1; | ||||
|  | ||||
| int md_major(void) | ||||
| { | ||||
| 	return _md_major; | ||||
| } | ||||
|  | ||||
| /* This list can be supplemented with devices/types in the config file */ | ||||
| static const 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 _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)])) { | ||||
| 		log_debug("%s: Skipping: Unrecognised LVM device type %" | ||||
| 			  PRIu64, name, (uint64_t) MAJOR(dev->dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Check it's accessible */ | ||||
| 	if ((fd = open(name, O_RDONLY)) < 0) { | ||||
| 		log_debug("%s: Skipping: open failed: %s", name, | ||||
| 			  strerror(errno)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	close(fd); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int *_scan_proc_dev(const char *proc, struct config_node *cn) | ||||
| { | ||||
| 	char line[80]; | ||||
| 	char proc_devices[PATH_MAX]; | ||||
| 	FILE *pd = NULL; | ||||
| 	int i, j = 0; | ||||
| 	int line_maj = 0; | ||||
| 	int blocksection = 0; | ||||
| 	size_t dev_len = 0; | ||||
| 	struct config_value *cv; | ||||
| 	int *max_partitions_by_major; | ||||
| 	char *name; | ||||
|  | ||||
| 	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 (!*proc) { | ||||
| 		log_verbose("No proc filesystem found: using all block device " | ||||
| 			    "types"); | ||||
| 		for (i = 0; i < NUMBER_OF_MAJORS; i++) | ||||
| 			max_partitions_by_major[i] = 1; | ||||
| 		return max_partitions_by_major; | ||||
| 	} | ||||
|  | ||||
| 	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; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (max_partitions_by_major[line_maj] || !cn) | ||||
| 			continue; | ||||
|  | ||||
| 		/* Check devices/types for local variations */ | ||||
| 		for (cv = cn->v; cv; cv = cv->next) { | ||||
| 			if (cv->type != CFG_STRING) { | ||||
| 				log_error("Expecting string in devices/types " | ||||
| 					  "in config file"); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			dev_len = strlen(cv->v.str); | ||||
| 			name = cv->v.str; | ||||
| 			cv = cv->next; | ||||
| 			if (!cv || cv->type != CFG_INT) { | ||||
| 				log_error("Max partition count missing for %s " | ||||
| 					  "in devices/types in config file", | ||||
| 					  name); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			if (!cv->v.i) { | ||||
| 				log_error("Zero partition count invalid for " | ||||
| 					  "%s in devices/types in config file", | ||||
| 					  name); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			if (dev_len <= strlen(line + i) && | ||||
| 			    !strncmp(name, line + i, dev_len) && | ||||
| 			    (line_maj < NUMBER_OF_MAJORS)) { | ||||
| 				max_partitions_by_major[line_maj] = cv->v.i; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	fclose(pd); | ||||
| 	return max_partitions_by_major; | ||||
| } | ||||
|  | ||||
| struct dev_filter *lvm_type_filter_create(const char *proc, | ||||
| 					  struct config_node *cn) | ||||
| { | ||||
| 	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, cn))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return f; | ||||
| } | ||||
|  | ||||
| void lvm_type_filter_destroy(struct dev_filter *f) | ||||
| { | ||||
| 	dbg_free(f->private); | ||||
| 	dbg_free(f); | ||||
| 	return; | ||||
| } | ||||
							
								
								
									
										43
									
								
								lib/filters/filter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								lib/filters/filter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /* | ||||
|  * 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 | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #ifdef linux | ||||
| #  include <linux/kdev_t.h> | ||||
| #else | ||||
| #  define MAJOR(x) major((x)) | ||||
| #  define MINOR(x) minor((x)) | ||||
| #  define MKDEV(x,y) makedev((x),(y)) | ||||
| #endif | ||||
|  | ||||
| struct dev_filter *lvm_type_filter_create(const char *proc, | ||||
| 					  struct config_node *cn); | ||||
|  | ||||
| void lvm_type_filter_destroy(struct dev_filter *f); | ||||
|  | ||||
| int md_major(void); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										7
									
								
								lib/format1/.export.sym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								lib/format1/.export.sym
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| Base {  | ||||
| 	global: | ||||
| 		init_format; | ||||
| 	local: | ||||
| 		*; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										31
									
								
								lib/format1/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/format1/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| # | ||||
| # Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
| # | ||||
| # This file is released under the LGPL. | ||||
| # | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| VPATH = @srcdir@ | ||||
|  | ||||
| SOURCES=\ | ||||
| 	disk-rep.c \ | ||||
| 	format1.c \ | ||||
| 	import-export.c \ | ||||
| 	import-extents.c \ | ||||
| 	layout.c \ | ||||
| 	lvm1-label.c \ | ||||
| 	vg_number.c | ||||
|  | ||||
| TARGETS=liblvm2format1.so | ||||
|  | ||||
| include ../../make.tmpl | ||||
|  | ||||
|  | ||||
| install: liblvm2format1.so | ||||
| 	$(INSTALL) -D -o $(OWNER) -g $(GROUP) -m 555 $(STRIP) $< \ | ||||
| 		$(libdir)/liblvm2format1.so.$(LIB_VERSION) | ||||
| 	$(LN_S) -f liblvm2format1.so.$(LIB_VERSION) $(libdir)/liblvm2format1.so | ||||
|  | ||||
| .PHONY: install | ||||
|  | ||||
							
								
								
									
										676
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										676
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,676 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "disk-rep.h" | ||||
| #include "pool.h" | ||||
| #include "xlate.h" | ||||
| #include "filter.h" | ||||
| #include "lvmcache.h" | ||||
|  | ||||
| #include <fcntl.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, uint32_t 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_SHIFT); | ||||
| 		break; | ||||
|  | ||||
| 	case 2: | ||||
| 		pvd->version = 1; | ||||
| 		pe_start = pvd->pe_start << SECTOR_SHIFT; | ||||
| 		pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base; | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_pvd(struct device *dev, struct pv_disk *pvd) | ||||
| { | ||||
| 	if (!dev_read(dev, UINT64_C(0), sizeof(*pvd), 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 LVM1 PV identifier", | ||||
| 				 dev_name(dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!_munge_formats(pvd)) { | ||||
| 		log_very_verbose("format1: Unknown metadata version %d " | ||||
| 				 "found on %s", pvd->version, dev_name(dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk) | ||||
| { | ||||
| 	if (!dev_read(dev, pos, sizeof(*disk), disk)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_lvd(disk); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_vgd(struct disk_list *data) | ||||
| { | ||||
| 	struct vg_disk *vgd = &data->vgd; | ||||
| 	uint64_t pos = data->pvd.vg_on_disk.base; | ||||
| 	if (!dev_read(data->dev, pos, sizeof(*vgd), 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]; | ||||
| 	uint64_t pos = data->pvd.pv_uuidlist_on_disk.base; | ||||
| 	uint64_t 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)) | ||||
| 			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) | ||||
| { | ||||
| 	unsigned int i, read = 0; | ||||
| 	uint64_t 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); | ||||
| 	uint64_t pos = data->pvd.pe_on_disk.base; | ||||
|  | ||||
| 	if (!extents) | ||||
| 		fail; | ||||
|  | ||||
| 	if (!dev_read(data->dev, pos, len, extents)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_extents(extents, data->pvd.pe_total); | ||||
| 	data->extents = extents; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /*  | ||||
|  * If exported, remove "PV_EXP" from end of VG name  | ||||
|  */ | ||||
| void munge_exported_vg(struct pv_disk *pvd) | ||||
| { | ||||
| 	int l; | ||||
| 	size_t s; | ||||
|  | ||||
| 	/* Return if PV not in a VG */ | ||||
| 	if ((!*pvd->vg_name)) | ||||
| 		return; | ||||
| 	/* FIXME also check vgd->status & VG_EXPORTED? */ | ||||
|  | ||||
| 	l = strlen(pvd->vg_name); | ||||
| 	s = sizeof(EXPORTED_TAG); | ||||
| 	if (!strncmp(pvd->vg_name + l - s + 1, EXPORTED_TAG, s)) | ||||
| 		pvd->vg_name[l - s + 1] = '\0'; | ||||
|  | ||||
| 	pvd->pv_status |= VG_EXPORTED; | ||||
| } | ||||
|  | ||||
| static struct disk_list *__read_disk(const 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); | ||||
| 	struct lvmcache_info *info; | ||||
|  | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
| 	/* If VG is exported, set VG name back to the real name */ | ||||
| 	munge_exported_vg(&dl->pvd); | ||||
|  | ||||
| 	if (!(info = lvmcache_add(fmt->labeller, dl->pvd.pv_uuid, dev, | ||||
| 				  dl->pvd.vg_name, NULL))) | ||||
| 		stack; | ||||
| 	else { | ||||
| 		info->device_size = xlate32(dl->pvd.pv_size) << SECTOR_SHIFT; | ||||
| 		list_init(&info->mdas); | ||||
| 		info->status &= ~CACHE_INVALID; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * 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; | ||||
| 	} | ||||
|  | ||||
| 	/* 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(const struct format_type *fmt, struct device *dev, | ||||
| 			    struct pool *mem, const char *vg_name) | ||||
| { | ||||
| 	struct disk_list *r; | ||||
|  | ||||
| 	if (!dev_open(dev)) { | ||||
| 		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(const 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 *vgih; | ||||
| 	struct lvmcache_vginfo *vginfo; | ||||
|  | ||||
| 	/* Fast path if we already saw this VG and cached the list of PVs */ | ||||
| 	if (vg_name && (vginfo = vginfo_from_vgname(vg_name)) && | ||||
| 	    vginfo->infos.n) { | ||||
| 		list_iterate(vgih, &vginfo->infos) { | ||||
| 			dev = list_item(vgih, struct lvmcache_info)->dev; | ||||
| 			if (dev && !(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; | ||||
|  | ||||
| 		/* Failed */ | ||||
| 		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; | ||||
| 	uint64_t pos = data->pvd.vg_on_disk.base; | ||||
|  | ||||
| 	_xlate_vgd(vgd); | ||||
| 	if (!dev_write(data->dev, pos, sizeof(*vgd), vgd)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_vgd(vgd); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_uuids(struct disk_list *data) | ||||
| { | ||||
| 	struct uuid_list *ul; | ||||
| 	struct list *uh; | ||||
| 	uint64_t pos = data->pvd.pv_uuidlist_on_disk.base; | ||||
| 	uint64_t 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)) | ||||
| 			fail; | ||||
|  | ||||
| 		pos += NAME_LEN; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk) | ||||
| { | ||||
| 	_xlate_lvd(disk); | ||||
| 	if (!dev_write(dev, pos, sizeof(*disk), disk)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_lvd(disk); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_lvs(struct disk_list *data) | ||||
| { | ||||
| 	struct list *lvh; | ||||
| 	uint64_t 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; | ||||
| 	uint64_t pos = data->pvd.pe_on_disk.base; | ||||
|  | ||||
| 	_xlate_extents(extents, data->pvd.pe_total); | ||||
| 	if (!dev_write(data->dev, pos, len, extents)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_extents(extents, data->pvd.pe_total); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_pvd(struct disk_list *data) | ||||
| { | ||||
| 	char *buf; | ||||
| 	uint64_t pos = data->pvd.pv_on_disk.base; | ||||
| 	size_t 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)) { | ||||
| 		dbg_free(buf); | ||||
| 		fail; | ||||
| 	} | ||||
|  | ||||
| 	dbg_free(buf); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * assumes the device has been opened. | ||||
|  */ | ||||
| static int __write_all_pvd(const 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(const struct format_type *fmt, struct disk_list *data) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	if (!dev_open(data->dev)) { | ||||
| 		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(const 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; | ||||
| } | ||||
							
								
								
									
										235
									
								
								lib/format1/disk-rep.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								lib/format1/disk-rep.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | ||||
| /* | ||||
|  * 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 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_SHIFT)	/* 8 KB in sectors */ | ||||
| #define MAX_PE_SIZE	(16L * 1024L * (1024L >> SECTOR_SHIFT) * 1024L) | ||||
| #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; | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| 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; | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| 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; | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| 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 */ | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| struct pe_disk { | ||||
| 	uint16_t lv_num; | ||||
| 	uint16_t le_num; | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| 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, uint32_t extent_size, | ||||
| 			   uint32_t max_extent_count); | ||||
|  | ||||
| /* | ||||
|  * Low level io routines which read/write | ||||
|  * disk_lists. | ||||
|  */ | ||||
|  | ||||
| struct disk_list *read_disk(const struct format_type *fmt, struct device *dev, | ||||
| 			    struct pool *mem, const char *vg_name); | ||||
|  | ||||
| int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name, | ||||
| 		   struct dev_filter *filter, | ||||
| 		   struct pool *mem, struct list *results); | ||||
|  | ||||
| int write_disks(const 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); | ||||
|  | ||||
| int import_extents(struct pool *mem, struct volume_group *vg, | ||||
| 		   struct list *pvds); | ||||
| int export_extents(struct disk_list *dl, uint32_t lv_num, | ||||
| 		   struct logical_volume *lv, struct physical_volume *pv); | ||||
|  | ||||
| int import_pvs(const struct format_type *fmt, 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); | ||||
| void munge_exported_vg(struct pv_disk *pvd); | ||||
|  | ||||
| /* 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 | ||||
							
								
								
									
										567
									
								
								lib/format1/format1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										567
									
								
								lib/format1/format1.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,567 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "disk-rep.h" | ||||
| #include "pool.h" | ||||
| #include "hash.h" | ||||
| #include "limits.h" | ||||
| #include "list.h" | ||||
| #include "display.h" | ||||
| #include "toolcontext.h" | ||||
| #include "lvmcache.h" | ||||
| #include "lvm1-label.h" | ||||
| #include "format1.h" | ||||
|  | ||||
| #define FMT_LVM1_NAME "lvm1" | ||||
|  | ||||
| /* 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; | ||||
|  | ||||
| 	uint32_t pv_count = 0; | ||||
| 	uint32_t exported = 0; | ||||
| 	int first_time = 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 (first_time) { | ||||
| 			exported = dl->pvd.pv_status & VG_EXPORTED; | ||||
| 			first_time = 0; | ||||
| 			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 != first->vgd.pv_cur) { | ||||
| 		log_error("%d PV(s) found for VG %s: expected %d", | ||||
| 			  pv_count, first->pvd.vg_name, first->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->fmt, 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, | ||||
| 				     struct metadata_area *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, | ||||
| 		     struct metadata_area *mda) | ||||
| { | ||||
| 	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)); | ||||
|  | ||||
| 	lvmcache_update_vg(vg); | ||||
| 	pool_destroy(mem); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _pv_read(const struct format_type *fmt, const char *pv_name, | ||||
| 		    struct physical_volume *pv, struct list *mdas) | ||||
| { | ||||
| 	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", pv_name); | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dev = dev_cache_get(pv_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->fmt = fmt; | ||||
|  | ||||
| 	r = 1; | ||||
|  | ||||
|       out: | ||||
| 	pool_destroy(mem); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _pv_setup(const struct format_type *fmt, | ||||
| 		     uint64_t pe_start, uint32_t extent_count, | ||||
| 		     uint32_t extent_size, | ||||
| 		     int pvmetadatacopies, | ||||
| 		     uint64_t pvmetadatasize, struct list *mdas, | ||||
| 		     struct physical_volume *pv, struct volume_group *vg) | ||||
| { | ||||
| 	if (pv->size > MAX_PV_SIZE) | ||||
| 		pv->size--; | ||||
| 	if (pv->size > MAX_PV_SIZE) { | ||||
| 		log_error("Physical volumes cannot be bigger than %s", | ||||
| 			  display_size(fmt->cmd, (uint64_t) MAX_PV_SIZE / 2, | ||||
| 				       SIZE_SHORT)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Nothing more to do if extent size isn't provided */ | ||||
| 	if (!extent_size) | ||||
| 		return 1; | ||||
|  | ||||
| 	/* | ||||
| 	 * This works out pe_start and pe_count. | ||||
| 	 */ | ||||
| 	if (!calculate_extent_count(pv, extent_size, extent_count)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Retain existing extent locations exactly */ | ||||
| 	/* FIXME Relax this so a non-overlapping existing pe_start can also  | ||||
| 	 * be used in place of the calculated one */ | ||||
| 	if (((pe_start || extent_count) && (pe_start != pv->pe_start)) || | ||||
| 	    (extent_count && (extent_count != pv->pe_count))) { | ||||
| 		log_error("Metadata would overwrite physical extents"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static uint32_t _find_free_lvnum(struct logical_volume *lv) | ||||
| { | ||||
| 	int lvnum_used[MAX_LV]; | ||||
| 	uint32_t 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) { | ||||
| 		log_error("logical volumes cannot be larger than %s", | ||||
| 			  display_size(fid->fmt->cmd, max_size / 2, | ||||
| 				       SIZE_SHORT)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _pv_write(const struct format_type *fmt, struct physical_volume *pv, | ||||
| 		     struct list *mdas, int64_t sector) | ||||
| { | ||||
| 	struct pool *mem; | ||||
| 	struct disk_list *dl; | ||||
| 	struct list pvs; | ||||
| 	struct label *label; | ||||
| 	struct lvmcache_info *info; | ||||
|  | ||||
| 	if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev, | ||||
| 				  pv->vg_name, NULL))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	label = info->label; | ||||
| 	info->device_size = pv->size << SECTOR_SHIFT; | ||||
| 	info->fmt = fmt; | ||||
|  | ||||
| 	list_init(&info->mdas); | ||||
|  | ||||
| 	list_init(&pvs); | ||||
|  | ||||
| 	/* 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_SHIFT; | ||||
|  | ||||
| 	list_add(&pvs, &dl->list); | ||||
| 	if (!write_disks(fmt, &pvs)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	return 1; | ||||
|  | ||||
|       bad: | ||||
| 	pool_destroy(mem); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static 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) { | ||||
| 		log_error("Extent size must be between %s and %s", | ||||
| 			  display_size(fid->fmt->cmd, (uint64_t) MIN_PE_SIZE | ||||
| 				       / 2, | ||||
| 				       SIZE_SHORT), display_size(fid->fmt->cmd, | ||||
| 								 (uint64_t) | ||||
| 								 MAX_PE_SIZE | ||||
| 								 / 2, | ||||
| 								 SIZE_SHORT)); | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (vg->extent_size % MIN_PE_SIZE) { | ||||
| 		log_error("Extent size must be multiple of %s", | ||||
| 			  display_size(fid->fmt->cmd, | ||||
| 				       (uint64_t) MIN_PE_SIZE / 2, SIZE_SHORT)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Redundant? */ | ||||
| 	if (vg->extent_size & (vg->extent_size - 1)) { | ||||
| 		log_error("Extent size must be power of 2"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct metadata_area_ops _metadata_format1_ops = { | ||||
| 	vg_read:_vg_read, | ||||
| 	vg_write:_vg_write, | ||||
| }; | ||||
|  | ||||
| static struct format_instance *_create_instance(const 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->ops = &_metadata_format1_ops; | ||||
| 	mda->metadata_locn = NULL; | ||||
| 	list_add(&fid->metadata_areas, &mda->list); | ||||
|  | ||||
| 	return fid; | ||||
| } | ||||
|  | ||||
| static void _destroy_instance(struct format_instance *fid) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static void _destroy(const struct format_type *fmt) | ||||
| { | ||||
| 	dbg_free((void *) fmt); | ||||
| } | ||||
|  | ||||
| static struct format_handler _format1_ops = { | ||||
| 	pv_read:_pv_read, | ||||
| 	pv_setup:_pv_setup, | ||||
| 	pv_write:_pv_write, | ||||
| 	lv_setup:_lv_setup, | ||||
| 	vg_setup:_vg_setup, | ||||
| 	create_instance:_create_instance, | ||||
| 	destroy_instance:_destroy_instance, | ||||
| 	destroy:_destroy, | ||||
| }; | ||||
|  | ||||
| #ifdef LVM1_INTERNAL | ||||
| struct format_type *init_lvm1_format(struct cmd_context *cmd) | ||||
| #else				/* Shared */ | ||||
| struct format_type *init_format(struct cmd_context *cmd); | ||||
| struct format_type *init_format(struct cmd_context *cmd) | ||||
| #endif | ||||
| { | ||||
| 	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->alias = NULL; | ||||
| 	fmt->features = 0; | ||||
| 	fmt->private = NULL; | ||||
|  | ||||
| 	if (!(fmt->labeller = lvm1_labeller_create(fmt))) { | ||||
| 		log_error("Couldn't create lvm1 label handler."); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(label_register_handler(FMT_LVM1_NAME, fmt->labeller))) { | ||||
| 		log_error("Couldn't register lvm1 label handler."); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return fmt; | ||||
| } | ||||
							
								
								
									
										16
									
								
								lib/format1/format1.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/format1/format1.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| /* | ||||
|  * 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" | ||||
|  | ||||
| #ifdef LVM1_INTERNAL | ||||
| struct format_type *init_lvm1_format(struct cmd_context *cmd); | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										722
									
								
								lib/format1/import-export.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										722
									
								
								lib/format1/import-export.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,722 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * Translates between disk and in-core formats. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "disk-rep.h" | ||||
| #include "pool.h" | ||||
| #include "hash.h" | ||||
| #include "list.h" | ||||
| #include "lvm-string.h" | ||||
| #include "filter.h" | ||||
|  | ||||
| #include <time.h> | ||||
| #include <sys/utsname.h> | ||||
|  | ||||
| static int _check_vg_name(const char *name) | ||||
| { | ||||
| 	return strlen(name) < NAME_LEN; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Extracts the last part of a path. | ||||
|  */ | ||||
| static char *_create_lv_name(struct pool *mem, const char *full_name) | ||||
| { | ||||
| 	const char *ptr = strrchr(full_name, '/'); | ||||
|  | ||||
| 	if (!ptr) | ||||
| 		ptr = full_name; | ||||
| 	else | ||||
| 		ptr++; | ||||
|  | ||||
| 	return pool_strdup(mem, ptr); | ||||
| } | ||||
|  | ||||
| int import_pv(struct pool *mem, struct device *dev, | ||||
| 	      struct volume_group *vg, | ||||
| 	      struct physical_volume *pv, struct pv_disk *pvd) | ||||
| { | ||||
| 	memset(pv, 0, sizeof(*pv)); | ||||
| 	memcpy(&pv->id, pvd->pv_uuid, ID_LEN); | ||||
|  | ||||
| 	pv->dev = dev; | ||||
| 	if (!(pv->vg_name = pool_strdup(mem, pvd->vg_name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Store system_id from first PV if PV belongs to a VG */ | ||||
| 	if (vg && !*vg->system_id) | ||||
| 		strncpy(vg->system_id, pvd->system_id, NAME_LEN); | ||||
|  | ||||
| 	if (vg && | ||||
| 	    strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id))) | ||||
| 		log_very_verbose("System ID %s on %s differs from %s for " | ||||
| 				 "volume group", pvd->system_id, | ||||
| 				 dev_name(pv->dev), vg->system_id); | ||||
|  | ||||
| 	/* | ||||
| 	 * If exported, we still need to flag in pv->status too because | ||||
| 	 * we don't always have a struct volume_group when we need this. | ||||
| 	 */ | ||||
| 	if (pvd->pv_status & VG_EXPORTED) | ||||
| 		pv->status |= EXPORTED_VG; | ||||
|  | ||||
| 	if (pvd->pv_allocatable) | ||||
| 		pv->status |= ALLOCATABLE_PV; | ||||
|  | ||||
| 	pv->size = pvd->pv_size; | ||||
| 	pv->pe_size = pvd->pe_size; | ||||
| 	pv->pe_start = pvd->pe_start; | ||||
| 	pv->pe_count = pvd->pe_total; | ||||
| 	pv->pe_alloc_count = pvd->pe_allocated; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static 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 */ | ||||
| 	if (vg) | ||||
| 		pvd->pe_size = vg->extent_size; | ||||
| 	else | ||||
| 		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; | ||||
| 	} | ||||
|  | ||||
| 	lv->status |= VISIBLE_LV; | ||||
|  | ||||
| 	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); | ||||
| 		lv->major = MAJOR(lvd->lv_dev); | ||||
| 	} else { | ||||
| 		lv->major = -1; | ||||
| 		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; | ||||
|  | ||||
| 	/* Drop the unused LV_STRICT here */ | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| static 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); | ||||
|  | ||||
| 	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(lv->major, lv->minor); | ||||
| 	} | ||||
|  | ||||
| 	lvd->lv_read_ahead = lv->read_ahead; | ||||
| 	lvd->lv_stripes = | ||||
| 	    list_item(lv->segments.n, struct lv_segment)->area_count; | ||||
| 	lvd->lv_stripesize = | ||||
| 	    list_item(lv->segments.n, struct lv_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_CONTIGUOUS) | ||||
| 		lvd->lv_allocation |= LV_CONTIGUOUS; | ||||
| } | ||||
|  | ||||
| int export_extents(struct disk_list *dl, uint32_t lv_num, | ||||
| 		   struct logical_volume *lv, struct physical_volume *pv) | ||||
| { | ||||
| 	struct list *segh; | ||||
| 	struct pe_disk *ped; | ||||
| 	struct lv_segment *seg; | ||||
| 	uint32_t pe, s; | ||||
|  | ||||
| 	list_iterate(segh, &lv->segments) { | ||||
| 		seg = list_item(segh, struct lv_segment); | ||||
|  | ||||
| 		for (s = 0; s < seg->area_count; s++) { | ||||
| 			if (seg->type != SEG_STRIPED) { | ||||
| 				log_error("Non-striped segment type in LV %s: " | ||||
| 					  "unsupported by format1", lv->name); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			if (seg->area[s].type != AREA_PV) { | ||||
| 				log_error("LV stripe found in LV %s: " | ||||
| 					  "unsupported by format1", lv->name); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			if (seg->area[s].u.pv.pv != pv) | ||||
| 				continue;	/* not our pv */ | ||||
|  | ||||
| 			for (pe = 0; pe < (seg->len / seg->area_count); pe++) { | ||||
| 				ped = &dl->extents[pe + seg->area[s].u.pv.pe]; | ||||
| 				ped->lv_num = lv_num; | ||||
| 				ped->le_num = (seg->le / seg->area_count) + pe + | ||||
| 				    s * (lv->le_count / seg->area_count); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int import_pvs(const struct format_type *fmt, 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_zalloc(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->fmt = fmt; | ||||
| 		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; | ||||
| 	size_t len; | ||||
| 	uint32_t lv_num; | ||||
| 	struct hash_table *lvd_hash; | ||||
|  | ||||
| 	if (!_check_vg_name(vg->name)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	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, NULL, | ||||
| 					     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; | ||||
| } | ||||
							
								
								
									
										377
									
								
								lib/format1/import-extents.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								lib/format1/import-extents.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,377 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "metadata.h" | ||||
| #include "hash.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 lv_segment *_alloc_seg(struct pool *mem, uint32_t stripes) | ||||
| { | ||||
| 	struct lv_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 lv_segment *seg; | ||||
|  | ||||
| 	while (le < lvm->lv->le_count) { | ||||
| 		seg = _alloc_seg(mem, 1); | ||||
|  | ||||
| 		seg->lv = lvm->lv; | ||||
| 		seg->type = SEG_STRIPED; | ||||
| 		seg->le = le; | ||||
| 		seg->len = 0; | ||||
| 		seg->area_len = 0; | ||||
| 		seg->stripe_size = 0; | ||||
| 		seg->area_count = 1; | ||||
|  | ||||
| 		seg->area[0].type = AREA_PV; | ||||
| 		seg->area[0].u.pv.pv = lvm->map[le].pv; | ||||
| 		seg->area[0].u.pv.pe = lvm->map[le].pe; | ||||
|  | ||||
| 		do { | ||||
| 			seg->len++; | ||||
| 			seg->area_len++; | ||||
| 		} while ((lvm->map[le + seg->len].pv == seg->area[0].u.pv.pv) && | ||||
| 			 (seg->area[0].u.pv.pv && | ||||
| 			  lvm->map[le + seg->len].pe == seg->area[0].u.pv.pe + | ||||
| 			  seg->len)); | ||||
|  | ||||
| 		le += seg->len; | ||||
|  | ||||
| 		list_add(&lvm->lv->segments, &seg->list); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _check_stripe(struct lv_map *lvm, struct lv_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->area_count; st++) | ||||
| 		if ((lvm->map[le + st * len].pv != seg->area[st].u.pv.pv) || | ||||
| 		    (seg->area[st].u.pv.pv && | ||||
| 		     lvm->map[le + st * len].pe != | ||||
| 		     seg->area[st].u.pv.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 lv_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->type = SEG_STRIPED; | ||||
| 		seg->stripe_size = lvm->stripe_size; | ||||
| 		seg->area_count = lvm->stripes; | ||||
| 		seg->le = seg->area_count * le; | ||||
| 		seg->len = 1; | ||||
| 		seg->area_len = 1; | ||||
|  | ||||
| 		/* | ||||
| 		 * Set up start positions of each stripe in this segment | ||||
| 		 */ | ||||
| 		for (st = 0; st < seg->area_count; st++) { | ||||
| 			seg->area[st].u.pv.pv = lvm->map[le + st * len].pv; | ||||
| 			seg->area[st].u.pv.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++; | ||||
| 			seg->area_len++; | ||||
| 		} | ||||
|  | ||||
| 		le += seg->len; | ||||
| 		seg->len *= seg->area_count; | ||||
|  | ||||
| 		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; | ||||
| } | ||||
							
								
								
									
										159
									
								
								lib/format1/layout.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								lib/format1/layout.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "disk-rep.h" | ||||
|  | ||||
| /* | ||||
|  * Only works with powers of 2. | ||||
|  */ | ||||
| static inline uint32_t _round_up(uint32_t n, uint32_t size) | ||||
| { | ||||
| 	size--; | ||||
| 	return (n + size) & ~size; | ||||
| } | ||||
|  | ||||
| static inline uint32_t _div_up(uint32_t n, uint32_t 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_SHIFT; | ||||
|  | ||||
| 	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); | ||||
| } | ||||
|  | ||||
| static 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; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * The number of extents that can fit on a disk is metadata format dependant. | ||||
|  */ | ||||
| int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size, | ||||
| 			   uint32_t max_extent_count) | ||||
| { | ||||
| 	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. | ||||
| 	 */ | ||||
| 	if (max_extent_count) | ||||
| 		pvd->pe_total = max_extent_count + 1; | ||||
| 	else | ||||
| 		pvd->pe_total = (pv->size / extent_size); | ||||
|  | ||||
| 	if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) { | ||||
| 		log_error("Too few extents on %s.  Try smaller extent size.", | ||||
| 			  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_SHIFT); | ||||
|  | ||||
| 		pvd->pe_start = _round_up(end, PE_ALIGN); | ||||
|  | ||||
| 	} while ((pvd->pe_start + (pvd->pe_total * extent_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; | ||||
| 	/* We can't set pe_size here without breaking LVM1 compatibility */ | ||||
| 	dbg_free(pvd); | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										109
									
								
								lib/format1/lvm1-label.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								lib/format1/lvm1-label.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "lvm1-label.h" | ||||
| #include "disk-rep.h" | ||||
| #include "label.h" | ||||
| #include "metadata.h" | ||||
| #include "xlate.h" | ||||
| #include "lvmcache.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, char *buf, uint64_t sector) | ||||
| { | ||||
| 	struct pv_disk *pvd = (struct pv_disk *) buf; | ||||
| 	uint32_t version; | ||||
|  | ||||
| 	/* LVM1 label must always be in first sector */ | ||||
| 	if (sector) | ||||
| 		return 0; | ||||
|  | ||||
| 	version = xlate16(pvd->version); | ||||
|  | ||||
| 	if (pvd->id[0] == 'H' && pvd->id[1] == 'M' && | ||||
| 	    (version == 1 || version == 2)) | ||||
| 		return 1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int _write(struct label *label, char *buf) | ||||
| { | ||||
| 	_not_supported("write"); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int _read(struct labeller *l, struct device *dev, char *buf, | ||||
| 		 struct label **label) | ||||
| { | ||||
| 	struct pv_disk *pvd = (struct pv_disk *) buf; | ||||
| 	struct lvmcache_info *info; | ||||
|  | ||||
| 	munge_exported_vg(pvd); | ||||
| 	if (!(info = lvmcache_add(l, pvd->pv_uuid, dev, pvd->vg_name, NULL))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	*label = info->label; | ||||
|  | ||||
| 	info->device_size = xlate32(pvd->pv_size) << SECTOR_SHIFT; | ||||
| 	list_init(&info->mdas); | ||||
|  | ||||
| 	info->status &= ~CACHE_INVALID; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _initialise_label(struct labeller *l, struct label *label) | ||||
| { | ||||
| 	strcpy(label->type, "LVM1"); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _destroy_label(struct labeller *l, struct label *label) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static void _destroy(struct labeller *l) | ||||
| { | ||||
| 	dbg_free(l); | ||||
| } | ||||
|  | ||||
| struct label_ops _lvm1_ops = { | ||||
| 	can_handle:_can_handle, | ||||
| 	write:_write, | ||||
| 	read:_read, | ||||
| 	verify:_can_handle, | ||||
| 	initialise_label:_initialise_label, | ||||
| 	destroy_label:_destroy_label, | ||||
| 	destroy:_destroy | ||||
| }; | ||||
|  | ||||
| struct labeller *lvm1_labeller_create(struct format_type *fmt) | ||||
| { | ||||
| 	struct labeller *l; | ||||
|  | ||||
| 	if (!(l = dbg_malloc(sizeof(*l)))) { | ||||
| 		log_err("Couldn't allocate labeller object."); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	l->ops = &_lvm1_ops; | ||||
| 	l->private = (const void *) fmt; | ||||
|  | ||||
| 	return l; | ||||
| } | ||||
							
								
								
									
										14
									
								
								lib/format1/lvm1-label.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/format1/lvm1-label.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_LVM1_LABEL_H | ||||
| #define _LVM_LVM1_LABEL_H | ||||
|  | ||||
| #include "metadata.h" | ||||
|  | ||||
| struct labeller *lvm1_labeller_create(struct format_type *fmt); | ||||
|  | ||||
| #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 "lib.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; | ||||
| } | ||||
							
								
								
									
										374
									
								
								lib/format_text/archive.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								lib/format_text/archive.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,374 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "format-text.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/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 *vgname, size_t vg_size, | ||||
| 		     uint32_t *ix) | ||||
| { | ||||
| 	size_t len, vg_len; | ||||
| 	const char *dot, *underscore; | ||||
|  | ||||
| 	len = strlen(filename); | ||||
| 	if (len < 7) | ||||
| 		return 0; | ||||
|  | ||||
| 	dot = (filename + len - 3); | ||||
| 	if (strcmp(".vg", dot)) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!(underscore = rindex(filename, '_'))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (sscanf(underscore + 1, "%u", ix) != 1) | ||||
| 		return 0; | ||||
|  | ||||
| 	vg_len = underscore - filename; | ||||
| 	if (vg_len + 1 > vg_size) | ||||
| 		return 0; | ||||
|  | ||||
| 	strncpy(vgname, filename, vg_len); | ||||
| 	vgname[vg_len] = '\0'; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _insert_file(struct list *head, struct archive_file *b) | ||||
| { | ||||
| 	struct list *bh; | ||||
| 	struct archive_file *bf = NULL; | ||||
|  | ||||
| 	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 *vgname, const char *dir) | ||||
| { | ||||
| 	int i, count, ix; | ||||
| 	char vgname_found[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, vgname_found, | ||||
| 			       sizeof(vgname_found), &ix)) | ||||
| 			continue; | ||||
|  | ||||
| 		/* is it the vg we're interested in ? */ | ||||
| 		if (strcmp(vgname, vgname_found)) | ||||
| 			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 = ix; | ||||
| 		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 ix = 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_file(vg, desc, fp)) { | ||||
| 		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)) | ||||
| 		ix = 0; | ||||
| 	else { | ||||
| 		last = list_item(archives->p, struct archive_file); | ||||
| 		ix = last->index + 1; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < 10; i++) { | ||||
| 		if (lvm_snprintf(archive_name, sizeof(archive_name), | ||||
| 				 "%s/%s_%05d.vg", dir, vg->name, ix) < 0) { | ||||
| 			log_error("Archive file name too long."); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if ((renamed = lvm_rename(temp_file, archive_name))) | ||||
| 			break; | ||||
|  | ||||
| 		ix++; | ||||
| 	} | ||||
|  | ||||
| 	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 archive_file *af) | ||||
| { | ||||
| 	struct volume_group *vg = NULL; | ||||
| 	struct format_instance *tf; | ||||
| 	time_t when; | ||||
| 	char *desc; | ||||
| 	void *context; | ||||
|  | ||||
| 	log_print(" "); | ||||
| 	log_print("File:\t\t%s", af->path); | ||||
|  | ||||
| 	if (!(context = create_text_context(cmd, af->path, NULL)) || | ||||
| 	    !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, 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_file(tf, af->path, &when, &desc))) { | ||||
| 		log_print("Unable to read archive file."); | ||||
| 		tf->fmt->ops->destroy_instance(tf); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	log_print("VG name:    \t%s", vg->name); | ||||
| 	log_print("Description:\t%s", desc ? desc : "<No description>"); | ||||
| 	log_print("Backup Time:\t%s", ctime(&when)); | ||||
|  | ||||
| 	pool_free(cmd->mem, vg); | ||||
| 	tf->fmt->ops->destroy_instance(tf); | ||||
| } | ||||
|  | ||||
| int archive_list(struct cmd_context *cmd, const char *dir, const char *vgname) | ||||
| { | ||||
| 	struct list *archives, *ah; | ||||
| 	struct archive_file *af; | ||||
|  | ||||
| 	if (!(archives = _scan_archive(cmd->mem, vgname, dir))) { | ||||
| 		log_err("Couldn't scan the archive directory (%s).", dir); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (list_empty(archives)) | ||||
| 		log_print("No archives found in %s.", dir); | ||||
|  | ||||
| 	list_iterate(ah, archives) { | ||||
| 		af = list_item(ah, struct archive_file); | ||||
|  | ||||
| 		_display_archive(cmd, af); | ||||
| 	} | ||||
|  | ||||
| 	pool_free(cmd->mem, archives); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname) | ||||
| { | ||||
| 	struct archive_file af; | ||||
|  | ||||
| 	if (!(af.path = _join(cmd->mem, dir, vgname))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (path_exists(af.path)) | ||||
| 		_display_archive(cmd, &af); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
							
								
								
									
										760
									
								
								lib/format_text/export.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										760
									
								
								lib/format_text/export.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,760 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "import-export.h" | ||||
| #include "metadata.h" | ||||
| #include "hash.h" | ||||
| #include "pool.h" | ||||
| #include "display.h" | ||||
| #include "lvm-string.h" | ||||
|  | ||||
| #include <stdarg.h> | ||||
| #include <time.h> | ||||
| #include <sys/utsname.h> | ||||
|  | ||||
| struct formatter; | ||||
| typedef int (*out_with_comment_fn) (struct formatter * f, const char *comment, | ||||
| 				    const char *fmt, va_list ap); | ||||
| typedef void (*nl_fn) (struct formatter * f); | ||||
| /* | ||||
|  * The first half of this file deals with | ||||
|  * exporting the vg, ie. writing it to a file. | ||||
|  */ | ||||
| struct formatter { | ||||
| 	struct pool *mem;	/* pv names allocated from here */ | ||||
| 	struct hash_table *pv_names;	/* dev_name -> pv_name (eg, pv1) */ | ||||
|  | ||||
| 	union { | ||||
| 		FILE *fp;	/* where we're writing to */ | ||||
| 		struct { | ||||
| 			char *buf; | ||||
| 			uint32_t size; | ||||
| 			uint32_t used; | ||||
| 		} buf; | ||||
| 	} data; | ||||
|  | ||||
| 	out_with_comment_fn out_with_comment; | ||||
| 	nl_fn nl; | ||||
|  | ||||
| 	int indent;		/* current level of indentation */ | ||||
| 	int error; | ||||
| 	int header;		/* 1 => comments at start; 0 => end */ | ||||
| }; | ||||
|  | ||||
| static struct utsname _utsname; | ||||
|  | ||||
| static void _init(void) | ||||
| { | ||||
| 	static int _initialised = 0; | ||||
|  | ||||
| 	if (_initialised) | ||||
| 		return; | ||||
|  | ||||
| 	if (uname(&_utsname)) { | ||||
| 		log_error("uname failed: %s", strerror(errno)); | ||||
| 		memset(&_utsname, 0, sizeof(_utsname)); | ||||
| 	} | ||||
|  | ||||
| 	_initialised = 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Formatting functions. | ||||
|  */ | ||||
| static int _out_size(struct formatter *f, uint64_t size, const char *fmt, ...) | ||||
|     __attribute__ ((format(printf, 3, 4))); | ||||
|  | ||||
| static int _out_hint(struct formatter *f, const char *fmt, ...) | ||||
|     __attribute__ ((format(printf, 2, 3))); | ||||
|  | ||||
| static int _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_file(struct formatter *f) | ||||
| { | ||||
| 	fprintf(f->data.fp, "\n"); | ||||
| } | ||||
|  | ||||
| static void _nl_raw(struct formatter *f) | ||||
| { | ||||
| 	if (f->data.buf.used >= f->data.buf.size - 1) | ||||
| 		return; | ||||
|  | ||||
| 	*f->data.buf.buf = '\n'; | ||||
| 	f->data.buf.buf += 1; | ||||
| 	f->data.buf.used += 1; | ||||
| 	*f->data.buf.buf = '\0'; | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| #define COMMENT_TAB 6 | ||||
| static int _out_with_comment_file(struct formatter *f, const char *comment, | ||||
| 				  const char *fmt, va_list ap) | ||||
| { | ||||
| 	int i; | ||||
| 	char white_space[MAX_INDENT + 1]; | ||||
|  | ||||
| 	if (ferror(f->data.fp)) | ||||
| 		return 0; | ||||
|  | ||||
| 	for (i = 0; i < f->indent; i++) | ||||
| 		white_space[i] = '\t'; | ||||
| 	white_space[i] = '\0'; | ||||
| 	fprintf(f->data.fp, white_space); | ||||
| 	i = vfprintf(f->data.fp, fmt, ap); | ||||
|  | ||||
| 	if (comment) { | ||||
| 		/* | ||||
| 		 * line comments up if possible. | ||||
| 		 */ | ||||
| 		i += 8 * f->indent; | ||||
| 		i /= 8; | ||||
| 		i++; | ||||
|  | ||||
| 		do | ||||
| 			fputc('\t', f->data.fp); | ||||
|  | ||||
| 		while (++i < COMMENT_TAB); | ||||
|  | ||||
| 		fprintf(f->data.fp, comment); | ||||
| 	} | ||||
| 	fputc('\n', f->data.fp); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _out_with_comment_raw(struct formatter *f, const char *comment, | ||||
| 				 const char *fmt, va_list ap) | ||||
| { | ||||
| 	int n; | ||||
|  | ||||
| 	n = vsnprintf(f->data.buf.buf, f->data.buf.size - f->data.buf.used, | ||||
| 		      fmt, ap); | ||||
|  | ||||
| 	if (n < 0 || (n > f->data.buf.size - f->data.buf.used - 1)) | ||||
| 		return 0; | ||||
|  | ||||
| 	f->data.buf.buf += n; | ||||
| 	f->data.buf.used += n; | ||||
|  | ||||
| 	f->nl(f); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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 const char *_units[] = { | ||||
| 		"Kilobytes", | ||||
| 		"Megabytes", | ||||
| 		"Gigabytes", | ||||
| 		"Terabytes", | ||||
| 		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 int _out_size(struct formatter *f, uint64_t size, const char *fmt, ...) | ||||
| { | ||||
| 	char buffer[64]; | ||||
| 	va_list ap; | ||||
| 	int r; | ||||
|  | ||||
| 	if (!_sectors_to_units(size, buffer, sizeof(buffer))) | ||||
| 		return 0; | ||||
|  | ||||
| 	va_start(ap, fmt); | ||||
| 	r = f->out_with_comment(f, buffer, fmt, ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Appends a comment indicating that the line is | ||||
|  * only a hint. | ||||
|  */ | ||||
| static int _out_hint(struct formatter *f, const char *fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	int r; | ||||
|  | ||||
| 	va_start(ap, fmt); | ||||
| 	r = f->out_with_comment(f, "# Hint only", fmt, ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * The normal output function. | ||||
|  */ | ||||
| static int _out(struct formatter *f, const char *fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	int r; | ||||
|  | ||||
| 	va_start(ap, fmt); | ||||
| 	r = f->out_with_comment(f, NULL, fmt, ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| #define _outf(args...) do {if (!_out(args)) {stack; return 0;}} while (0) | ||||
|  | ||||
| static int _print_header(struct formatter *f, | ||||
| 			 struct volume_group *vg, const char *desc) | ||||
| { | ||||
| 	time_t t; | ||||
|  | ||||
| 	t = time(NULL); | ||||
|  | ||||
| 	_outf(f, "# Generated by LVM2: %s", ctime(&t)); | ||||
| 	_outf(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\""); | ||||
| 	_outf(f, FORMAT_VERSION_FIELD " = %d", FORMAT_VERSION_VALUE); | ||||
| 	f->nl(f); | ||||
|  | ||||
| 	_outf(f, "description = \"%s\"", desc); | ||||
| 	f->nl(f); | ||||
| 	_outf(f, "creation_host = \"%s\"\t# %s %s %s %s %s", _utsname.nodename, | ||||
| 	      _utsname.sysname, _utsname.nodename, _utsname.release, | ||||
| 	      _utsname.version, _utsname.machine); | ||||
| 	_outf(f, "creation_time = %lu\t# %s", t, ctime(&t)); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _print_vg(struct formatter *f, struct volume_group *vg) | ||||
| { | ||||
| 	char buffer[256]; | ||||
|  | ||||
| 	if (!id_write_format(&vg->id, buffer, sizeof(buffer))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_outf(f, "id = \"%s\"", buffer); | ||||
|  | ||||
| 	_outf(f, "seqno = %u", vg->seqno); | ||||
| 	if (!print_flags(vg->status, VG_FLAGS, buffer, sizeof(buffer))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_outf(f, "status = %s", buffer); | ||||
| 	if (vg->system_id && *vg->system_id) | ||||
| 		_outf(f, "system_id = \"%s\"", vg->system_id); | ||||
| 	if (!_out_size(f, (uint64_t) vg->extent_size, "extent_size = %u", | ||||
| 		       vg->extent_size)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	_outf(f, "max_lv = %u", vg->max_lv); | ||||
| 	_outf(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; | ||||
|  | ||||
| 	_outf(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; | ||||
| 		} | ||||
|  | ||||
| 		f->nl(f); | ||||
| 		_outf(f, "%s {", name); | ||||
| 		_inc_indent(f); | ||||
|  | ||||
| 		if (!id_write_format(&pv->id, buffer, sizeof(buffer))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		_outf(f, "id = \"%s\"", buffer); | ||||
| 		if (!_out_hint(f, "device = \"%s\"", dev_name(pv->dev))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		f->nl(f); | ||||
|  | ||||
| 		if (!print_flags(pv->status, PV_FLAGS, buffer, sizeof(buffer))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		_outf(f, "status = %s", buffer); | ||||
| 		_outf(f, "pe_start = %" PRIu64, pv->pe_start); | ||||
| 		if (!_out_size(f, vg->extent_size * (uint64_t) pv->pe_count, | ||||
| 			       "pe_count = %u", pv->pe_count)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		_dec_indent(f); | ||||
| 		_outf(f, "}"); | ||||
| 	} | ||||
|  | ||||
| 	_dec_indent(f); | ||||
| 	_outf(f, "}"); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _print_segment(struct formatter *f, struct volume_group *vg, | ||||
| 			  int count, struct lv_segment *seg) | ||||
| { | ||||
| 	unsigned int s; | ||||
| 	const char *name; | ||||
| 	const char *type; | ||||
|  | ||||
| 	_outf(f, "segment%u {", count); | ||||
| 	_inc_indent(f); | ||||
|  | ||||
| 	_outf(f, "start_extent = %u", seg->le); | ||||
| 	if (!_out_size(f, (uint64_t) seg->len * vg->extent_size, | ||||
| 		       "extent_count = %u", seg->len)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	f->nl(f); | ||||
| 	_outf(f, "type = \"%s\"", get_segtype_string(seg->type)); | ||||
|  | ||||
| 	switch (seg->type) { | ||||
| 	case SEG_SNAPSHOT: | ||||
| 		_outf(f, "chunk_size = %u", seg->chunk_size); | ||||
| 		_outf(f, "origin = \"%s\"", seg->origin->name); | ||||
| 		_outf(f, "cow_store = \"%s\"", seg->cow->name); | ||||
| 		break; | ||||
|  | ||||
| 	case SEG_MIRRORED: | ||||
| 	case SEG_STRIPED: | ||||
| 		type = (seg->type == SEG_MIRRORED) ? "mirror" : "stripe"; | ||||
| 		_outf(f, "%s_count = %u%s", type, seg->area_count, | ||||
| 		      (seg->area_count == 1) ? "\t# linear" : ""); | ||||
|  | ||||
| 		if ((seg->type == SEG_MIRRORED) && (seg->status & PVMOVE)) | ||||
| 			_out_size(f, (uint64_t) seg->extents_moved, | ||||
| 				  "extents_moved = %u", seg->extents_moved); | ||||
|  | ||||
| 		if ((seg->type == SEG_STRIPED) && (seg->area_count > 1)) | ||||
| 			_out_size(f, (uint64_t) seg->stripe_size, | ||||
| 				  "stripe_size = %u", seg->stripe_size); | ||||
|  | ||||
| 		f->nl(f); | ||||
|  | ||||
| 		_outf(f, "%ss = [", type); | ||||
| 		_inc_indent(f); | ||||
|  | ||||
| 		for (s = 0; s < seg->area_count; s++) { | ||||
| 			switch (seg->area[s].type) { | ||||
| 			case AREA_PV: | ||||
| 				if (!(name = _get_pv_name(f, seg-> | ||||
| 							  area[s].u.pv.pv))) { | ||||
| 					stack; | ||||
| 					return 0; | ||||
| 				} | ||||
|  | ||||
| 				_outf(f, "\"%s\", %u%s", name, | ||||
| 				      seg->area[s].u.pv.pe, | ||||
| 				      (s == seg->area_count - 1) ? "" : ","); | ||||
| 				break; | ||||
| 			case AREA_LV: | ||||
| 				_outf(f, "\"%s\", %u%s", | ||||
| 				      seg->area[s].u.lv.lv->name, | ||||
| 				      seg->area[s].u.lv.le, | ||||
| 				      (s == seg->area_count - 1) ? "" : ","); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		_dec_indent(f); | ||||
| 		_outf(f, "]"); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	_dec_indent(f); | ||||
| 	_outf(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_snapshot(struct formatter *f, struct snapshot *snap, | ||||
| 			   unsigned int count) | ||||
| { | ||||
| 	char buffer[256]; | ||||
| 	struct lv_segment seg; | ||||
|  | ||||
| 	f->nl(f); | ||||
|  | ||||
| 	_outf(f, "snapshot%u {", count); | ||||
| 	_inc_indent(f); | ||||
|  | ||||
| 	if (!id_write_format(&snap->id, buffer, sizeof(buffer))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_outf(f, "id = \"%s\"", buffer); | ||||
| 	if (!print_flags(LVM_READ | LVM_WRITE | VISIBLE_LV, LV_FLAGS, | ||||
| 			 buffer, sizeof(buffer))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_outf(f, "status = %s", buffer); | ||||
| 	_outf(f, "segment_count = 1"); | ||||
|  | ||||
| 	f->nl(f); | ||||
|  | ||||
| 	seg.type = SEG_SNAPSHOT; | ||||
| 	seg.le = 0; | ||||
| 	seg.len = snap->origin->le_count; | ||||
| 	seg.origin = snap->origin; | ||||
| 	seg.cow = snap->cow; | ||||
| 	seg.chunk_size = snap->chunk_size; | ||||
|  | ||||
| 	if (!_print_segment(f, snap->origin->vg, 1, &seg)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_dec_indent(f); | ||||
| 	_outf(f, "}"); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _print_snapshots(struct formatter *f, struct volume_group *vg) | ||||
| { | ||||
| 	struct list *sh; | ||||
| 	struct snapshot *s; | ||||
| 	unsigned int count = 0; | ||||
|  | ||||
| 	list_iterate(sh, &vg->snapshots) { | ||||
| 		s = list_item(sh, struct snapshot_list)->snapshot; | ||||
|  | ||||
| 		if (!_print_snapshot(f, s, count++)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _print_lvs(struct formatter *f, struct volume_group *vg) | ||||
| { | ||||
| 	struct list *lvh; | ||||
| 	struct logical_volume *lv; | ||||
| 	struct lv_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; | ||||
|  | ||||
| 	_outf(f, "logical_volumes {"); | ||||
| 	_inc_indent(f); | ||||
|  | ||||
| 	list_iterate(lvh, &vg->lvs) { | ||||
| 		lv = list_item(lvh, struct lv_list)->lv; | ||||
|  | ||||
| 		f->nl(f); | ||||
| 		_outf(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; | ||||
| 		} | ||||
|  | ||||
| 		_outf(f, "id = \"%s\"", buffer); | ||||
|  | ||||
| 		if (!print_flags(lv->status, LV_FLAGS, buffer, sizeof(buffer))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		_outf(f, "status = %s", buffer); | ||||
| 		if (lv->alloc != ALLOC_DEFAULT) | ||||
| 			_outf(f, "allocation_policy = \"%s\"", | ||||
| 			      get_alloc_string(lv->alloc)); | ||||
| 		if (lv->read_ahead) | ||||
| 			_outf(f, "read_ahead = %u", lv->read_ahead); | ||||
| 		if (lv->major >= 0) | ||||
| 			_outf(f, "major = %d", lv->major); | ||||
| 		if (lv->minor >= 0) | ||||
| 			_outf(f, "minor = %d", lv->minor); | ||||
| 		_outf(f, "segment_count = %u", _count_segments(lv)); | ||||
| 		f->nl(f); | ||||
|  | ||||
| 		seg_count = 1; | ||||
| 		list_iterate_items(seg, &lv->segments) { | ||||
| 			if (!_print_segment(f, vg, seg_count++, seg)) { | ||||
| 				stack; | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		_dec_indent(f); | ||||
| 		_outf(f, "}"); | ||||
| 	} | ||||
|  | ||||
| 	if (!_print_snapshots(f, vg)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_dec_indent(f); | ||||
| 	_outf(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; | ||||
|  | ||||
| 		/* FIXME But skip if there's already an LV called pv%d ! */ | ||||
| 		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; | ||||
| } | ||||
|  | ||||
| static int _text_vg_export(struct formatter *f, | ||||
| 			   struct volume_group *vg, const char *desc) | ||||
| { | ||||
| 	int r = 0; | ||||
|  | ||||
| 	if (!_build_pv_names(f, vg)) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
| #define fail do {stack; goto out;} while(0) | ||||
|  | ||||
| 	if (f->header && !_print_header(f, vg, desc)) | ||||
| 		fail; | ||||
|  | ||||
| 	if (!_out(f, "%s {", vg->name)) | ||||
| 		fail; | ||||
|  | ||||
| 	_inc_indent(f); | ||||
|  | ||||
| 	if (!_print_vg(f, vg)) | ||||
| 		fail; | ||||
|  | ||||
| 	f->nl(f); | ||||
| 	if (!_print_pvs(f, vg)) | ||||
| 		fail; | ||||
|  | ||||
| 	f->nl(f); | ||||
| 	if (!_print_lvs(f, vg)) | ||||
| 		fail; | ||||
|  | ||||
| 	_dec_indent(f); | ||||
| 	if (!_out(f, "}")) | ||||
| 		fail; | ||||
|  | ||||
| 	if (!f->header && !_print_header(f, vg, desc)) | ||||
| 		fail; | ||||
|  | ||||
| #undef fail | ||||
| 	r = 1; | ||||
|  | ||||
|       out: | ||||
| 	if (f->mem) | ||||
| 		pool_destroy(f->mem); | ||||
|  | ||||
| 	if (f->pv_names) | ||||
| 		hash_destroy(f->pv_names); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp) | ||||
| { | ||||
| 	struct formatter *f; | ||||
| 	int r; | ||||
|  | ||||
| 	_init(); | ||||
|  | ||||
| 	if (!(f = dbg_malloc(sizeof(*f)))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	memset(f, 0, sizeof(*f)); | ||||
| 	f->data.fp = fp; | ||||
| 	f->indent = 0; | ||||
| 	f->header = 1; | ||||
| 	f->out_with_comment = &_out_with_comment_file; | ||||
| 	f->nl = &_nl_file; | ||||
|  | ||||
| 	r = _text_vg_export(f, vg, desc); | ||||
| 	if (r) | ||||
| 		r = !ferror(f->data.fp); | ||||
| 	dbg_free(f); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* Returns amount of buffer used incl. terminating NUL */ | ||||
| int text_vg_export_raw(struct volume_group *vg, const char *desc, char *buf, | ||||
| 		       uint32_t size) | ||||
| { | ||||
| 	struct formatter *f; | ||||
| 	int r; | ||||
|  | ||||
| 	_init(); | ||||
|  | ||||
| 	if (!(f = dbg_malloc(sizeof(*f)))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	memset(f, 0, sizeof(*f)); | ||||
| 	f->data.buf.buf = buf; | ||||
| 	f->data.buf.size = size; | ||||
| 	f->indent = 0; | ||||
| 	f->header = 0; | ||||
| 	f->out_with_comment = &_out_with_comment_raw; | ||||
| 	f->nl = &_nl_raw; | ||||
|  | ||||
| 	if (!_text_vg_export(f, vg, desc)) { | ||||
| 		stack; | ||||
| 		r = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	r = f->data.buf.used + 1; | ||||
|  | ||||
|       out: | ||||
| 	dbg_free(f); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| #undef _outf | ||||
							
								
								
									
										165
									
								
								lib/format_text/flags.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								lib/format_text/flags.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.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 { | ||||
| 	const int mask; | ||||
| 	const char *description; | ||||
| }; | ||||
|  | ||||
| static struct flag _vg_flags[] = { | ||||
| 	{EXPORTED_VG, "EXPORTED"}, | ||||
| 	{RESIZEABLE_VG, "RESIZEABLE"}, | ||||
| 	{PARTIAL_VG, "PARTIAL"}, | ||||
| 	{PVMOVE, "PVMOVE"}, | ||||
| 	{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"}, | ||||
| 	{PVMOVE, "PVMOVE"}, | ||||
| 	{LOCKED, "LOCKED"}, | ||||
| 	{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, ...) | ||||
| { | ||||
| 	int 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; | ||||
| 	} | ||||
|  | ||||
| 	if (cv->type == CFG_EMPTY_ARRAY) | ||||
| 		goto out; | ||||
|  | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
|       out: | ||||
| 	*status = s; | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										1686
									
								
								lib/format_text/format-text.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1686
									
								
								lib/format_text/format-text.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										52
									
								
								lib/format_text/format-text.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								lib/format_text/format-text.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /* | ||||
|  * 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 "pool.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, const char *dir, const char *vgname); | ||||
| int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname); | ||||
|  | ||||
| /* | ||||
|  * 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 cmd_context *cmd, const char *path, | ||||
| 			  const char *desc); | ||||
|  | ||||
| struct labeller *text_labeller_create(const struct format_type *fmt); | ||||
|  | ||||
| int pvhdr_read(struct device *dev, char *buf); | ||||
|  | ||||
| int add_da(const struct format_type *fmt, struct pool *mem, struct list *das, | ||||
| 	   uint64_t start, uint64_t size); | ||||
| void del_das(struct list *das); | ||||
|  | ||||
| int add_mda(const struct format_type *fmt, struct pool *mem, struct list *mdas, | ||||
| 	    struct device *dev, uint64_t start, uint64_t size); | ||||
| void del_mdas(struct list *mdas); | ||||
|  | ||||
| int vgname_from_mda(const struct format_type *fmt, struct device_area *dev_area, | ||||
| 		    char *buf, uint32_t size); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										64
									
								
								lib/format_text/import-export.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								lib/format_text/import-export.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
|  * 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 "pool.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 | ||||
| }; | ||||
|  | ||||
| struct text_vg_version_ops { | ||||
| 	int (*check_version) (struct config_tree * cf); | ||||
| 	struct volume_group *(*read_vg) (struct format_instance * fid, | ||||
| 					 struct config_tree * cf); | ||||
| 	void (*read_desc) (struct pool * mem, struct config_tree * cf, | ||||
| 			   time_t *when, char **desc); | ||||
| }; | ||||
|  | ||||
| struct text_vg_version_ops *text_vg_vsn1_init(void); | ||||
|  | ||||
| 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(struct volume_group *vg, const char *desc, FILE *fp); | ||||
| int text_vg_export_raw(struct volume_group *vg, const char *desc, char *buf, | ||||
| 		       uint32_t size); | ||||
| struct volume_group *text_vg_import_file(struct format_instance *fid, | ||||
| 					 const char *file, | ||||
| 					 time_t *when, char **desc); | ||||
| struct volume_group *text_vg_import_fd(struct format_instance *fid, | ||||
| 				       const char *file, | ||||
| 				       struct device *dev, | ||||
| 				       off_t offset, uint32_t size, | ||||
| 				       off_t offset2, uint32_t size2, | ||||
| 				       checksum_fn_t checksum_fn, | ||||
| 				       uint32_t checksum, | ||||
| 				       time_t *when, char **desc); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										82
									
								
								lib/format_text/import.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								lib/format_text/import.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "metadata.h" | ||||
| #include "import-export.h" | ||||
| #include "pool.h" | ||||
| #include "display.h" | ||||
| #include "hash.h" | ||||
| #include "toolcontext.h" | ||||
| #include "lvmcache.h" | ||||
|  | ||||
| /* FIXME Use tidier inclusion method */ | ||||
| static struct text_vg_version_ops *(_text_vsn_list[2]); | ||||
|  | ||||
| struct volume_group *text_vg_import_fd(struct format_instance *fid, | ||||
| 				       const char *file, | ||||
| 				       struct device *dev, | ||||
| 				       off_t offset, uint32_t size, | ||||
| 				       off_t offset2, uint32_t size2, | ||||
| 				       checksum_fn_t checksum_fn, | ||||
| 				       uint32_t checksum, | ||||
| 				       time_t *when, char **desc) | ||||
| { | ||||
| 	struct volume_group *vg = NULL; | ||||
| 	struct config_tree *cf; | ||||
| 	struct text_vg_version_ops **vsn; | ||||
|  | ||||
| 	static int _initialised = 0; | ||||
|  | ||||
| 	if (!_initialised) { | ||||
| 		_text_vsn_list[0] = text_vg_vsn1_init(); | ||||
| 		_text_vsn_list[1] = NULL; | ||||
| 		_initialised = 1; | ||||
| 	} | ||||
|  | ||||
| 	*desc = NULL; | ||||
| 	*when = 0; | ||||
|  | ||||
| 	if (!(cf = create_config_tree())) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if ((!dev && !read_config_file(cf, file)) || | ||||
| 	    (dev && !read_config_fd(cf, dev, offset, size, | ||||
| 				    offset2, size2, checksum_fn, checksum))) { | ||||
| 		log_error("Couldn't read volume group metadata."); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/*  | ||||
| 	 * Find a set of version functions that can read this file | ||||
| 	 */ | ||||
| 	for (vsn = &_text_vsn_list[0]; *vsn; vsn++) { | ||||
| 		if (!(*vsn)->check_version(cf)) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!(vg = (*vsn)->read_vg(fid, cf))) { | ||||
| 			stack; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		(*vsn)->read_desc(fid->fmt->cmd->mem, cf, when, desc); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
|       out: | ||||
| 	destroy_config_tree(cf); | ||||
| 	return vg; | ||||
| } | ||||
|  | ||||
| struct volume_group *text_vg_import_file(struct format_instance *fid, | ||||
| 					 const char *file, | ||||
| 					 time_t *when, char **desc) | ||||
| { | ||||
| 	return text_vg_import_fd(fid, file, NULL, 0, 0, 0, 0, NULL, 0, | ||||
| 				 when, desc); | ||||
| } | ||||
							
								
								
									
										810
									
								
								lib/format_text/import_vsn1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										810
									
								
								lib/format_text/import_vsn1.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,810 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "metadata.h" | ||||
| #include "import-export.h" | ||||
| #include "pool.h" | ||||
| #include "display.h" | ||||
| #include "hash.h" | ||||
| #include "toolcontext.h" | ||||
| #include "lvmcache.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); | ||||
|  | ||||
| #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_tree *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 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; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Convert the uuid into a device. | ||||
| 	 */ | ||||
| 	if (!(pv->dev = device_from_pvid(fid->fmt->cmd, &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 = vg->extent_size * (uint64_t) pv->pe_count; | ||||
| 	pv->pe_alloc_count = 0; | ||||
| 	pv->fmt = fid->fmt; | ||||
|  | ||||
| 	vg->pv_count++; | ||||
| 	list_add(&vg->pvs, &pvl->list); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _insert_segment(struct logical_volume *lv, struct lv_segment *seg) | ||||
| { | ||||
| 	struct list *segh; | ||||
| 	struct lv_segment *comp; | ||||
|  | ||||
| 	list_iterate(segh, &lv->segments) { | ||||
| 		comp = list_item(segh, struct lv_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) | ||||
| { | ||||
| 	unsigned int s; | ||||
| 	uint32_t area_count = 0; | ||||
| 	struct lv_segment *seg; | ||||
| 	struct config_node *cn; | ||||
| 	struct config_value *cv; | ||||
| 	const char *seg_name = sn->key; | ||||
| 	uint32_t start_extent, extent_count; | ||||
| 	uint32_t chunk_size, extents_moved = 0u, seg_status = 0u; | ||||
| 	const char *org_name, *cow_name; | ||||
| 	struct logical_volume *org, *cow, *lv1; | ||||
| 	segment_type_t segtype; | ||||
|  | ||||
| 	if (!(sn = sn->child)) { | ||||
| 		log_error("Empty segment section."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_int32(sn, "start_extent", &start_extent)) { | ||||
| 		log_error("Couldn't read 'start_extent' for segment '%s'.", | ||||
| 			  sn->key); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_int32(sn, "extent_count", &extent_count)) { | ||||
| 		log_error("Couldn't read 'extent_count' for segment '%s'.", | ||||
| 			  sn->key); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	segtype = SEG_STRIPED;	/* Default */ | ||||
| 	if ((cn = find_config_node(sn, "type", '/'))) { | ||||
| 		cv = cn->v; | ||||
| 		if (!cv || !cv->v.str) { | ||||
| 			log_error("Segment type must be a string."); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		segtype = get_segtype_from_string(cv->v.str); | ||||
| 	} | ||||
|  | ||||
| 	if (segtype == SEG_STRIPED) { | ||||
| 		if (!_read_int32(sn, "stripe_count", &area_count)) { | ||||
| 			log_error("Couldn't read 'stripe_count' for " | ||||
| 				  "segment '%s'.", sn->key); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (segtype == SEG_MIRRORED) { | ||||
| 		if (!_read_int32(sn, "mirror_count", &area_count)) { | ||||
| 			log_error("Couldn't read 'mirror_count' for " | ||||
| 				  "segment '%s'.", sn->key); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (find_config_node(sn, "extents_moved", '/')) { | ||||
| 			if (_read_uint32(sn, "extents_moved", &extents_moved)) | ||||
| 				seg_status |= PVMOVE; | ||||
| 			else { | ||||
| 				log_error("Couldn't read 'extents_moved' for " | ||||
| 					  "segment '%s'.", sn->key); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(seg = pool_zalloc(mem, sizeof(*seg) + | ||||
| 				(sizeof(seg->area[0]) * area_count)))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	seg->lv = lv; | ||||
| 	seg->le = start_extent; | ||||
| 	seg->len = extent_count; | ||||
| 	seg->area_len = extent_count; | ||||
| 	seg->type = segtype; | ||||
| 	seg->status = seg_status; | ||||
| 	seg->extents_moved = extents_moved; | ||||
|  | ||||
| 	switch (segtype) { | ||||
| 	case SEG_SNAPSHOT: | ||||
| 		lv->status |= SNAPSHOT; | ||||
|  | ||||
| 		if (!_read_uint32(sn, "chunk_size", &chunk_size)) { | ||||
| 			log_error("Couldn't read chunk size for snapshot."); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		log_suppress(1); | ||||
|  | ||||
| 		if (!(cow_name = find_config_str(sn, "cow_store", '/', NULL))) { | ||||
| 			log_suppress(0); | ||||
| 			log_error("Snapshot cow storage not specified."); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (!(org_name = find_config_str(sn, "origin", '/', NULL))) { | ||||
| 			log_suppress(0); | ||||
| 			log_error("Snapshot origin not specified."); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		log_suppress(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, &lv->lvid.id[1], chunk_size)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	case SEG_STRIPED: | ||||
| 		if ((area_count != 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, "stripes", '/'))) { | ||||
| 			log_error("Couldn't find stripes array for segment " | ||||
| 				  "'%s'.", sn->key); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		seg->area_len /= area_count; | ||||
|  | ||||
| 	case SEG_MIRRORED: | ||||
| 		seg->area_count = area_count; | ||||
|  | ||||
| 		if (!seg->area_count) { | ||||
| 			log_error("Zero areas not allowed for segment '%s'", | ||||
| 				  sn->key); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if ((seg->type == SEG_MIRRORED) && | ||||
| 		    !(cn = find_config_node(sn, "mirrors", '/'))) { | ||||
| 			log_error("Couldn't find mirrors array for segment " | ||||
| 				  "'%s'.", sn->key); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		for (cv = cn->v, s = 0; cv && s < seg->area_count; | ||||
| 		     s++, cv = cv->next) { | ||||
|  | ||||
| 			/* first we read the pv */ | ||||
| 			const char *bad = "Badly formed areas array for " | ||||
| 			    "segment '%s'."; | ||||
| 			struct physical_volume *pv; | ||||
|  | ||||
| 			if (cv->type != CFG_STRING) { | ||||
| 				log_error(bad, sn->key); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			if (!cv->next) { | ||||
| 				log_error(bad, sn->key); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			if (cv->next->type != CFG_INT) { | ||||
| 				log_error(bad, sn->key); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			/* FIXME Cope if LV not yet read in */ | ||||
| 			if ((pv = hash_lookup(pv_hash, cv->v.str))) { | ||||
| 				seg->area[s].type = AREA_PV; | ||||
| 				seg->area[s].u.pv.pv = pv; | ||||
| 				seg->area[s].u.pv.pe = cv->next->v.i; | ||||
| 				/* | ||||
| 				 * Adjust extent counts in the pv and vg. | ||||
| 				 */ | ||||
| 				pv->pe_alloc_count += seg->area_len; | ||||
| 				vg->free_count -= seg->area_len; | ||||
|  | ||||
| 			} else if ((lv1 = find_lv(vg, cv->v.str))) { | ||||
| 				seg->area[s].type = AREA_LV; | ||||
| 				seg->area[s].u.lv.lv = lv1; | ||||
| 				seg->area[s].u.lv.le = cv->next->v.i; | ||||
| 			} else { | ||||
| 				log_error("Couldn't find volume '%s' " | ||||
| 					  "for segment '%s'.", | ||||
| 					  cv->v.str ? cv->v.str : "NULL", | ||||
| 					  seg_name); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			cv = cv->next; | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		 * Check we read the correct number of stripes. | ||||
| 		 */ | ||||
| 		if (cv || (s < seg->area_count)) { | ||||
| 			log_error("Incorrect number of areas 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++; | ||||
| 		} | ||||
| 		/* FIXME Remove this restriction */ | ||||
| 		if ((lv->status & SNAPSHOT) && count > 1) { | ||||
| 			log_error("Only one segment permitted for snapshot"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	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_lvnames(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 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; | ||||
| 	} | ||||
|  | ||||
| 	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->alloc = ALLOC_DEFAULT; | ||||
| 	if ((cn = find_config_node(lvn, "allocation_policy", '/'))) { | ||||
| 		struct config_value *cv = cn->v; | ||||
| 		if (!cv || !cv->v.str) { | ||||
| 			log_error("allocation_policy must be a string."); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		lv->alloc = get_alloc_from_string(cv->v.str); | ||||
| 	} | ||||
|  | ||||
| 	/* read_ahead defaults to 0 */ | ||||
| 	if (!_read_int32(lvn, "read_ahead", &lv->read_ahead)) | ||||
| 		lv->read_ahead = 0; | ||||
|  | ||||
| 	list_init(&lv->segments); | ||||
|  | ||||
| 	lv->vg = vg; | ||||
| 	vg->lv_count++; | ||||
| 	list_add(&vg->lvs, &lvl->list); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_lvsegs(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 logical_volume *lv; | ||||
| 	struct lv_list *lvl; | ||||
|  | ||||
| 	if (!(lvl = find_lv_in_vg(vg, lvn->key))) { | ||||
| 		log_error("Lost logical volume reference %s", lvn->key); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	lv = lvl->lv; | ||||
|  | ||||
| 	if (!(lvn = lvn->child)) { | ||||
| 		log_error("Empty logical volume section."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* 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 (!_read_segments(mem, vg, lv, lvn, pv_hash)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	lv->size = (uint64_t) lv->le_count * (uint64_t) vg->extent_size; | ||||
|  | ||||
| 	/* Skip this for now for snapshots */ | ||||
| 	if (!(lv->status & SNAPSHOT)) { | ||||
| 		lv->minor = -1; | ||||
| 		if ((lv->status & FIXED_MINOR) && | ||||
| 		    !_read_int32(lvn, "minor", &lv->minor)) { | ||||
| 			log_error("Couldn't read minor number for logical " | ||||
| 				  "volume %s.", lv->name); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		lv->major = -1; | ||||
| 		if ((lv->status & FIXED_MINOR) && | ||||
| 		    !_read_int32(lvn, "major", &lv->major)) { | ||||
| 			log_error("Couldn't read major number for logical " | ||||
| 				  "volume %s.", lv->name); | ||||
| 		} | ||||
| 	} else { | ||||
| 		vg->lv_count--; | ||||
| 		list_del(&lvl->list); | ||||
| 	} | ||||
|  | ||||
| 	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, 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)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct volume_group *_read_vg(struct format_instance *fid, | ||||
| 				     struct config_tree *cf) | ||||
| { | ||||
| 	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, 0)) { | ||||
| 		log_error("Couldn't find all physical volumes for volume " | ||||
| 			  "group %s.", vg->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	list_init(&vg->lvs); | ||||
| 	list_init(&vg->snapshots); | ||||
|  | ||||
| 	if (!_read_sections(fid, "logical_volumes", _read_lvnames, mem, vg, | ||||
| 			    vgn, pv_hash, 1)) { | ||||
| 		log_error("Couldn't read all logical volume names for volume " | ||||
| 			  "group %s.", vg->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_sections(fid, "logical_volumes", _read_lvsegs, mem, vg, | ||||
| 			    vgn, pv_hash, 1)) { | ||||
| 		log_error("Couldn't read all logical volumes 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_tree *cf, time_t *when, char **desc) | ||||
| { | ||||
| 	const char *d; | ||||
| 	unsigned int u = 0u; | ||||
|  | ||||
| 	log_suppress(1); | ||||
| 	d = find_config_str(cf->root, "description", '/', ""); | ||||
| 	log_suppress(0); | ||||
| 	*desc = pool_strdup(mem, d); | ||||
|  | ||||
| 	get_config_uint32(cf->root, "creation_time", '/', &u); | ||||
| 	*when = u; | ||||
| } | ||||
|  | ||||
| static struct text_vg_version_ops _vsn1_ops = { | ||||
| 	check_version:_check_version, | ||||
| 	read_vg:_read_vg, | ||||
| 	read_desc:_read_desc | ||||
| }; | ||||
|  | ||||
| struct text_vg_version_ops *text_vg_vsn1_init(void) | ||||
| { | ||||
| 	return &_vsn1_ops; | ||||
| }; | ||||
							
								
								
									
										76
									
								
								lib/format_text/layout.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								lib/format_text/layout.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_TEXT_LAYOUT_H | ||||
| #define _LVM_TEXT_LAYOUT_H | ||||
|  | ||||
| #include "config.h" | ||||
| #include "lvm-types.h" | ||||
| #include "metadata.h" | ||||
| #include "uuid.h" | ||||
|  | ||||
| /* On disk */ | ||||
| struct disk_locn { | ||||
| 	uint64_t offset;	/* Offset in bytes to start sector */ | ||||
| 	uint64_t size;		/* Bytes */ | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| /* Data areas (holding PEs) */ | ||||
| struct data_area_list { | ||||
| 	struct list list; | ||||
| 	struct disk_locn disk_locn; | ||||
| }; | ||||
|  | ||||
| /* Fields with the suffix _xl should be xlate'd wherever they appear */ | ||||
| /* On disk */ | ||||
| struct pv_header { | ||||
| 	uint8_t pv_uuid[ID_LEN]; | ||||
| 	uint64_t device_size_xl;	/* Bytes */ | ||||
|  | ||||
| 	/* NULL-terminated list of data areas followed by */ | ||||
| 	/* NULL-terminated list of metadata area headers */ | ||||
| 	struct disk_locn disk_areas_xl[0];	/* Two lists */ | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| /* On disk */ | ||||
| struct raw_locn { | ||||
| 	uint64_t offset;	/* Offset in bytes to start sector */ | ||||
| 	uint64_t size;		/* Bytes */ | ||||
| 	uint32_t checksum; | ||||
| 	uint32_t filler; | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| /* On disk */ | ||||
| /* Structure size limited to one sector */ | ||||
| struct mda_header { | ||||
| 	uint32_t checksum_xl;	/* Checksum of rest of mda_header */ | ||||
| 	uint8_t magic[16];	/* To aid scans for metadata */ | ||||
| 	uint32_t version; | ||||
| 	uint64_t start;		/* Absolute start byte of mda_header */ | ||||
| 	uint64_t size;		/* Size of metadata area */ | ||||
|  | ||||
| 	struct raw_locn raw_locns[0];	/* NULL-terminated list */ | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| struct mda_lists { | ||||
| 	struct list dirs; | ||||
| 	struct list raws; | ||||
| 	struct metadata_area_ops *file_ops; | ||||
| 	struct metadata_area_ops *raw_ops; | ||||
| }; | ||||
|  | ||||
| struct mda_context { | ||||
| 	struct device_area area; | ||||
| 	struct raw_locn rlocn;	/* Store inbetween write and commit */ | ||||
| }; | ||||
|  | ||||
| /* FIXME Convert this at runtime */ | ||||
| #define FMTT_MAGIC "\040\114\126\115\062\040\170\133\065\101\045\162\060\116\052\076" | ||||
| #define FMTT_VERSION 1 | ||||
| #define MDA_HEADER_SIZE 512 | ||||
| #define LVM2_LABEL "LVM2 001" | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										280
									
								
								lib/format_text/text_label.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								lib/format_text/text_label.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "format-text.h" | ||||
| #include "layout.h" | ||||
| #include "label.h" | ||||
| #include "xlate.h" | ||||
|  | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
|  | ||||
| static int _can_handle(struct labeller *l, char *buf, uint64_t sector) | ||||
| { | ||||
| 	struct label_header *lh = (struct label_header *) buf; | ||||
|  | ||||
| 	if (!strncmp(lh->type, LVM2_LABEL, sizeof(lh->type))) | ||||
| 		return 1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int _write(struct label *label, char *buf) | ||||
| { | ||||
| 	struct label_header *lh = (struct label_header *) buf; | ||||
| 	struct pv_header *pvhdr; | ||||
| 	struct lvmcache_info *info; | ||||
| 	struct disk_locn *pvh_dlocn_xl; | ||||
| 	struct list *mdash, *dash; | ||||
| 	struct metadata_area *mda; | ||||
| 	struct mda_context *mdac; | ||||
| 	struct data_area_list *da; | ||||
|  | ||||
| 	/* FIXME Move to where label is created */ | ||||
| 	strncpy(label->type, LVM2_LABEL, sizeof(label->type)); | ||||
|  | ||||
| 	strncpy(lh->type, label->type, sizeof(label->type)); | ||||
|  | ||||
| 	pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl)); | ||||
| 	info = (struct lvmcache_info *) label->info; | ||||
| 	pvhdr->device_size_xl = xlate64(info->device_size); | ||||
| 	memcpy(pvhdr->pv_uuid, &info->dev->pvid, sizeof(struct id)); | ||||
|  | ||||
| 	pvh_dlocn_xl = &pvhdr->disk_areas_xl[0]; | ||||
|  | ||||
| 	/* List of data areas (holding PEs) */ | ||||
| 	list_iterate(dash, &info->das) { | ||||
| 		da = list_item(dash, struct data_area_list); | ||||
|  | ||||
| 		pvh_dlocn_xl->offset = xlate64(da->disk_locn.offset); | ||||
| 		pvh_dlocn_xl->size = xlate64(da->disk_locn.size); | ||||
| 		pvh_dlocn_xl++; | ||||
| 	} | ||||
|  | ||||
| 	/* NULL-termination */ | ||||
| 	pvh_dlocn_xl->offset = xlate64(UINT64_C(0)); | ||||
| 	pvh_dlocn_xl->size = xlate64(UINT64_C(0)); | ||||
| 	pvh_dlocn_xl++; | ||||
|  | ||||
| 	/* List of metadata area header locations */ | ||||
| 	list_iterate(mdash, &info->mdas) { | ||||
| 		mda = list_item(mdash, struct metadata_area); | ||||
| 		mdac = (struct mda_context *) mda->metadata_locn; | ||||
|  | ||||
| 		if (mdac->area.dev != info->dev) | ||||
| 			continue; | ||||
|  | ||||
| 		pvh_dlocn_xl->offset = xlate64(mdac->area.start); | ||||
| 		pvh_dlocn_xl->size = xlate64(mdac->area.size); | ||||
| 		pvh_dlocn_xl++; | ||||
| 	} | ||||
|  | ||||
| 	/* NULL-termination */ | ||||
| 	pvh_dlocn_xl->offset = xlate64(UINT64_C(0)); | ||||
| 	pvh_dlocn_xl->size = xlate64(UINT64_C(0)); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int add_da(const struct format_type *fmt, struct pool *mem, struct list *das, | ||||
| 	   uint64_t start, uint64_t size) | ||||
| { | ||||
| 	struct data_area_list *dal; | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		if (!(dal = dbg_malloc(sizeof(*dal)))) { | ||||
| 			log_error("struct data_area_list allocation failed"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (!(dal = pool_alloc(mem, sizeof(*dal)))) { | ||||
| 			log_error("struct data_area_list allocation failed"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	dal->disk_locn.offset = start; | ||||
| 	dal->disk_locn.size = size; | ||||
|  | ||||
| 	list_add(das, &dal->list); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void del_das(struct list *das) | ||||
| { | ||||
| 	struct list *dah, *tmp; | ||||
| 	struct data_area_list *da; | ||||
|  | ||||
| 	list_iterate_safe(dah, tmp, das) { | ||||
| 		da = list_item(dah, struct data_area_list); | ||||
| 		list_del(&da->list); | ||||
| 		dbg_free(da); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int add_mda(const struct format_type *fmt, struct pool *mem, struct list *mdas, | ||||
| 	    struct device *dev, uint64_t start, uint64_t size) | ||||
| { | ||||
| /* FIXME List size restricted by pv_header SECTOR_SIZE */ | ||||
| 	struct metadata_area *mdal; | ||||
| 	struct mda_lists *mda_lists = (struct mda_lists *) fmt->private; | ||||
| 	struct mda_context *mdac; | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		if (!(mdal = dbg_malloc(sizeof(struct metadata_area)))) { | ||||
| 			log_error("struct mda_list allocation failed"); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (!(mdac = dbg_malloc(sizeof(struct mda_context)))) { | ||||
| 			log_error("struct mda_context allocation failed"); | ||||
| 			dbg_free(mdal); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (!(mdal = pool_alloc(mem, sizeof(struct metadata_area)))) { | ||||
| 			log_error("struct mda_list allocation failed"); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (!(mdac = pool_alloc(mem, sizeof(struct mda_context)))) { | ||||
| 			log_error("struct mda_context allocation failed"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	mdal->ops = mda_lists->raw_ops; | ||||
| 	mdal->metadata_locn = mdac; | ||||
|  | ||||
| 	mdac->area.dev = dev; | ||||
| 	mdac->area.start = start; | ||||
| 	mdac->area.size = size; | ||||
| 	memset(&mdac->rlocn, 0, sizeof(mdac->rlocn)); | ||||
|  | ||||
| 	list_add(mdas, &mdal->list); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void del_mdas(struct list *mdas) | ||||
| { | ||||
| 	struct list *mdah, *tmp; | ||||
| 	struct metadata_area *mda; | ||||
|  | ||||
| 	list_iterate_safe(mdah, tmp, mdas) { | ||||
| 		mda = list_item(mdah, struct metadata_area); | ||||
| 		dbg_free(mda->metadata_locn); | ||||
| 		list_del(&mda->list); | ||||
| 		dbg_free(mda); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int _initialise_label(struct labeller *l, struct label *label) | ||||
| { | ||||
| 	strncpy(label->type, LVM2_LABEL, sizeof(label->type)); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read(struct labeller *l, struct device *dev, char *buf, | ||||
| 		 struct label **label) | ||||
| { | ||||
| 	struct label_header *lh = (struct label_header *) buf; | ||||
| 	struct pv_header *pvhdr; | ||||
| 	struct lvmcache_info *info; | ||||
| 	struct disk_locn *dlocn_xl; | ||||
| 	uint64_t offset; | ||||
| 	struct list *mdah; | ||||
| 	struct metadata_area *mda; | ||||
| 	char vgnamebuf[NAME_LEN + 2]; | ||||
| 	struct mda_context *mdac; | ||||
|  | ||||
| 	pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl)); | ||||
|  | ||||
| 	if (!(info = lvmcache_add(l, pvhdr->pv_uuid, dev, NULL, NULL))) | ||||
| 		return 0; | ||||
| 	*label = info->label; | ||||
|  | ||||
| 	info->device_size = xlate64(pvhdr->device_size_xl); | ||||
|  | ||||
| 	if (info->das.n) | ||||
| 		del_das(&info->das); | ||||
| 	list_init(&info->das); | ||||
|  | ||||
| 	if (info->mdas.n) | ||||
| 		del_mdas(&info->mdas); | ||||
| 	list_init(&info->mdas); | ||||
|  | ||||
| 	/* Data areas holding the PEs */ | ||||
| 	dlocn_xl = pvhdr->disk_areas_xl; | ||||
| 	while ((offset = xlate64(dlocn_xl->offset))) { | ||||
| 		add_da(info->fmt, NULL, &info->das, offset, | ||||
| 		       xlate64(dlocn_xl->size)); | ||||
| 		dlocn_xl++; | ||||
| 	} | ||||
|  | ||||
| 	/* Metadata area headers */ | ||||
| 	dlocn_xl++; | ||||
| 	while ((offset = xlate64(dlocn_xl->offset))) { | ||||
| 		add_mda(info->fmt, NULL, &info->mdas, dev, offset, | ||||
| 			xlate64(dlocn_xl->size)); | ||||
| 		dlocn_xl++; | ||||
| 	} | ||||
|  | ||||
| 	list_iterate(mdah, &info->mdas) { | ||||
| 		mda = list_item(mdah, struct metadata_area); | ||||
| 		mdac = (struct mda_context *) mda->metadata_locn; | ||||
| 		if (vgname_from_mda(info->fmt, &mdac->area, vgnamebuf, | ||||
| 				    sizeof(vgnamebuf))) { | ||||
| 			lvmcache_update_vgname(info, vgnamebuf); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	info->status &= ~CACHE_INVALID; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _destroy_label(struct labeller *l, struct label *label) | ||||
| { | ||||
| 	struct lvmcache_info *info = (struct lvmcache_info *) label->info; | ||||
|  | ||||
| 	if (info->mdas.n) | ||||
| 		del_mdas(&info->mdas); | ||||
| 	if (info->das.n) | ||||
| 		del_das(&info->das); | ||||
| } | ||||
|  | ||||
| static void _destroy(struct labeller *l) | ||||
| { | ||||
| 	dbg_free(l); | ||||
| } | ||||
|  | ||||
| struct label_ops _text_ops = { | ||||
| 	can_handle:_can_handle, | ||||
| 	write:_write, | ||||
| 	read:_read, | ||||
| 	verify:_can_handle, | ||||
| 	initialise_label:_initialise_label, | ||||
| 	destroy_label:_destroy_label, | ||||
| 	destroy:_destroy | ||||
| }; | ||||
|  | ||||
| struct labeller *text_labeller_create(const struct format_type *fmt) | ||||
| { | ||||
| 	struct labeller *l; | ||||
|  | ||||
| 	if (!(l = dbg_malloc(sizeof(*l)))) { | ||||
| 		log_err("Couldn't allocate labeller object."); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	l->ops = &_text_ops; | ||||
| 	l->private = (const void *) fmt; | ||||
|  | ||||
| 	return l; | ||||
| } | ||||
							
								
								
									
										361
									
								
								lib/label/label.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								lib/label/label.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,361 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "label.h" | ||||
| #include "list.h" | ||||
| #include "crc.h" | ||||
| #include "xlate.h" | ||||
| #include "lvmcache.h" | ||||
|  | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| /* FIXME Allow for larger labels?  Restricted to single sector currently */ | ||||
|  | ||||
| /* | ||||
|  * 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); | ||||
| 		li->l->ops->destroy(li->l); | ||||
| 		_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, char *buf, | ||||
| 				       uint64_t *label_sector) | ||||
| { | ||||
| 	struct list *lih; | ||||
| 	struct labeller_i *li; | ||||
| 	struct labeller *r = NULL; | ||||
| 	struct label_header *lh; | ||||
| 	uint64_t sector; | ||||
| 	int found = 0; | ||||
| 	char readbuf[LABEL_SCAN_SIZE]; | ||||
|  | ||||
| 	if (!dev_open(dev)) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) { | ||||
| 		log_debug("%s: Failed to read label area", dev_name(dev)); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* Scan first few sectors for a valid label */ | ||||
| 	for (sector = 0; sector < LABEL_SCAN_SECTORS; | ||||
| 	     sector += LABEL_SIZE >> SECTOR_SHIFT) { | ||||
| 		lh = (struct label_header *) (readbuf + | ||||
| 					      (sector << SECTOR_SHIFT)); | ||||
|  | ||||
| 		if (!strncmp(lh->id, LABEL_ID, sizeof(lh->id))) { | ||||
| 			if (found) { | ||||
| 				log_error("Ignoring additional label on %s at " | ||||
| 					  "sector %" PRIu64, dev_name(dev), | ||||
| 					  sector); | ||||
| 			} | ||||
| 			if (xlate64(lh->sector_xl) != sector) { | ||||
| 				log_info("%s: Label for sector %" PRIu64 | ||||
| 					 " found at sector %" PRIu64 | ||||
| 					 " - ignoring", dev_name(dev), | ||||
| 					 xlate64(lh->sector_xl), sector); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE - | ||||
| 				     ((void *) &lh->offset_xl - (void *) lh)) != | ||||
| 			    xlate32(lh->crc_xl)) { | ||||
| 				log_info("Label checksum incorrect on %s - " | ||||
| 					 "ignoring", dev_name(dev)); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (found) | ||||
| 				continue; | ||||
| 		} | ||||
|  | ||||
| 		list_iterate(lih, &_labellers) { | ||||
| 			li = list_item(lih, struct labeller_i); | ||||
| 			if (li->l->ops->can_handle(li->l, (char *) lh, sector)) { | ||||
| 				log_very_verbose("%s: %s label detected", | ||||
| 						 dev_name(dev), li->name); | ||||
| 				if (found) { | ||||
| 					log_error("Ignoring additional label " | ||||
| 						  "on %s at sector %" PRIu64, | ||||
| 						  dev_name(dev), sector); | ||||
| 					continue; | ||||
| 				} | ||||
| 				r = li->l; | ||||
| 				memcpy(buf, lh, LABEL_SIZE); | ||||
| 				if (label_sector) | ||||
| 					*label_sector = sector; | ||||
| 				found = 1; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!found) | ||||
| 		log_very_verbose("%s: No label detected", dev_name(dev)); | ||||
|  | ||||
|       out: | ||||
| 	if (!dev_close(dev)) | ||||
| 		stack; | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* FIXME Also wipe associated metadata area headers? */ | ||||
| int label_remove(struct device *dev) | ||||
| { | ||||
| 	char buf[LABEL_SIZE]; | ||||
| 	char readbuf[LABEL_SCAN_SIZE]; | ||||
| 	int r = 1; | ||||
| 	uint64_t sector; | ||||
| 	int wipe; | ||||
| 	struct list *lih; | ||||
| 	struct labeller_i *li; | ||||
| 	struct label_header *lh; | ||||
|  | ||||
| 	memset(buf, 0, LABEL_SIZE); | ||||
|  | ||||
| 	log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev)); | ||||
|  | ||||
| 	if (!dev_open(dev)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * We flush the device just in case someone is stupid | ||||
| 	 * enough to be trying to import an open pv into lvm. | ||||
| 	 */ | ||||
| 	dev_flush(dev); | ||||
|  | ||||
| 	if (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) { | ||||
| 		log_debug("%s: Failed to read label area", dev_name(dev)); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* Scan first few sectors for anything looking like a label */ | ||||
| 	for (sector = 0; sector < LABEL_SCAN_SECTORS; | ||||
| 	     sector += LABEL_SIZE >> SECTOR_SHIFT) { | ||||
| 		lh = (struct label_header *) (readbuf + | ||||
| 					      (sector << SECTOR_SHIFT)); | ||||
|  | ||||
| 		wipe = 0; | ||||
|  | ||||
| 		if (!strncmp(lh->id, LABEL_ID, sizeof(lh->id))) { | ||||
| 			if (xlate64(lh->sector_xl) == sector) | ||||
| 				wipe = 1; | ||||
| 		} else { | ||||
| 			list_iterate(lih, &_labellers) { | ||||
| 				li = list_item(lih, struct labeller_i); | ||||
| 				if (li->l->ops->can_handle(li->l, (char *) lh, | ||||
| 							   sector)) { | ||||
| 					wipe = 1; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (wipe) { | ||||
| 			log_info("%s: Wiping label at sector %" PRIu64, | ||||
| 				 dev_name(dev), sector); | ||||
| 			if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE, | ||||
| 				       buf)) { | ||||
| 				log_error("Failed to remove label from %s at " | ||||
| 					  "sector %" PRIu64, dev_name(dev), | ||||
| 					  sector); | ||||
| 				r = 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|       out: | ||||
| 	if (!dev_close(dev)) | ||||
| 		stack; | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* FIXME Avoid repeated re-reading if cache lock held */ | ||||
| int label_read(struct device *dev, struct label **result) | ||||
| { | ||||
| 	char buf[LABEL_SIZE]; | ||||
| 	struct labeller *l; | ||||
| 	uint64_t sector; | ||||
| 	int r; | ||||
|  | ||||
| 	if (!(l = _find_labeller(dev, buf, §or))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if ((r = l->ops->read(l, dev, buf, result)) && result && *result) | ||||
| 		(*result)->sector = sector; | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* Caller may need to use label_get_handler to create label struct! */ | ||||
| int label_write(struct device *dev, struct label *label) | ||||
| { | ||||
| 	char buf[LABEL_SIZE]; | ||||
| 	struct label_header *lh = (struct label_header *) buf; | ||||
| 	int r = 1; | ||||
|  | ||||
| 	if (!label->labeller->ops->write) { | ||||
| 		log_err("Label handler does not support label writes"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if ((LABEL_SIZE + (label->sector << SECTOR_SHIFT)) > LABEL_SCAN_SIZE) { | ||||
| 		log_error("Label sector %" PRIu64 " beyond range (%ld)", | ||||
| 			  label->sector, LABEL_SCAN_SECTORS); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	memset(buf, 0, LABEL_SIZE); | ||||
|  | ||||
| 	strncpy(lh->id, LABEL_ID, sizeof(lh->id)); | ||||
| 	lh->sector_xl = xlate64(label->sector); | ||||
| 	lh->offset_xl = xlate32(sizeof(*lh)); | ||||
|  | ||||
| 	if (!label->labeller->ops->write(label, buf)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	lh->crc_xl = xlate32(calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE - | ||||
| 				      ((void *) &lh->offset_xl - (void *) lh))); | ||||
|  | ||||
| 	if (!dev_open(dev)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_info("%s: Writing label to sector %" PRIu64, dev_name(dev), | ||||
| 		 label->sector); | ||||
| 	if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) { | ||||
| 		log_debug("Failed to write label to %s", dev_name(dev)); | ||||
| 		r = 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_close(dev)) | ||||
| 		stack; | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int label_verify(struct device *dev) | ||||
| { | ||||
| 	struct labeller *l; | ||||
| 	char buf[LABEL_SIZE]; | ||||
| 	uint64_t sector; | ||||
|  | ||||
| 	if (!(l = _find_labeller(dev, buf, §or))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return ((l->ops->verify) ? l->ops->verify(l, buf, sector) : 1); | ||||
| } | ||||
|  | ||||
| void label_destroy(struct label *label) | ||||
| { | ||||
| 	label->labeller->ops->destroy_label(label->labeller, label); | ||||
| 	dbg_free(label); | ||||
| } | ||||
|  | ||||
| struct label *label_create(struct labeller *labeller) | ||||
| { | ||||
| 	struct label *label; | ||||
|  | ||||
| 	if (!(label = dbg_malloc(sizeof(*label)))) { | ||||
| 		log_error("label allocaction failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	memset(label, 0, sizeof(*label)); | ||||
|  | ||||
| 	label->labeller = labeller; | ||||
|  | ||||
| 	labeller->ops->initialise_label(labeller, label); | ||||
|  | ||||
| 	return label; | ||||
| } | ||||
							
								
								
									
										95
									
								
								lib/label/label.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								lib/label/label.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_LABEL_H | ||||
| #define _LVM_LABEL_H | ||||
|  | ||||
| #include "lvmcache.h" | ||||
| #include "uuid.h" | ||||
| #include "device.h" | ||||
|  | ||||
| #define LABEL_ID "LABELONE" | ||||
| #define LABEL_SIZE SECTOR_SIZE	/* Think very carefully before changing this */ | ||||
| #define LABEL_SCAN_SECTORS 4L | ||||
| #define LABEL_SCAN_SIZE (LABEL_SCAN_SECTORS << SECTOR_SHIFT) | ||||
|  | ||||
| /* On disk - 32 bytes */ | ||||
| struct label_header { | ||||
| 	uint8_t id[8];		/* LABELONE */ | ||||
| 	uint64_t sector_xl;	/* Sector number of this label */ | ||||
| 	uint32_t crc_xl;	/* From next field to end of sector */ | ||||
| 	uint32_t offset_xl;	/* Offset from start of struct to contents */ | ||||
| 	uint8_t type[8];	/* LVM2 001 */ | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| /* In core */ | ||||
| struct label { | ||||
| 	char type[8]; | ||||
| 	uint64_t sector; | ||||
| 	struct labeller *labeller; | ||||
| 	void *info; | ||||
| }; | ||||
|  | ||||
| struct labeller; | ||||
|  | ||||
| struct label_ops { | ||||
| 	/* | ||||
| 	 * Is the device labelled with this format ? | ||||
| 	 */ | ||||
| 	int (*can_handle) (struct labeller * l, char *buf, uint64_t sector); | ||||
|  | ||||
| 	/* | ||||
| 	 * Write a label to a volume. | ||||
| 	 */ | ||||
| 	int (*write) (struct label * label, char *buf); | ||||
|  | ||||
| 	/* | ||||
| 	 * Read a label from a volume. | ||||
| 	 */ | ||||
| 	int (*read) (struct labeller * l, struct device * dev, | ||||
| 		     char *buf, struct label ** label); | ||||
|  | ||||
| 	/* | ||||
| 	 * Additional consistency checks for the paranoid. | ||||
| 	 */ | ||||
| 	int (*verify) (struct labeller * l, char *buf, uint64_t sector); | ||||
|  | ||||
| 	/* | ||||
| 	 * Populate label_type etc. | ||||
| 	 */ | ||||
| 	int (*initialise_label) (struct labeller * l, struct label * label); | ||||
|  | ||||
| 	/* | ||||
| 	 * 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; | ||||
| 	const 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_write(struct device *dev, struct label *label); | ||||
| int label_verify(struct device *dev); | ||||
| struct label *label_create(struct labeller *labeller); | ||||
| void label_destroy(struct label *label); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										74
									
								
								lib/locking/external_locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								lib/locking/external_locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "locking_types.h" | ||||
| #include "defaults.h" | ||||
| #include "sharedlib.h" | ||||
|  | ||||
| static void *_locking_lib = 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_tree * 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_lib); | ||||
|  | ||||
| 	_locking_lib = NULL; | ||||
| 	_init_fn = NULL; | ||||
| 	_end_fn = NULL; | ||||
| 	_lock_fn = NULL; | ||||
| } | ||||
|  | ||||
| int init_external_locking(struct locking_type *locking, struct config_tree *cf) | ||||
| { | ||||
| 	const char *libname; | ||||
|  | ||||
| 	if (_locking_lib) { | ||||
| 		log_error("External locking already initialised"); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	locking->lock_resource = _lock_resource; | ||||
| 	locking->fin_locking = _fin_external_locking; | ||||
|  | ||||
| 	libname = find_config_str(cf->root, "global/locking_library", '/', | ||||
| 				  DEFAULT_LOCKING_LIB); | ||||
|  | ||||
| 	if (!(_locking_lib = load_shared_library(cf, libname, "locking"))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Get the functions we need */ | ||||
| 	if (!(_init_fn = dlsym(_locking_lib, "init_locking")) || | ||||
| 	    !(_lock_fn = dlsym(_locking_lib, "lock_resource")) || | ||||
| 	    !(_end_fn = dlsym(_locking_lib, "end_locking"))) { | ||||
| 		log_error("Shared library %s does not contain locking " | ||||
| 			  "functions", libname); | ||||
| 		dlclose(_locking_lib); | ||||
| 		_locking_lib = NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_verbose("Loaded external locking library %s", libname); | ||||
| 	return _init_fn(2, cf); | ||||
| } | ||||
							
								
								
									
										286
									
								
								lib/locking/file_locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								lib/locking/file_locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,286 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "lib.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 "lvmcache.h" | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <unistd.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, int unlock) | ||||
| { | ||||
| 	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); | ||||
| 			if (unlock) { | ||||
| 				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; | ||||
| } | ||||
|  | ||||
| static void _fin_file_locking(void) | ||||
| { | ||||
| 	_release_lock(NULL, 1); | ||||
| } | ||||
|  | ||||
| static void _reset_file_locking(void) | ||||
| { | ||||
| 	_release_lock(NULL, 0); | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| static void _trap_ctrl_c(int sig) | ||||
| { | ||||
| 	_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; | ||||
| 	char state; | ||||
|  | ||||
| 	switch (flags & LCK_TYPE_MASK) { | ||||
| 	case LCK_READ: | ||||
| 		operation = LOCK_SH; | ||||
| 		state = 'R'; | ||||
| 		break; | ||||
| 	case LCK_WRITE: | ||||
| 		operation = LOCK_EX; | ||||
| 		state = 'W'; | ||||
| 		break; | ||||
| 	case LCK_UNLOCK: | ||||
| 		return _release_lock(file, 1); | ||||
| 	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 %c%c", ll->res, state, | ||||
| 			 flags & LCK_NONBLOCK ? ' ' : 'B'); | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| static 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; | ||||
|  | ||||
| 		switch (flags & LCK_TYPE_MASK) { | ||||
| 		case LCK_UNLOCK: | ||||
| 			lvmcache_unlock_vgname(resource); | ||||
| 			break; | ||||
| 		default: | ||||
| 			lvmcache_lock_vgname(resource, | ||||
| 					     (flags & LCK_TYPE_MASK) == | ||||
| 					     LCK_READ); | ||||
| 		} | ||||
| 		break; | ||||
| 	case LCK_LV: | ||||
| 		switch (flags & LCK_TYPE_MASK) { | ||||
| 		case LCK_UNLOCK: | ||||
| 			log_debug("Unlocking LV %s", resource); | ||||
| 			if (!lv_resume_if_active(cmd, resource)) | ||||
| 				return 0; | ||||
| 			break; | ||||
| 		case LCK_READ: | ||||
| 			log_debug("Locking LV %s (R)", resource); | ||||
| 			if (!lv_activate(cmd, resource)) | ||||
| 				return 0; | ||||
| 			break; | ||||
| 		case LCK_WRITE: | ||||
| 			log_debug("Locking LV %s (W)", resource); | ||||
| 			if (!lv_suspend_if_active(cmd, resource)) | ||||
| 				return 0; | ||||
| 			break; | ||||
| 		case LCK_EXCL: | ||||
| 			log_debug("Locking LV %s (EX)", resource); | ||||
| 			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_tree *cf) | ||||
| { | ||||
| 	locking->lock_resource = _file_lock_resource; | ||||
| 	locking->reset_locking = _reset_file_locking; | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										211
									
								
								lib/locking/locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								lib/locking/locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "locking.h" | ||||
| #include "locking_types.h" | ||||
| #include "lvm-string.h" | ||||
| #include "activate.h" | ||||
| #include "toolcontext.h" | ||||
|  | ||||
| #include <signal.h> | ||||
| #include <sys/stat.h> | ||||
| #include <limits.h> | ||||
| #include <unistd.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(int flags) | ||||
| { | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| void reset_locking(void) | ||||
| { | ||||
| 	int was_locked = _lock_count; | ||||
|  | ||||
| 	_lock_count = 0; | ||||
|  | ||||
| 	_locking.reset_locking(); | ||||
|  | ||||
| 	if (was_locked) | ||||
| 		_unblock_signals(); | ||||
| } | ||||
|  | ||||
| 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_tree *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; | ||||
|  | ||||
| #ifdef HAVE_LIBDL | ||||
| 	case 2: | ||||
| 		if (!init_external_locking(&_locking, cf)) | ||||
| 			break; | ||||
| 		log_very_verbose("External locking enabled."); | ||||
| 		return 1; | ||||
| #endif | ||||
|  | ||||
| 	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]; | ||||
|  | ||||
| 	/* We'll allow operations on orphans */ | ||||
| 	if (!*vgname) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (lvm_snprintf(path, sizeof(path), "%s/lvm/VGs/%s", cmd->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. | ||||
|  */ | ||||
| static int _lock_vol(struct cmd_context *cmd, const char *resource, int flags) | ||||
| { | ||||
| 	_block_signals(flags); | ||||
|  | ||||
| 	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, 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; | ||||
| } | ||||
							
								
								
									
										72
									
								
								lib/locking/locking.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lib/locking/locking.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| /* | ||||
|  * 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_tree *cf); | ||||
| void fin_locking(void); | ||||
| void reset_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) | ||||
							
								
								
									
										31
									
								
								lib/locking/locking_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/locking/locking_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| /* | ||||
|  * 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); | ||||
| typedef void (*reset_lock_fn) (void); | ||||
|  | ||||
| struct locking_type { | ||||
| 	lock_resource_fn lock_resource; | ||||
|  | ||||
| 	reset_lock_fn reset_locking; | ||||
| 	fin_lock_fn fin_locking; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Locking types | ||||
|  */ | ||||
| int init_no_locking(struct locking_type *locking, struct config_tree *cf); | ||||
|  | ||||
| int init_file_locking(struct locking_type *locking, struct config_tree *cf); | ||||
|  | ||||
| int init_external_locking(struct locking_type *locking, struct config_tree *cf); | ||||
							
								
								
									
										76
									
								
								lib/locking/no_locking.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								lib/locking/no_locking.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "locking.h" | ||||
| #include "locking_types.h" | ||||
| #include "lvm-string.h" | ||||
| #include "activate.h" | ||||
| #include "lvmcache.h" | ||||
|  | ||||
| #include <signal.h> | ||||
|  | ||||
| /* | ||||
|  * No locking | ||||
|  */ | ||||
|  | ||||
| static void _no_fin_locking(void) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static void _no_reset_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: | ||||
| 		switch (flags & LCK_TYPE_MASK) { | ||||
| 		case LCK_UNLOCK: | ||||
| 			lvmcache_unlock_vgname(resource); | ||||
| 			break; | ||||
| 		default: | ||||
| 			lvmcache_lock_vgname(resource, | ||||
| 					     (flags & LCK_TYPE_MASK) == | ||||
| 					     LCK_READ); | ||||
| 		} | ||||
| 		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_tree *cf) | ||||
| { | ||||
| 	locking->lock_resource = _no_lock_resource; | ||||
| 	locking->reset_locking = _no_reset_locking; | ||||
| 	locking->fin_locking = _no_fin_locking; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										291
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,291 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the LGPL. | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "device.h" | ||||
| #include "memlock.h" | ||||
| #include "lvm-string.h" | ||||
|  | ||||
| #include <stdarg.h> | ||||
| #include <syslog.h> | ||||
|  | ||||
| static FILE *_log_file; | ||||
| static struct device _log_dev; | ||||
| static struct str_list _log_dev_alias; | ||||
|  | ||||
| static int _verbose_level = 0; | ||||
| static int _test = 0; | ||||
| static int _partial = 0; | ||||
| static int _pvmove = 0; | ||||
| static int _debug_level = 0; | ||||
| static int _syslog = 0; | ||||
| static int _log_to_file = 0; | ||||
| static int _log_direct = 0; | ||||
| static int _log_while_suspended = 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] = "  "; | ||||
| static int _already_logging = 0; | ||||
|  | ||||
| void init_log_file(const char *log_file, int append) | ||||
| { | ||||
| 	const char *open_mode = append ? "a" : "w"; | ||||
|  | ||||
| 	if (!(_log_file = fopen(log_file, open_mode))) { | ||||
| 		log_sys_error("fopen", log_file); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	_log_to_file = 1; | ||||
| } | ||||
|  | ||||
| void init_log_direct(const char *log_file, int append) | ||||
| { | ||||
| 	const char *filename; | ||||
| 	int open_flags = append ? 0 : O_TRUNC; | ||||
|  | ||||
| 	filename = dbg_strdup(log_file); | ||||
| 	dev_create_file(filename, &_log_dev, &_log_dev_alias); | ||||
| 	if (!dev_open_flags(&_log_dev, O_RDWR | O_CREAT | open_flags, 1, 0)) | ||||
| 		return; | ||||
|  | ||||
| 	_log_direct = 1; | ||||
| } | ||||
|  | ||||
| void init_log_while_suspended(int log_while_suspended) | ||||
| { | ||||
| 	_log_while_suspended = log_while_suspended; | ||||
| } | ||||
|  | ||||
| void init_syslog(int facility) | ||||
| { | ||||
| 	openlog("lvm", LOG_PID, facility); | ||||
| 	_syslog = 1; | ||||
| } | ||||
|  | ||||
| void log_suppress(int suppress) | ||||
| { | ||||
| 	_log_suppress = suppress; | ||||
| } | ||||
|  | ||||
| void release_log_memory(void) | ||||
| { | ||||
| 	dbg_free((char *) _log_dev_alias.str); | ||||
| 	_log_dev_alias.str = "activate_log file"; | ||||
| } | ||||
|  | ||||
| void fin_log(void) | ||||
| { | ||||
| 	if (_log_direct) { | ||||
| 		dev_close(&_log_dev); | ||||
| 		_log_direct = 0; | ||||
| 	} | ||||
|  | ||||
| 	if (_log_to_file) { | ||||
| 		fclose(_log_file); | ||||
| 		_log_to_file = 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void fin_syslog() | ||||
| { | ||||
| 	if (_syslog) | ||||
| 		closelog(); | ||||
| 	_syslog = 0; | ||||
| } | ||||
|  | ||||
| void init_verbose(int level) | ||||
| { | ||||
| 	_verbose_level = level; | ||||
| } | ||||
|  | ||||
| void init_test(int level) | ||||
| { | ||||
| 	if (!_test && level) | ||||
| 		log_print("Test mode: Metadata will NOT be updated."); | ||||
| 	_test = level; | ||||
| } | ||||
|  | ||||
| void init_partial(int level) | ||||
| { | ||||
| 	_partial = level; | ||||
| } | ||||
|  | ||||
| void init_pvmove(int level) | ||||
| { | ||||
| 	_pvmove = 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 pvmove_mode() | ||||
| { | ||||
| 	return _pvmove; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| 	char buf[1024]; | ||||
| 	int bufused, n; | ||||
|  | ||||
| 	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_to_file && (_log_while_suspended || !memlock())) { | ||||
| 		fprintf(_log_file, "%s:%d %s%s", file, line, _cmd_name, | ||||
| 			_msg_prefix); | ||||
|  | ||||
| 		va_start(ap, format); | ||||
| 		vfprintf(_log_file, format, ap); | ||||
| 		va_end(ap); | ||||
|  | ||||
| 		fprintf(_log_file, "\n"); | ||||
| 		fflush(_log_file); | ||||
| 	} | ||||
|  | ||||
| 	if (_syslog && (_log_while_suspended || !memlock())) { | ||||
| 		va_start(ap, format); | ||||
| 		vsyslog(level, format, ap); | ||||
| 		va_end(ap); | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME This code is unfinished - pre-extend & condense. */ | ||||
| 	if (!_already_logging && _log_direct && memlock()) { | ||||
| 		_already_logging = 1; | ||||
| 		memset(&buf, ' ', sizeof(buf)); | ||||
| 		bufused = 0; | ||||
| 		if ((n = lvm_snprintf(buf, sizeof(buf) - bufused - 1, | ||||
| 				      "%s:%d %s%s", file, line, _cmd_name, | ||||
| 				      _msg_prefix)) == -1) | ||||
| 			goto done; | ||||
|  | ||||
| 		bufused += n; | ||||
|  | ||||
| 		va_start(ap, format); | ||||
| 		n = vsnprintf(buf + bufused - 1, sizeof(buf) - bufused - 1, | ||||
| 			      format, ap); | ||||
| 		va_end(ap); | ||||
| 		bufused += n; | ||||
|  | ||||
| 	      done: | ||||
| 		buf[bufused - 1] = '\n'; | ||||
| 		buf[bufused] = '\n'; | ||||
| 		buf[sizeof(buf) - 1] = '\n'; | ||||
| 		/* FIXME real size bufused */ | ||||
| 		dev_append(&_log_dev, sizeof(buf), buf); | ||||
| 		_already_logging = 0; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										102
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| /* | ||||
|  * 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>		/* FILE */ | ||||
| #include <string.h>		/* strerror() */ | ||||
| #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(const char *log_file, int append); | ||||
| void init_log_direct(const char *log_file, int append); | ||||
| void init_log_while_suspended(int log_while_suspended); | ||||
| void fin_log(void); | ||||
| void release_log_memory(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_pvmove(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 pvmove_mode(void); | ||||
| int debug_level(void); | ||||
| int ignorelockingfailure(void); | ||||
|  | ||||
| /* Suppress messages to stdout/stderr */ | ||||
| void log_suppress(int suppress); | ||||
|  | ||||
| /* Suppress messages to syslog */ | ||||
| void syslog_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(args...) log_err(args) | ||||
| #define log_print(args...) log_warn(args) | ||||
| #define log_verbose(args...) log_notice(args) | ||||
| #define log_very_verbose(args...) log_info(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)) | ||||
| #define log_sys_debug(x, y) \ | ||||
| 		log_debug("%s: %s failed: %s", y, x, strerror(errno)) | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										14
									
								
								lib/metadata/lv_alloc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/metadata/lv_alloc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| /* | ||||
|  * Copyright (C) 2003 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_LV_ALLOC_H | ||||
| #include "pool.h" | ||||
|  | ||||
| struct lv_segment *alloc_lv_segment(struct pool *mem, uint32_t stripes); | ||||
| #endif | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user