mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-11-01 00:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			254 Commits
		
	
	
		
			dev-mcsont
			...
			PRE_DMFS
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | 
							
								
								
									
										26
									
								
								Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # | ||||
| # 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 | ||||
|  | ||||
| include make.tmpl | ||||
|  | ||||
							
								
								
									
										141
									
								
								configure.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								configure.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| ################################################################################ | ||||
| ## | ||||
| ##    Copyright 1999-2000 Sistina Software, Inc. | ||||
| ## | ||||
| ##    This is free software released under the GNU General Public License. | ||||
| ##    There is no warranty for this software.  See the file COPYING for | ||||
| ##    details. | ||||
| ## | ||||
| ##    See the file CONTRIBUTORS for a list of contributors. | ||||
| ## | ||||
| ##    This file is maintained by: | ||||
| ##      AJ Lewis <lewis@sistina.com> | ||||
| ##  | ||||
| ##    File name: configure.in | ||||
| ## | ||||
| ##    Description: Input file for autoconf.  Generates the configure script  | ||||
| ##                 that tries to keep everything nice and portable.  It also | ||||
| ##                 simplifies distribution package building considerably. | ||||
| ################################################################################ | ||||
|  | ||||
| dnl Process this file with autoconf to produce a configure script. | ||||
| AC_INIT(lib/device/dev-cache.h) | ||||
|  | ||||
| dnl setup the directory where autoconf has auxilary files | ||||
| AC_CONFIG_AUX_DIR(autoconf)  | ||||
|  | ||||
| dnl Checks for programs. | ||||
| AC_PROG_AWK | ||||
| AC_PROG_CC | ||||
| AC_PROG_INSTALL | ||||
| AC_PROG_LN_S | ||||
| AC_PROG_MAKE_SET | ||||
| AC_PROG_RANLIB | ||||
|  | ||||
| dnl Checks for header files. | ||||
| AC_HEADER_DIRENT | ||||
| AC_HEADER_STDC | ||||
| AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h unistd.h) | ||||
|  | ||||
| dnl Checks for typedefs, structures, and compiler characteristics. | ||||
| AC_C_CONST | ||||
| AC_C_INLINE | ||||
| AC_TYPE_OFF_T | ||||
| AC_TYPE_PID_T | ||||
| AC_TYPE_SIZE_T | ||||
| AC_STRUCT_ST_RDEV | ||||
| AC_HEADER_TIME | ||||
|  | ||||
| dnl -- prefix is /usr by default, the exec_prefix default is setup later | ||||
| AC_PREFIX_DEFAULT(/usr) | ||||
|  | ||||
| dnl -- setup the ownership of the files | ||||
| AC_ARG_WITH(user, | ||||
|   [  --with-user=USER        Set the owner of installed files ], | ||||
|   [ OWNER="$withval" ], | ||||
|   [ OWNER="root" ]) | ||||
|  | ||||
| dnl -- setup the group ownership of the files | ||||
| AC_ARG_WITH(group, | ||||
|   [  --with-group=GROUP      Set the group owner of installed files ], | ||||
|   [ GROUP="$withval" ], | ||||
|   [ GROUP="root" ]) | ||||
|  | ||||
| dnl -- Where the linux src tree is | ||||
| AC_ARG_WITH(kernel_dir, | ||||
|   [  --with-kernel_dir=DIR   linux kernel source in DIR [/usr/src/linux]], | ||||
|   [ kernel_dir="$withval" ], | ||||
|   [ kernel_dir=/usr/src/linux ]) | ||||
|  | ||||
| AC_ARG_ENABLE(jobs, [  --enable-jobs=NUM       Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=) | ||||
|  | ||||
| dnl Enables staticly linked tools | ||||
| AC_ARG_ENABLE(static_link, [  --enable-static_link    Use this to link the tools to the liblvm library | ||||
|                           statically.  Default is dynamic linking],  STATIC_LINK=$enableval, STATIC_LINK=no) | ||||
|  | ||||
| dnl Disable readline | ||||
| AC_ARG_ENABLE(readline, [  --disable-readline      Disable readline support],  \ | ||||
| READLINE=$enableval, READLINE=yes) | ||||
|  | ||||
| dnl Mess with default exec_prefix | ||||
| if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]]; | ||||
|  then  exec_prefix=""; | ||||
| fi; | ||||
|  | ||||
| dnl Checks for library functions. | ||||
| AC_PROG_GCC_TRADITIONAL | ||||
| AC_TYPE_SIGNAL | ||||
| AC_FUNC_VPRINTF | ||||
| AC_CHECK_FUNCS(mkdir rmdir uname) | ||||
|  | ||||
| dnl check for termcap (Shamelessly copied from parted 1.4.17) | ||||
| if test x$READLINE = xyes; then | ||||
| 	AC_SEARCH_LIBS(tgetent, ncurses curses termcap termlib, , | ||||
| 		AC_MSG_ERROR( | ||||
| termcap could not be found which is required for the | ||||
| --enable-readline option (which is enabled by default).  Either disable readline | ||||
| support with --disable-readline or download and install termcap from: | ||||
| 	ftp.gnu.org/gnu/termcap | ||||
| Note: if you are using precompiled packages you will also need the development | ||||
|   package as well (which may be called termcap-devel or something similar). | ||||
| Note: (n)curses also seems to work as a substitute for termcap.  This was | ||||
|   not found either - but you could try installing that as well. | ||||
| ) | ||||
| 	exit | ||||
| 	) | ||||
| fi | ||||
|  | ||||
| dnl Check for readline (Shamelessly copied from parted 1.4.17) | ||||
| if test x$READLINE = xyes; then | ||||
| 	AC_CHECK_LIB(readline, readline, , | ||||
| 		AC_MSG_ERROR( | ||||
| GNU Readline could not be found which is required for the | ||||
| --enable-readline option (which is enabled by default).  Either disable readline | ||||
| support with --disable-readline or download and install readline from: | ||||
| 	ftp.gnu.org/gnu/readline | ||||
| Note: if you are using precompiled packages you will also need the development | ||||
| package as well (which may be called readline-devel or something similar). | ||||
| ) | ||||
| 		exit | ||||
| 	) | ||||
| fi | ||||
|  | ||||
| AC_SUBST(JOBS) | ||||
| AC_SUBST(STATIC_LINK) | ||||
| AC_SUBST(READLINE) | ||||
| AC_SUBST(kernel_dir) | ||||
| AC_SUBST(OWNER) | ||||
| AC_SUBST(GROUP) | ||||
| dnl First and last lines should not contain files to generate in order to  | ||||
| dnl keep utility scripts running properly | ||||
| AC_OUTPUT( 								\ | ||||
| Makefile								\ | ||||
| make.tmpl                                                               \ | ||||
| include/Makefile						 	\ | ||||
| lib/Makefile							 	\ | ||||
| man/Makefile							 	\ | ||||
| tools/Makefile							 	\ | ||||
| test/mm/Makefile							\ | ||||
| test/device/Makefile							\ | ||||
| test/format1/Makefile							\ | ||||
| ) | ||||
							
								
								
									
										104
									
								
								driver/device-mapper/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								driver/device-mapper/README
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| The main goal of this driver is to support volume management in | ||||
| general, not just for LVM.  The kernel should provide general | ||||
| services, not support specific applications.  eg, The driver has no | ||||
| concept of volume groups. | ||||
|  | ||||
| The driver does this by mapping sector ranges for the logical device | ||||
| onto 'targets'. | ||||
|  | ||||
| When the logical device is accessed, the make_request function looks | ||||
| up the correct target for the given sector, and then asks this target | ||||
| to do the remapping. | ||||
|  | ||||
| A btree structure is used to hold the sector range -> target mapping. | ||||
| Since we know all the entries in the btree in advance we can make a | ||||
| very compact tree, omitting pointers to child nodes, (child nodes | ||||
| locations can be calculated).  Typical users would find they only have | ||||
| a handful of targets for each logical volume LV. | ||||
|  | ||||
| Benchmarking with bonnie++ suggests that this is certainly no slower | ||||
| than current LVM. | ||||
|  | ||||
|  | ||||
| Target types are not hard coded, instead the register_mapping_type | ||||
| function should be called.  A target type is specified using three | ||||
| functions (see the header): | ||||
|  | ||||
| dm_ctr_fn - takes a string and contructs a target specific piece of | ||||
|             context data. | ||||
| dm_dtr_fn - destroy contexts. | ||||
| dm_map_fn - function that takes a buffer_head and some previously | ||||
|             constructed context and performs the remapping. | ||||
|  | ||||
| Currently there are two two trivial mappers, which are automatically | ||||
| registered: 'linear', and 'io_error'.  Linear alone is enough to | ||||
| implement most of LVM. | ||||
|  | ||||
|  | ||||
| I do not like ioctl interfaces so this driver is currently controlled | ||||
| through a /proc interface.  /proc/device-mapper/control allows you to | ||||
| create and remove devices by 'cat'ing a line of the following format: | ||||
|  | ||||
| create <device name> [minor no] | ||||
| remove <device name> | ||||
|  | ||||
| If you're not using devfs you'll have to do the mknod'ing yourself, | ||||
| otherwise the device will appear in /dev/device-mapper automatically. | ||||
|  | ||||
| /proc/device-mapper/<device name> accepts the mapping table: | ||||
|  | ||||
| begin | ||||
| <sector start> <length> <target name> <target args>... | ||||
| ... | ||||
| end | ||||
|  | ||||
| where <target args> are specific to the target type, eg. for a linear | ||||
| mapping: | ||||
|  | ||||
| <sector start> <length> linear <major> <minor> <start> | ||||
|  | ||||
| and the io-err mapping: | ||||
|  | ||||
| <sector start> <length> io-err | ||||
|  | ||||
| The begin/end lines around the table are nasty, they should be handled | ||||
| by open/close of the file. | ||||
|  | ||||
| The interface is far from complete, currently loading a table either | ||||
| succeeds or fails, you have no way of knowing which line of the | ||||
| mapping table was erroneous.  Also there is no way to get status | ||||
| information out, though this should be easy to add, either as another | ||||
| /proc file, or just by reading the same /proc/device-mapper/<device> | ||||
| file.  I will be seperating the loading and validation of a table from | ||||
| the binding of a valid table to a device. | ||||
|  | ||||
| It has been suggested that I should implement a little custom | ||||
| filesystem rather than labouring with /proc.  For example doing a | ||||
| mkdir foo in /wherever/device-mapper would create a new device. People | ||||
| waiting for a status change (eg, a mirror operation to complete) could | ||||
| poll a file.  Does the community find this an acceptable way to go ? | ||||
|  | ||||
|  | ||||
| At the moment the table assumes 32 bit keys (sectors), the move to 64 | ||||
| bits will involve no interface changes, since the tables will be read | ||||
| in as ascii data.  A different table implementation can therefor be | ||||
| provided at another time.  Either just by changing offset_t to 64 | ||||
| bits, or maybe implementing a structure which looks up the keys in | ||||
| stages (ie, 32 bits at a time). | ||||
|  | ||||
|  | ||||
| More interesting targets: | ||||
|  | ||||
| striped mapping; given a stripe size and a number of device regions | ||||
| this would stripe data across the regions.  Especially useful, since | ||||
| we could limit each striped region to a 32 bit area and then avoid | ||||
| nasty 64 bit %'s. | ||||
|  | ||||
| mirror mapping; would set off a kernel thread slowly copying data from | ||||
| one region to another, ensuring that any new writes got copied to both | ||||
| destinations correctly.  Enabling us to implement a live pvmove | ||||
| correctly. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										88
									
								
								driver/device-mapper/device-mapper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								driver/device-mapper/device-mapper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| /* | ||||
|  * device-mapper.h | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Changelog | ||||
|  * | ||||
|  *     14/08/2001 - First version [Joe Thornber] | ||||
|  */ | ||||
|  | ||||
| #ifndef DEVICE_MAPPER_H | ||||
| #define DEVICE_MAPPER_H | ||||
|  | ||||
| #include <linux/major.h> | ||||
|  | ||||
| /* FIXME: Use value from local range for now, for co-existence with LVM 1 */ | ||||
| #define DM_BLK_MAJOR 124 | ||||
|  | ||||
| struct dm_table; | ||||
| struct dm_dev; | ||||
| struct text_region; | ||||
| typedef unsigned int offset_t; | ||||
|  | ||||
| typedef void (*dm_error_fn)(const char *message, void *private); | ||||
|  | ||||
| /* | ||||
|  * constructor, destructor and map fn types | ||||
|  */ | ||||
| typedef int (*dm_ctr_fn)(struct dm_table *t, offset_t b, offset_t l, | ||||
| 			 struct text_region *args, void **context, | ||||
| 			 dm_error_fn err, void *e_private); | ||||
|  | ||||
| typedef void (*dm_dtr_fn)(struct dm_table *t, void *c); | ||||
| typedef int (*dm_map_fn)(struct buffer_head *bh, int rw, void *context); | ||||
| typedef int (*dm_err_fn)(struct buffer_head *bh, int rw, void *context); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Contructors should call this to make sure any | ||||
|  * destination devices are handled correctly | ||||
|  * (ie. opened/closed). | ||||
|  */ | ||||
| int dm_table_get_device(struct dm_table *t, const char *path, | ||||
| 			struct dm_dev **result); | ||||
| void dm_table_put_device(struct dm_table *table, struct dm_dev *d); | ||||
|  | ||||
| /* | ||||
|  * information about a target type | ||||
|  */ | ||||
| struct target_type { | ||||
|         const char *name; | ||||
|         struct module *module; | ||||
|         dm_ctr_fn ctr; | ||||
|         dm_dtr_fn dtr; | ||||
|         dm_map_fn map; | ||||
| 	dm_err_fn err; | ||||
| }; | ||||
|  | ||||
| int dm_register_target(struct target_type *t); | ||||
| int dm_unregister_target(struct target_type *t); | ||||
|  | ||||
| /* | ||||
|  * These may be useful for people writing target | ||||
|  * types. | ||||
|  */ | ||||
| struct text_region { | ||||
|         const char *b; | ||||
|         const char *e; | ||||
| }; | ||||
|  | ||||
| int dm_get_number(struct text_region *txt, unsigned int *n); | ||||
| int dm_get_line(struct text_region *txt, struct text_region *line); | ||||
| int dm_get_word(struct text_region *txt, struct text_region *word); | ||||
| void dm_txt_copy(char *dest, size_t max, struct text_region *txt); | ||||
| void dm_eat_space(struct text_region *txt); | ||||
|  | ||||
|  | ||||
| #endif /* DEVICE_MAPPER_H */ | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
							
								
								
									
										553
									
								
								driver/device-mapper/dm-fs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										553
									
								
								driver/device-mapper/dm-fs.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,553 @@ | ||||
| /* | ||||
|  * *very* heavily based on ramfs | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include <linux/module.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/pagemap.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/locks.h> | ||||
| #include <linux/file.h> | ||||
|  | ||||
| #include <asm/uaccess.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| /* some magic number */ | ||||
| #define DM_MAGIC	0x444D4653 | ||||
|  | ||||
| static struct super_operations dm_ops; | ||||
| static struct address_space_operations dm_aops; | ||||
| static struct file_operations dm_dir_operations; | ||||
| static struct file_operations dm_file_operations; | ||||
| static struct inode_operations dm_dir_inode_operations; | ||||
|  | ||||
| struct vfsmount *_mnt; | ||||
|  | ||||
| static int _unlink(struct inode *dir, struct dentry *dentry); | ||||
|  | ||||
| #define NOT_A_TABLE ((struct dm_table *) 1) | ||||
|  | ||||
| /* | ||||
|  * context for the line splitter and error function. | ||||
|  */ | ||||
| struct line_c { | ||||
| 	unsigned int line_num; | ||||
| 	loff_t next_read; | ||||
| 	char data[MAX_TARGET_LINE]; | ||||
|  | ||||
| 	struct file *in; | ||||
| 	struct file *out; | ||||
| }; | ||||
|  | ||||
| static int is_identifier(const char *str, int len) | ||||
| { | ||||
| 	if (len > DM_NAME_LEN - 1) | ||||
| 		return 0; | ||||
|  | ||||
| 	while(len--) { | ||||
| 		if (!isalnum(*str) && *str != '_') | ||||
| 			return 0; | ||||
| 		str++; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Grabs lines one at a time from the table file. | ||||
|  */ | ||||
| int extract_line(struct text_region *line, void *private) | ||||
| { | ||||
| 	struct line_c *lc = (struct line_c *) private; | ||||
| 	struct text_region text; | ||||
| 	ssize_t n; | ||||
| 	loff_t off = lc->next_read; | ||||
| 	const char *read_begin; | ||||
| 	mm_segment_t fs; | ||||
|  | ||||
| 	fs = get_fs(); | ||||
| 	set_fs(get_ds()); | ||||
|  | ||||
| 	n = lc->in->f_op->read(lc->in, lc->data, sizeof (lc->data), &off); | ||||
|  | ||||
| 	set_fs(fs); | ||||
|  | ||||
| 	if (n <= 0) | ||||
| 		return 0; | ||||
|  | ||||
| 	read_begin = text.b = lc->data; | ||||
| 	text.e = lc->data + n; | ||||
|  | ||||
| 	if (!dm_get_line(&text, line)) | ||||
| 		return 0; | ||||
|  | ||||
| 	lc->line_num++; | ||||
| 	lc->next_read += line->e - read_begin; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct file *open_error_file(struct file *table) | ||||
| { | ||||
| 	struct file *f; | ||||
| 	char *name, *buffer; | ||||
| 	int bufsize = PATH_MAX + 1; | ||||
|  | ||||
| 	if (bufsize < PAGE_SIZE) | ||||
| 		bufsize = PAGE_SIZE; | ||||
|  | ||||
| 	/* Get space to append ".err" */ | ||||
|  	buffer = (char *) kmalloc(bufsize + 4, GFP_KERNEL); | ||||
|  | ||||
| 	if (!buffer) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* Get path name */ | ||||
| 	name = d_path(table->f_dentry, table->f_vfsmnt, buffer, bufsize); | ||||
|  | ||||
| 	if (!name) { | ||||
| 		kfree(buffer); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Create error file */ | ||||
| 	strcat(name, ".err"); | ||||
| 	f = filp_open(name, O_WRONLY | O_TRUNC | O_CREAT, S_IRUGO); | ||||
|  | ||||
| 	kfree(buffer); | ||||
|  | ||||
| 	if (f) | ||||
| 		f->f_dentry->d_inode->u.generic_ip = NOT_A_TABLE; | ||||
|  | ||||
| 	return f; | ||||
| } | ||||
|  | ||||
| static void close_error_file(struct file *out) | ||||
| { | ||||
| 	fput(out); | ||||
| } | ||||
|  | ||||
| static void parse_error(const char *message, void *private) | ||||
| { | ||||
| 	struct line_c *lc = (struct line_c *) private; | ||||
| 	char buffer[32]; | ||||
|  | ||||
| #define emit(b, l) lc->out->f_op->write(lc->out, (b), (l), &lc->out->f_pos) | ||||
|  | ||||
| 	emit(lc->in->f_dentry->d_name.name, lc->in->f_dentry->d_name.len); | ||||
| 	sprintf(buffer, "(%d): ", lc->line_num); | ||||
| 	emit(buffer, strlen(buffer)); | ||||
| 	emit(message, strlen(message)); | ||||
| 	emit("\n", 1); | ||||
|  | ||||
| #undef emit | ||||
| } | ||||
|  | ||||
| static int _release(struct inode *inode, struct file *f) | ||||
| { | ||||
| 	/* FIXME: we should lock the inode to | ||||
| 	   prevent someone else opening it while | ||||
| 	   we are parsing */ | ||||
| 	struct line_c *lc; | ||||
| 	struct dm_table *table = (struct dm_table *) inode->u.generic_ip; | ||||
|  | ||||
| 	/* noop for files without tables (.err files) */ | ||||
| 	if (table == NOT_A_TABLE) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* only bother parsing if it was open for a write */ | ||||
| 	if (!(f->f_mode & S_IWUGO)) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* free off the old table */ | ||||
| 	if (table) { | ||||
| 		dm_table_destroy(table); | ||||
| 		inode->u.generic_ip = 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(lc = kmalloc(sizeof (*lc), GFP_KERNEL))) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	memset(lc, 0, sizeof (*lc)); | ||||
| 	lc->in = f; | ||||
|  | ||||
| 	if (!(lc->out = open_error_file(lc->in))) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	table = dm_parse(extract_line, lc, parse_error, lc); | ||||
| 	close_error_file(lc->out); | ||||
|  | ||||
| 	kfree(lc); | ||||
| 	inode->u.generic_ip = table; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void _put_inode(struct inode *inode) | ||||
| { | ||||
| 	struct mapped_device *md =  | ||||
| 		(struct mapped_device *) inode->u.generic_ip; | ||||
| 	struct dm_table *table = (struct dm_table *) inode->u.generic_ip; | ||||
|  | ||||
| 	if (inode->i_mode & S_IFDIR) { | ||||
| 		if (md) | ||||
| 			dm_remove(md); | ||||
|  | ||||
| 	} else { | ||||
| 		if (table) | ||||
| 			dm_table_destroy(table); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	inode->u.generic_ip = 0; | ||||
| 	force_delete(inode); | ||||
| } | ||||
|  | ||||
| static int _statfs(struct super_block *sb, struct statfs *buf) | ||||
| { | ||||
| 	buf->f_type = DM_MAGIC; | ||||
| 	buf->f_bsize = PAGE_CACHE_SIZE; | ||||
| 	buf->f_namelen = 255; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Lookup the data. This is trivial - if the dentry didn't already | ||||
|  * exist, we know it is negative. | ||||
|  */ | ||||
| static struct dentry *_lookup(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	d_add(dentry, NULL); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Read a page. Again trivial. If it didn't already exist | ||||
|  * in the page cache, it is zero-filled. | ||||
|  */ | ||||
| static int _readpage(struct file *file, struct page *page) | ||||
| { | ||||
| 	if (!Page_Uptodate(page)) { | ||||
| 		memset(kmap(page), 0, PAGE_CACHE_SIZE); | ||||
| 		kunmap(page); | ||||
| 		flush_dcache_page(page); | ||||
| 		SetPageUptodate(page); | ||||
| 	} | ||||
| 	UnlockPage(page); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Writing: just make sure the page gets marked dirty, so that | ||||
|  * the page stealer won't grab it. | ||||
|  */ | ||||
| static int _writepage(struct page *page) | ||||
| { | ||||
| 	SetPageDirty(page); | ||||
| 	UnlockPage(page); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int _prepare_write(struct file *file, struct page *page, | ||||
| 			      unsigned offset, unsigned to) | ||||
| { | ||||
| 	void *addr = kmap(page); | ||||
| 	if (!Page_Uptodate(page)) { | ||||
| 		memset(addr, 0, PAGE_CACHE_SIZE); | ||||
| 		flush_dcache_page(page); | ||||
| 		SetPageUptodate(page); | ||||
| 	} | ||||
| 	SetPageDirty(page); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int _commit_write(struct file *file, struct page *page, | ||||
| 			     unsigned offset, unsigned to) | ||||
| { | ||||
| 	struct inode *inode = page->mapping->host; | ||||
| 	loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to; | ||||
|  | ||||
| 	kunmap(page); | ||||
| 	if (pos > inode->i_size) | ||||
| 		inode->i_size = pos; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct inode *_get_inode(struct super_block *sb, int mode, int dev) | ||||
| { | ||||
| 	struct inode *inode = new_inode(sb); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_mode = mode; | ||||
| 		inode->i_uid = current->fsuid; | ||||
| 		inode->i_gid = current->fsgid; | ||||
| 		inode->i_blksize = PAGE_CACHE_SIZE; | ||||
| 		inode->i_blocks = 0; | ||||
| 		inode->i_rdev = NODEV; | ||||
| 		inode->i_mapping->a_ops = &dm_aops; | ||||
| 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||||
| 		switch (mode & S_IFMT) { | ||||
| 		case S_IFBLK: | ||||
| 		case S_IFCHR: | ||||
| 			init_special_inode(inode, mode, dev); | ||||
| 			break; | ||||
| 		case S_IFREG: | ||||
| 			inode->i_fop = &dm_file_operations; | ||||
| 			break; | ||||
| 		case S_IFDIR: | ||||
| 			inode->i_op = &dm_dir_inode_operations; | ||||
| 			inode->i_fop = &dm_dir_operations; | ||||
| 			break; | ||||
| 		case S_IFLNK: | ||||
| 			inode->i_op = &page_symlink_inode_operations; | ||||
| 			break; | ||||
| 		default: | ||||
| 			make_bad_inode(inode); | ||||
| 		} | ||||
| 	} | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * File creation. Allocate an inode, and we're done.. | ||||
|  */ | ||||
| static int _mknod(struct inode *dir, struct dentry *dentry, int mode) | ||||
| { | ||||
| 	int error = -ENOSPC; | ||||
| 	struct inode *inode = _get_inode(dir->i_sb, mode, 0); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		d_instantiate(dentry, inode); | ||||
| 		dget(dentry);	/* Extra count - pin the dentry in core */ | ||||
| 		error = 0; | ||||
| 	} | ||||
|  | ||||
| 	return error; | ||||
| } | ||||
|  | ||||
| static int _mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||||
| { | ||||
| 	int r; | ||||
| 	const char *name = (const char *) dentry->d_name.name; | ||||
| 	struct mapped_device *md; | ||||
|  | ||||
| 	if (!is_identifier(name, dentry->d_name.len)) | ||||
| 		return -EPERM;	/* or EINVAL ? */ | ||||
|  | ||||
| 	md = dm_create(name, -1); | ||||
| 	if (IS_ERR(md)) | ||||
| 		return PTR_ERR(md); | ||||
|  | ||||
| 	r = _mknod(dir, dentry, mode | S_IFDIR); | ||||
| 	if (r) { | ||||
| 		dm_remove(md); | ||||
| 		return r; | ||||
| 	} | ||||
|  | ||||
| 	dentry->d_inode->u.generic_ip = md; | ||||
| 	md->inode = dentry->d_inode; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int _rmdir(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	int r = _unlink(dir, dentry); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _create(struct inode *dir, struct dentry *dentry, int mode) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	if ((r = _mknod(dir, dentry, mode | S_IFREG))) | ||||
| 		return r; | ||||
|  | ||||
| 	dentry->d_inode->u.generic_ip = 0; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static inline int positive(struct dentry *dentry) | ||||
| { | ||||
| 	return dentry->d_inode && !d_unhashed(dentry); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Check that a directory is empty (this works | ||||
|  * for regular files too, they'll just always be | ||||
|  * considered empty..). | ||||
|  * | ||||
|  * Note that an empty directory can still have | ||||
|  * children, they just all have to be negative.. | ||||
|  */ | ||||
| static int _empty(struct dentry *dentry) | ||||
| { | ||||
| 	struct list_head *list; | ||||
|  | ||||
| 	spin_lock(&dcache_lock); | ||||
| 	list = dentry->d_subdirs.next; | ||||
|  | ||||
| 	while (list != &dentry->d_subdirs) { | ||||
| 		struct dentry *de = list_entry(list, struct dentry, d_child); | ||||
|  | ||||
| 		if (positive(de)) { | ||||
| 			spin_unlock(&dcache_lock); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		list = list->next; | ||||
| 	} | ||||
| 	spin_unlock(&dcache_lock); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * This works for both directories and regular files. | ||||
|  * (non-directories will always have empty subdirs) | ||||
|  */ | ||||
| static int _unlink(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	int retval = -ENOTEMPTY; | ||||
|  | ||||
| 	if (_empty(dentry)) { | ||||
| 		struct inode *inode = dentry->d_inode; | ||||
|  | ||||
| 		inode->i_nlink--; | ||||
| 		dput(dentry);	/* Undo the count from "create" - this does all the work */ | ||||
| 		retval = 0; | ||||
| 	} | ||||
| 	return retval; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * The VFS layer already does all the dentry stuff for rename, | ||||
|  * we just have to decrement the usage count for the target if | ||||
|  * it exists so that the VFS layer correctly free's it when it | ||||
|  * gets overwritten. | ||||
|  */ | ||||
| static int _rename(struct inode *old_dir, struct dentry *old_dentry, | ||||
| 		       struct inode *new_dir, struct dentry *new_dentry) | ||||
| { | ||||
| 	struct inode *inode = new_dentry->d_inode; | ||||
| 	struct mapped_device *md = old_dir->u.generic_ip; | ||||
| 	struct dm_table *table = old_dentry->d_inode->u.generic_ip; | ||||
|  | ||||
| 	if (!md || !table) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (!_empty(new_dentry)) | ||||
| 		return -ENOTEMPTY; | ||||
|  | ||||
| 	if (!strcmp(new_dentry->d_name.name, "ACTIVE")) { | ||||
| 		/* activate the table */ | ||||
| 		dm_activate(md, table); | ||||
|  | ||||
| 	} else if (!strcmp(old_dentry->d_name.name, "ACTIVE")) { | ||||
| 		dm_suspend(md); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_nlink--; | ||||
| 		dput(new_dentry); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int _sync_file(struct file *file, struct dentry *dentry, | ||||
| 			  int datasync) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct address_space_operations dm_aops = { | ||||
| 	readpage:	_readpage, | ||||
| 	writepage:	_writepage, | ||||
| 	prepare_write:	_prepare_write, | ||||
| 	commit_write:	_commit_write | ||||
| }; | ||||
|  | ||||
| static struct file_operations dm_file_operations = { | ||||
| 	read:		generic_file_read, | ||||
| 	write:		generic_file_write, | ||||
| 	fsync:		_sync_file, | ||||
| 	release:	_release, | ||||
| }; | ||||
|  | ||||
| static struct file_operations dm_dir_operations = { | ||||
| 	read:		generic_read_dir, | ||||
| 	readdir:	dcache_readdir, | ||||
| 	fsync:		_sync_file, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations root_dir_inode_operations = { | ||||
| 	lookup:		_lookup, | ||||
| 	mkdir:		_mkdir, | ||||
| 	rmdir:		_rmdir, | ||||
| 	rename:		_rename, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations dm_dir_inode_operations = { | ||||
| 	create:		_create, | ||||
| 	lookup:		_lookup, | ||||
| 	unlink:		_unlink, | ||||
| 	rename:		_rename, | ||||
| }; | ||||
|  | ||||
| static struct super_operations dm_ops = { | ||||
| 	statfs:		_statfs, | ||||
| 	put_inode:	_put_inode, | ||||
| }; | ||||
|  | ||||
| static struct super_block *_read_super(struct super_block *sb, void *data, | ||||
| 				       int silent) | ||||
| { | ||||
| 	struct inode *inode; | ||||
| 	struct dentry *root; | ||||
|  | ||||
| 	sb->s_blocksize = PAGE_CACHE_SIZE; | ||||
| 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | ||||
| 	sb->s_magic = DM_MAGIC; | ||||
| 	sb->s_op = &dm_ops; | ||||
| 	inode = _get_inode(sb, S_IFDIR | 0755, 0); | ||||
| 	inode->i_op = &root_dir_inode_operations; | ||||
| 	if (!inode) | ||||
| 		return NULL; | ||||
|  | ||||
| 	root = d_alloc_root(inode); | ||||
| 	if (!root) { | ||||
| 		iput(inode); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	sb->s_root = root; | ||||
| 	return sb; | ||||
| } | ||||
|  | ||||
| static DECLARE_FSTYPE(_fs_type, "dmfs", _read_super, FS_SINGLE); | ||||
|  | ||||
| int __init dm_fs_init(void) | ||||
| { | ||||
| 	int r; | ||||
| 	if ((r = register_filesystem(&_fs_type))) | ||||
| 		return r; | ||||
|  | ||||
| 	_mnt = kern_mount(&_fs_type); | ||||
|  | ||||
| 	if (IS_ERR(_mnt)) { | ||||
| 		unregister_filesystem(&_fs_type); | ||||
| 		return PTR_ERR(_mnt); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void __exit dm_fs_exit(void) | ||||
| { | ||||
| 	unregister_filesystem(&_fs_type); | ||||
| } | ||||
							
								
								
									
										125
									
								
								driver/device-mapper/dm-linear.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								driver/device-mapper/dm-linear.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| /* | ||||
|  * dm-linear.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include <linux/config.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/blkdev.h> | ||||
| #include <linux/device-mapper.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| /* | ||||
|  * linear: maps a linear range of a device. | ||||
|  */ | ||||
| struct linear_c { | ||||
| 	long delta;		/* FIXME: we need a signed offset type */ | ||||
| 	struct dm_dev *dev; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * construct a linear mapping. | ||||
|  * <dev_path> <offset> | ||||
|  */ | ||||
| static int linear_ctr(struct dm_table *t, offset_t b, offset_t l, | ||||
| 		      struct text_region *args, void **context, | ||||
| 		      dm_error_fn err, void *e_private) | ||||
| { | ||||
| 	struct linear_c *lc; | ||||
| 	unsigned int start; | ||||
| 	struct text_region word; | ||||
| 	char path[256];		/* FIXME: magic */ | ||||
| 	int r = -EINVAL; | ||||
|  | ||||
| 	if (!(lc = kmalloc(sizeof(lc), GFP_KERNEL))) { | ||||
| 		err("couldn't allocate memory for linear context", e_private); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_get_word(args, &word)) { | ||||
| 		err("couldn't get device path", e_private); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	dm_txt_copy(path, sizeof(path) - 1, &word); | ||||
|  | ||||
| 	if (!dm_get_number(args, &start)) { | ||||
| 		err("destination start not given", e_private); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if ((r = dm_table_get_device(t, path, &lc->dev))) { | ||||
| 		err("couldn't lookup device", e_private); | ||||
| 		r = -ENXIO; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	lc->delta = (int) start - (int) b; | ||||
| 	*context = lc; | ||||
| 	return 0; | ||||
|  | ||||
|  bad: | ||||
| 	kfree(lc); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static void linear_dtr(struct dm_table *t, void *c) | ||||
| { | ||||
| 	struct linear_c *lc = (struct linear_c *) c; | ||||
| 	dm_table_put_device(t, lc->dev); | ||||
| 	kfree(c); | ||||
| } | ||||
|  | ||||
| static int linear_map(struct buffer_head *bh, int rw, void *context) | ||||
| { | ||||
| 	struct linear_c *lc = (struct linear_c *) context; | ||||
|  | ||||
| 	bh->b_rdev = lc->dev->dev; | ||||
| 	bh->b_rsector = bh->b_rsector + lc->delta; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct target_type linear_target = { | ||||
| 	name: "linear", | ||||
| 	module: THIS_MODULE, | ||||
| 	ctr: linear_ctr, | ||||
| 	dtr: linear_dtr, | ||||
| 	map: linear_map, | ||||
| }; | ||||
|  | ||||
| static int __init linear_init(void) | ||||
| { | ||||
| 	int r = dm_register_target(&linear_target); | ||||
|  | ||||
| 	if (r < 0) | ||||
| 		printk(KERN_ERR | ||||
| 		       "Device mapper: Linear: register failed %d\n", r); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static void __exit linear_exit(void) | ||||
| { | ||||
| 	int r = dm_unregister_target(&linear_target); | ||||
|  | ||||
| 	if (r < 0) | ||||
| 		printk(KERN_ERR | ||||
| 		       "Device mapper: Linear: unregister failed %d\n", r); | ||||
| } | ||||
|  | ||||
| module_init(linear_init); | ||||
| module_exit(linear_exit); | ||||
|  | ||||
| MODULE_AUTHOR("Joe Thornber <thornber@uk.sistina.com>"); | ||||
| MODULE_DESCRIPTION("Device Mapper: Linear mapping"); | ||||
| #ifdef MODULE_LICENSE | ||||
| MODULE_LICENSE("GPL"); | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										201
									
								
								driver/device-mapper/dm-parse.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								driver/device-mapper/dm-parse.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
| /* | ||||
|  * dm-parse.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  *     4/09/2001 - First version [Joe Thornber] | ||||
|  */ | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| struct dm_table *dm_parse(extract_line_fn line_fn, void *l_private, | ||||
| 			  dm_error_fn err_fn, void *e_private) | ||||
| { | ||||
| 	struct text_region line, word; | ||||
| 	struct dm_table *table = dm_table_create(); | ||||
| 	struct target_type *ttype; | ||||
| 	offset_t start, size, high; | ||||
| 	char target_name[64]; | ||||
| 	void *context; | ||||
| 	int last_line_good = 1, was_error = 0; | ||||
|  | ||||
| 	if (!table) | ||||
| 		return 0; | ||||
|  | ||||
| #define PARSE_ERROR(msg) {\ | ||||
|   last_line_good = 0;\ | ||||
|   was_error = 1;\ | ||||
|   err_fn(msg, e_private);\ | ||||
|   continue;} | ||||
|  | ||||
| 	while (line_fn(&line, l_private)) { | ||||
|  | ||||
| 		/* | ||||
| 		 * each line is of the format: | ||||
| 		 * <sector start> <length (sectors)> <target type> <args...> | ||||
| 		 */ | ||||
|  | ||||
| 		/* the line may be blank ... */ | ||||
| 		dm_eat_space(&line); | ||||
| 		if (dm_empty_tok(&line) || (*line.b == '#')) | ||||
| 			continue; | ||||
|  | ||||
| 		/* sector start */ | ||||
| 		if (!dm_get_number(&line, &start)) | ||||
| 			PARSE_ERROR("expecting a number for sector start"); | ||||
|  | ||||
| 		/* length */ | ||||
| 		if (!dm_get_number(&line, &size)) | ||||
| 			PARSE_ERROR("expecting a number for region length"); | ||||
|  | ||||
| 		/* target type */ | ||||
| 		if (!dm_get_word(&line, &word)) | ||||
| 			PARSE_ERROR("target type missing"); | ||||
|  | ||||
| 		/* we have to copy the target type to a C str */ | ||||
| 		dm_txt_copy(target_name, sizeof(target_name), &word); | ||||
|  | ||||
| 		/* lookup the target type */ | ||||
| 		if (!(ttype = dm_get_target_type(target_name))) | ||||
| 			PARSE_ERROR("unable to find target type"); | ||||
|  | ||||
| 		/* check there isn't a gap, but only if the last target | ||||
| 		   parsed ok. */ | ||||
| 		if (last_line_good && | ||||
|  | ||||
| 		    ((table->num_targets && | ||||
| 		      start != table->highs[table->num_targets - 1] + 1) || | ||||
| 		     (!table->num_targets && start))) | ||||
| 			PARSE_ERROR("gap in target ranges"); | ||||
|  | ||||
| 		/* build the target */ | ||||
| 		if (ttype->ctr(table, start, size, &line, &context, | ||||
| 			       err_fn, e_private)) | ||||
| 			PARSE_ERROR("target constructor failed"); | ||||
|  | ||||
| 		/* no point registering the target | ||||
|                    if there was an error. */ | ||||
| 		if (was_error) { | ||||
| 			ttype->dtr(table, context); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* add the target to the table */ | ||||
| 		high = start + (size - 1); | ||||
| 		if (dm_table_add_target(table, high, ttype, context)) | ||||
| 			PARSE_ERROR("internal error adding target to table"); | ||||
| 	} | ||||
|  | ||||
| #undef PARSE_ERROR | ||||
|  | ||||
| 	if (was_error || dm_table_complete(table)) { | ||||
| 		dm_table_destroy(table); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return table; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * convert the text in txt to an unsigned int, | ||||
|  * returns 0 on failure. | ||||
|  */ | ||||
| int dm_get_number(struct text_region *txt, unsigned int *n) | ||||
| { | ||||
| 	char *ptr; | ||||
|  | ||||
| 	dm_eat_space(txt); | ||||
| 	if (dm_empty_tok(txt)) | ||||
| 		return 0; | ||||
|  | ||||
| 	*n = simple_strtoul(txt->b, &ptr, 10); | ||||
| 	if (ptr == txt->b) | ||||
| 		return 0; | ||||
|  | ||||
| 	txt->b = ptr; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * extracts text up to the next '\n'. | ||||
|  */ | ||||
| int dm_get_line(struct text_region *txt, struct text_region *line) | ||||
| { | ||||
| 	const char *ptr; | ||||
|  | ||||
| 	dm_eat_space(txt); | ||||
| 	if (dm_empty_tok(txt)) | ||||
| 		return 0; | ||||
|  | ||||
| 	ptr = line->b = txt->b; | ||||
| 	while((ptr != txt->e) && (*ptr != '\n')) | ||||
| 		ptr++; | ||||
|  | ||||
| 	txt->b = line->e = ptr; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * extracts the next non-whitespace token from the file. | ||||
|  */ | ||||
| int dm_get_word(struct text_region *txt, struct text_region *word) | ||||
| { | ||||
| 	const char *ptr; | ||||
|  | ||||
| 	dm_eat_space(txt); | ||||
|  | ||||
| 	if (dm_empty_tok(txt)) | ||||
| 		return 0; | ||||
|  | ||||
| 	word->b = txt->b; | ||||
| 	for (ptr = word->b = txt->b; | ||||
| 	     ptr != txt->e && !isspace((int) *ptr); ptr++) | ||||
| 		; | ||||
|  | ||||
| 	word->e = txt->b = ptr; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * copy a text region into a traditional C str. | ||||
|  */ | ||||
| void dm_txt_copy(char *dest, size_t max, struct text_region *txt) | ||||
| { | ||||
| 	size_t len = txt->e - txt->b; | ||||
| 	if (len > --max) | ||||
| 		len = max; | ||||
| 	strncpy(dest, txt->b, len); | ||||
| 	dest[len] = '\0'; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * skip leading whitespace | ||||
|  */ | ||||
| void dm_eat_space(struct text_region *txt) | ||||
| { | ||||
| 	while(txt->b != txt->e && isspace((int) *txt->b)) | ||||
| 		(txt->b)++; | ||||
| } | ||||
|  | ||||
|  | ||||
| EXPORT_SYMBOL(dm_get_number); | ||||
| EXPORT_SYMBOL(dm_get_word); | ||||
| EXPORT_SYMBOL(dm_txt_copy); | ||||
| EXPORT_SYMBOL(dm_eat_space); | ||||
							
								
								
									
										338
									
								
								driver/device-mapper/dm-table.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								driver/device-mapper/dm-table.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,338 @@ | ||||
| /* | ||||
|  * dm-table.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Changelog | ||||
|  * | ||||
|  *     16/08/2001 - First version [Joe Thornber] | ||||
|  */ | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| /* ceiling(n / size) * size */ | ||||
| static inline ulong round_up(ulong n, ulong size) | ||||
| { | ||||
| 	ulong r = n % size; | ||||
| 	return n + (r ? (size - r) : 0); | ||||
| } | ||||
|  | ||||
| /* ceiling(n / size) */ | ||||
| static inline ulong div_up(ulong n, ulong size) | ||||
| { | ||||
| 	return round_up(n, size) / size; | ||||
| } | ||||
|  | ||||
| /* similar to ceiling(log_size(n)) */ | ||||
| static uint int_log(ulong n, ulong base) | ||||
| { | ||||
| 	int result = 0; | ||||
|  | ||||
| 	while (n > 1) { | ||||
| 		n = div_up(n, base); | ||||
| 		result++; | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * return the highest key that you could lookup | ||||
|  * from the n'th node on level l of the btree. | ||||
|  */ | ||||
| static offset_t high(struct dm_table *t, int l, int n) | ||||
| { | ||||
| 	for (; l < t->depth - 1; l++) | ||||
| 		n = get_child(n, CHILDREN_PER_NODE - 1); | ||||
|  | ||||
| 	if (n >= t->counts[l]) | ||||
| 		return (offset_t) -1; | ||||
|  | ||||
| 	return get_node(t, l, n)[KEYS_PER_NODE - 1]; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * fills in a level of the btree based on the | ||||
|  * highs of the level below it. | ||||
|  */ | ||||
| static int setup_btree_index(int l, struct dm_table *t) | ||||
| { | ||||
| 	int n, k; | ||||
| 	offset_t *node; | ||||
|  | ||||
| 	for (n = 0; n < t->counts[l]; n++) { | ||||
| 		node = get_node(t, l, n); | ||||
|  | ||||
| 		for (k = 0; k < KEYS_PER_NODE; k++) | ||||
| 			node[k] = high(t, l + 1, get_child(n, k)); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * highs, and targets are managed as dynamic | ||||
|  * arrays during a table load. | ||||
|  */ | ||||
| static int alloc_targets(struct dm_table *t, int num) | ||||
| { | ||||
| 	offset_t *n_highs; | ||||
| 	struct target *n_targets; | ||||
| 	int n = t->num_targets; | ||||
| 	int size = (sizeof(struct target) + sizeof(offset_t)) * num; | ||||
|  | ||||
| 	n_highs = vmalloc(size); | ||||
| 	if (!n_highs) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	n_targets = (struct target *) (n_highs + num); | ||||
|  | ||||
| 	if (n) { | ||||
| 		memcpy(n_highs, t->highs, sizeof(*n_highs) * n); | ||||
| 		memcpy(n_targets, t->targets, sizeof(*n_targets) * n); | ||||
| 	} | ||||
|  | ||||
| 	vfree(t->highs); | ||||
|  | ||||
| 	t->num_allocated = num; | ||||
| 	t->highs = n_highs; | ||||
| 	t->targets = n_targets; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct dm_table *dm_table_create(void) | ||||
| { | ||||
| 	struct dm_table *t = kmalloc(sizeof(struct dm_table), GFP_NOIO); | ||||
|  | ||||
| 	if (!t) | ||||
| 		return 0; | ||||
|  | ||||
| 	memset(t, 0, sizeof(*t)); | ||||
| 	INIT_LIST_HEAD(&t->devices); | ||||
|  | ||||
| 	/* allocate a single nodes worth of targets to | ||||
| 	   begin with */ | ||||
| 	if (alloc_targets(t, KEYS_PER_NODE)) { | ||||
| 		kfree(t); | ||||
| 		t = 0; | ||||
| 	} | ||||
|  | ||||
| 	return t; | ||||
| } | ||||
|  | ||||
| static void free_devices(struct list_head *devices) | ||||
| { | ||||
| 	struct list_head *tmp, *next; | ||||
|  | ||||
| 	for (tmp = devices->next; tmp != devices; tmp = next) { | ||||
| 		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); | ||||
| 		next = tmp->next; | ||||
| 		kfree(dd); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void dm_table_destroy(struct dm_table *t) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	/* free the indexes (see dm_table_complete) */ | ||||
| 	if (t->depth >= 2) | ||||
| 		vfree(t->index[t->depth - 2]); | ||||
| 	vfree(t->highs); | ||||
|  | ||||
| 	/* free the targets */ | ||||
| 	for (i = 0; i < t->num_targets; i++) { | ||||
| 		struct target *tgt = &t->targets[i]; | ||||
| 		if (tgt->private) | ||||
| 			tgt->type->dtr(t, tgt->private); | ||||
| 	} | ||||
|  | ||||
| 	/* free the device list */ | ||||
| 	if (t->devices.next != &t->devices) { | ||||
| 		WARN("there are still devices present, someone isn't " | ||||
| 		     "calling dm_table_remove_device"); | ||||
|  | ||||
| 		free_devices(&t->devices); | ||||
| 	} | ||||
|  | ||||
| 	kfree(t); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Checks to see if we need to extend | ||||
|  * highs or targets. | ||||
|  */ | ||||
| static inline int check_space(struct dm_table *t) | ||||
| { | ||||
| 	if (t->num_targets >= t->num_allocated) | ||||
| 		return alloc_targets(t, t->num_allocated * 2); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * convert a device path to a kdev_t. | ||||
|  */ | ||||
| int lookup_device(const char *path, kdev_t *dev) | ||||
| { | ||||
|        int r; | ||||
|        struct nameidata nd; | ||||
|        struct inode *inode; | ||||
|  | ||||
|        if (!path_init(path, LOOKUP_FOLLOW, &nd)) | ||||
|                return 0; | ||||
|  | ||||
|        if ((r = path_walk(path, &nd))) | ||||
|                goto bad; | ||||
|  | ||||
|        inode = nd.dentry->d_inode; | ||||
|        if (!inode) { | ||||
|                r = -ENOENT; | ||||
|                goto bad; | ||||
|        } | ||||
|  | ||||
|        if (!S_ISBLK(inode->i_mode)) { | ||||
|                r = -EINVAL; | ||||
|                goto bad; | ||||
|        } | ||||
|  | ||||
|        *dev = inode->i_bdev->bd_dev; | ||||
|  | ||||
|  bad: | ||||
|        path_release(&nd); | ||||
|        return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * see if we've already got a device in the list. | ||||
|  */ | ||||
| static struct dm_dev *find_device(struct list_head *l, kdev_t dev) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
|  | ||||
| 	for (tmp = l->next; tmp != l; tmp = tmp->next) { | ||||
|  | ||||
| 		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); | ||||
| 		if (dd->dev == dev) | ||||
| 			return dd; | ||||
| 	} | ||||
|  | ||||
|        return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * add a device to the list, or just increment the | ||||
|  * usage count if it's already present. | ||||
|  */ | ||||
| int dm_table_get_device(struct dm_table *t, const char *path, | ||||
| 			struct dm_dev **result) | ||||
| { | ||||
| 	int r; | ||||
| 	kdev_t dev; | ||||
| 	struct dm_dev *dd; | ||||
|  | ||||
| 	/* convert the path to a device */ | ||||
| 	if ((r = lookup_device(path, &dev))) | ||||
| 		return r; | ||||
|  | ||||
| 	dd = find_device(&t->devices, dev); | ||||
| 	if (!dd) { | ||||
| 		dd = kmalloc(sizeof(*dd), GFP_KERNEL); | ||||
| 		if (!dd) | ||||
| 			return -ENOMEM; | ||||
|  | ||||
| 		dd->dev = dev; | ||||
| 		dd->bd = 0; | ||||
| 		atomic_set(&dd->count, 0); | ||||
| 		list_add(&dd->list, &t->devices); | ||||
| 	} | ||||
| 	atomic_inc(&dd->count); | ||||
| 	*result = dd; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| /* | ||||
|  * decrement a devices use count and remove it if | ||||
|  * neccessary. | ||||
|  */ | ||||
| void dm_table_put_device(struct dm_table *t, struct dm_dev *dd) | ||||
| { | ||||
|        if (atomic_dec_and_test(&dd->count)) { | ||||
| 	       list_del(&dd->list); | ||||
|                kfree(dd); | ||||
|        } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * adds a target to the map | ||||
|  */ | ||||
| int dm_table_add_target(struct dm_table *t, offset_t high, | ||||
| 			struct target_type *type, void *private) | ||||
| { | ||||
| 	int r, n; | ||||
|  | ||||
| 	if ((r = check_space(t))) | ||||
| 		return r; | ||||
|  | ||||
| 	n = t->num_targets++; | ||||
| 	t->highs[n] = high; | ||||
| 	t->targets[n].type = type; | ||||
| 	t->targets[n].private = private; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int setup_indexes(struct dm_table *t) | ||||
| { | ||||
| 	int i, total = 0; | ||||
| 	offset_t *indexes; | ||||
|  | ||||
| 	/* allocate the space for *all* the indexes */ | ||||
| 	for (i = t->depth - 2; i >= 0; i--) { | ||||
| 		t->counts[i] = div_up(t->counts[i + 1], CHILDREN_PER_NODE); | ||||
| 		total += t->counts[i]; | ||||
| 	} | ||||
|  | ||||
| 	if (!(indexes = vmalloc(NODE_SIZE * total))) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	/* set up internal nodes, bottom-up */ | ||||
| 	for (i = t->depth - 2, total = 0; i >= 0; i--) { | ||||
| 		t->index[i] = indexes + (KEYS_PER_NODE * t->counts[i]); | ||||
| 		setup_btree_index(i, t); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * builds the btree to index the map | ||||
|  */ | ||||
| int dm_table_complete(struct dm_table *t) | ||||
| { | ||||
| 	int leaf_nodes, r = 0; | ||||
|  | ||||
| 	/* how many indexes will the btree have ? */ | ||||
| 	leaf_nodes = div_up(t->num_targets, KEYS_PER_NODE); | ||||
| 	t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); | ||||
|  | ||||
| 	/* leaf layer has already been set up */ | ||||
| 	t->counts[t->depth - 1] = leaf_nodes; | ||||
| 	t->index[t->depth - 1] = t->highs; | ||||
|  | ||||
| 	if (t->depth >= 2) | ||||
| 		r = setup_indexes(t); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| EXPORT_SYMBOL(dm_table_get_device); | ||||
| EXPORT_SYMBOL(dm_table_put_device); | ||||
							
								
								
									
										181
									
								
								driver/device-mapper/dm-target.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								driver/device-mapper/dm-target.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| /* | ||||
|  * dm-target.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * 16/08/2001 - First Version [Joe Thornber] | ||||
|  */ | ||||
|  | ||||
| #include "dm.h" | ||||
| #include <linux/kmod.h> | ||||
|  | ||||
| struct tt_internal { | ||||
| 	struct target_type tt; | ||||
|  | ||||
|         struct list_head list; | ||||
|         long use; | ||||
| }; | ||||
|  | ||||
| static LIST_HEAD(_targets); | ||||
| static rwlock_t _lock = RW_LOCK_UNLOCKED; | ||||
|  | ||||
| #define DM_MOD_NAME_SIZE 32 | ||||
|  | ||||
| static inline struct tt_internal *__find_target_type(const char *name) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct tt_internal *ti; | ||||
|  | ||||
| 	for(tmp = _targets.next; tmp != &_targets; tmp = tmp->next) { | ||||
|  | ||||
| 		ti = list_entry(tmp, struct tt_internal, list); | ||||
| 		if (!strcmp(name, ti->tt.name)) | ||||
| 			return ti; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct tt_internal *get_target_type(const char *name) | ||||
| { | ||||
| 	struct tt_internal *ti; | ||||
|  | ||||
| 	read_lock(&_lock); | ||||
| 	ti = __find_target_type(name); | ||||
|  | ||||
| 	if (ti) { | ||||
| 		if (ti->use == 0 && ti->tt.module) | ||||
| 			__MOD_INC_USE_COUNT(ti->tt.module); | ||||
| 		ti->use++; | ||||
| 	} | ||||
| 	read_unlock(&_lock); | ||||
|  | ||||
| 	return ti; | ||||
| } | ||||
|  | ||||
| static void load_module(const char *name) | ||||
| { | ||||
| 	char module_name[DM_MOD_NAME_SIZE] = "dm-"; | ||||
|  | ||||
| 	/* Length check for strcat() below */ | ||||
| 	if (strlen(name) > (DM_MOD_NAME_SIZE - 4)) | ||||
| 		return; | ||||
|  | ||||
| 	strcat(module_name, name); | ||||
| 	request_module(module_name); | ||||
| } | ||||
|  | ||||
| struct target_type *dm_get_target_type(const char *name) | ||||
| { | ||||
| 	struct tt_internal *ti = get_target_type(name); | ||||
|  | ||||
| 	if (!ti) { | ||||
| 		load_module(name); | ||||
| 		ti = get_target_type(name); | ||||
| 	} | ||||
|  | ||||
| 	return ti ? &ti->tt : 0; | ||||
| } | ||||
|  | ||||
| void dm_put_target_type(struct target_type *t) | ||||
| { | ||||
| 	struct tt_internal *ti = (struct tt_internal *) t; | ||||
|  | ||||
| 	read_lock(&_lock); | ||||
| 	if (--ti->use == 0 && ti->tt.module) | ||||
| 		__MOD_DEC_USE_COUNT(ti->tt.module); | ||||
|  | ||||
| 	if (ti->use < 0) | ||||
| 		BUG(); | ||||
| 	read_unlock(&_lock); | ||||
| } | ||||
|  | ||||
| static struct tt_internal *alloc_target(struct target_type *t) | ||||
| { | ||||
| 	struct tt_internal *ti = kmalloc(sizeof(*ti), GFP_KERNEL); | ||||
|  | ||||
| 	if (ti) { | ||||
| 		memset(ti, 0, sizeof(*ti)); | ||||
| 		ti->tt = *t; | ||||
| 	} | ||||
|  | ||||
| 	return ti; | ||||
| } | ||||
|  | ||||
| int dm_register_target(struct target_type *t) | ||||
| { | ||||
| 	int rv = 0; | ||||
| 	struct tt_internal *ti = alloc_target(t); | ||||
|  | ||||
| 	if (!ti) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	write_lock(&_lock); | ||||
| 	if (__find_target_type(t->name)) | ||||
| 		rv = -EEXIST; | ||||
| 	else | ||||
| 		list_add(&ti->list, &_targets); | ||||
|  | ||||
| 	write_unlock(&_lock); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| int dm_unregister_target(struct target_type *t) | ||||
| { | ||||
| 	struct tt_internal *ti = (struct tt_internal *) t; | ||||
| 	int rv = -ETXTBSY; | ||||
|  | ||||
| 	write_lock(&_lock); | ||||
| 	if (ti->use == 0) { | ||||
| 		list_del(&ti->list); | ||||
| 		kfree(ti); | ||||
| 		rv = 0; | ||||
| 	} | ||||
| 	write_unlock(&_lock); | ||||
|  | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * io-err: always fails an io, useful for bringing | ||||
|  * up LV's that have holes in them. | ||||
|  */ | ||||
| static int io_err_ctr(struct dm_table *t, offset_t b, offset_t l, | ||||
| 		      struct text_region *args, void **context, | ||||
| 		      dm_error_fn err, void *e_private) | ||||
| { | ||||
| 	*context = 0; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void io_err_dtr(struct dm_table *t, void *c) | ||||
| { | ||||
| 	/* empty */ | ||||
| } | ||||
|  | ||||
| static int io_err_map(struct buffer_head *bh, int rw, void *context) | ||||
| { | ||||
| 	buffer_IO_error(bh); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct target_type error_target = { | ||||
| 	name: "error", | ||||
| 	ctr: io_err_ctr, | ||||
| 	dtr: io_err_dtr, | ||||
| 	map: io_err_map | ||||
| }; | ||||
|  | ||||
|  | ||||
| int dm_target_init(void) | ||||
| { | ||||
| 	return dm_register_target(&error_target); | ||||
| } | ||||
|  | ||||
| EXPORT_SYMBOL(dm_register_target); | ||||
| EXPORT_SYMBOL(dm_unregister_target); | ||||
|  | ||||
							
								
								
									
										920
									
								
								driver/device-mapper/dm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										920
									
								
								driver/device-mapper/dm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,920 @@ | ||||
| /* | ||||
|  * device-mapper.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Changelog | ||||
|  * | ||||
|  *    14/08/2001 - First Version [Joe Thornber] | ||||
|  */ | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| /* defines for blk.h */ | ||||
| #define MAJOR_NR DM_BLK_MAJOR | ||||
| #define DEVICE_NR(device) MINOR(device)	/* has no partition bits */ | ||||
| #define DEVICE_NAME "device-mapper"	/* name for messaging */ | ||||
| #define DEVICE_NO_RANDOM		/* no entropy to contribute */ | ||||
| #define DEVICE_OFF(d)			/* do-nothing */ | ||||
|  | ||||
| #include <linux/blk.h> | ||||
| #include <linux/blkpg.h> | ||||
|  | ||||
| /* we only need this for the lv_bmap struct definition, not happy */ | ||||
| #include <linux/lvm.h> | ||||
|  | ||||
| #define MAX_DEVICES 64 | ||||
| #define DEFAULT_READ_AHEAD 64 | ||||
|  | ||||
| const char *_name = "device-mapper"; | ||||
| int _version[3] = {0, 1, 0}; | ||||
|  | ||||
| struct io_hook { | ||||
| 	struct mapped_device *md; | ||||
| 	struct target *target; | ||||
| 	int rw; | ||||
|  | ||||
| 	void (*end_io)(struct buffer_head * bh, int uptodate); | ||||
| 	void *context; | ||||
| }; | ||||
|  | ||||
| kmem_cache_t *_io_hook_cache; | ||||
|  | ||||
| #define rl down_read(&_dev_lock) | ||||
| #define ru up_read(&_dev_lock) | ||||
| #define wl down_write(&_dev_lock) | ||||
| #define wu up_write(&_dev_lock) | ||||
|  | ||||
| struct rw_semaphore _dev_lock; | ||||
| static struct mapped_device *_devs[MAX_DEVICES]; | ||||
|  | ||||
| /* block device arrays */ | ||||
| static int _block_size[MAX_DEVICES]; | ||||
| static int _blksize_size[MAX_DEVICES]; | ||||
| static int _hardsect_size[MAX_DEVICES]; | ||||
|  | ||||
| const char *_fs_dir = "device-mapper"; | ||||
| static devfs_handle_t _dev_dir; | ||||
|  | ||||
| static int request(request_queue_t *q, int rw, struct buffer_head *bh); | ||||
| static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb); | ||||
|  | ||||
| /* | ||||
|  * setup and teardown the driver | ||||
|  */ | ||||
| static int dm_init(void) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	init_rwsem(&_dev_lock); | ||||
|  | ||||
| 	if (!_io_hook_cache) | ||||
| 		_io_hook_cache = kmem_cache_create("dm io hooks", | ||||
| 						   sizeof(struct io_hook), | ||||
| 						   0, 0, NULL, NULL); | ||||
|  | ||||
| 	if (!_io_hook_cache) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	if ((ret = dm_fs_init()) || (ret = dm_target_init())) | ||||
| 		return ret; | ||||
|  | ||||
| 	/* set up the arrays */ | ||||
| 	read_ahead[MAJOR_NR] = DEFAULT_READ_AHEAD; | ||||
| 	blk_size[MAJOR_NR] = _block_size; | ||||
| 	blksize_size[MAJOR_NR] = _blksize_size; | ||||
| 	hardsect_size[MAJOR_NR] = _hardsect_size; | ||||
|  | ||||
| 	if (devfs_register_blkdev(MAJOR_NR, _name, &dm_blk_dops) < 0) { | ||||
| 		printk(KERN_ERR "%s -- register_blkdev failed\n", _name); | ||||
| 		return -EIO; | ||||
| 	} | ||||
|  | ||||
| 	blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), request); | ||||
|  | ||||
| 	_dev_dir = devfs_mk_dir(0, _fs_dir, NULL); | ||||
|  | ||||
| 	printk(KERN_INFO "%s %d.%d.%d initialised\n", _name, | ||||
| 	       _version[0], _version[1], _version[2]); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void dm_exit(void) | ||||
| { | ||||
| 	if (kmem_cache_destroy(_io_hook_cache)) | ||||
| 		WARN("it looks like there are still some io_hooks allocated"); | ||||
| 	_io_hook_cache = 0; | ||||
|  | ||||
| 	dm_fs_exit(); | ||||
|  | ||||
| 	if (devfs_unregister_blkdev(MAJOR_NR, _name) < 0) | ||||
| 		printk(KERN_ERR "%s -- unregister_blkdev failed\n", _name); | ||||
|  | ||||
| 	read_ahead[MAJOR_NR] = 0; | ||||
| 	blk_size[MAJOR_NR] = 0; | ||||
| 	blksize_size[MAJOR_NR] = 0; | ||||
| 	hardsect_size[MAJOR_NR] = 0; | ||||
|  | ||||
| 	printk(KERN_INFO "%s %d.%d.%d cleaned up\n", _name, | ||||
| 	       _version[0], _version[1], _version[2]); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * block device functions | ||||
|  */ | ||||
| static int dm_blk_open(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	int minor = MINOR(inode->i_rdev); | ||||
| 	struct mapped_device *md; | ||||
|  | ||||
| 	if (minor >= MAX_DEVICES) | ||||
| 		return -ENXIO; | ||||
|  | ||||
| 	wl; | ||||
| 	md = _devs[minor]; | ||||
|  | ||||
| 	if (!md || !is_active(md)) { | ||||
| 		wu; | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
|  | ||||
| 	md->use_count++; | ||||
| 	wu; | ||||
|  | ||||
| 	MOD_INC_USE_COUNT; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dm_blk_close(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	int minor = MINOR(inode->i_rdev); | ||||
| 	struct mapped_device *md; | ||||
|  | ||||
| 	if (minor >= MAX_DEVICES) | ||||
| 		return -ENXIO; | ||||
|  | ||||
| 	wl; | ||||
| 	md = _devs[minor]; | ||||
| 	if (!md || md->use_count < 1) { | ||||
| 		WARN("reference count in mapped_device incorrect"); | ||||
| 		wu; | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
|  | ||||
| 	md->use_count--; | ||||
| 	wu; | ||||
|  | ||||
| 	MOD_DEC_USE_COUNT; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* In 512-byte units */ | ||||
| #define VOLUME_SIZE(minor) (_block_size[(minor)] >> 1) | ||||
|  | ||||
| static int dm_blk_ioctl(struct inode *inode, struct file *file, | ||||
| 			uint command, ulong a) | ||||
| { | ||||
| 	int minor = MINOR(inode->i_rdev); | ||||
| 	long size; | ||||
|  | ||||
| 	if (minor >= MAX_DEVICES) | ||||
| 		return -ENXIO; | ||||
|  | ||||
| 	switch (command) { | ||||
| 	case BLKSSZGET: | ||||
| 	case BLKROGET: | ||||
| 	case BLKROSET: | ||||
| #if 0 | ||||
| 	case BLKELVSET: | ||||
| 	case BLKELVGET: | ||||
| #endif | ||||
| 		return blk_ioctl(inode->i_dev, command, a); | ||||
| 		break; | ||||
|  | ||||
| 	case BLKGETSIZE: | ||||
| 		size = VOLUME_SIZE(minor); | ||||
| 		if (copy_to_user((void *) a, &size, sizeof (long))) | ||||
| 			return -EFAULT; | ||||
| 		break; | ||||
|  | ||||
| 	case BLKFLSBUF: | ||||
| 		if (!capable(CAP_SYS_ADMIN)) | ||||
| 			return -EACCES; | ||||
| 		fsync_dev(inode->i_rdev); | ||||
| 		invalidate_buffers(inode->i_rdev); | ||||
| 		return 0; | ||||
|  | ||||
| 	case BLKRAGET: | ||||
| 		if (copy_to_user | ||||
| 		    ((void *) a, &read_ahead[MAJOR(inode->i_rdev)], | ||||
| 		     sizeof (long))) | ||||
| 			return -EFAULT; | ||||
| 		return 0; | ||||
|  | ||||
| 	case BLKRASET: | ||||
| 		if (!capable(CAP_SYS_ADMIN)) | ||||
| 			return -EACCES; | ||||
| 		read_ahead[MAJOR(inode->i_rdev)] = a; | ||||
| 		return 0; | ||||
|  | ||||
| 	case BLKRRPART: | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	case LV_BMAP: | ||||
| 		return dm_user_bmap(inode, (struct lv_bmap *) a); | ||||
|  | ||||
| 	default: | ||||
| 		WARN("unknown block ioctl %d", command); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static inline struct io_hook *alloc_io_hook(void) | ||||
| { | ||||
| 	return kmem_cache_alloc(_io_hook_cache, GFP_NOIO); | ||||
| } | ||||
|  | ||||
| static inline void free_io_hook(struct io_hook *ih) | ||||
| { | ||||
| 	kmem_cache_free(_io_hook_cache, ih); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * FIXME: need to decide if deferred_io's need | ||||
|  * their own slab, I say no for now since they are | ||||
|  * only used when the device is suspended. | ||||
|  */ | ||||
| static inline struct deferred_io *alloc_deferred(void) | ||||
| { | ||||
| 	return kmalloc(sizeof(struct deferred_io), GFP_NOIO); | ||||
| } | ||||
|  | ||||
| static inline void free_deferred(struct deferred_io *di) | ||||
| { | ||||
| 	kfree(di); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * call a targets optional error function if | ||||
|  * an io failed. | ||||
|  */ | ||||
| static inline int call_err_fn(struct io_hook *ih, struct buffer_head *bh) | ||||
| { | ||||
| 	dm_err_fn err = ih->target->type->err; | ||||
| 	if (err) | ||||
| 		return err(bh, ih->rw, ih->target->private); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * bh->b_end_io routine that decrements the | ||||
|  * pending count and then calls the original | ||||
|  * bh->b_end_io fn. | ||||
|  */ | ||||
| static void dec_pending(struct buffer_head *bh, int uptodate) | ||||
| { | ||||
| 	struct io_hook *ih = bh->b_private; | ||||
|  | ||||
| 	if (!uptodate && call_err_fn(ih, bh)) | ||||
| 		return; | ||||
|  | ||||
| 	if (atomic_dec_and_test(&ih->md->pending)) | ||||
| 		/* nudge anyone waiting on suspend queue */ | ||||
| 		wake_up(&ih->md->wait); | ||||
|  | ||||
| 	bh->b_end_io = ih->end_io; | ||||
| 	bh->b_private = ih->context; | ||||
| 	free_io_hook(ih); | ||||
|  | ||||
| 	bh->b_end_io(bh, uptodate); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * add the bh to the list of deferred io. | ||||
|  */ | ||||
| static int queue_io(struct mapped_device *md, struct buffer_head *bh, int rw) | ||||
| { | ||||
| 	struct deferred_io *di = alloc_deferred(); | ||||
|  | ||||
| 	if (!di) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	wl; | ||||
| 	if (test_bit(DM_ACTIVE, &md->state)) { | ||||
| 		wu; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	di->bh = bh; | ||||
| 	di->rw = rw; | ||||
| 	di->next = md->deferred; | ||||
| 	md->deferred = di; | ||||
| 	wu; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * do the bh mapping for a given leaf | ||||
|  */ | ||||
| static inline int __map_buffer(struct mapped_device *md, | ||||
| 			       struct buffer_head *bh, int rw, int leaf) | ||||
| { | ||||
| 	int r; | ||||
| 	dm_map_fn fn; | ||||
| 	void *context; | ||||
| 	struct io_hook *ih = NULL; | ||||
| 	struct target *ti = md->map->targets + leaf; | ||||
|  | ||||
| 	fn = ti->type->map; | ||||
| 	context = ti->private; | ||||
|  | ||||
| 	ih = alloc_io_hook(); | ||||
|  | ||||
| 	if (!ih) | ||||
| 		return 0; | ||||
|  | ||||
| 	ih->md = md; | ||||
| 	ih->rw = rw; | ||||
| 	ih->target = ti; | ||||
| 	ih->end_io = bh->b_end_io; | ||||
| 	ih->context = bh->b_private; | ||||
|  | ||||
| 	r = fn(bh, rw, context); | ||||
|  | ||||
| 	if (r > 0) { | ||||
| 		/* hook the end io request fn */ | ||||
| 		atomic_inc(&md->pending); | ||||
| 		bh->b_end_io = dec_pending; | ||||
| 		bh->b_private = ih; | ||||
|  | ||||
| 	} else if (r == 0) | ||||
| 		/* we don't need to hook */ | ||||
| 		free_io_hook(ih); | ||||
|  | ||||
| 	else if (r < 0) { | ||||
| 		free_io_hook(ih); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * search the btree for the correct target. | ||||
|  */ | ||||
| static inline int __find_node(struct dm_table *t, struct buffer_head *bh) | ||||
| { | ||||
| 	int l, n = 0, k = 0; | ||||
| 	offset_t *node; | ||||
|  | ||||
| 	for (l = 0; l < t->depth; l++) { | ||||
| 		n = get_child(n, k); | ||||
| 		node = get_node(t, l, n); | ||||
|  | ||||
| 		for (k = 0; k < KEYS_PER_NODE; k++) | ||||
| 			if (node[k] >= bh->b_rsector) | ||||
| 				break; | ||||
| 	} | ||||
|  | ||||
| 	return (KEYS_PER_NODE * n) + k; | ||||
| } | ||||
|  | ||||
| static int request(request_queue_t *q, int rw, struct buffer_head *bh) | ||||
| { | ||||
| 	struct mapped_device *md; | ||||
| 	int r, minor = MINOR(bh->b_rdev); | ||||
|  | ||||
| 	if (minor >= MAX_DEVICES) | ||||
| 		goto bad_no_lock; | ||||
|  | ||||
| 	rl; | ||||
| 	md = _devs[minor]; | ||||
|  | ||||
| 	if (!md || !md->map) | ||||
| 		goto bad; | ||||
|  | ||||
| 	/* if we're suspended we have to queue this io for later */ | ||||
| 	if (!test_bit(DM_ACTIVE, &md->state)) { | ||||
| 		ru; | ||||
| 		r = queue_io(md, bh, rw); | ||||
|  | ||||
| 		if (r < 0) | ||||
| 			goto bad_no_lock; | ||||
|  | ||||
| 		else if (r > 0) | ||||
| 			return 0; /* deferred successfully */ | ||||
|  | ||||
| 		rl;	/* FIXME: there's still a race here */ | ||||
| 	} | ||||
|  | ||||
| 	if (!__map_buffer(md, bh, rw, __find_node(md->map, bh))) | ||||
| 		goto bad; | ||||
|  | ||||
| 	ru; | ||||
| 	return 1; | ||||
|  | ||||
|  bad: | ||||
| 	ru; | ||||
|  | ||||
|  bad_no_lock: | ||||
| 	buffer_IO_error(bh); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int check_dev_size(int minor, unsigned long block) | ||||
| { | ||||
| 	/* FIXME: check this */ | ||||
| 	unsigned long max_sector = (_block_size[minor] << 1) + 1; | ||||
| 	unsigned long sector = (block + 1) * (_blksize_size[minor] >> 9); | ||||
|  | ||||
| 	return (sector > max_sector) ? 0 : 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * creates a dummy buffer head and maps it (for lilo). | ||||
|  */ | ||||
| static int do_bmap(kdev_t dev, unsigned long block, | ||||
| 		   kdev_t *r_dev, unsigned long *r_block) | ||||
| { | ||||
| 	struct mapped_device *md; | ||||
| 	struct buffer_head bh; | ||||
| 	int minor = MINOR(dev), r; | ||||
| 	struct target *t; | ||||
|  | ||||
| 	rl; | ||||
| 	if ((minor >= MAX_DEVICES) || !(md = _devs[minor]) || | ||||
| 	    !test_bit(DM_ACTIVE, &md->state)) { | ||||
| 		r = -ENXIO; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!check_dev_size(minor, block)) { | ||||
| 		r = -EINVAL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* setup dummy bh */ | ||||
| 	memset(&bh, 0, sizeof(bh)); | ||||
| 	bh.b_blocknr = block; | ||||
| 	bh.b_dev = bh.b_rdev = dev; | ||||
| 	bh.b_size = _blksize_size[minor]; | ||||
| 	bh.b_rsector = block * (bh.b_size >> 9); | ||||
|  | ||||
| 	/* find target */ | ||||
| 	t = md->map->targets + __find_node(md->map, &bh); | ||||
|  | ||||
| 	/* do the mapping */ | ||||
| 	r = t->type->map(&bh, READ, t->private); | ||||
|  | ||||
| 	*r_dev = bh.b_rdev; | ||||
| 	*r_block = bh.b_rsector / (bh.b_size >> 9); | ||||
|  | ||||
|  out: | ||||
| 	ru; | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * marshals arguments and results between user and | ||||
|  * kernel space. | ||||
|  */ | ||||
| static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb) | ||||
| { | ||||
| 	unsigned long block, r_block; | ||||
| 	kdev_t r_dev; | ||||
| 	int r; | ||||
|  | ||||
| 	if (get_user(block, &lvb->lv_block)) | ||||
| 		return -EFAULT; | ||||
|  | ||||
| 	if ((r = do_bmap(inode->i_rdev, block, &r_dev, &r_block))) | ||||
| 		return r; | ||||
|  | ||||
| 	if (put_user(kdev_t_to_nr(r_dev), &lvb->lv_dev) || | ||||
| 	    put_user(r_block, &lvb->lv_block)) | ||||
| 		return -EFAULT; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * see if the device with a specific minor # is | ||||
|  * free. | ||||
|  */ | ||||
| static inline int __specific_dev(int minor) | ||||
| { | ||||
| 	if (minor > MAX_DEVICES) { | ||||
| 		WARN("request for a mapped_device > than MAX_DEVICES"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!_devs[minor]) | ||||
| 		return minor; | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * find the first free device. | ||||
|  */ | ||||
| static inline int __any_old_dev(void) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < MAX_DEVICES; i++) | ||||
| 		if (!_devs[i]) | ||||
| 			return i; | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * allocate and initialise a blank device. | ||||
|  */ | ||||
| static struct mapped_device *alloc_dev(int minor) | ||||
| { | ||||
| 	struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL); | ||||
|  | ||||
| 	if (!md) | ||||
| 		return 0; | ||||
|  | ||||
| 	memset(md, 0, sizeof (*md)); | ||||
|  | ||||
| 	wl; | ||||
| 	minor = (minor < 0) ? __any_old_dev() : __specific_dev(minor); | ||||
|  | ||||
| 	if (minor < 0) { | ||||
| 		WARN("no free devices available"); | ||||
| 		wu; | ||||
| 		kfree(md); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	md->dev = MKDEV(DM_BLK_MAJOR, minor); | ||||
| 	md->name[0] = '\0'; | ||||
| 	md->state = 0; | ||||
|  | ||||
| 	init_waitqueue_head(&md->wait); | ||||
|  | ||||
| 	_devs[minor] = md; | ||||
| 	wu; | ||||
|  | ||||
| 	return md; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * open a device so we can use it as a map | ||||
|  * destination. | ||||
|  */ | ||||
| static int open_dev(struct dm_dev *d) | ||||
| { | ||||
|        int err; | ||||
|  | ||||
|        if (d->bd) | ||||
| 	       BUG(); | ||||
|  | ||||
|        if (!(d->bd = bdget(kdev_t_to_nr(d->dev)))) | ||||
|                return -ENOMEM; | ||||
|  | ||||
|        if ((err = blkdev_get(d->bd, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE))) { | ||||
|                bdput(d->bd); | ||||
|                return err; | ||||
|        } | ||||
|  | ||||
|        return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * close a device that we've been using. | ||||
|  */ | ||||
| static void close_dev(struct dm_dev *d) | ||||
| { | ||||
| 	if (!d->bd) | ||||
| 		return; | ||||
|  | ||||
| 	blkdev_put(d->bd, BDEV_FILE); | ||||
| 	bdput(d->bd); | ||||
| 	d->bd = 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Close a list of devices. | ||||
|  */ | ||||
| static void close_devices(struct list_head *devices) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
|  | ||||
| 	for (tmp = devices->next; tmp != devices; tmp = tmp->next) { | ||||
| 		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); | ||||
| 		close_dev(dd); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Open a list of devices. | ||||
|  */ | ||||
| static int open_devices(struct list_head *devices) | ||||
| { | ||||
| 	int r = 0; | ||||
| 	struct list_head *tmp; | ||||
|  | ||||
| 	for (tmp = devices->next; tmp != devices; tmp = tmp->next) { | ||||
| 		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); | ||||
| 		if ((r = open_dev(dd))) | ||||
| 			goto bad; | ||||
| 	} | ||||
| 	return 0; | ||||
|  | ||||
|  bad: | ||||
| 	close_devices(devices); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
|  | ||||
| struct mapped_device *dm_find_by_minor(int minor) | ||||
| { | ||||
| 	struct mapped_device *md; | ||||
|  | ||||
| 	rl; | ||||
| 	md = _devs[minor]; | ||||
| 	ru; | ||||
|  | ||||
| 	return md; | ||||
| } | ||||
|  | ||||
| static int register_device(struct mapped_device *md) | ||||
| { | ||||
| 	md->devfs_entry = | ||||
| 		devfs_register(_dev_dir, md->name, DEVFS_FL_CURRENT_OWNER, | ||||
| 			       MAJOR(md->dev), MINOR(md->dev), | ||||
| 			       S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, | ||||
| 			       &dm_blk_dops, NULL); | ||||
|  | ||||
| 	if (!md->devfs_entry) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int unregister_device(struct mapped_device *md) | ||||
| { | ||||
| 	devfs_unregister(md->devfs_entry); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * constructor for a new device | ||||
|  */ | ||||
| struct mapped_device *dm_create(const char *name, int minor) | ||||
| { | ||||
| 	int r; | ||||
| 	struct mapped_device *md; | ||||
|  | ||||
| 	if (minor >= MAX_DEVICES) | ||||
| 		return ERR_PTR(-ENXIO); | ||||
|  | ||||
| 	if (!(md = alloc_dev(minor))) | ||||
| 		return ERR_PTR(-ENXIO); | ||||
|  | ||||
| 	wl; | ||||
| 	strcpy(md->name, name); | ||||
| 	_devs[minor] = md; | ||||
| 	if ((r = register_device(md))) { | ||||
| 		wu; | ||||
| 		return ERR_PTR(r); | ||||
| 	} | ||||
| 	wu; | ||||
|  | ||||
| 	return md; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * destructor for the device.  md->map is | ||||
|  * deliberately not destroyed, dm-fs should manage | ||||
|  * table objects. | ||||
|  */ | ||||
| int dm_remove(struct mapped_device *md) | ||||
| { | ||||
| 	int minor, r; | ||||
|  | ||||
| 	wl; | ||||
| 	if (md->use_count) { | ||||
| 		wu; | ||||
| 		return -EPERM; | ||||
| 	} | ||||
|  | ||||
| 	if ((r = unregister_device(md))) { | ||||
| 		wu; | ||||
| 		return r; | ||||
| 	} | ||||
|  | ||||
| 	minor = MINOR(md->dev); | ||||
| 	_devs[minor] = 0; | ||||
| 	wu; | ||||
|  | ||||
| 	kfree(md); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * the hardsect size for a mapped device is the | ||||
|  * smallest hard sect size from the devices it | ||||
|  * maps onto. | ||||
|  */ | ||||
| static int __find_hardsect_size(struct list_head *devices) | ||||
| { | ||||
| 	int result = INT_MAX, size; | ||||
| 	struct list_head *tmp; | ||||
|  | ||||
| 	for (tmp = devices->next; tmp != devices; tmp = tmp->next) { | ||||
| 		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); | ||||
| 		size = get_hardsect_size(dd->dev); | ||||
| 		if (size < result) | ||||
| 			result = size; | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Bind a table to the device. | ||||
|  */ | ||||
| void __bind(struct mapped_device *md, struct dm_table *t) | ||||
| { | ||||
| 	int minor = MINOR(md->dev); | ||||
|  | ||||
| 	md->map = t; | ||||
|  | ||||
| 	/* in k */ | ||||
| 	_block_size[minor] = (t->highs[t->num_targets - 1] + 1) >> 1; | ||||
|  | ||||
| 	_blksize_size[minor] = BLOCK_SIZE; | ||||
| 	_hardsect_size[minor] = __find_hardsect_size(&t->devices); | ||||
| 	register_disk(NULL, md->dev, 1, &dm_blk_dops, _block_size[minor]); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * requeue the deferred buffer_heads by calling | ||||
|  * generic_make_request. | ||||
|  */ | ||||
| static void __flush_deferred_io(struct mapped_device *md) | ||||
| { | ||||
| 	struct deferred_io *c, *n; | ||||
|  | ||||
| 	for (c = md->deferred, md->deferred = 0; c; c = n) { | ||||
| 		n = c->next; | ||||
| 		generic_make_request(c->rw, c->bh); | ||||
| 		free_deferred(c); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * make the device available for use, if was | ||||
|  * previously suspended rather than newly created | ||||
|  * then all queued io is flushed | ||||
|  */ | ||||
| int dm_activate(struct mapped_device *md, struct dm_table *table) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	/* check that the mapping has at least been loaded. */ | ||||
| 	if (!table->num_targets) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	wl; | ||||
|  | ||||
| 	/* you must be deactivated first */ | ||||
| 	if (is_active(md)) { | ||||
| 		wu; | ||||
| 		return -EPERM; | ||||
| 	} | ||||
|  | ||||
| 	__bind(md, table); | ||||
|  | ||||
| 	if ((r = open_devices(&md->map->devices))) { | ||||
| 		wu; | ||||
| 		return r; | ||||
| 	} | ||||
|  | ||||
| 	set_bit(DM_ACTIVE, &md->state); | ||||
| 	__flush_deferred_io(md); | ||||
| 	wu; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Deactivate the device, the device must not be | ||||
|  * opened by anyone. | ||||
|  */ | ||||
| int dm_deactivate(struct mapped_device *md) | ||||
| { | ||||
| 	rl; | ||||
| 	if (md->use_count) { | ||||
| 		ru; | ||||
| 		return -EPERM; | ||||
| 	} | ||||
|  | ||||
| 	fsync_dev(md->dev); | ||||
|  | ||||
| 	ru; | ||||
|  | ||||
| 	wl; | ||||
| 	if (md->use_count) { | ||||
| 		/* drat, somebody got in quick ... */ | ||||
| 		wu; | ||||
| 		return -EPERM; | ||||
| 	} | ||||
|  | ||||
| 	close_devices(&md->map->devices); | ||||
| 	md->map = 0; | ||||
| 	clear_bit(DM_ACTIVE, &md->state); | ||||
| 	wu; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * We need to be able to change a mapping table | ||||
|  * under a mounted filesystem.  for example we | ||||
|  * might want to move some data in the background. | ||||
|  * Before the table can be swapped with | ||||
|  * dm_bind_table, dm_suspend must be called to | ||||
|  * flush any in flight buffer_heads and ensure | ||||
|  * that any further io gets deferred. | ||||
|  */ | ||||
| void dm_suspend(struct mapped_device *md) | ||||
| { | ||||
| 	DECLARE_WAITQUEUE(wait, current); | ||||
|  | ||||
| 	wl; | ||||
| 	if (!is_active(md)) { | ||||
| 		wu; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	clear_bit(DM_ACTIVE, &md->state); | ||||
| 	wu; | ||||
|  | ||||
| 	/* wait for all the pending io to flush */ | ||||
| 	add_wait_queue(&md->wait, &wait); | ||||
| 	current->state = TASK_UNINTERRUPTIBLE; | ||||
| 	do { | ||||
| 		wl; | ||||
| 		if (!atomic_read(&md->pending)) | ||||
| 			break; | ||||
|  | ||||
| 		wu; | ||||
| 		schedule(); | ||||
|  | ||||
| 	} while (1); | ||||
|  | ||||
| 	current->state = TASK_RUNNING; | ||||
| 	remove_wait_queue(&md->wait, &wait); | ||||
| 	close_devices(&md->map->devices); | ||||
|  | ||||
| 	md->map = 0; | ||||
| 	wu; | ||||
| } | ||||
|  | ||||
| struct block_device_operations dm_blk_dops = { | ||||
| 	open:	  dm_blk_open, | ||||
| 	release:  dm_blk_close, | ||||
| 	ioctl:	  dm_blk_ioctl | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * module hooks | ||||
|  */ | ||||
| module_init(dm_init); | ||||
| module_exit(dm_exit); | ||||
|  | ||||
| MODULE_DESCRIPTION("device-mapper driver"); | ||||
| MODULE_AUTHOR("Joe Thornber <thornber@btconnect.com>"); | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
							
								
								
									
										276
									
								
								driver/device-mapper/dm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								driver/device-mapper/dm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,276 @@ | ||||
| /* | ||||
|  * dm.h | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Internal header file for device mapper | ||||
|  * | ||||
|  * Changelog | ||||
|  * | ||||
|  *     16/08/2001 - First version [Joe Thornber] | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * This driver attempts to provide a generic way of specifying logical | ||||
|  * devices which are mapped onto other devices. | ||||
|  * | ||||
|  * It does this by mapping sections of the logical device onto 'targets'. | ||||
|  * | ||||
|  * When the logical device is accessed the make_request function looks up | ||||
|  * the correct target for the given sector, and then asks this target | ||||
|  * to do the remapping. | ||||
|  * | ||||
|  * (dm-table.c) A btree like structure is used to hold the sector | ||||
|  * range -> target mapping.  Because we know all the entries in the | ||||
|  * btree in advance we can make a very compact tree, omitting pointers | ||||
|  * to child nodes, (child nodes locations can be calculated). Each | ||||
|  * node of the btree is 1 level cache line in size, this gives a small | ||||
|  * performance boost. | ||||
|  * | ||||
|  * A userland test program for the btree gave the following results on a | ||||
|  * 1 Gigahertz Athlon machine: | ||||
|  * | ||||
|  * entries in btree               lookups per second | ||||
|  * ----------------               ------------------ | ||||
|  * 5                              25,000,000 | ||||
|  * 1000                           7,700,000 | ||||
|  * 10,000,000                     3,800,000 | ||||
|  * | ||||
|  * Of course these results should be taken with a pinch of salt; the | ||||
|  * lookups were sequential and there were no other applications (other | ||||
|  * than X + emacs) running to give any pressure on the level 1 cache. | ||||
|  * | ||||
|  * Typical LVM users would find they have very few targets for each | ||||
|  * LV (probably less than 10). | ||||
|  * | ||||
|  * (dm-target.c) Target types are not hard coded, instead the | ||||
|  * register_mapping_type function should be called.  A target type is | ||||
|  * specified using three functions (see the header): | ||||
|  * | ||||
|  * dm_ctr_fn - takes a string and contructs a target specific piece of | ||||
|  *             context data. | ||||
|  * dm_dtr_fn - destroy contexts. | ||||
|  * dm_map_fn - function that takes a buffer_head and some previously | ||||
|  *             constructed context and performs the remapping. | ||||
|  * | ||||
|  * Currently there are two two trivial mappers, which are | ||||
|  * automatically registered: 'linear', and 'io_error'.  Linear alone | ||||
|  * is enough to implement most LVM features (omitting striped volumes | ||||
|  * and snapshots). | ||||
|  * | ||||
|  * (dm-fs.c) The driver is controlled through a /proc interface: | ||||
|  * /proc/device-mapper/control allows you to create and remove devices | ||||
|  * by 'cat'ing a line of the following format: | ||||
|  * | ||||
|  * create <device name> [minor no] | ||||
|  * remove <device name> | ||||
|  * | ||||
|  * /proc/device-mapper/<device name> accepts the mapping table: | ||||
|  * | ||||
|  * begin | ||||
|  * <sector start> <length> <target name> <target args>... | ||||
|  * ... | ||||
|  * end | ||||
|  * | ||||
|  * The begin/end lines are nasty, they should be handled by open/close | ||||
|  * for the file. | ||||
|  * | ||||
|  * At the moment the table assumes 32 bit keys (sectors), the move to | ||||
|  * 64 bits will involve no interface changes, since the tables will be | ||||
|  * read in as ascii data.  A different table implementation can | ||||
|  * therefor be provided at another time.  Either just by changing offset_t | ||||
|  * to 64 bits, or maybe implementing a structure which looks up the keys in | ||||
|  * stages (ie, 32 bits at a time). | ||||
|  * | ||||
|  * More interesting targets: | ||||
|  * | ||||
|  * striped mapping; given a stripe size and a number of device regions | ||||
|  * this would stripe data across the regions.  Especially useful, since | ||||
|  * we could limit each striped region to a 32 bit area and then avoid | ||||
|  * nasty 64 bit %'s. | ||||
|  * | ||||
|  * mirror mapping (reflector ?); would set off a kernel thread slowly | ||||
|  * copying data from one region to another, ensuring that any new | ||||
|  * writes got copied to both destinations correctly.  Great for | ||||
|  * implementing pvmove.  Not sure how userland would be notified that | ||||
|  * the copying process had completed.  Possibly by reading a /proc entry | ||||
|  * for the LV.  Could also use poll() for this kind of thing. | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #ifndef DM_INTERNAL_H | ||||
| #define DM_INTERNAL_H | ||||
|  | ||||
| #include <linux/version.h> | ||||
| #include <linux/major.h> | ||||
| #include <linux/iobuf.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/vmalloc.h> | ||||
| #include <linux/compatmac.h> | ||||
| #include <linux/cache.h> | ||||
| #include <linux/devfs_fs_kernel.h> | ||||
| #include <linux/ctype.h> | ||||
| #include <linux/device-mapper.h> | ||||
| #include <linux/list.h> | ||||
|  | ||||
| #define MAX_DEPTH 16 | ||||
| #define NODE_SIZE L1_CACHE_BYTES | ||||
| #define KEYS_PER_NODE (NODE_SIZE / sizeof(offset_t)) | ||||
| #define CHILDREN_PER_NODE (KEYS_PER_NODE + 1) | ||||
| #define DM_NAME_LEN 128 | ||||
| #define MAX_TARGET_LINE 256 | ||||
|  | ||||
| enum { | ||||
| 	DM_BOUND = 0,		/* device has been bound to a table */ | ||||
| 	DM_ACTIVE,		/* device is running */ | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * list of devices that a metadevice uses | ||||
|  * and hence should open/close. | ||||
|  */ | ||||
| struct dm_dev { | ||||
| 	atomic_t count; | ||||
| 	struct list_head list; | ||||
|  | ||||
| 	kdev_t dev; | ||||
| 	struct block_device *bd; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * io that had to be deferred while we were | ||||
|  * suspended | ||||
|  */ | ||||
| struct deferred_io { | ||||
| 	int rw; | ||||
| 	struct buffer_head *bh; | ||||
| 	struct deferred_io *next; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * btree leaf, these do the actual mapping | ||||
|  */ | ||||
| struct target { | ||||
| 	struct target_type *type; | ||||
| 	void *private; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * the btree | ||||
|  */ | ||||
| struct dm_table { | ||||
| 	/* btree table */ | ||||
| 	int depth; | ||||
| 	int counts[MAX_DEPTH];	/* in nodes */ | ||||
| 	offset_t *index[MAX_DEPTH]; | ||||
|  | ||||
| 	int num_targets; | ||||
| 	int num_allocated; | ||||
| 	offset_t *highs; | ||||
| 	struct target *targets; | ||||
|  | ||||
| 	/* a list of devices used by this table */ | ||||
| 	struct list_head devices; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * the actual device struct | ||||
|  */ | ||||
| struct mapped_device { | ||||
| 	kdev_t dev; | ||||
| 	char name[DM_NAME_LEN]; | ||||
| 	struct inode *inode; | ||||
|  | ||||
| 	int use_count; | ||||
| 	int state; | ||||
|  | ||||
| 	/* a list of io's that arrived while we were suspended */ | ||||
| 	atomic_t pending; | ||||
| 	wait_queue_head_t wait; | ||||
| 	struct deferred_io *deferred; | ||||
|  | ||||
| 	struct dm_table *map; | ||||
|  | ||||
| 	/* used by dm-fs.c */ | ||||
| 	devfs_handle_t devfs_entry; | ||||
| }; | ||||
|  | ||||
| extern struct block_device_operations dm_blk_dops; | ||||
|  | ||||
|  | ||||
| /* dm-target.c */ | ||||
| int dm_target_init(void); | ||||
| struct target_type *dm_get_target_type(const char *name); | ||||
| void dm_put_target_type(struct target_type *t); | ||||
|  | ||||
| /* dm.c */ | ||||
| struct mapped_device *dm_find_by_minor(int minor); | ||||
|  | ||||
| struct mapped_device *dm_create(const char *name, int minor); | ||||
| int dm_remove(struct mapped_device *md); | ||||
|  | ||||
| int dm_activate(struct mapped_device *md, struct dm_table *t); | ||||
| int dm_deactivate(struct mapped_device *md); | ||||
|  | ||||
| void dm_suspend(struct mapped_device *md); | ||||
|  | ||||
|  | ||||
| /* dm-table.c */ | ||||
| struct dm_table *dm_table_create(void); | ||||
| void dm_table_destroy(struct dm_table *t); | ||||
|  | ||||
| int dm_table_add_target(struct dm_table *t, offset_t high, | ||||
| 			struct target_type *type, void *private); | ||||
| int dm_table_complete(struct dm_table *t); | ||||
|  | ||||
| /* dm-parse.c */ | ||||
| typedef int (*extract_line_fn)(struct text_region *line, | ||||
| 			       void *private); | ||||
|  | ||||
| struct dm_table *dm_parse(extract_line_fn line_fn, void *line_private, | ||||
| 			  dm_error_fn err_fn, void *err_private); | ||||
|  | ||||
|  | ||||
| static inline int dm_empty_tok(struct text_region *txt) | ||||
| { | ||||
| 	return txt->b >= txt->e; | ||||
| } | ||||
|  | ||||
| /* dm-fs.c */ | ||||
| int dm_fs_init(void); | ||||
| void dm_fs_exit(void); | ||||
|  | ||||
|  | ||||
|  | ||||
| #define WARN(f, x...) printk(KERN_WARNING "device-mapper: " f "\n" , ## x) | ||||
|  | ||||
| /* | ||||
|  * calculate the index of the child node of the | ||||
|  * n'th node k'th key. | ||||
|  */ | ||||
| static inline int get_child(int n, int k) | ||||
| { | ||||
| 	return (n * CHILDREN_PER_NODE) + k; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * returns the n'th node of level l from table t. | ||||
|  */ | ||||
| static inline offset_t *get_node(struct dm_table *t, int l, int n) | ||||
| { | ||||
| 	return t->index[l] + (n * KEYS_PER_NODE); | ||||
| } | ||||
|  | ||||
| static inline int is_active(struct mapped_device *md) | ||||
| { | ||||
| 	return test_bit(DM_ACTIVE, &md->state); | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										156
									
								
								driver/device-mapper/dmfs-error.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								driver/device-mapper/dmfs-error.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| /* | ||||
|  * dmfs-error.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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 <linux/config.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/fs.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| struct dmfs_error { | ||||
| 	struct list_head list; | ||||
| 	unsigned len; | ||||
| 	char *msg; | ||||
| }; | ||||
|  | ||||
| void dmfs_add_error(struct dm_table *t, unsigned num, char *str) | ||||
| { | ||||
| 	int len = strlen(str) + sizeof(struct dmfs_error) + 12; | ||||
| 	struct dmfs_error *e = kmalloc(len, GFP_KERNEL); | ||||
| 	if (e) { | ||||
| 		e->msg = (char *)(e + 1); | ||||
| 		e->len = sprintf(e->msg, "%8u: %s\n", num, str); | ||||
| 		list_add(&e->list, &t->errors); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void dmfs_zap_errors(struct dm_table *t) | ||||
| { | ||||
| 	struct dmfs_error *e; | ||||
|  | ||||
| 	while(!list_empty(&t->errors)) { | ||||
| 		e = list_entry(t->errors.next, struct dmfs_error, list); | ||||
| 		list_del(&e->list); | ||||
| 		kfree(e); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static struct dmfs_error *find_initial_message(struct dm_table *t, loff_t *pos) | ||||
| { | ||||
| 	struct dmfs_error *e; | ||||
| 	struct list_head *tmp, *head; | ||||
|  | ||||
| 	tmp = head = &t->errors; | ||||
| 	for(;;) { | ||||
| 		tmp = tmp->next; | ||||
| 		if (tmp == head) | ||||
| 			break; | ||||
| 		e = list_entry(tmp, struct dmfs_error, list); | ||||
| 		if (*pos < e->len) | ||||
| 			return e; | ||||
| 		(*pos) -= e->len; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int copy_sequence(struct dm_table *t, struct dmfs_error *e, char *buf, | ||||
| 			size_t size, loff_t offset) | ||||
| { | ||||
| 	char *from; | ||||
| 	int amount; | ||||
| 	int copied = 0; | ||||
|  | ||||
| 	do { | ||||
| 		from = e->msg + offset; | ||||
| 		amount = e->len - offset; | ||||
|  | ||||
| 		if (copy_to_user(buf, from, amount)) | ||||
| 			return -EFAULT; | ||||
|  | ||||
| 		buf += amount; | ||||
| 		copied += amount; | ||||
| 		size -= amount; | ||||
| 		offset = 0; | ||||
|  | ||||
| 		if (e->list.next == &t->errors) | ||||
| 			break; | ||||
| 		e = list_entry(e->list.next, struct dmfs_error, list); | ||||
| 	} while(size > 0); | ||||
|  | ||||
| 	return amount; | ||||
| } | ||||
|  | ||||
| static ssize_t dmfs_error_read(struct file *file, char *buf, size_t size, loff_t *pos) | ||||
| { | ||||
| 	struct dmfs_i *dmi = DMFS_I(file->f_dentry->d_parent->d_inode); | ||||
| 	struct dm_table *t = dmi->table; | ||||
| 	int copied = 0; | ||||
| 	loff_t offset = *pos; | ||||
|  | ||||
| 	if (!access_ok(VERIFY_WRITE, buf, size)) | ||||
| 		return -EFAULT; | ||||
|  | ||||
| 	down(&dmi->sem); | ||||
| 	if (dmi->table) { | ||||
| 		struct dmfs_error *e = find_initial_message(t, &offset); | ||||
| 		if (e) { | ||||
| 			copied = copy_sequence(t, e, buf, size, offset); | ||||
| 			if (copied > 0) | ||||
| 				(*pos) += copied; | ||||
| 		} | ||||
| 	} | ||||
| 	up(&dmi->sem); | ||||
| 	return copied; | ||||
| } | ||||
|  | ||||
| static int dmfs_error_sync(struct file *file, struct dentry *dentry, int datasync) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct file_operations dmfs_error_file_operations = { | ||||
| 	read:		dmfs_error_read, | ||||
| 	fsync:		dmfs_error_sync, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations dmfs_error_inode_operations = { | ||||
| }; | ||||
|  | ||||
| struct inode *dmfs_create_error(struct inode *dir, int mode) | ||||
| { | ||||
| 	struct inode *inode = new_inode(dir->i_sb); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_mode = mode | S_IFREG; | ||||
| 		inode->i_uid = current->fsuid; | ||||
| 		inode->i_gid = current->fsgid; | ||||
| 		inode->i_blksize = PAGE_CACHE_SIZE; | ||||
| 		inode->i_blocks = 0; | ||||
| 		inode->i_rdev = NODEV; | ||||
| 		inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; | ||||
| 		inode->i_fop = &dmfs_error_file_operations; | ||||
| 		inode->i_op = &dmfs_error_inode_operations; | ||||
| 	} | ||||
|  | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
							
								
								
									
										282
									
								
								driver/device-mapper/dmfs-lv.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								driver/device-mapper/dmfs-lv.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,282 @@ | ||||
| /* | ||||
|  * dmfs-lv.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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. | ||||
|  */ | ||||
|  | ||||
| /* Heavily based upon ramfs */ | ||||
|  | ||||
| #include <linux/config.h> | ||||
| #include <linux/ctype.h> | ||||
| #include <linux/fs.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| extern struct address_space_operations dmfs_address_space_operations; | ||||
| extern struct inode *dmfs_create_tdir(struct super_block *sb, int mode); | ||||
|  | ||||
| struct dentry *dmfs_verify_name(struct inode *dir, const char *name) | ||||
| { | ||||
| 	struct nameidata nd; | ||||
| 	int err = -ENOENT; | ||||
| 	struct file file; | ||||
| 	struct dentry *dentry; | ||||
|  | ||||
| 	memset(&file, 0, sizeof(struct file)); | ||||
|  | ||||
| 	if (!path_init(name, LOOKUP_FOLLOW, &nd)) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
|  | ||||
| 	err = path_walk(name, &nd); | ||||
| 	if (err) | ||||
| 		goto err_out; | ||||
|  | ||||
| 	err = -EINVAL; | ||||
| 	if (nd.mnt->mnt_sb != dir->i_sb) | ||||
| 		goto err_out; | ||||
|  | ||||
| 	if (nd.dentry->d_parent->d_inode != dir) | ||||
| 		goto err_out; | ||||
|  | ||||
| 	err = -ENODATA; | ||||
| 	if (DMFS_I(nd.dentry->d_inode) == NULL ||  | ||||
| 	    DMFS_I(nd.dentry->d_inode)->table == NULL) | ||||
| 		goto err_out; | ||||
|  | ||||
| 	if (!list_empty(&(DMFS_I(nd.dentry->d_inode)->table->errors))) | ||||
| 		goto err_out; | ||||
|  | ||||
| 	dentry = nd.dentry; | ||||
| 	file.f_dentry = nd.dentry->d_parent; | ||||
| 	err = deny_write_access(&file); | ||||
| 	if (err) | ||||
| 		goto err_out; | ||||
|  | ||||
| 	dget(dentry); | ||||
| 	path_release(&nd); | ||||
| 	return dentry; | ||||
| err_out: | ||||
| 	path_release(&nd); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
|  | ||||
| struct inode *dmfs_create_symlink(struct inode *dir, int mode) | ||||
| { | ||||
| 	struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFLNK); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_mapping->a_ops = &dmfs_address_space_operations; | ||||
| 		inode->i_op = &page_symlink_inode_operations; | ||||
| 	} | ||||
|  | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
| static int dmfs_lv_unlink(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	struct inode *inode = dentry->d_inode; | ||||
| 	struct file file = { f_dentry: dentry->d_parent }; | ||||
|  | ||||
| 	if (!(inode->i_mode & S_IFLNK)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	dm_suspend(DMFS_I(dir)->md); | ||||
| 	allow_write_access(&file); | ||||
| 	inode->i_nlink--; | ||||
| 	dput(dentry); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_lv_symlink(struct inode *dir, struct dentry *dentry,  | ||||
| 			   const char *symname) | ||||
| { | ||||
| 	struct inode *inode; | ||||
| 	struct dentry *de; | ||||
| 	int rv; | ||||
| 	int l; | ||||
|  | ||||
| 	if (dentry->d_name.len != 6 ||  | ||||
| 	    memcmp(dentry->d_name.name, "ACTIVE", 6) != 0) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	de = dmfs_verify_name(dir, symname); | ||||
| 	if (IS_ERR(de)) | ||||
| 		return PTR_ERR(de); | ||||
|  | ||||
| 	inode = dmfs_create_symlink(dir, S_IRWXUGO); | ||||
| 	if (inode == NULL) { | ||||
| 		rv = -ENOSPC; | ||||
| 		goto out_allow_write; | ||||
| 	} | ||||
|  | ||||
| 	DMFS_I(inode)->dentry = de; | ||||
| 	d_instantiate(dentry, inode); | ||||
| 	dget(dentry); | ||||
|  | ||||
| 	l = strlen(symname) + 1; | ||||
| 	rv = block_symlink(inode, symname, l); | ||||
| 	if (rv) | ||||
| 		goto out_dput; | ||||
|  | ||||
| 	rv = dm_activate(DMFS_I(dir)->md, DMFS_I(de->d_inode)->table); | ||||
| 	if (rv) | ||||
| 		goto out_dput; | ||||
|  | ||||
| 	return rv; | ||||
|  | ||||
| out_dput: | ||||
| 	DMFS_I(inode)->dentry = NULL; | ||||
| out_allow_write: | ||||
| 	{ | ||||
| 		struct file file = { f_dentry: de->d_parent }; | ||||
| 		allow_write_access(&file); | ||||
| 		dput(de); | ||||
| 	} | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static int is_identifier(const char *str, int len) | ||||
| { | ||||
| 	while(len--) { | ||||
| 		if (!isalnum(*str) && *str != '_') | ||||
| 			return 0; | ||||
| 		str++; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int dmfs_lv_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||||
| { | ||||
| 	struct inode *inode; | ||||
| 	int rv = -ENOSPC; | ||||
|  | ||||
| 	if (dentry->d_name.len >= DM_NAME_LEN) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (!is_identifier(dentry->d_name.name, dentry->d_name.len)) | ||||
| 		return -EPERM; | ||||
|  | ||||
| 	if (dentry->d_name.len == 6 &&  | ||||
| 	    memcmp(dentry->d_name.name, "ACTIVE", 6) == 0) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (dentry->d_name.name[0] == '.') | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	inode = dmfs_create_tdir(dir->i_sb, mode); | ||||
| 	if (inode) { | ||||
| 		d_instantiate(dentry, inode); | ||||
| 		dget(dentry); | ||||
| 		rv = 0; | ||||
| 	} | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * if u.generic_ip is not NULL, then it indicates an inode which | ||||
|  * represents a table. If it is NULL then the inode is a virtual | ||||
|  * file and should be deleted along with the directory. | ||||
|  */ | ||||
| static inline int positive(struct dentry *dentry) | ||||
| { | ||||
| 	return dentry->d_inode && !d_unhashed(dentry); | ||||
| } | ||||
|  | ||||
| static int empty(struct dentry *dentry) | ||||
| { | ||||
| 	struct list_head *list; | ||||
|  | ||||
| 	spin_lock(&dcache_lock); | ||||
| 	list = dentry->d_subdirs.next; | ||||
|  | ||||
| 	while(list != &dentry->d_subdirs) { | ||||
| 		struct dentry *de = list_entry(list, struct dentry, d_child); | ||||
|  | ||||
| 		if (positive(de)) { | ||||
| 			spin_unlock(&dcache_lock); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		list = list->next; | ||||
| 	} | ||||
| 	spin_unlock(&dcache_lock); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int dmfs_lv_rmdir(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	int ret = -ENOTEMPTY; | ||||
|  | ||||
| 	if (empty(dentry)) { | ||||
| 		struct inode *inode = dentry->d_inode; | ||||
| 		inode->i_nlink--; | ||||
| 		dput(dentry); | ||||
| 		ret = 0; | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static struct dentry *dmfs_lv_lookup(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	d_add(dentry, NULL); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int dmfs_lv_sync(struct file *file, struct dentry *dentry, int datasync) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct file_operations dmfs_lv_file_operations = { | ||||
| 	read:		generic_read_dir, | ||||
| 	readdir:	dcache_readdir, | ||||
| 	fsync:		dmfs_lv_sync, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations dmfs_lv_inode_operations = { | ||||
| 	lookup:		dmfs_lv_lookup, | ||||
| 	unlink:		dmfs_lv_unlink, | ||||
| 	symlink:	dmfs_lv_symlink, | ||||
| 	mkdir:		dmfs_lv_mkdir, | ||||
| 	rmdir:		dmfs_lv_rmdir, | ||||
| }; | ||||
|  | ||||
| struct inode *dmfs_create_lv(struct super_block *sb, int mode, struct dentry *dentry) | ||||
| { | ||||
| 	struct inode *inode = dmfs_new_inode(sb, mode | S_IFDIR); | ||||
| 	struct mapped_device *md; | ||||
| 	const char *name = dentry->d_name.name; | ||||
| 	char tmp_name[DM_NAME_LEN + 1]; | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_fop = &dmfs_lv_file_operations; | ||||
| 		inode->i_op = &dmfs_lv_inode_operations; | ||||
| 		memcpy(tmp_name, name, dentry->d_name.len); | ||||
| 		tmp_name[dentry->d_name.len] = 0; | ||||
| 		md = dm_create(tmp_name, -1); | ||||
| 		if (IS_ERR(md)) { | ||||
| 			iput(inode); | ||||
| 			return ERR_PTR(PTR_ERR(md)); | ||||
| 		} | ||||
| 		DMFS_I(inode)->md = md; | ||||
| 	} | ||||
|  | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										164
									
								
								driver/device-mapper/dmfs-root.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								driver/device-mapper/dmfs-root.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| /* | ||||
|  * dmfs-root.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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. | ||||
|  */ | ||||
|  | ||||
| /* Heavily based upon ramfs */ | ||||
|  | ||||
| #include <linux/config.h> | ||||
| #include <linux/ctype.h> | ||||
| #include <linux/fs.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| extern struct inode *dmfs_create_lv(struct super_block *sb, int mode, struct dentry *dentry); | ||||
|  | ||||
| static int is_identifier(const char *str, int len) | ||||
| { | ||||
| 	while(len--) { | ||||
| 		if (!isalnum(*str) && *str != '_') | ||||
| 			return 0; | ||||
| 		str++; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int dmfs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||||
| { | ||||
| 	struct inode *inode; | ||||
| 	int rv = -ENOSPC; | ||||
|  | ||||
| 	if (dentry->d_name.len >= DM_NAME_LEN) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (!is_identifier(dentry->d_name.name, dentry->d_name.len)) | ||||
| 		return -EPERM; | ||||
|  | ||||
| 	if (dentry->d_name.name[0] == '.') | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	inode = dmfs_create_lv(dir->i_sb, mode, dentry); | ||||
| 	if (inode) { | ||||
| 		d_instantiate(dentry, inode); | ||||
| 		dget(dentry); | ||||
| 		rv = 0; | ||||
| 	} | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * if u.generic_ip is not NULL, then it indicates an inode which | ||||
|  * represents a table. If it is NULL then the inode is a virtual | ||||
|  * file and should be deleted along with the directory. | ||||
|  */ | ||||
| static inline int positive(struct dentry *dentry) | ||||
| { | ||||
| 	return dentry->d_inode && !d_unhashed(dentry); | ||||
| } | ||||
|  | ||||
| static int empty(struct dentry *dentry) | ||||
| { | ||||
| 	struct list_head *list; | ||||
|  | ||||
| 	spin_lock(&dcache_lock); | ||||
| 	list = dentry->d_subdirs.next; | ||||
|  | ||||
| 	while(list != &dentry->d_subdirs) { | ||||
| 		struct dentry *de = list_entry(list, struct dentry, d_child); | ||||
|  | ||||
| 		if (positive(de)) { | ||||
| 			spin_unlock(&dcache_lock); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		list = list->next; | ||||
| 	} | ||||
| 	spin_unlock(&dcache_lock); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int dmfs_root_rmdir(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	int ret = -ENOTEMPTY; | ||||
|  | ||||
| 	if (empty(dentry)) { | ||||
| 		struct inode *inode = dentry->d_inode; | ||||
| 		ret = dm_deactivate(DMFS_I(inode)->md); | ||||
| 		if (ret == 0) { | ||||
| 			inode->i_nlink--; | ||||
| 			dput(dentry); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static struct dentry *dmfs_root_lookup(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	d_add(dentry, NULL); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int dmfs_root_rename(struct inode *old_dir, struct dentry *old_dentry, | ||||
| 			struct inode *new_dir, struct dentry *new_dentry) | ||||
| { | ||||
| 	/* Can only rename - not move between directories! */ | ||||
| 	if (old_dir != new_dir) | ||||
| 		return -EPERM; | ||||
|  | ||||
| 	return -EINVAL; /* FIXME: a change of LV name here */ | ||||
| } | ||||
|  | ||||
| static int dmfs_root_sync(struct file *file, struct dentry *dentry, int datasync) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct file_operations dmfs_root_file_operations = { | ||||
| 	read:		generic_read_dir, | ||||
| 	readdir:	dcache_readdir, | ||||
| 	fsync:		dmfs_root_sync, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations dmfs_root_inode_operations = { | ||||
| 	lookup:		dmfs_root_lookup, | ||||
| 	mkdir:		dmfs_root_mkdir, | ||||
| 	rmdir:		dmfs_root_rmdir, | ||||
| 	rename:		dmfs_root_rename, | ||||
| }; | ||||
|  | ||||
| struct inode *dmfs_create_root(struct super_block *sb, int mode) | ||||
| { | ||||
| 	struct inode *inode = new_inode(sb); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_mode = mode | S_IFDIR; | ||||
| 		inode->i_uid = current->fsuid; | ||||
| 		inode->i_gid = current->fsgid; | ||||
| 		inode->i_blksize = PAGE_CACHE_SIZE; | ||||
| 		inode->i_blocks = 0; | ||||
| 		inode->i_rdev = NODEV; | ||||
| 		inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; | ||||
| 		inode->i_fop = &dmfs_root_file_operations; | ||||
| 		inode->i_op = &dmfs_root_inode_operations; | ||||
| 	} | ||||
|  | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										63
									
								
								driver/device-mapper/dmfs-status.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								driver/device-mapper/dmfs-status.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| /* | ||||
|  * dmfs-status.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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 <linux/config.h> | ||||
| #include <linux/fs.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| static ssize_t dmfs_status_read(struct file *file, char *buf, size_t size, loff_t *pos) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_status_sync(struct file *file, struct dentry *dentry, int datasync) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct file_operations dmfs_status_file_operations = { | ||||
| 	read:		dmfs_status_read, | ||||
| 	fsync:		dmfs_status_sync, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations dmfs_status_inode_operations = { | ||||
| }; | ||||
|  | ||||
| struct inode *dmfs_create_status(struct inode *dir, int mode) | ||||
| { | ||||
| 	struct inode *inode = new_inode(dir->i_sb); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_mode = mode | S_IFREG; | ||||
| 		inode->i_uid = current->fsuid; | ||||
| 		inode->i_gid = current->fsgid; | ||||
| 		inode->i_blksize = PAGE_CACHE_SIZE; | ||||
| 		inode->i_blocks = 0; | ||||
| 		inode->i_rdev = NODEV; | ||||
| 		inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; | ||||
| 		inode->i_fop = &dmfs_status_file_operations; | ||||
| 		inode->i_op = &dmfs_status_inode_operations; | ||||
| 	} | ||||
|  | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
							
								
								
									
										112
									
								
								driver/device-mapper/dmfs-super.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								driver/device-mapper/dmfs-super.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| /* | ||||
|  * dmfs-super.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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 <linux/config.h> | ||||
| #include <linux/fs.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| #define DMFS_MAGIC 0x444D4653 | ||||
|  | ||||
| extern struct inode *dmfs_create_root(struct super_block *sb, int); | ||||
|  | ||||
| static int dmfs_statfs(struct super_block *sb, struct statfs *buf) | ||||
| { | ||||
| 	buf->f_type = sb->s_magic; | ||||
| 	buf->f_bsize = sb->s_blocksize; | ||||
| 	buf->f_namelen = DM_NAME_LEN - 1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void dmfs_delete_inode(struct inode *inode) | ||||
| { | ||||
| 	struct dmfs_i *dmi = DMFS_I(inode); | ||||
|  | ||||
| 	if (dmi) { | ||||
| 		if (dmi->md) | ||||
| 			dm_remove(dmi->md); | ||||
| 		if (dmi->table) | ||||
| 			dm_table_destroy(dmi->table); | ||||
| 		if (dmi->dentry) | ||||
| 			dput(dmi->dentry); | ||||
| 		kfree(dmi); | ||||
| 	} | ||||
|  | ||||
| 	inode->u.generic_ip = NULL; | ||||
| 	clear_inode(inode); | ||||
| } | ||||
|  | ||||
| static struct super_operations dmfs_super_operations = { | ||||
| 	statfs:		dmfs_statfs, | ||||
| 	put_inode:	force_delete, | ||||
| 	delete_inode:	dmfs_delete_inode, | ||||
| }; | ||||
|  | ||||
| struct super_block *dmfs_read_super(struct super_block *sb, void *data, int silent) | ||||
| { | ||||
| 	struct inode *inode; | ||||
| 	struct dentry *root; | ||||
|  | ||||
| 	sb->s_blocksize = PAGE_CACHE_SIZE; | ||||
| 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | ||||
| 	sb->s_magic = DMFS_MAGIC; | ||||
| 	sb->s_op = &dmfs_super_operations; | ||||
| 	sb->s_maxbytes = MAX_NON_LFS; | ||||
|  | ||||
| 	inode = dmfs_create_root(sb, 0755); | ||||
| 	if (IS_ERR(inode)) | ||||
| 		return NULL; | ||||
| 	root = d_alloc_root(inode); | ||||
| 	if (!root) { | ||||
| 		iput(inode); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	sb->s_root = root; | ||||
|  | ||||
| 	return sb; | ||||
| } | ||||
|  | ||||
| struct inode *dmfs_new_inode(struct super_block *sb, int mode) | ||||
| { | ||||
| 	struct inode *inode = new_inode(sb); | ||||
| 	struct dmfs_i *dmi; | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_mode = mode; | ||||
| 		inode->i_uid = current->fsuid; | ||||
| 		inode->i_gid = current->fsgid; | ||||
| 		inode->i_blksize = PAGE_CACHE_SIZE; | ||||
| 		inode->i_blocks = 0; | ||||
| 		inode->i_rdev = NODEV; | ||||
| 		inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; | ||||
|  | ||||
| 		dmi = kmalloc(sizeof(struct dmfs_i), GFP_KERNEL); | ||||
| 		if (dmi == NULL) { | ||||
| 			iput(inode); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		memset(dmi, 0, sizeof(struct dmfs_i)); | ||||
| 		init_MUTEX(&dmi->sem); | ||||
| 		inode->u.generic_ip = dmi; | ||||
| 	} | ||||
| 	return inode; | ||||
| } | ||||
							
								
								
									
										379
									
								
								driver/device-mapper/dmfs-table.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								driver/device-mapper/dmfs-table.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,379 @@ | ||||
| /* | ||||
|  * dmfs-table.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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 <linux/config.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/mm.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| static offset_t start_of_next_range(struct dm_table *t) | ||||
| { | ||||
| 	offset_t n = 0; | ||||
| 	if (t->num_targets) { | ||||
| 		n = t->highs[t->num_targets - 1] + 1; | ||||
| 	} | ||||
| 	return n; | ||||
| } | ||||
|  | ||||
| static void dmfs_parse_line(struct dm_table *t, unsigned num, char *str) | ||||
| { | ||||
| 	char *p = str; | ||||
| 	const char *tok; | ||||
| 	offset_t start, size, high; | ||||
| 	void *context; | ||||
| 	struct target_type *ttype; | ||||
| 	int rv = 0; | ||||
| 	char *msg; | ||||
|  | ||||
| 	printk("dmfs_parse_line: (%s)\n", str); | ||||
|  | ||||
| 	msg = "No start argument"; | ||||
| 	tok = next_token(&p); | ||||
| 	if (!tok) | ||||
| 		goto out; | ||||
| 	start = simple_strtoul(tok, NULL, 10); | ||||
|  | ||||
| 	msg = "No size argument"; | ||||
| 	tok = next_token(&p); | ||||
| 	if (!tok) | ||||
| 		goto out; | ||||
| 	size = simple_strtoul(tok, NULL, 10); | ||||
|  | ||||
| 	msg = "Gap in table"; | ||||
| 	if (start != start_of_next_range(t)) | ||||
| 		goto out; | ||||
|  | ||||
| 	msg = "No target type"; | ||||
| 	tok = next_token(&p); | ||||
| 	if (!tok) | ||||
| 		goto out; | ||||
|  | ||||
| 	msg = "Target type unknown"; | ||||
| 	ttype = dm_get_target_type(tok); | ||||
| 	if (ttype) { | ||||
| 		msg = "This message should never appear (constructor error)"; | ||||
| 		rv = ttype->ctr(t, start, size, p, &context); | ||||
| 		msg = context; | ||||
| 		if (rv == 0) { | ||||
| 			printk("dmfs_parse: %ul %ul %s %s\n", start, size,  | ||||
| 				ttype->name, | ||||
| 				ttype->print ? ttype->print(context) : "-"); | ||||
| 			msg = "Error adding target to table"; | ||||
| 			high = start + (size - 1); | ||||
| 			if (dm_table_add_target(t, high, ttype, context) == 0) | ||||
| 				return; | ||||
| 			ttype->dtr(t, context); | ||||
| 		} | ||||
| 		dm_put_target_type(ttype); | ||||
| 	} | ||||
| out: | ||||
| 	dmfs_add_error(t, num, msg); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int dmfs_copy(char *dst, int dstlen, char *src, int srclen, int *flag) | ||||
| { | ||||
| 	int len = min(dstlen, srclen); | ||||
| 	char *start = dst; | ||||
|  | ||||
| 	while(len) { | ||||
| 		*dst = *src++; | ||||
| 		if (*dst == '\n') | ||||
| 			goto end_of_line; | ||||
| 		dst++; | ||||
| 		len--; | ||||
| 	} | ||||
| out: | ||||
| 	return (dst - start); | ||||
| end_of_line: | ||||
| 	dst++; | ||||
| 	*flag = 1; | ||||
| 	goto out; | ||||
| } | ||||
|  | ||||
| static int dmfs_line_is_not_comment(char *str) | ||||
| { | ||||
| 	while(*str) { | ||||
| 		if (*str == '#') | ||||
| 			break; | ||||
| 		if (!isspace(*str)) | ||||
| 			return 1; | ||||
| 		str++; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_parse_page(struct dm_table *t, char *buf, int end, unsigned long end_index, char *tmp, unsigned long *tmpl, int *num) | ||||
| { | ||||
| 	int copied; | ||||
| 	unsigned long len = end ? end_index : PAGE_CACHE_SIZE - 1; | ||||
|  | ||||
| 	do { | ||||
| 		int flag = 0; | ||||
| 		copied = dmfs_copy(tmp + *tmpl, PAGE_SIZE - *tmpl - 1, buf, len, &flag); | ||||
| 		buf += copied; | ||||
| 		len -= copied; | ||||
| 		if (*tmpl + copied == PAGE_SIZE - 1) | ||||
| 			goto line_too_long; | ||||
| 		(*tmpl) += copied; | ||||
| 		if (flag || (len == 0 && end)) { | ||||
| 			*(tmp + *tmpl) = 0; | ||||
| 			if (dmfs_line_is_not_comment(tmp)) | ||||
| 				dmfs_parse_line(t, *num, tmp); | ||||
| 			(*num)++; | ||||
| 			*tmpl = 0; | ||||
| 		} | ||||
| 	} while(len > 0); | ||||
| 	return 0; | ||||
|  | ||||
| line_too_long: | ||||
| 	dmfs_add_error(t, *num, "Line too long"); | ||||
| 	/* FIXME: Add code to recover from this */ | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static struct dm_table *dmfs_parse(struct inode *inode) | ||||
| { | ||||
| 	struct address_space *mapping = inode->i_mapping; | ||||
| 	unsigned long index = 0; | ||||
| 	unsigned long end_index, end_offset; | ||||
| 	unsigned long page; | ||||
| 	unsigned long rem = 0; | ||||
| 	struct dm_table *t; | ||||
| 	struct page *pg; | ||||
| 	int num = 0; | ||||
|  | ||||
| 	if (inode->i_size == 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	page = __get_free_page(GFP_KERNEL); | ||||
|  | ||||
| 	if (!page) | ||||
| 		return NULL; | ||||
|  | ||||
| 	t = dm_table_create(); | ||||
| 	if (!t) { | ||||
| 		free_page(page); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	end_index = inode->i_size >> PAGE_CACHE_SHIFT; | ||||
| 	end_offset = inode->i_size & (PAGE_CACHE_SIZE - 1); | ||||
|  | ||||
| 	do { | ||||
| 		pg = find_get_page(mapping, index); | ||||
|  | ||||
| 		if (pg) { | ||||
| 			char *kaddr; | ||||
| 			int rv; | ||||
|  | ||||
| 			if (!Page_Uptodate(pg)) | ||||
| 				goto broken; | ||||
|  | ||||
| 			kaddr = kmap(pg); | ||||
| 			rv = dmfs_parse_page(t, kaddr, (index == end_index), end_offset, (char *)page, &rem, &num); | ||||
| 			kunmap(pg); | ||||
| 			page_cache_release(pg); | ||||
|  | ||||
| 			if (rv) | ||||
| 				goto parse_error; | ||||
| 		} | ||||
|  | ||||
| 		index++; | ||||
| 	} while(index <= end_index); | ||||
|  | ||||
| 	free_page(page); | ||||
| 	if (list_empty(&t->errors)) { | ||||
| 		dm_table_complete(t); | ||||
| 	} | ||||
|  | ||||
| 	return t; | ||||
|  | ||||
| broken: | ||||
| 	printk(KERN_ERR "dmfs_parse: Page not uptodate\n"); | ||||
| 	page_cache_release(pg); | ||||
| 	free_page(page); | ||||
| 	dm_table_destroy(t); | ||||
| 	return NULL; | ||||
|  | ||||
| parse_error: | ||||
| 	printk(KERN_ERR "dmfs_parse: Parse error\n"); | ||||
| 	free_page(page); | ||||
| 	dm_table_destroy(t); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int dmfs_table_release(struct inode *inode, struct file *f) | ||||
| { | ||||
| 	struct dentry *dentry = f->f_dentry; | ||||
| 	struct inode *parent = dentry->d_parent->d_inode; | ||||
| 	struct dmfs_i *dmi = DMFS_I(parent); | ||||
| 	struct dm_table *table; | ||||
|  | ||||
| 	if (f->f_mode & FMODE_WRITE) { | ||||
|  | ||||
| 		down(&dmi->sem); | ||||
| 		table = dmfs_parse(dentry->d_parent->d_inode); | ||||
|  | ||||
| 		if (table) { | ||||
| 			if (dmi->table) | ||||
| 				dm_table_destroy(dmi->table); | ||||
| 			dmi->table = table; | ||||
| 		} | ||||
| 		up(&dmi->sem); | ||||
|  | ||||
|                 put_write_access(parent); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_readpage(struct file *file, struct page *page) | ||||
| { | ||||
| 	if (!Page_Uptodate(page)) { | ||||
| 		memset(kmap(page), 0, PAGE_CACHE_SIZE); | ||||
| 		kunmap(page); | ||||
| 		flush_dcache_page(page); | ||||
| 		SetPageUptodate(page); | ||||
| 	} | ||||
| 	UnlockPage(page); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_writepage(struct page *page) | ||||
| { | ||||
| 	SetPageDirty(page); | ||||
| 	UnlockPage(page); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_prepare_write(struct file *file, struct page *page, | ||||
| 			      unsigned offset, unsigned to) | ||||
| { | ||||
| 	void *addr = kmap(page); | ||||
| 	if (!Page_Uptodate(page)) { | ||||
| 		memset(addr, 0, PAGE_CACHE_SIZE); | ||||
| 		flush_dcache_page(page); | ||||
| 		SetPageUptodate(page); | ||||
| 	} | ||||
| 	SetPageDirty(page); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_commit_write(struct file *file, struct page *page, | ||||
| 			     unsigned offset, unsigned to) | ||||
| { | ||||
| 	struct inode *inode = page->mapping->host; | ||||
| 	loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to; | ||||
|  | ||||
| 	kunmap(page); | ||||
| 	if (pos > inode->i_size) | ||||
| 		inode->i_size = pos; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * There is a small race here in that two processes might call this at | ||||
|  * the same time and both fail. So its a fail safe race :-) This should | ||||
|  * move into namei.c (and thus use the spinlock and do this properly) | ||||
|  * at some stage if we continue to use this set of functions for ensuring | ||||
|  * exclusive write access to the file | ||||
|  */ | ||||
| static int get_exclusive_write_access(struct inode *inode) | ||||
| { | ||||
| 	if (get_write_access(inode)) | ||||
| 		return -1; | ||||
| 	if (atomic_read(&inode->i_writecount) != 1) { | ||||
| 		put_write_access(inode); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_table_open(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	struct dentry *dentry = file->f_dentry; | ||||
| 	struct inode *parent = dentry->d_parent->d_inode; | ||||
|  | ||||
| 	if (file->f_mode & FMODE_WRITE) { | ||||
| 		if (get_exclusive_write_access(parent)) | ||||
| 			return -EPERM; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_table_sync(struct file *file, struct dentry *dentry, int datasync) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int dmfs_table_revalidate(struct dentry *dentry) | ||||
| { | ||||
| 	struct inode *inode = dentry->d_inode; | ||||
| 	struct inode *parent = dentry->d_parent->d_inode; | ||||
|  | ||||
| 	inode->i_size = parent->i_size; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct address_space_operations dmfs_address_space_operations = { | ||||
| 	readpage:	dmfs_readpage, | ||||
| 	writepage:	dmfs_writepage, | ||||
| 	prepare_write:	dmfs_prepare_write, | ||||
| 	commit_write:	dmfs_commit_write, | ||||
| }; | ||||
|  | ||||
| static struct file_operations dmfs_table_file_operations = { | ||||
|  	llseek:		generic_file_llseek, | ||||
| 	read:		generic_file_read, | ||||
| 	write:		generic_file_write, | ||||
| 	open:		dmfs_table_open, | ||||
| 	release:	dmfs_table_release, | ||||
| 	fsync:		dmfs_table_sync, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations dmfs_table_inode_operations = { | ||||
| 	revalidate:	dmfs_table_revalidate, | ||||
| }; | ||||
|  | ||||
| struct inode *dmfs_create_table(struct inode *dir, int mode) | ||||
| { | ||||
| 	struct inode *inode = new_inode(dir->i_sb); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_mode = mode | S_IFREG; | ||||
| 		inode->i_uid = current->fsuid; | ||||
| 		inode->i_gid = current->fsgid; | ||||
| 		inode->i_blksize = PAGE_CACHE_SIZE; | ||||
| 		inode->i_blocks = 0; | ||||
| 		inode->i_rdev = NODEV; | ||||
| 		inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; | ||||
| 		inode->i_mapping = dir->i_mapping; | ||||
| 		inode->i_mapping->a_ops = &dmfs_address_space_operations; | ||||
| 		inode->i_fop = &dmfs_table_file_operations; | ||||
| 		inode->i_op = &dmfs_table_inode_operations; | ||||
| 	} | ||||
|  | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
							
								
								
									
										137
									
								
								driver/device-mapper/dmfs-tdir.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								driver/device-mapper/dmfs-tdir.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| /* | ||||
|  * dmfs-tdir.c | ||||
|  * | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This software 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. | ||||
|  * | ||||
|  * This software 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. | ||||
|  */ | ||||
|  | ||||
| /* Heavily based upon ramfs */ | ||||
|  | ||||
| #include <linux/config.h> | ||||
| #include <linux/fs.h> | ||||
|  | ||||
| #include "dm.h" | ||||
|  | ||||
| extern struct inode *dmfs_create_error(struct inode *, int); | ||||
| extern struct inode *dmfs_create_table(struct inode *, int); | ||||
| extern struct inode *dmfs_create_status(struct inode *, int); | ||||
|  | ||||
|  | ||||
| static int dmfs_tdir_unlink(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	struct inode *inode = dentry->d_inode; | ||||
|  | ||||
| 	inode->i_mapping = &inode->i_data; | ||||
| 	inode->i_nlink--; | ||||
| 	dput(dentry); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct dentry *dmfs_tdir_lookup(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	struct inode *inode = NULL; | ||||
| 	const char *name = dentry->d_name.name; | ||||
|  | ||||
| 	switch(dentry->d_name.len) { | ||||
| 		case 5: | ||||
| 			if (memcmp("table", name, 5) == 0) { | ||||
| 				inode = dmfs_create_table(dir, 0600); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (memcmp("error", name, 5) == 0) | ||||
| 				inode = dmfs_create_error(dir, 0600); | ||||
| 			break; | ||||
| 		case 6: | ||||
| 			if (memcmp("status", name, 6) == 0) | ||||
| 				inode = dmfs_create_status(dir, 0600); | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	d_add(dentry, inode); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int dmfs_tdir_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||||
| { | ||||
| 	int i; | ||||
| 	struct dentry *dentry = filp->f_dentry; | ||||
|  | ||||
| 	i = filp->f_pos; | ||||
| 	switch(i) { | ||||
| 		case 0: | ||||
| 			if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) | ||||
| 				break; | ||||
| 			i++; | ||||
| 			filp->f_pos++; | ||||
| 			/* fallthrough */ | ||||
| 		case 1: | ||||
| 			if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) | ||||
| 				break; | ||||
| 			i++; | ||||
| 			filp->f_pos++; | ||||
| 			/* fallthrough */ | ||||
| 		case 2: | ||||
| 			if (filldir(dirent, "table", 5, i, 2, DT_REG) < 0) | ||||
| 				break; | ||||
| 			i++; | ||||
| 			filp->f_pos++; | ||||
| 			/* fallthrough */ | ||||
| 		case 3: | ||||
| 			if (filldir(dirent, "error", 5, i, 3, DT_REG) < 0) | ||||
| 				break; | ||||
| 			i++; | ||||
| 			filp->f_pos++; | ||||
| 			/* fallthrough */ | ||||
| 		case 4: | ||||
| 			if (filldir(dirent, "status", 6, i, 4, DT_REG) < 0) | ||||
| 				break; | ||||
| 			i++; | ||||
| 			filp->f_pos++; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int dmfs_tdir_sync(struct file *file, struct dentry *dentry, int datasync) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct file_operations dmfs_tdir_file_operations = { | ||||
| 	read:		generic_read_dir, | ||||
| 	readdir:	dmfs_tdir_readdir, | ||||
| 	fsync:		dmfs_tdir_sync, | ||||
| }; | ||||
|  | ||||
| static struct inode_operations dmfs_tdir_inode_operations = { | ||||
| 	lookup:		dmfs_tdir_lookup, | ||||
| 	unlink:		dmfs_tdir_unlink, | ||||
| }; | ||||
|  | ||||
| struct inode *dmfs_create_tdir(struct super_block *sb, int mode) | ||||
| { | ||||
| 	struct inode *inode = dmfs_new_inode(sb, mode | S_IFDIR); | ||||
|  | ||||
| 	if (inode) { | ||||
| 		inode->i_fop = &dmfs_tdir_file_operations; | ||||
| 		inode->i_op = &dmfs_tdir_inode_operations; | ||||
| 	} | ||||
|  | ||||
| 	return inode; | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										88
									
								
								driver/device-mapper/patches/00_bh-async-3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								driver/device-mapper/patches/00_bh-async-3
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| diff -urN 2.4.7pre6/fs/buffer.c bh_async/fs/buffer.c | ||||
| --- 2.4.7pre6/fs/buffer.c	Wed Jul 11 06:03:18 2001 | ||||
| +++ bh_async/fs/buffer.c	Thu Jul 12 07:55:08 2001 | ||||
| @@ -827,10 +827,11 @@ | ||||
|  	 * that unlock the page.. | ||||
|  	 */ | ||||
|  	spin_lock_irqsave(&page_uptodate_lock, flags); | ||||
| +	mark_buffer_async(bh, 0); | ||||
|  	unlock_buffer(bh); | ||||
|  	tmp = bh->b_this_page; | ||||
|  	while (tmp != bh) { | ||||
| -		if (tmp->b_end_io == end_buffer_io_async && buffer_locked(tmp)) | ||||
| +		if (buffer_async(tmp) && buffer_locked(tmp)) | ||||
|  			goto still_busy; | ||||
|  		tmp = tmp->b_this_page; | ||||
|  	} | ||||
| @@ -862,8 +863,9 @@ | ||||
|  	return; | ||||
|  } | ||||
|   | ||||
| -void set_buffer_async_io(struct buffer_head *bh) { | ||||
| +inline void set_buffer_async_io(struct buffer_head *bh) { | ||||
|      bh->b_end_io = end_buffer_io_async ; | ||||
| +    mark_buffer_async(bh, 1); | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| @@ -1553,7 +1555,7 @@ | ||||
|  	/* Stage 2: lock the buffers, mark them clean */ | ||||
|  	do { | ||||
|  		lock_buffer(bh); | ||||
| -		bh->b_end_io = end_buffer_io_async; | ||||
| +		set_buffer_async_io(bh); | ||||
|  		get_bh(bh); | ||||
|  		set_bit(BH_Uptodate, &bh->b_state); | ||||
|  		clear_bit(BH_Dirty, &bh->b_state); | ||||
| @@ -1755,7 +1757,7 @@ | ||||
|  	for (i = 0; i < nr; i++) { | ||||
|  		struct buffer_head * bh = arr[i]; | ||||
|  		lock_buffer(bh); | ||||
| -		bh->b_end_io = end_buffer_io_async; | ||||
| +		set_buffer_async_io(bh); | ||||
|  		get_bh(bh); | ||||
|  	} | ||||
|   | ||||
| @@ -2200,7 +2202,7 @@ | ||||
|  		lock_buffer(bh); | ||||
|  		bh->b_blocknr = *(b++); | ||||
|  		set_bit(BH_Mapped, &bh->b_state); | ||||
| -		bh->b_end_io = end_buffer_io_async; | ||||
| +		set_buffer_async_io(bh); | ||||
|  		get_bh(bh); | ||||
|  		bh = bh->b_this_page; | ||||
|  	} while (bh != head); | ||||
| diff -urN 2.4.7pre6/include/linux/fs.h bh_async/include/linux/fs.h | ||||
| --- 2.4.7pre6/include/linux/fs.h	Wed Jul 11 06:03:19 2001 | ||||
| +++ bh_async/include/linux/fs.h	Thu Jul 12 07:54:26 2001 | ||||
| @@ -215,6 +215,7 @@ | ||||
|  	BH_New,		/* 1 if the buffer is new and not yet written out */ | ||||
|  	BH_Protected,	/* 1 if the buffer is protected */ | ||||
| 	BH_JBD,		/* 1 if it has an attached journal_head */ | ||||
| +	BH_Async,	/* 1 if the buffer is under end_buffer_io_async I/O */ | ||||
|   | ||||
|  	BH_PrivateStart,/* not a state bit, but the first bit available | ||||
|  			 * for private allocation by other entities | ||||
| @@ -275,6 +276,7 @@ | ||||
|  #define buffer_mapped(bh)	__buffer_state(bh,Mapped) | ||||
|  #define buffer_new(bh)		__buffer_state(bh,New) | ||||
|  #define buffer_protected(bh)	__buffer_state(bh,Protected) | ||||
| +#define buffer_async(bh)	__buffer_state(bh,Async) | ||||
|   | ||||
|  #define bh_offset(bh)		((unsigned long)(bh)->b_data & ~PAGE_MASK) | ||||
|   | ||||
| @@ -1110,6 +1112,14 @@ | ||||
|  extern void FASTCALL(mark_buffer_dirty(struct buffer_head *bh)); | ||||
|   | ||||
|  #define atomic_set_buffer_dirty(bh) test_and_set_bit(BH_Dirty, &(bh)->b_state) | ||||
| + | ||||
| +static inline void mark_buffer_async(struct buffer_head * bh, int on) | ||||
| +{ | ||||
| +	if (on) | ||||
| +		set_bit(BH_Async, &bh->b_state); | ||||
| +	else | ||||
| +		clear_bit(BH_Async, &bh->b_state); | ||||
| +} | ||||
|   | ||||
|  /* | ||||
|   * If an error happens during the make_request, this function | ||||
							
								
								
									
										10
									
								
								driver/device-mapper/patches/00_config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								driver/device-mapper/patches/00_config
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| --- linux-2.4.9-ac5/drivers/md/Config.in	Sun Mar 11 13:33:24 2001 | ||||
| +++ linux/drivers/md/Config.in	Thu Sep 13 18:02:17 2001 | ||||
| @@ -13,5 +13,7 @@ | ||||
|  dep_tristate '  RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD | ||||
|   | ||||
|  dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD | ||||
| +dep_tristate ' Device mapper support' CONFIG_BLK_DEV_DM $CONFIG_MD | ||||
| +dep_tristate '  Device mapper linear target' CONFIG_BLK_DEV_DM_LINEAR $CONFIG_BLK_DEV_DM | ||||
|   | ||||
|  endmenu | ||||
							
								
								
									
										13
									
								
								driver/device-mapper/patches/00_config_uml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								driver/device-mapper/patches/00_config_uml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| --- uml_build/arch/um/config.in.orig	Tue Jan  2 14:33:42 2001 | ||||
| +++ uml_build/arch/um/config.in	Tue Jan  2 14:35:42 2001 | ||||
| @@ -15,6 +15,8 @@ | ||||
|  bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL | ||||
|  endmenu | ||||
|   | ||||
| +source drivers/md/Config.in | ||||
| + | ||||
|  mainmenu_option next_comment | ||||
|  comment 'Processor features' | ||||
|  bool 'Symmetric multi-processing support' CONFIG_SMP | ||||
|  | ||||
|  | ||||
							
								
								
									
										1732
									
								
								driver/device-mapper/patches/00_latest
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1732
									
								
								driver/device-mapper/patches/00_latest
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										25
									
								
								driver/device-mapper/patches/00_makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								driver/device-mapper/patches/00_makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| --- linux-2.4.9-ac5/drivers/md/Makefile	Sat Sep  1 16:24:46 2001 | ||||
| +++ linux/drivers/md/Makefile	Fri Sep 14 09:12:39 2001 | ||||
| @@ -7,6 +7,7 @@ | ||||
|  export-objs	:= md.o xor.o | ||||
|  list-multi	:= lvm-mod.o | ||||
|  lvm-mod-objs	:= lvm.o lvm-snap.o lvm-fs.o | ||||
| +dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-fs.o dm-parse.o dm-blkdev.o | ||||
|   | ||||
|  # Note: link order is important.  All raid personalities | ||||
|  # and xor.o must come before md.o, as they each initialise  | ||||
| @@ -19,8 +20,14 @@ | ||||
|  obj-$(CONFIG_MD_RAID5)		+= raid5.o xor.o | ||||
|  obj-$(CONFIG_BLK_DEV_MD)	+= md.o | ||||
|  obj-$(CONFIG_BLK_DEV_LVM)	+= lvm-mod.o | ||||
| +obj-$(CONFIG_BLK_DEV_DM)	+= dm-mod.o | ||||
| +obj-$(CONFIG_BLK_DEV_DM_LINEAR)	+= dm-linear.o | ||||
|   | ||||
|  include $(TOPDIR)/Rules.make | ||||
|   | ||||
|  lvm-mod.o: $(lvm-mod-objs) | ||||
|  	$(LD) -r -o $@ $(lvm-mod-objs) | ||||
| + | ||||
| +dm-mod.o: $(dm-mod-objs) | ||||
| +	$(LD) -r -o $@ $(dm-mod-objs) | ||||
| + | ||||
							
								
								
									
										13
									
								
								driver/device-mapper/patches/INDEX
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								driver/device-mapper/patches/INDEX
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| 00_latest		Latest source - I only tend to update this before  | ||||
|                         making a release.  So if you got this from CVS copy | ||||
|                         or link the source files in by hand. | ||||
|  | ||||
| 00_config		Add device-mapper to the MD section | ||||
|  | ||||
| 00_config_uml           only apply for uml, turns on the md section | ||||
|  | ||||
| 00_makefile		Add device-mapper to the MD Makefile. | ||||
|  | ||||
| 00_bh-async-3		AA's async bh patch so we can hook b_end_io | ||||
|                         to keep track of pending io. | ||||
|  | ||||
							
								
								
									
										30
									
								
								driver/user_mode_linux/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								driver/user_mode_linux/README
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| FILES | ||||
| ----- | ||||
|  | ||||
| setup-uml - script to build a user mode linux system, with the lvm driver | ||||
|             symbolically linked back to the LVM dir so I can work from CVS. | ||||
|  | ||||
| uml-lvm.patch - patch to enable lvm in the uml configuration | ||||
|  | ||||
| uml.patch.bz2 - uml patch from  | ||||
|                 http://sourceforge.net/project/showfiles.php?group_id=429 | ||||
|  | ||||
| config-uml - .config which turns on LVM | ||||
|  | ||||
|  | ||||
| RUNNING  | ||||
| ------- | ||||
|  | ||||
| o checkout an LVM dir for use with uml | ||||
|   make sure you've got a copy of a root filesystem kicking about | ||||
|  | ||||
| o edit the variables at the top of 'setup' | ||||
|  | ||||
| o run setup-uml | ||||
|  | ||||
| o move to your uml dir and run lvm-install as root | ||||
|  | ||||
| o then run the 'up' script to run uml | ||||
|  | ||||
| o if you want to rebuild uml after changing the LVM driver just change into | ||||
|   the linux directory and do a 'make linux ARCH=um'.  Don't forget the ARCH=um. | ||||
							
								
								
									
										299
									
								
								driver/user_mode_linux/config-uml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								driver/user_mode_linux/config-uml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,299 @@ | ||||
| # | ||||
| # Automatically generated by make menuconfig: don't edit | ||||
| # | ||||
| CONFIG_USERMODE=y | ||||
| # CONFIG_ISA is not set | ||||
| # CONFIG_SBUS is not set | ||||
| # CONFIG_PCI is not set | ||||
| CONFIG_UID16=y | ||||
| CONFIG_RWSEM_XCHGADD_ALGORITHM=y | ||||
|  | ||||
| # | ||||
| # Code maturity level options | ||||
| # | ||||
| CONFIG_EXPERIMENTAL=y | ||||
|  | ||||
| # | ||||
| # Multi-device support (RAID and LVM) | ||||
| # | ||||
| CONFIG_MD=y | ||||
| # CONFIG_BLK_DEV_MD is not set | ||||
| # CONFIG_MD_LINEAR is not set | ||||
| # CONFIG_MD_RAID0 is not set | ||||
| # CONFIG_MD_RAID1 is not set | ||||
| # CONFIG_MD_RAID5 is not set | ||||
| # CONFIG_BLK_DEV_LVM is not set | ||||
| CONFIG_BLK_DEV_DM=y | ||||
|  | ||||
| # | ||||
| # General Setup | ||||
| # | ||||
| CONFIG_STDIO_CONSOLE=y | ||||
| CONFIG_NET=y | ||||
| CONFIG_SYSVIPC=y | ||||
| CONFIG_BSD_PROCESS_ACCT=y | ||||
| CONFIG_SYSCTL=y | ||||
| CONFIG_BINFMT_AOUT=y | ||||
| CONFIG_BINFMT_ELF=y | ||||
| CONFIG_BINFMT_MISC=y | ||||
| CONFIG_UNIX98_PTYS=y | ||||
| CONFIG_UNIX98_PTY_COUNT=256 | ||||
| CONFIG_SSL=y | ||||
| CONFIG_HOSTFS=m | ||||
| CONFIG_MCONSOLE=y | ||||
| CONFIG_MAGIC_SYSRQ=y | ||||
|  | ||||
| # | ||||
| # Loadable module support | ||||
| # | ||||
| CONFIG_MODULES=y | ||||
| # CONFIG_KMOD is not set | ||||
|  | ||||
| # | ||||
| # Devices | ||||
| # | ||||
| CONFIG_BLK_DEV_UBD=y | ||||
| # CONFIG_BLK_DEV_UBD_SYNC is not set | ||||
| CONFIG_BLK_DEV_LOOP=y | ||||
| CONFIG_BLK_DEV_NBD=y | ||||
| CONFIG_BLK_DEV_RAM=y | ||||
| CONFIG_BLK_DEV_RAM_SIZE=4096 | ||||
| CONFIG_BLK_DEV_INITRD=y | ||||
| # CONFIG_MMAPPER is not set | ||||
|  | ||||
| # | ||||
| # Networking options | ||||
| # | ||||
| # CONFIG_PACKET is not set | ||||
| CONFIG_NETLINK=y | ||||
| # CONFIG_RTNETLINK is not set | ||||
| # CONFIG_NETLINK_DEV is not set | ||||
| # CONFIG_NETFILTER is not set | ||||
| # CONFIG_FILTER is not set | ||||
| CONFIG_UNIX=y | ||||
| CONFIG_INET=y | ||||
| # CONFIG_IP_MULTICAST is not set | ||||
| # CONFIG_IP_ADVANCED_ROUTER is not set | ||||
| # CONFIG_IP_PNP is not set | ||||
| # CONFIG_NET_IPIP is not set | ||||
| # CONFIG_NET_IPGRE is not set | ||||
| # CONFIG_INET_ECN is not set | ||||
| # CONFIG_SYN_COOKIES is not set | ||||
| # CONFIG_IPV6 is not set | ||||
| # CONFIG_KHTTPD is not set | ||||
| # CONFIG_ATM is not set | ||||
| # CONFIG_IPX is not set | ||||
| # CONFIG_ATALK is not set | ||||
| # CONFIG_DECNET is not set | ||||
| # CONFIG_BRIDGE is not set | ||||
| # CONFIG_X25 is not set | ||||
| # CONFIG_LAPB is not set | ||||
| # CONFIG_LLC is not set | ||||
| # CONFIG_NET_DIVERT is not set | ||||
| # CONFIG_ECONET is not set | ||||
| # CONFIG_WAN_ROUTER is not set | ||||
| # CONFIG_NET_FASTROUTE is not set | ||||
| # CONFIG_NET_HW_FLOWCONTROL is not set | ||||
|  | ||||
| # | ||||
| # QoS and/or fair queueing | ||||
| # | ||||
| # CONFIG_NET_SCHED is not set | ||||
|  | ||||
| # | ||||
| # Network drivers | ||||
| # | ||||
|  | ||||
| # | ||||
| # ARCnet devices | ||||
| # | ||||
| # CONFIG_ARCNET is not set | ||||
| CONFIG_DUMMY=y | ||||
| # CONFIG_BONDING is not set | ||||
| # CONFIG_EQUALIZER is not set | ||||
| CONFIG_TUN=y | ||||
| CONFIG_ETHERTAP=y | ||||
|  | ||||
| # | ||||
| # Ethernet (10 or 100Mbit) | ||||
| # | ||||
| # CONFIG_NET_ETHERNET is not set | ||||
|  | ||||
| # | ||||
| # Ethernet (1000 Mbit) | ||||
| # | ||||
| # CONFIG_ACENIC is not set | ||||
| # CONFIG_ACENIC_OMIT_TIGON_I is not set | ||||
| # CONFIG_DL2K is not set | ||||
| # CONFIG_MYRI_SBUS is not set | ||||
| # CONFIG_HAMACHI is not set | ||||
| # CONFIG_YELLOWFIN is not set | ||||
| # CONFIG_SK98LIN is not set | ||||
| # CONFIG_FDDI is not set | ||||
| # CONFIG_HIPPI is not set | ||||
| # CONFIG_PLIP is not set | ||||
| CONFIG_PPP=y | ||||
| # CONFIG_PPP_MULTILINK is not set | ||||
| # CONFIG_PPP_FILTER is not set | ||||
| # CONFIG_PPP_ASYNC is not set | ||||
| # CONFIG_PPP_SYNC_TTY is not set | ||||
| # CONFIG_PPP_DEFLATE is not set | ||||
| # CONFIG_PPP_BSDCOMP is not set | ||||
| # CONFIG_PPPOE is not set | ||||
| CONFIG_SLIP=y | ||||
| # CONFIG_SLIP_COMPRESSED is not set | ||||
| # CONFIG_SLIP_SMART is not set | ||||
| # CONFIG_SLIP_MODE_SLIP6 is not set | ||||
|  | ||||
| # | ||||
| # Wireless LAN (non-hamradio) | ||||
| # | ||||
| # CONFIG_NET_RADIO is not set | ||||
|  | ||||
| # | ||||
| # Token Ring devices | ||||
| # | ||||
| # CONFIG_TR is not set | ||||
| # CONFIG_NET_FC is not set | ||||
| # CONFIG_RCPCI is not set | ||||
| # CONFIG_SHAPER is not set | ||||
|  | ||||
| # | ||||
| # Wan interfaces | ||||
| # | ||||
| # CONFIG_WAN is not set | ||||
|  | ||||
| # | ||||
| # Network device support | ||||
| # | ||||
| CONFIG_NETDEVICES=y | ||||
| CONFIG_UML_NET=y | ||||
| CONFIG_UML_NET_ETHERTAP=y | ||||
| CONFIG_UML_NET_SLIP=y | ||||
| CONFIG_UML_NET_DAEMON=y | ||||
| CONFIG_UML_NET_MCAST=y | ||||
| CONFIG_ETHERTAP=y | ||||
| CONFIG_TUN=y | ||||
|  | ||||
| # | ||||
| # File systems | ||||
| # | ||||
| CONFIG_QUOTA=y | ||||
| CONFIG_AUTOFS_FS=m | ||||
| CONFIG_AUTOFS4_FS=m | ||||
| CONFIG_REISERFS_FS=m | ||||
| # CONFIG_REISERFS_CHECK is not set | ||||
| # CONFIG_ADFS_FS is not set | ||||
| # CONFIG_ADFS_FS_RW is not set | ||||
| # CONFIG_AFFS_FS is not set | ||||
| # CONFIG_HFS_FS is not set | ||||
| # CONFIG_BFS_FS is not set | ||||
| CONFIG_FAT_FS=m | ||||
| CONFIG_MSDOS_FS=m | ||||
| CONFIG_UMSDOS_FS=m | ||||
| CONFIG_VFAT_FS=m | ||||
| # CONFIG_EFS_FS is not set | ||||
| # CONFIG_JFFS_FS is not set | ||||
| # CONFIG_CRAMFS is not set | ||||
| # CONFIG_TMPFS is not set | ||||
| # CONFIG_RAMFS is not set | ||||
| CONFIG_ISO9660_FS=m | ||||
| # CONFIG_JOLIET is not set | ||||
| CONFIG_MINIX_FS=m | ||||
| # CONFIG_VXFS_FS is not set | ||||
| # CONFIG_NTFS_FS is not set | ||||
| # CONFIG_NTFS_RW is not set | ||||
| # CONFIG_HPFS_FS is not set | ||||
| CONFIG_PROC_FS=y | ||||
| CONFIG_DEVFS_FS=y | ||||
| CONFIG_DEVFS_MOUNT=y | ||||
| # CONFIG_DEVFS_DEBUG is not set | ||||
| CONFIG_DEVPTS_FS=y | ||||
| # CONFIG_QNX4FS_FS is not set | ||||
| # CONFIG_QNX4FS_RW is not set | ||||
| # CONFIG_ROMFS_FS is not set | ||||
| CONFIG_EXT2_FS=y | ||||
| # CONFIG_SYSV_FS is not set | ||||
| # CONFIG_UDF_FS is not set | ||||
| # CONFIG_UDF_RW is not set | ||||
| # CONFIG_UFS_FS is not set | ||||
| # CONFIG_UFS_FS_WRITE is not set | ||||
|  | ||||
| # | ||||
| # Network File Systems | ||||
| # | ||||
| # CONFIG_CODA_FS is not set | ||||
| # CONFIG_NFS_FS is not set | ||||
| # CONFIG_NFS_V3 is not set | ||||
| # CONFIG_ROOT_NFS is not set | ||||
| # CONFIG_NFSD is not set | ||||
| # CONFIG_NFSD_V3 is not set | ||||
| # CONFIG_SUNRPC is not set | ||||
| # CONFIG_LOCKD is not set | ||||
| # CONFIG_SMB_FS is not set | ||||
| # CONFIG_NCP_FS is not set | ||||
| # CONFIG_NCPFS_PACKET_SIGNING is not set | ||||
| # CONFIG_NCPFS_IOCTL_LOCKING is not set | ||||
| # CONFIG_NCPFS_STRONG is not set | ||||
| # CONFIG_NCPFS_NFS_NS is not set | ||||
| # CONFIG_NCPFS_OS2_NS is not set | ||||
| # CONFIG_NCPFS_SMALLDOS is not set | ||||
| # CONFIG_NCPFS_NLS is not set | ||||
| # CONFIG_NCPFS_EXTRAS is not set | ||||
|  | ||||
| # | ||||
| # Partition Types | ||||
| # | ||||
| # CONFIG_PARTITION_ADVANCED is not set | ||||
| CONFIG_MSDOS_PARTITION=y | ||||
| # CONFIG_SMB_NLS is not set | ||||
| CONFIG_NLS=y | ||||
|  | ||||
| # | ||||
| # Native Language Support | ||||
| # | ||||
| CONFIG_NLS_DEFAULT="iso8859-1" | ||||
| # CONFIG_NLS_CODEPAGE_437 is not set | ||||
| # CONFIG_NLS_CODEPAGE_737 is not set | ||||
| # CONFIG_NLS_CODEPAGE_775 is not set | ||||
| # CONFIG_NLS_CODEPAGE_850 is not set | ||||
| # CONFIG_NLS_CODEPAGE_852 is not set | ||||
| # CONFIG_NLS_CODEPAGE_855 is not set | ||||
| # CONFIG_NLS_CODEPAGE_857 is not set | ||||
| # CONFIG_NLS_CODEPAGE_860 is not set | ||||
| # CONFIG_NLS_CODEPAGE_861 is not set | ||||
| # CONFIG_NLS_CODEPAGE_862 is not set | ||||
| # CONFIG_NLS_CODEPAGE_863 is not set | ||||
| # CONFIG_NLS_CODEPAGE_864 is not set | ||||
| # CONFIG_NLS_CODEPAGE_865 is not set | ||||
| # CONFIG_NLS_CODEPAGE_866 is not set | ||||
| # CONFIG_NLS_CODEPAGE_869 is not set | ||||
| # CONFIG_NLS_CODEPAGE_936 is not set | ||||
| # CONFIG_NLS_CODEPAGE_950 is not set | ||||
| # CONFIG_NLS_CODEPAGE_932 is not set | ||||
| # CONFIG_NLS_CODEPAGE_949 is not set | ||||
| # CONFIG_NLS_CODEPAGE_874 is not set | ||||
| # CONFIG_NLS_ISO8859_8 is not set | ||||
| # CONFIG_NLS_CODEPAGE_1251 is not set | ||||
| # CONFIG_NLS_ISO8859_1 is not set | ||||
| # CONFIG_NLS_ISO8859_2 is not set | ||||
| # CONFIG_NLS_ISO8859_3 is not set | ||||
| # CONFIG_NLS_ISO8859_4 is not set | ||||
| # CONFIG_NLS_ISO8859_5 is not set | ||||
| # CONFIG_NLS_ISO8859_6 is not set | ||||
| # CONFIG_NLS_ISO8859_7 is not set | ||||
| # CONFIG_NLS_ISO8859_9 is not set | ||||
| # CONFIG_NLS_ISO8859_13 is not set | ||||
| # CONFIG_NLS_ISO8859_14 is not set | ||||
| # CONFIG_NLS_ISO8859_15 is not set | ||||
| # CONFIG_NLS_KOI8_R is not set | ||||
| # CONFIG_NLS_KOI8_U is not set | ||||
| # CONFIG_NLS_UTF8 is not set | ||||
|  | ||||
| # | ||||
| # Kernel hacking | ||||
| # | ||||
| CONFIG_DEBUGSYM=y | ||||
| CONFIG_PT_PROXY=y | ||||
| # CONFIG_GPROF is not set | ||||
| # CONFIG_GCOV is not set | ||||
							
								
								
									
										179
									
								
								driver/user_mode_linux/setup-uml
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										179
									
								
								driver/user_mode_linux/setup-uml
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| #! /usr/bin/perl -w | ||||
|  | ||||
| use Cwd; | ||||
|  | ||||
| ############################## | ||||
| # Start user configurable bit | ||||
| ############################## | ||||
|  | ||||
| # Please use absolute paths too. | ||||
| # you can't use ~'s in your paths here. | ||||
|  | ||||
| # the directory where you've checked out lvm, keep a seperate copy for uml, | ||||
| # the uml kernel will have links to these files | ||||
| $lvm_src="/home/thornber/sistina/LVM2"; | ||||
|  | ||||
|  | ||||
| # the debian root image, get it from here: | ||||
| # http://prdownloads.sourceforge.net/user-mode-linux/root_fs_debian2.2_small.bz2 | ||||
| # unzip it once you've downloaded it | ||||
| $root_fs="/home/thornber/uml/root_fs_debian2.2_small"; | ||||
|  | ||||
| # these are 100 Meg files created with dd | ||||
| # these become our PV's /dev/ubd/[1-4] | ||||
| # I sometimes use ubd/1 as swap though. | ||||
| @block_devices = ("/home/thornber/uml/scratch1", | ||||
| 		  "/home/thornber/uml/scratch2", | ||||
| 		  "/home/thornber/uml/scratch3", | ||||
| 		  "/home/thornber/uml/scratch4"); | ||||
|  | ||||
| # directory where uml will be built, and the up, lvm-install scripts will | ||||
| # be placed | ||||
| $dest_dir="/home/thornber/builds/uml-lvm2"; | ||||
|  | ||||
| # It must be 2.4.8, can be .gz or .bz2 | ||||
| $kernel_tarball="/home/thornber/packages/2.4/linux-2.4.9.tar"; | ||||
|  | ||||
| ############################### | ||||
| # end of user configurable bit | ||||
| ############################### | ||||
|  | ||||
|  | ||||
| $wd = cwd; | ||||
| $uml_patch = $wd . "/uml.patch.bz2"; | ||||
| $lvm_uml_patch = $wd . "/uml-lvm.patch"; | ||||
| $driver = $lvm_src . "/driver/device-mapper"; | ||||
|  | ||||
|  | ||||
| # check we've got everything we need | ||||
| &check_file($root_fs); | ||||
| &check_dir($lvm_src); | ||||
| &check_file($kernel_tarball); | ||||
| &check_dir($dest_dir); | ||||
| &check_file($uml_patch); | ||||
| &check_file($lvm_uml_patch); | ||||
|  | ||||
|  | ||||
| chdir($dest_dir); | ||||
| &extract_kernel($dest_dir, $kernel_tarball); | ||||
| chdir("linux"); | ||||
| &run_command("bzip2 -dc $uml_patch | patch -p1", "patching kernel with uml"); | ||||
| &run_command("patch -p1 < $lvm_uml_patch", "enabling LVM driver"); | ||||
|  | ||||
| chdir("$dest_dir/linux"); | ||||
|  | ||||
| &run_command("cd include/linux; ln -s $driver/device-mapper.h", | ||||
| 	     "linking device-mapper.h"); | ||||
|  | ||||
| &run_command("cd drivers/md; ln -s $driver/dm.h", "linking dm.h"); | ||||
| &run_command("cd drivers/md; ln -s $driver/dm-fs.c", "linking dm-fs.c"); | ||||
| &run_command("cd drivers/md; ln -s $driver/dm-table.c", "linking dm-table.c"); | ||||
| &run_command("cd drivers/md; ln -s $driver/dm-target.c",  | ||||
| 	     "linking dm-target.c"); | ||||
| &run_command("cd drivers/md; ln -s $driver/dm.c", "linking dm.c"); | ||||
|  | ||||
| chdir("$dest_dir/linux"); | ||||
| &run_command("make oldconfig ARCH=um", "making oldconfig ARCH=um"); | ||||
| &run_command("make dep ARCH=um", "making dependencies"); | ||||
| &run_command("make linux ARCH=um", "building linux uml"); | ||||
|  | ||||
| chdir($dest_dir); | ||||
| &run_command("ln -s $dest_dir/linux/linux uml", "creating link for linux"); | ||||
|  | ||||
| chdir($dest_dir); | ||||
| &run_command("ln -s $root_fs ./root_fs", "linking root filesystem"); | ||||
|  | ||||
| chdir($dest_dir); | ||||
| &link_devices(); | ||||
| &write_up(); | ||||
| &run_command("mkdir root_fs_mnt"); | ||||
| &write_lvm_install(); | ||||
|  | ||||
| print "Dont forget to run $dest_dir/lvm-install as root\n"; | ||||
|  | ||||
|  | ||||
| sub write_lvm_install { | ||||
|     open(OUT, "> lvm-install"); | ||||
|     print OUT "#! /bin/sh\n\n"; | ||||
|     print OUT <<"end"; | ||||
| mount root_fs root_fs_mnt -o loop | ||||
| cd $lvm_src; make install; cd $dest_dir | ||||
| umount root_fs_mnt | ||||
| end | ||||
|  | ||||
|     close OUT; | ||||
|     system "chmod +x lvm-install"; | ||||
| } | ||||
|  | ||||
|  | ||||
| sub write_up { | ||||
|     open(UP, "> up"); | ||||
|     print UP "#! /bin/sh\n\n./uml "; | ||||
|     $count = 1; | ||||
|     for $d (@block_devices) { | ||||
| 	print UP "ubd$count=ubd$count "; | ||||
| 	$count++; | ||||
|     } | ||||
|     print UP "\n"; | ||||
|     close UP; | ||||
|     system("chmod +x up"); | ||||
| } | ||||
|  | ||||
| sub link_devices { | ||||
|     $count = 1; | ||||
|     foreach $d (@block_devices) { | ||||
| 	&run_command("ln -s $d ubd$count", | ||||
| 		     "linking block device ubd$count"); | ||||
| 	$count++; | ||||
|     } | ||||
| } | ||||
|  | ||||
| sub extract_kernel { | ||||
|     my($dest, $tb) = @_; | ||||
|     my($cmd); | ||||
|     if($tb =~ m/\.bz2/) { | ||||
| 	$cmd = "tar Ixf $tb"; | ||||
|  | ||||
|     } elsif($tb =~ m/\.gz/) { | ||||
| 	$cmd = "tar zxf $tb"; | ||||
|  | ||||
|     } else { | ||||
| 	$cmd = "tar xf $tb"; | ||||
|     } | ||||
|  | ||||
|     &run_command($cmd, "extracting kernel"); | ||||
| } | ||||
|  | ||||
| sub run_command { | ||||
|     my($cmd) = shift; | ||||
|     my($desc) = shift; | ||||
|     my($r); | ||||
|     print STDERR $desc, " ... "; | ||||
|     $r = system("$cmd > /dev/null"); | ||||
|     if(!$r) { | ||||
| 	print STDERR "done.\n"; | ||||
| 	return; | ||||
|     } else { | ||||
| 	print STDERR "failed.\n"; | ||||
| 	exit(1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| sub check_file { | ||||
|     $f = shift; | ||||
|     if(! -e $f) { | ||||
| 	print STDERR "couldn't find $f\n"; | ||||
| 	exit; | ||||
|     } | ||||
| } | ||||
|  | ||||
| sub check_dir { | ||||
|     $f = shift; | ||||
|     if(! -e $f || ! -d $f) { | ||||
| 	print STDERR "couldn't find a directory called $f\n"; | ||||
| 	exit; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										30
									
								
								driver/user_mode_linux/uml-lvm.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								driver/user_mode_linux/uml-lvm.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| --- uml_build/arch/um/config.in.orig	Tue Jan  2 14:33:42 2001 | ||||
| +++ uml_build/arch/um/config.in	Tue Jan  2 14:35:42 2001 | ||||
| @@ -15,6 +15,8 @@ | ||||
|  bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL | ||||
|  endmenu | ||||
|   | ||||
| +source drivers/md/Config.in | ||||
| + | ||||
|  mainmenu_option next_comment | ||||
|  comment 'Processor features' | ||||
|  bool 'Symmetric multi-processing support' CONFIG_SMP | ||||
| --- linux/drivers/md/Config.in.orig	Tue Aug 21 14:18:30 2001 | ||||
| +++ linux/drivers/md/Config.in	Tue Aug 21 14:19:08 2001 | ||||
| @@ -14,4 +14,6 @@ | ||||
|   | ||||
|  dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD | ||||
|   | ||||
| +dep_tristate ' Device mapper support' CONFIG_BLK_DEV_DM $CONFIG_MD | ||||
| + | ||||
|  endmenu | ||||
| --- linux/drivers/md/Makefile.orig	Tue Aug 21 14:19:14 2001 | ||||
| +++ linux/drivers/md/Makefile	Tue Aug 21 14:20:06 2001 | ||||
| @@ -19,6 +19,7 @@ | ||||
|  obj-$(CONFIG_MD_RAID5)		+= raid5.o xor.o | ||||
|  obj-$(CONFIG_BLK_DEV_MD)	+= md.o | ||||
|  obj-$(CONFIG_BLK_DEV_LVM)	+= lvm-mod.o | ||||
| +obj-$(CONFIG_BLK_DEV_DM)	+= dm.o dm-table.o dm-target.o dm-fs.o dm-parse.o | ||||
|   | ||||
|  include $(TOPDIR)/Rules.make | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								driver/user_mode_linux/uml.patch.bz2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								driver/user_mode_linux/uml.patch.bz2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										16
									
								
								include/.symlinks
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								include/.symlinks
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| ../lib/activate/activate.h | ||||
| ../lib/config/config.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.h | ||||
| ../lib/format1/format1.h | ||||
| ../lib/log/log.h | ||||
| ../lib/metadata/metadata.h | ||||
| ../lib/mm/dbg_malloc.h | ||||
| ../lib/mm/pool.h | ||||
| ../lib/mm/xlate.h | ||||
| ../lib/uuid/uuid.h | ||||
							
								
								
									
										41
									
								
								include/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								include/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| # | ||||
| # 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 $@ | ||||
|  | ||||
| clean: | ||||
|  | ||||
| distclean: | ||||
| 	find . -maxdepth 1 -type l -exec $(RM) \{\} \; | ||||
| 	$(RM) Makefile .include_symlinks .symlinks_created | ||||
|  | ||||
| .PHONY: clean distclean all | ||||
|  | ||||
							
								
								
									
										39
									
								
								lib/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								lib/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| # | ||||
| # Copyright (C) 2001 Sistina Software (UK) Limited | ||||
| # | ||||
| # This file is released under the GPL. | ||||
| # | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| VPATH = @srcdir@ | ||||
|  | ||||
| SOURCES=\ | ||||
| 	activate/activate.c \ | ||||
| 	config/config.c \ | ||||
| 	datastruct/hash.c \ | ||||
| 	device/dev-cache.c \ | ||||
| 	device/dev-io.c \ | ||||
| 	device/device.c \ | ||||
| 	display/display.c \ | ||||
| 	filters/filter.c \ | ||||
| 	format1/disk-rep.c \ | ||||
| 	format1/format1.c \ | ||||
| 	format1/import-export.c \ | ||||
| 	format1/layout.c \ | ||||
| 	format1/vg_number.c \ | ||||
| 	log/log.c \ | ||||
| 	metadata/metadata.c \ | ||||
| 	mm/dbg_malloc.c \ | ||||
| 	mm/pool.c \ | ||||
| 	uuid/uuid.c | ||||
|  | ||||
| TARGETS=liblvm.a | ||||
|  | ||||
| include ../make.tmpl | ||||
|  | ||||
| liblvm.a: $(OBJECTS) | ||||
| 	$(RM) $@ | ||||
| 	$(AR) r $@ $(OBJECTS) | ||||
| 	$(RANLIB) $@ | ||||
|  | ||||
							
								
								
									
										35
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/activate/activate.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "metadata.h" | ||||
| #include "activate.h" | ||||
|  | ||||
| int lv_activate(struct volume_group *vg, struct logical_volume *lv) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lv_deactivate(struct volume_group *vg, struct logical_volume *lv) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lvs_in_vg_activated(struct volume_group *vg) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int activate_lvs_in_vg(struct volume_group *vg) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int deactivate_lvs_in_vg(struct volume_group *vg) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										28
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								lib/activate/activate.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef LVM_ACTIVATE_H | ||||
| #define LVM_ACTIVATE_H | ||||
|  | ||||
| /* FIXME Snapshot handling? */ | ||||
|  | ||||
| int lv_activate(struct volume_group *vg, | ||||
| 		struct logical_volume *lv); | ||||
|  | ||||
| int lv_deactivate(struct volume_group *vg, | ||||
| 		  struct logical_volume *lv); | ||||
|  | ||||
| /* Return number of LVs in the VG that are active */ | ||||
| int lvs_in_vg_activated(struct volume_group *vg); | ||||
|  | ||||
| /* Activate all LVs in the VG.  Ignore any that are already active. */ | ||||
| /* Return number activated */ | ||||
| int activate_lvs_in_vg(struct volume_group *vg); | ||||
|  | ||||
| /* Deactivate all LVs in the VG */ | ||||
| int deactivate_lvs_in_vg(struct volume_group *vg); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										31
									
								
								lib/activate/dmfs-driver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/activate/dmfs-driver.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef DMFS_INTERFACE_H | ||||
| #define DMFS_INTERFACE_H | ||||
|  | ||||
| struct dmfs; | ||||
|  | ||||
| struct dmfs *dmfs_create(void); | ||||
| void dmfs_destroy(struct dmfs *dm); | ||||
|  | ||||
| int dmfs_dev_is_present(struct dmfs *dm, const char *dev); | ||||
| int dmfs_dev_is_active(struct dmfs *dm, const char *dev); | ||||
|  | ||||
| int dmfs_table_is_present(struct dmfs *dm, const char *dev, const char *table); | ||||
| int dmfs_table_is_active(struct dmfs *dm, const char *dev, const char *table); | ||||
|  | ||||
| int dmfs_dev_create(struct dmfs *dm, const char *name); | ||||
| int dmfs_dev_load_table(struct dmfs *dm, const char *dev,  | ||||
| 			const char *table, const char *file); | ||||
| int dmfs_dev_drop_table(struct dmfs *dm, const char *dev, const char *table); | ||||
|  | ||||
| int dmfs_dev_activate_table(struct dmfs *dm, const char *dev, | ||||
| 			    const char *table); | ||||
|  | ||||
| int dmfs_dev_deactivate(struct dmfs *dm, const char *dev); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										35
									
								
								lib/activate/table-build.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/activate/table-build.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "table-build.c" | ||||
|  | ||||
| /* FIXME: optimise linear runs */ | ||||
| int build_table(struct volume_group *vg, struct logical_volume *lv, | ||||
| 		const char *file) | ||||
| { | ||||
| 	int i; | ||||
| 	uint64_t sector = 0; | ||||
| 	uint64_t pe_size = vg->extent_size; | ||||
| 	uint64_t dest; | ||||
| 	struct pe_specifier *pes; | ||||
| 	FILE *fp = fopen(file, "w"); | ||||
|  | ||||
| 	if (!fp) { | ||||
| 		log_err("couldn't open '%s' to write table", file); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < lv->le_count; i++) { | ||||
| 		pes = lv->map + i; | ||||
| 		dest = pes->pv->pe_start + (pe_size * pes->pe); | ||||
| 		fprintf(fp, "%ull %ull linear %s %ull\n", | ||||
| 			sector, pe_size, pes->pv->dev->name, dest); | ||||
| 		sector += pe_size; | ||||
| 	} | ||||
| 	fclose(fp); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										13
									
								
								lib/activate/table-build.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								lib/activate/table-build.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef TABLE_BUILD_H | ||||
| #define TABLE_BUILD_H | ||||
|  | ||||
| int build_table(struct volume_group *vg, struct logical_volume *lv, | ||||
| 		const char *file); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										595
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										595
									
								
								lib/config/config.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,595 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/mman.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <ctype.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include "config.h" | ||||
| #include "pool.h" | ||||
| #include "log.h" | ||||
|  | ||||
| enum { | ||||
|         TOK_INT, | ||||
|         TOK_FLOAT, | ||||
|         TOK_STRING, | ||||
|         TOK_EQ, | ||||
|         TOK_SECTION_B, | ||||
|         TOK_SECTION_E, | ||||
|         TOK_ARRAY_B, | ||||
|         TOK_ARRAY_E, | ||||
|         TOK_IDENTIFIER, | ||||
| 	TOK_COMMA, | ||||
| 	TOK_EOF | ||||
| }; | ||||
|  | ||||
| struct parser { | ||||
|         const char *fb, *fe;    /* file limits */ | ||||
|  | ||||
|         int t;                  /* token limits and type */ | ||||
|         const char *tb, *te; | ||||
|  | ||||
|         int fd;                 /* descriptor for file being parsed */ | ||||
| 	int line;		/* line number we are on */ | ||||
|  | ||||
|         struct pool *mem; | ||||
| }; | ||||
|  | ||||
| struct cs { | ||||
|         struct config_file cf; | ||||
|         struct pool *mem; | ||||
| }; | ||||
|  | ||||
| static void _get_token(struct parser *p); | ||||
| static void _eat_space(struct parser *p); | ||||
| static struct config_node *_file(struct parser *p); | ||||
| static struct config_node *_section(struct parser *p); | ||||
| static struct config_value *_value(struct parser *p); | ||||
| static struct config_value *_type(struct parser *p); | ||||
| static void _parse_error(struct parser *p, const char *file, int line, | ||||
| 			 const char *mess); | ||||
| 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); | ||||
| static int _tok_match(const char *str, const char *b, const char *e); | ||||
|  | ||||
| #define MAX_INDENT 32 | ||||
|  | ||||
| #define match(t) do {\ | ||||
|    if (!_match_aux(p, (t))) {\ | ||||
|       _parse_error(p, __FILE__, __LINE__, "unexpected token"); \ | ||||
|       return 0;\ | ||||
|    } \ | ||||
| } while(0); | ||||
|  | ||||
| /* | ||||
|  * public interface | ||||
|  */ | ||||
| struct config_file *create_config_file() | ||||
| { | ||||
|         struct cs *c; | ||||
|         struct pool *mem = pool_create(10 * 1024); | ||||
|  | ||||
|         if (!mem) { | ||||
|                 stack; | ||||
|                 return 0; | ||||
|         } | ||||
|  | ||||
|         if (!(c = pool_alloc(mem, sizeof(*c)))) { | ||||
|                 stack; | ||||
|                 pool_destroy(mem); | ||||
|                 return 0; | ||||
|         } | ||||
|  | ||||
|         c->mem = mem; | ||||
| 	c->cf.root = (struct config_node *)NULL; | ||||
|         return &c->cf; | ||||
| } | ||||
|  | ||||
| void destroy_config_file(struct config_file *cf) | ||||
| { | ||||
|         pool_destroy(((struct cs *) cf)->mem); | ||||
| } | ||||
|  | ||||
| int read_config(struct config_file *cf, const char *file) | ||||
| { | ||||
| 	struct cs *c = (struct cs *) cf; | ||||
|         struct parser *p; | ||||
|         struct stat info; | ||||
|         int r = 1, fd; | ||||
|  | ||||
|         if (!(p = pool_alloc(c->mem, sizeof(*p)))) { | ||||
|                 stack; | ||||
|                 return 0; | ||||
|         } | ||||
| 	p->mem = c->mem; | ||||
|  | ||||
|         /* memory map the file */ | ||||
|         if (stat(file, &info) || S_ISDIR(info.st_mode)) { | ||||
|                 log_sys_error("stat", file); | ||||
|                 return 0; | ||||
|         } | ||||
|  | ||||
|         if ((fd = open(file, O_RDONLY)) < 0) { | ||||
|                 log_sys_error("open", file); | ||||
|                 return 0; | ||||
|         } | ||||
|  | ||||
|         p->fb = mmap((caddr_t) 0, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | ||||
|         if (p->fb == MAP_FAILED) { | ||||
|                 log_sys_error("mmap", file); | ||||
|                 close(fd); | ||||
|                 return 0; | ||||
|         } | ||||
|         p->fe = p->fb + info.st_size; | ||||
|  | ||||
|         /* parse */ | ||||
|         p->tb = p->te = p->fb; | ||||
| 	p->line = 1; | ||||
| 	_get_token(p); | ||||
|         if (!(cf->root = _file(p))) { | ||||
|                 stack; | ||||
|                 r = 0; | ||||
|         } | ||||
|  | ||||
|         /* unmap the file */ | ||||
|         if (munmap((char *) p->fb, info.st_size)) { | ||||
|                 log_sys_error("munmap", file); | ||||
|                 r = 0; | ||||
|         } | ||||
|  | ||||
|         close(fd); | ||||
|         return r; | ||||
| } | ||||
|  | ||||
| static void _write_value(FILE *fp, struct config_value *v) | ||||
| { | ||||
| 	switch (v->type) { | ||||
| 	case CFG_STRING: | ||||
| 		fprintf(fp, "\"%s\"", v->v.str); | ||||
| 		break; | ||||
|  | ||||
| 	case CFG_FLOAT: | ||||
| 		fprintf(fp, "%f", v->v.r); | ||||
| 		break; | ||||
|  | ||||
| 	case CFG_INT: | ||||
| 		fprintf(fp, "%d", v->v.i); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int _write_config(struct config_node *n, FILE *fp, int level) | ||||
| { | ||||
|         char space[MAX_INDENT + 1]; | ||||
|         int l = (level < MAX_INDENT) ? level : MAX_INDENT; | ||||
|         int i; | ||||
|  | ||||
| 	if (!n) | ||||
| 		return 1; | ||||
|  | ||||
|         for (i = 0; i < l; i++) | ||||
|                 space[i] = ' '; | ||||
|         space[i] = '\0'; | ||||
|  | ||||
|         while (n) { | ||||
|                 fprintf(fp, "%s%s", space, n->key); | ||||
| 		if (!n->v) { | ||||
| 			/* it's a sub section */ | ||||
| 			fprintf(fp, " {\n"); | ||||
| 			_write_config(n->child, fp, level + 1); | ||||
| 			fprintf(fp, "%s}", space); | ||||
| 		} else { | ||||
| 			/* it's a value */ | ||||
| 			struct config_value *v = n->v; | ||||
| 			fprintf(fp, "="); | ||||
| 			if (v->next) { | ||||
| 				fprintf(fp, "["); | ||||
| 				while (v) { | ||||
| 					_write_value(fp, v); | ||||
| 					v = v->next; | ||||
| 					if (v) | ||||
| 						fprintf(fp, ", "); | ||||
| 				} | ||||
| 				fprintf(fp, "]"); | ||||
| 			} else | ||||
| 				_write_value(fp, v); | ||||
| 		} | ||||
| 		fprintf(fp, "\n"); | ||||
|                 n = n->sib; | ||||
|         } | ||||
| 	/* FIXME: add error checking */ | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int write_config(struct config_file *cf, const char *file) | ||||
| { | ||||
| 	int r = 1; | ||||
|         FILE *fp = fopen(file, "w"); | ||||
|         if (!fp) { | ||||
|                 log_sys_error("open", file); | ||||
|                 return 0; | ||||
|         } | ||||
|  | ||||
|         if (!_write_config(cf->root, fp, 0)) { | ||||
| 		stack; | ||||
| 		r = 0; | ||||
| 	} | ||||
|         fclose(fp); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * parser | ||||
|  */ | ||||
| static struct config_node *_file(struct parser *p) | ||||
| { | ||||
| 	struct config_node *root = 0, *n, *l; | ||||
| 	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; | ||||
| 	if (!(root = _create_node(p))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(root->key = _dup_tok(p))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
|         match (TOK_IDENTIFIER); | ||||
|  | ||||
| 	if (p->t == TOK_SECTION_B) { | ||||
| 		match(TOK_SECTION_B); | ||||
| 		while (p->t != TOK_SECTION_E) { | ||||
| 			if (!(n = _section(p))) { | ||||
| 				stack; | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			if (!root->child) | ||||
| 				root->child = n; | ||||
| 			else | ||||
| 				l->sib = n; | ||||
| 			l = n; | ||||
| 		} | ||||
| 		match(TOK_SECTION_E); | ||||
| 	} else { | ||||
| 		match(TOK_EQ); | ||||
| 		if (!(root->v = _value(p))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|         return root; | ||||
| } | ||||
|  | ||||
| static struct config_value *_value(struct parser *p) | ||||
| { | ||||
|         /* '[' TYPE* ']' | TYPE */ | ||||
| 	struct config_value *h = 0, *l, *ll = 0; | ||||
|         if (p->t == TOK_ARRAY_B) { | ||||
|                 match (TOK_ARRAY_B); | ||||
|                 while (p->t != TOK_ARRAY_E) { | ||||
|                         if (!(l = _type(p))) { | ||||
| 				stack; | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			if (!h) | ||||
| 				h = l; | ||||
| 			else | ||||
| 				ll->next = l; | ||||
| 			ll = l; | ||||
|  | ||||
| 			if (p->t == TOK_COMMA) | ||||
| 				match(TOK_COMMA); | ||||
| 		} | ||||
|                 match(TOK_ARRAY_E); | ||||
|         } else | ||||
| 		h = _type(p); | ||||
|  | ||||
|         return h; | ||||
| } | ||||
|  | ||||
| static struct config_value *_type(struct parser *p) { | ||||
|         /* [0-9]+ | [0-9]*\.[0-9]* | ".*" */ | ||||
| 	struct config_value *v = _create_value(p); | ||||
|  | ||||
|         switch (p->t) { | ||||
|         case TOK_INT: | ||||
| 		v->type = CFG_INT; | ||||
| 		v->v.i = strtol(p->tb, 0, 10); /* FIXME: check error */ | ||||
|                 match(TOK_INT); | ||||
|                 break; | ||||
|  | ||||
|         case TOK_FLOAT: | ||||
| 		v->type = CFG_FLOAT; | ||||
| 		v->v.r = strtod(p->tb, 0); /* FIXME: check error */ | ||||
|                 match(TOK_FLOAT); | ||||
|                 break; | ||||
|  | ||||
|         case TOK_STRING: | ||||
| 		v->type = CFG_STRING; | ||||
|  | ||||
| 		p->tb++, p->te--; /* strip "'s */ | ||||
| 		if (!(v->v.str = _dup_tok(p))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		p->te++; | ||||
|                 match(TOK_STRING); | ||||
|                 break; | ||||
|  | ||||
|         default: | ||||
|                 _parse_error(p, __FILE__, __LINE__, "expected a value"); | ||||
|                 return 0; | ||||
|         } | ||||
|         return v; | ||||
| } | ||||
|  | ||||
| static void _parse_error(struct parser *p, const char *file, int line, | ||||
| 			 const char *mess) | ||||
| { | ||||
|         plog(_LOG_ERR, file, line, "parse error at %d: %s", p->line, mess); | ||||
| } | ||||
|  | ||||
| static int _match_aux(struct parser *p, int t) | ||||
| { | ||||
| 	if (p->t != t) | ||||
| 		return 0; | ||||
|  | ||||
| 	_get_token(p); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * tokeniser | ||||
|  */ | ||||
| static void _get_token(struct parser *p) | ||||
| { | ||||
|         p->tb = p->te; | ||||
|         _eat_space(p); | ||||
|         if (p->tb == p->fe) { | ||||
| 		p->t = TOK_EOF; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
|         p->t = TOK_INT;         /* fudge so the fall through for | ||||
|                                    floats works */ | ||||
|         switch (*p->te) { | ||||
|         case '{': | ||||
|                 p->t = TOK_SECTION_B; | ||||
|                 p->te++; | ||||
|                 break; | ||||
|  | ||||
|         case '}': | ||||
|                 p->t = TOK_SECTION_E; | ||||
|                 p->te++; | ||||
|                 break; | ||||
|  | ||||
|         case '[': | ||||
|                 p->t = TOK_ARRAY_B; | ||||
|                 p->te++; | ||||
|                 break; | ||||
|  | ||||
|         case ']': | ||||
|                 p->t = TOK_ARRAY_E; | ||||
|                 p->te++; | ||||
|                 break; | ||||
|  | ||||
|         case ',': | ||||
|                 p->t = TOK_COMMA; | ||||
|                 p->te++; | ||||
|                 break; | ||||
|  | ||||
|         case '=': | ||||
|                 p->t = TOK_EQ; | ||||
|                 p->te++; | ||||
|                 break; | ||||
|  | ||||
|         case '"': | ||||
|                 p->t = TOK_STRING; | ||||
| 		p->te++; | ||||
|                 while ((p->te != p->fe) && (*p->te != '"')) { | ||||
|                         if ((*p->te == '\\') && (p->te + 1 != p->fe)) | ||||
|                                 p->te++; | ||||
|                         p->te++; | ||||
|                 } | ||||
|  | ||||
|                 if (p->te != p->fe) | ||||
|                         p->te++; | ||||
|                 break; | ||||
|  | ||||
|         case '.': | ||||
|                 p->t = TOK_FLOAT; | ||||
|         case '0': case '1': case '2': case '3': case '4': | ||||
|         case '5': case '6': case '7': case '8': case '9': | ||||
|                 p->te++; | ||||
|                 while (p->te != p->fe) { | ||||
|                         if (*p->te == '.') { | ||||
|                                 if (p->t == TOK_FLOAT) | ||||
|                                         break; | ||||
|                                 p->t = TOK_FLOAT; | ||||
|                         } else if (!isdigit((int) *p->te)) | ||||
|                                 break; | ||||
|                         p->te++; | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|         default: | ||||
|                 p->t = TOK_IDENTIFIER; | ||||
|                 while ((p->te != p->fe) && !isspace(*p->te) && | ||||
| 		      (*p->te != '#') && (*p->te != '=')) | ||||
|                         p->te++; | ||||
|                 break; | ||||
|         } | ||||
| } | ||||
|  | ||||
| static void _eat_space(struct parser *p) | ||||
| { | ||||
|         while (p->tb != p->fe) { | ||||
|                 if (*p->te == '#') { | ||||
|                         while ((p->te != p->fe) && (*p->te != '\n')) | ||||
|                                 p->te++; | ||||
| 			p->line++; | ||||
| 		} | ||||
|  | ||||
| 		else if (isspace(*p->te)) { | ||||
|                         while ((p->te != p->fe) && isspace(*p->te)) { | ||||
| 				if (*p->te == '\n') | ||||
| 					p->line++; | ||||
|                                 p->te++; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|                 else | ||||
|                         return; | ||||
|  | ||||
|                 p->tb = p->te; | ||||
|         } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * memory management | ||||
|  */ | ||||
| static struct config_value *_create_value(struct parser *p) | ||||
| { | ||||
| 	struct config_value *v = pool_alloc(p->mem, sizeof(*v)); | ||||
| 	memset(v, 0, sizeof(*v)); | ||||
| 	return v; | ||||
| } | ||||
|  | ||||
| static struct config_node *_create_node(struct parser *p) | ||||
| { | ||||
| 	struct config_node *n = pool_alloc(p->mem, sizeof(*n)); | ||||
| 	memset(n, 0, sizeof(*n)); | ||||
| 	return n; | ||||
| } | ||||
|  | ||||
| static char *_dup_tok(struct parser *p) | ||||
| { | ||||
| 	int len = p->te - p->tb; | ||||
| 	char *str = pool_alloc(p->mem, len + 1); | ||||
| 	if (!str) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	strncpy(str, p->tb, len); | ||||
| 	str[len] = '\0'; | ||||
| 	return str; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * utility functions | ||||
|  */ | ||||
| struct config_node *find_config_node(struct config_node *cn, | ||||
| 				     const char *path, char sep) | ||||
| { | ||||
| 	const char *e; | ||||
|  | ||||
| 	while (cn) { | ||||
| 		/* trim any leading slashes */ | ||||
| 		while (*path && (*path == sep)) | ||||
| 			path++; | ||||
|  | ||||
| 		/* find the end of this segment */ | ||||
| 		for (e = path; *e && (*e != sep); e++) | ||||
| 			; | ||||
|  | ||||
| 		/* hunt for the node */ | ||||
| 		while (cn) { | ||||
| 			if (_tok_match(cn->key, path, e)) | ||||
| 				break; | ||||
|  | ||||
| 			cn = cn->sib; | ||||
| 		} | ||||
|  | ||||
| 		if (cn && *e) | ||||
| 			cn = cn->child; | ||||
| 		else | ||||
| 			break;	/* don't move into the last node */ | ||||
|  | ||||
| 		path = e; | ||||
| 	} | ||||
|  | ||||
| 	return cn; | ||||
| } | ||||
|  | ||||
| const char * | ||||
| find_config_str(struct config_node *cn,  | ||||
| 		const char *path, char sep, const char *fail) | ||||
| { | ||||
| 	struct config_node *n = find_config_node(cn, path, sep); | ||||
|  | ||||
| 	if (n && n->v->type == CFG_STRING) | ||||
| 		return n->v->v.str; | ||||
|  | ||||
| 	return fail; | ||||
| } | ||||
|  | ||||
| int find_config_int(struct config_node *cn, const char *path,  | ||||
| 		    char sep, int fail) | ||||
| { | ||||
| 	struct config_node *n = find_config_node(cn, path, sep); | ||||
|  | ||||
| 	if (n && n->v->type == CFG_INT) | ||||
| 		return n->v->v.i; | ||||
|  | ||||
| 	return fail; | ||||
| } | ||||
|  | ||||
| float find_config_float(struct config_node *cn, const char *path, | ||||
| 			char sep, float fail) | ||||
| { | ||||
| 	struct config_node *n = find_config_node(cn, path, sep); | ||||
|  | ||||
| 	if (n && n->v->type == CFG_FLOAT) | ||||
| 		return n->v->v.r; | ||||
|  | ||||
| 	return fail; | ||||
|  | ||||
| } | ||||
|  | ||||
| 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)); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
							
								
								
									
										61
									
								
								lib/config/config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								lib/config/config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_CONFIG_H | ||||
| #define _LVM_CONFIG_H | ||||
|  | ||||
|  | ||||
| enum { | ||||
|         CFG_STRING, | ||||
|         CFG_FLOAT, | ||||
|         CFG_INT, | ||||
| }; | ||||
|  | ||||
| struct config_value { | ||||
|         int type; | ||||
|         union { | ||||
|                 int i; | ||||
|                 float r; | ||||
|                 char *str; | ||||
|         } v; | ||||
|         struct config_value *next; /* for arrays */ | ||||
| }; | ||||
|  | ||||
| struct config_node { | ||||
|         char *key; | ||||
|         struct config_node *sib, *child; | ||||
|         struct config_value *v; | ||||
| }; | ||||
|  | ||||
| struct config_file { | ||||
|         struct config_node *root; | ||||
| }; | ||||
|  | ||||
| struct config_file *create_config_file(); | ||||
| void destroy_config_file(struct config_file *cf); | ||||
|  | ||||
| int read_config(struct config_file *cf, const char *file); | ||||
| int write_config(struct config_file *cf, const char *file); | ||||
|  | ||||
| struct config_node *find_config_node(struct config_node *cn, | ||||
| 				     const char *path, char seperator); | ||||
|  | ||||
| const char *find_config_str(struct config_node *cn, | ||||
| 			    const char *path, char sep, const char *fail); | ||||
|  | ||||
| int find_config_int(struct config_node *cn, const char *path, | ||||
| 		    char sep, int fail); | ||||
|  | ||||
| float find_config_float(struct config_node *cn, const char *path, | ||||
| 			char sep, float fail); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
							
								
								
									
										210
									
								
								lib/datastruct/hash.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								lib/datastruct/hash.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "hash.h" | ||||
| #include "log.h" | ||||
|  | ||||
| struct hash_node { | ||||
| 	struct hash_node *next; | ||||
| 	void *data; | ||||
| 	char key[1]; | ||||
| }; | ||||
|  | ||||
| struct hash_table { | ||||
| 	int num_nodes; | ||||
| 	int num_slots; | ||||
| 	struct hash_node **slots; | ||||
| }; | ||||
|  | ||||
| /* Permutation of the Integers 0 through 255 */ | ||||
| static unsigned char _nums[] = { | ||||
| 	1, 14,110, 25, 97,174,132,119,138,170,125,118, 27,233,140, 51, | ||||
| 	87,197,177,107,234,169, 56, 68, 30,  7,173, 73,188, 40, 36, 65, | ||||
| 	49,213,104,190, 57,211,148,223, 48,115, 15,  2, 67,186,210, 28, | ||||
| 	12,181,103, 70, 22, 58, 75, 78,183,167,238,157,124,147,172,144, | ||||
| 	176,161,141, 86, 60, 66,128, 83,156,241, 79, 46,168,198, 41,254, | ||||
| 	178, 85,253,237,250,154,133, 88, 35,206, 95,116,252,192, 54,221, | ||||
| 	102,218,255,240, 82,106,158,201, 61,  3, 89,  9, 42,155,159, 93, | ||||
| 	166, 80, 50, 34,175,195,100, 99, 26,150, 16,145,  4, 33,  8,189, | ||||
| 	121, 64, 77, 72,208,245,130,122,143, 55,105,134, 29,164,185,194, | ||||
| 	193,239,101,242,  5,171,126, 11, 74, 59,137,228,108,191,232,139, | ||||
| 	6, 24, 81, 20,127, 17, 91, 92,251,151,225,207, 21, 98,113,112, | ||||
| 	84,226, 18,214,199,187, 13, 32, 94,220,224,212,247,204,196, 43, | ||||
| 	249,236, 45,244,111,182,153,136,129, 90,217,202, 19,165,231, 71, | ||||
| 	230,142, 96,227, 62,179,246,114,162, 53,160,215,205,180, 47,109, | ||||
| 	44, 38, 31,149,135,  0,216, 52, 63, 23, 37, 69, 39,117,146,184, | ||||
| 	163,200,222,235,248,243,219, 10,152,131,123,229,203, 76,120,209 | ||||
| }; | ||||
|  | ||||
| static struct hash_node *_create_node(const char *str) | ||||
| { | ||||
| 	/* remember sizeof(n) includes an extra char from key[1], | ||||
| 	   so not adding 1 to the strlen as you would expect */ | ||||
| 	struct hash_node *n = dbg_malloc(sizeof(*n) + strlen(str)); | ||||
|  | ||||
| 	if (n) | ||||
| 		strcpy(n->key, str); | ||||
|  | ||||
| 	return n; | ||||
| } | ||||
|  | ||||
| static unsigned _hash(const char *str) | ||||
| { | ||||
| 	unsigned long int h = 0, g; | ||||
| 	while (*str) { | ||||
| 		h <<= 4; | ||||
| 		h += _nums[(int) *str++]; | ||||
| 		g = h & ((unsigned long) 0xf << 16u); | ||||
| 		if (g) { | ||||
| 			h ^= g >> 16u; | ||||
| 			h ^= g >> 5u; | ||||
| 		} | ||||
| 	} | ||||
| 	return h; | ||||
| } | ||||
|  | ||||
| struct hash_table *hash_create(unsigned size_hint) | ||||
| { | ||||
| 	size_t len; | ||||
| 	unsigned new_size = 16u; | ||||
| 	struct hash_table *hc = dbg_malloc(sizeof(*hc)); | ||||
|  | ||||
| 	if (!hc) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	memset(hc, 0, sizeof(*hc)); | ||||
|  | ||||
| 	/* round size hint up to a power of two */ | ||||
| 	while (new_size < size_hint) | ||||
| 		new_size  = new_size << 1; | ||||
|  | ||||
| 	hc->num_slots = new_size; | ||||
| 	len = sizeof(*(hc->slots)) * new_size; | ||||
| 	if (!(hc->slots = dbg_malloc(len))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
| 	memset(hc->slots, 0, len); | ||||
| 	return hc; | ||||
|  | ||||
|  bad: | ||||
| 	dbg_free(hc->slots); | ||||
| 	dbg_free(hc); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void hash_destroy(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); | ||||
| 		} | ||||
|  | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| char *hash_lookup(struct hash_table *t, const char *key) | ||||
| { | ||||
| 	struct hash_node **c = _find(t, key); | ||||
| 	return *c ? (*c)->data : 0; | ||||
| } | ||||
|  | ||||
| int hash_insert(struct hash_table *t, const char *key, void *data) | ||||
| { | ||||
| 	struct hash_node **c = _find(t, key); | ||||
|  | ||||
| 	if(*c) | ||||
| 		(*c)->data = data; | ||||
| 	else { | ||||
| 		struct hash_node *n = _create_node(key); | ||||
|  | ||||
| 		if (!n) | ||||
| 			return 0; | ||||
|  | ||||
| 		n->data = data; | ||||
| 		n->next = 0; | ||||
| 		*c = n; | ||||
| 		t->num_nodes++; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void hash_remove(struct hash_table *t, const char *key) | ||||
| { | ||||
| 	struct hash_node **c = _find(t, key); | ||||
|  | ||||
| 	if (*c) { | ||||
| 		struct hash_node *old = *c; | ||||
| 		*c = (*c)->next; | ||||
| 		dbg_free(old); | ||||
| 		t->num_nodes--; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| unsigned hash_get_num_entries(struct hash_table *t) | ||||
| { | ||||
| 	return t->num_nodes; | ||||
| } | ||||
|  | ||||
| void hash_iterate(struct hash_table *t, iterate_fn f) | ||||
| { | ||||
| 	struct hash_node *c; | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < t->num_slots; i++) | ||||
| 		for (c = t->slots[i]; c; c = c->next) | ||||
| 			f(c->data); | ||||
| } | ||||
|  | ||||
| void *hash_get_data(struct hash_table *t, struct hash_node *n) | ||||
| { | ||||
| 	return n->data; | ||||
| } | ||||
|  | ||||
| static struct hash_node *_next_slot(struct hash_table *t, unsigned int s) | ||||
| { | ||||
| 	struct hash_node *c = NULL; | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = s; i < t->num_slots && !c; i++) | ||||
| 		c = t->slots[i]; | ||||
|  | ||||
| 	return c; | ||||
| } | ||||
|  | ||||
| struct hash_node *hash_get_first(struct hash_table *t) | ||||
| { | ||||
| 	return _next_slot(t, 0); | ||||
| } | ||||
|  | ||||
| struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n) | ||||
| { | ||||
| 	unsigned int h = _hash(n->key) & (t->num_slots - 1); | ||||
| 	return n->next ? n->next : _next_slot(t, h + 1); | ||||
| } | ||||
|  | ||||
							
								
								
									
										30
									
								
								lib/datastruct/hash.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/datastruct/hash.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| /* | ||||
|  * 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); | ||||
|  | ||||
| char *hash_lookup(struct hash_table *t, const char *key); | ||||
| int hash_insert(struct hash_table *t, const char *key, void *data); | ||||
| void hash_remove(struct hash_table *t, const char *key); | ||||
|  | ||||
| unsigned hash_get_num_entries(struct hash_table *t); | ||||
| void hash_iterate(struct hash_table *t, iterate_fn f); | ||||
|  | ||||
| void *hash_get_data(struct hash_table *t, struct hash_node *n); | ||||
| struct hash_node *hash_get_first(struct hash_table *t); | ||||
| struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										110
									
								
								lib/datastruct/list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								lib/datastruct/list.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| /* stolen from the Linux kernel. */ | ||||
|  | ||||
| #ifndef _LVM_LIST_H | ||||
| #define _LVM_LIST_H | ||||
|  | ||||
| /* | ||||
|  * Simple doubly linked list implementation. | ||||
|  * | ||||
|  * Some of the internal functions ("__xxx") are useful when | ||||
|  * manipulating whole lists rather than single entries, as | ||||
|  * sometimes we already know the next/prev entries and we can | ||||
|  * generate better code by using them directly rather than | ||||
|  * using the generic single-entry routines. | ||||
|  */ | ||||
|  | ||||
| struct list_head { | ||||
| 	struct list_head *next, *prev; | ||||
| }; | ||||
|  | ||||
| #define LIST_HEAD_INIT(name) { &(name), &(name) } | ||||
|  | ||||
| #define LIST_HEAD(name) \ | ||||
| 	struct list_head name = { &name, &name } | ||||
|  | ||||
| #define INIT_LIST_HEAD(ptr) do { \ | ||||
| 	(ptr)->next = (ptr); (ptr)->prev = (ptr); \ | ||||
| } while (0) | ||||
|  | ||||
| /* | ||||
|  * Insert a new entry between two known consecutive entries.  | ||||
|  * | ||||
|  * This is only for internal list manipulation where we know | ||||
|  * the prev/next entries already! | ||||
|  */ | ||||
| static __inline__ void __list_add(struct list_head *new, | ||||
| 				  struct list_head *prev, | ||||
| 				  struct list_head *next) | ||||
| { | ||||
| 	next->prev = new; | ||||
| 	new->next = next; | ||||
| 	new->prev = prev; | ||||
| 	prev->next = new; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Insert a new entry after the specified head.. | ||||
|  */ | ||||
| static __inline__ void list_add(struct list_head *new, struct list_head *head) | ||||
| { | ||||
| 	__list_add(new, head, head->next); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Insert a new entry at the tail | ||||
|  */ | ||||
| static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) | ||||
| { | ||||
| 	__list_add(new, head->prev, head); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Delete a list entry by making the prev/next entries | ||||
|  * point to each other. | ||||
|  * | ||||
|  * This is only for internal list manipulation where we know | ||||
|  * the prev/next entries already! | ||||
|  */ | ||||
| static __inline__ void __list_del(struct list_head * prev, | ||||
| 				  struct list_head * next) | ||||
| { | ||||
| 	next->prev = prev; | ||||
| 	prev->next = next; | ||||
| } | ||||
|  | ||||
| static __inline__ void list_del(struct list_head *entry) | ||||
| { | ||||
| 	__list_del(entry->prev, entry->next); | ||||
| } | ||||
|  | ||||
| static __inline__ int list_empty(struct list_head *head) | ||||
| { | ||||
| 	return head->next == head; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Splice in "list" into "head" | ||||
|  */ | ||||
| static __inline__ void list_splice(struct list_head *list, struct list_head *head) | ||||
| { | ||||
| 	struct list_head *first = list->next; | ||||
|  | ||||
| 	if (first != list) { | ||||
| 		struct list_head *last = list->prev; | ||||
| 		struct list_head *at = head->next; | ||||
|  | ||||
| 		first->prev = head; | ||||
| 		head->next = first; | ||||
|  | ||||
| 		last->next = at; | ||||
| 		at->prev = last; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #define list_entry(ptr, type, member) \ | ||||
| 	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) | ||||
|  | ||||
| #define list_for_each(pos, head) \ | ||||
|         for (pos = (head)->next; pos != (head); pos = pos->next) | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										24
									
								
								lib/datastruct/lvm-types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/datastruct/lvm-types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_TYPES_H | ||||
| #define _LVM_TYPES_H | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| typedef __uint8_t uint8_t; | ||||
| typedef __uint16_t uint16_t; | ||||
| typedef __uint32_t uint32_t; | ||||
| typedef __uint64_t uint64_t; | ||||
|  | ||||
| #if 0 | ||||
| typedef __int8_t int8_t; | ||||
| typedef __int16_t int16_t; | ||||
| typedef __int32_t int32_t; | ||||
| typedef __int64_t int64_t; | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										301
									
								
								lib/device/dev-cache.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								lib/device/dev-cache.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,301 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "dev-cache.h" | ||||
| #include "log.h" | ||||
| #include "pool.h" | ||||
| #include "hash.h" | ||||
| #include "list.h" | ||||
| #include "dbg_malloc.h" | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/param.h> | ||||
| #include <dirent.h> | ||||
|  | ||||
| /* | ||||
|  * FIXME: really need to seperate names from the devices since | ||||
|  * multiple names can point to the same device. | ||||
|  */ | ||||
|  | ||||
| struct dev_iter { | ||||
| 	struct hash_node *current; | ||||
| 	struct dev_filter *filter; | ||||
| }; | ||||
|  | ||||
| struct dir_list { | ||||
| 	struct list_head list; | ||||
| 	char dir[0]; | ||||
| }; | ||||
|  | ||||
| static struct { | ||||
| 	struct pool *mem; | ||||
| 	struct hash_table *devices; | ||||
|  | ||||
| 	int has_scanned; | ||||
| 	struct list_head dirs; | ||||
|  | ||||
| } _cache; | ||||
|  | ||||
|  | ||||
| #define _alloc(x) pool_alloc(_cache.mem, (x)) | ||||
| #define _free(x) pool_free(_cache.mem, (x)) | ||||
|  | ||||
| static int _dir_scan(const char *dir); | ||||
|  | ||||
| /* | ||||
|  * return a new path for the destination of the path. | ||||
|  */ | ||||
| static char *_follow_link(const char *path, struct stat *info) | ||||
| { | ||||
| 	char buffer[PATH_MAX + 1]; | ||||
| 	int n; | ||||
| 	n = readlink(path, buffer, sizeof(buffer) - 1); | ||||
|  | ||||
| 	if (n <= 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	buffer[n] = '\0'; | ||||
|  | ||||
| 	if (stat(buffer, info) < 0) { | ||||
| 		log_sys_very_verbose("stat", buffer); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return pool_strdup(_cache.mem, buffer); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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 struct device *_create_dev(const char *path, struct stat *info) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 	char *name = pool_strdup(_cache.mem, path); | ||||
|  | ||||
| 	if (!name) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	_collapse_slashes(name); | ||||
|  | ||||
| 	if (!(dev = _alloc(sizeof(*dev)))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	dev->name = name; | ||||
| 	dev->dev = info->st_rdev; | ||||
| 	return dev; | ||||
|  | ||||
|  bad: | ||||
| 	_free(name); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int _insert(const char *path, int recurse) | ||||
| { | ||||
| 	struct stat info; | ||||
| 	struct device *dev; | ||||
|  | ||||
| 	log_very_verbose("dev-cache adding %s", path); | ||||
|  | ||||
| 	if (stat(path, &info) < 0) { | ||||
| 		log_sys_very_verbose("stat", path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (S_ISDIR(info.st_mode)) { | ||||
| 		if (recurse) | ||||
| 			return _dir_scan(path); | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (S_ISLNK(info.st_mode)) { | ||||
| 		log_debug("%s is a symbolic link, following", path); | ||||
| 		if (!(path = _follow_link(path, &info))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!S_ISBLK(info.st_mode)) { | ||||
| 		log_debug("%s is not a block device", path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dev = _create_dev(path, &info))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	hash_insert(_cache.devices, path, dev); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static char *_join(const char *dir, const char *name) | ||||
| { | ||||
| 	int len = strlen(dir) + strlen(name) + 2; | ||||
| 	char *r = dbg_malloc(len); | ||||
| 	if (r) | ||||
| 		snprintf(r, len, "%s/%s", dir, name); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _dir_scan(const char *dir) | ||||
| { | ||||
| 	int n, dirent_count; | ||||
| 	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))) | ||||
| 				_insert(path, 1); | ||||
|  | ||||
| 			dbg_free(path); | ||||
| 			free(dirent[n]); | ||||
| 		} | ||||
| 		free(dirent); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _full_scan(void) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
|  | ||||
| 	if (_cache.has_scanned) | ||||
| 		return; | ||||
|  | ||||
| 	list_for_each(tmp, &_cache.dirs) { | ||||
| 		struct dir_list *dl = list_entry(tmp, struct dir_list, list); | ||||
| 		_dir_scan(dl->dir); | ||||
| 	} | ||||
|  | ||||
| 	_cache.has_scanned = 1; | ||||
| } | ||||
|  | ||||
| int dev_cache_init(void) | ||||
| { | ||||
| 	if (!(_cache.mem = pool_create(10 * 1024))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(_cache.devices = hash_create(128))) { | ||||
| 		stack; | ||||
| 		pool_destroy(_cache.mem); | ||||
| 		_cache.mem = 0; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	INIT_LIST_HEAD(&_cache.dirs); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void dev_cache_exit(void) | ||||
| { | ||||
| 	pool_destroy(_cache.mem); | ||||
| 	hash_destroy(_cache.devices); | ||||
| } | ||||
|  | ||||
| int dev_cache_add_dir(const char *path) | ||||
| { | ||||
| 	struct dir_list *dl; | ||||
|  | ||||
| 	if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) | ||||
| 		return 0; | ||||
|  | ||||
| 	strcpy(dl->dir, path); | ||||
| 	list_add(&dl->list, &_cache.dirs); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| struct device *dev_cache_get(const char *name, struct dev_filter *f) | ||||
| { | ||||
| 	struct device *d = (struct device *) hash_lookup(_cache.devices, name); | ||||
|  | ||||
| 	if (!d) { | ||||
| 		_insert(name, 0); | ||||
| 		d = (struct device *) hash_lookup(_cache.devices, name); | ||||
| 	} | ||||
|  | ||||
| 	return (d && (!f || f->passes_filter(f, d))) ? d : NULL; | ||||
| } | ||||
|  | ||||
| struct dev_iter *dev_iter_create(struct dev_filter *f) | ||||
| { | ||||
| 	struct dev_iter *di = dbg_malloc(sizeof(*di)); | ||||
|  | ||||
| 	if (!di) | ||||
| 		return NULL; | ||||
|  | ||||
| 	_full_scan(); | ||||
| 	di->current = hash_get_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 = hash_get_data(_cache.devices, iter->current); | ||||
| 	iter->current = hash_get_next(_cache.devices, 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; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										41
									
								
								lib/device/dev-cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/device/dev-cache.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_DEV_CACHE_H | ||||
| #define _LVM_DEV_CACHE_H | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include "lvm-types.h" | ||||
| #include "device.h" | ||||
|  | ||||
| /* | ||||
|  * predicate for devices. | ||||
|  */ | ||||
| struct dev_filter { | ||||
| 	int (*passes_filter)(struct dev_filter *f, struct device *dev); | ||||
| 	void *private; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * The global device cache. | ||||
|  */ | ||||
| int dev_cache_init(void); | ||||
| void dev_cache_exit(void); | ||||
|  | ||||
| int dev_cache_add_dir(const char *path); | ||||
| struct device *dev_cache_get(const char *name, struct dev_filter *f); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Object for iterating through the cache. | ||||
|  */ | ||||
| struct dev_iter; | ||||
| struct dev_iter *dev_iter_create(struct dev_filter *f); | ||||
| void dev_iter_destroy(struct dev_iter *iter); | ||||
| struct device *dev_iter_get(struct dev_iter *iter); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										121
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								lib/device/dev-io.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "device.h" | ||||
| #include "lvm-types.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/mount.h> | ||||
|  | ||||
| int dev_get_size(struct device *dev, uint64_t *size) | ||||
| { | ||||
| 	int fd; | ||||
| 	long s; | ||||
|  | ||||
| 	log_very_verbose("Getting size of %s", dev->name); | ||||
| 	if ((fd = open(dev->name, O_RDONLY)) < 0) { | ||||
| 		log_sys_error("open", dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME: add 64 bit ioctl */ | ||||
| 	if (ioctl(fd, BLKGETSIZE, &s) < 0) { | ||||
| 		log_sys_error("ioctl BLKGETSIZE", dev->name); | ||||
| 		close(fd); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	close(fd); | ||||
| 	*size = (uint64_t) s; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  *  FIXME: factor common code out. | ||||
|  */ | ||||
|  | ||||
| int _read(int fd, void *buf, size_t count) | ||||
| { | ||||
| 	size_t n = 0; | ||||
| 	int tot = 0; | ||||
|  | ||||
| 	while (tot < count) { | ||||
| 		n = read(fd, buf, count - tot); | ||||
|  | ||||
| 		if (n <= 0) | ||||
| 			return tot ? tot : n; | ||||
|  | ||||
| 		tot += n; | ||||
| 		buf += n; | ||||
| 	} | ||||
|  | ||||
| 	return tot; | ||||
| } | ||||
|  | ||||
| int64_t dev_read(struct device *dev, uint64_t offset, | ||||
| 		 int64_t len, void *buffer) | ||||
| { | ||||
| 	int64_t r; | ||||
| 	int fd = open(dev->name, O_RDONLY); | ||||
|  | ||||
| 	if (fd < 0) { | ||||
| 		log_sys_very_verbose("open", dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lseek(fd, offset, SEEK_SET) < 0) { | ||||
| 		log_sys_error("lseek", dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	r = _read(fd, buffer, len); | ||||
| 	close(fd); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int _write(int fd, const void *buf, size_t count) | ||||
| { | ||||
| 	size_t n = 0; | ||||
| 	int tot = 0; | ||||
|  | ||||
| 	while (tot < count) { | ||||
| 		n = write(fd, buf, count - tot); | ||||
|  | ||||
| 		if (n <= 0) | ||||
| 			return tot ? tot : n; | ||||
|  | ||||
| 		tot += n; | ||||
| 		buf += n; | ||||
| 	} | ||||
|  | ||||
| 	return tot; | ||||
| } | ||||
|  | ||||
| int64_t dev_write(struct device *dev, uint64_t offset, | ||||
| 		  int64_t len, void *buffer) | ||||
| { | ||||
| 	int64_t r; | ||||
| 	int fd = open(dev->name, O_WRONLY); | ||||
|  | ||||
| 	if (fd < 0) { | ||||
| 		log_sys_error("open", dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lseek(fd, offset, SEEK_SET) < 0) { | ||||
| 		log_sys_error("lseek", dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	r = _write(fd, buffer, len); | ||||
| 	close(fd); | ||||
| 	return r; | ||||
| } | ||||
							
								
								
									
										214
									
								
								lib/device/device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								lib/device/device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This LVM library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Library General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This LVM library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Library General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Library General Public | ||||
|  * License along with this LVM library; if not, write to the Free | ||||
|  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | ||||
|  * MA 02111-1307, USA | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/mman.h> | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <ctype.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| #include <sys/ioctl.h> | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
| #include "dev-cache.h" | ||||
| #include "metadata.h" | ||||
| #include "device.h" | ||||
|  | ||||
| #include <linux/fs.h> | ||||
| #include <linux/major.h> | ||||
| #include <linux/genhd.h> | ||||
|  | ||||
|  | ||||
|  | ||||
| #if 0 | ||||
| int _get_partition_type(struct dev_filter *filter, struct device *d); | ||||
|  | ||||
| #define MINOR_PART(dm, d) (MINOR((d)->dev) % dev_max_partitions(dm, (d)->dev)) | ||||
|  | ||||
| int is_whole_disk(struct dev_filter *filter, struct device *d) | ||||
| { | ||||
| 	return (MINOR_PART(dm, d)) ? 0 : 1; | ||||
| } | ||||
|  | ||||
| int is_extended_partition(struct dev_mgr *dm, struct device *d) | ||||
| { | ||||
| 	return (MINOR_PART(dm, d) > 4) ? 1 : 0; | ||||
| } | ||||
|  | ||||
| struct device *dev_primary(struct dev_mgr *dm, struct device *d) | ||||
| { | ||||
| 	struct device *ret; | ||||
|  | ||||
| 	ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d)); | ||||
| 	/* FIXME: Needs replacing with a 'refresh' */ | ||||
| 	if (!ret) { | ||||
| 		init_dev_scan(dm); | ||||
| 		ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d)); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
|  | ||||
| } | ||||
|  | ||||
| int partition_type_is_lvm(struct dev_mgr *dm, struct device *d) | ||||
| { | ||||
| 	int pt; | ||||
|  | ||||
| 	pt = _get_partition_type(dm, d); | ||||
|  | ||||
| 	if (!pt) { | ||||
| 		if (is_whole_disk(dm, d)) | ||||
| 			/* FIXME: Overloaded pt=0 in error cases */ | ||||
| 			return 1; | ||||
| 		else { | ||||
| 			log_error | ||||
| 			    ("%s: missing partition table " | ||||
| 			     "on partitioned device", d->name); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (is_whole_disk(dm, d)) { | ||||
| 		log_error("%s: looks to possess partition table", d->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* check part type */ | ||||
| 	if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) { | ||||
| 		log_error("%s: invalid partition type 0x%x " | ||||
| 			  "(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (pt == LVM_PARTITION) { | ||||
| 		log_error | ||||
| 		    ("%s: old LVM partition type found - please change to 0x%x", | ||||
| 		     d->name, LVM_NEW_PARTITION); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int _get_partition_type(struct dev_mgr *dm, struct device *d) | ||||
| { | ||||
| 	int pv_handle = -1; | ||||
| 	struct device *primary; | ||||
| 	ssize_t read_ret; | ||||
| 	ssize_t bytes_read = 0; | ||||
| 	char *buffer; | ||||
| 	unsigned short *s_buffer; | ||||
| 	struct partition *part; | ||||
| 	loff_t offset = 0; | ||||
| 	loff_t extended_offset = 0; | ||||
| 	int part_sought; | ||||
| 	int part_found = 0; | ||||
| 	int first_partition = 1; | ||||
| 	int extended_partition = 0; | ||||
| 	int p; | ||||
|  | ||||
| 	if (!(primary = dev_primary(dm, d))) { | ||||
| 		log_error | ||||
| 		    ("Failed to find main device containing partition %s", | ||||
| 		     d->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(buffer = dbg_malloc(SECTOR_SIZE))) { | ||||
| 		log_error("Failed to allocate partition table buffer"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Get partition table */ | ||||
| 	if ((pv_handle = open(primary->name, O_RDONLY)) < 0) { | ||||
| 		log_error("%s: open failed: %s", primary->name, | ||||
| 			  strerror(errno)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	s_buffer = (unsigned short *) buffer; | ||||
| 	part = (struct partition *) (buffer + 0x1be); | ||||
| 	part_sought = MINOR_PART(dm, d); | ||||
|  | ||||
| 	do { | ||||
| 		bytes_read = 0; | ||||
|  | ||||
| 		if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) { | ||||
| 			log_error("%s: llseek failed: %s", | ||||
| 				  primary->name, strerror(errno)); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		while ((bytes_read < SECTOR_SIZE) && | ||||
| 		       (read_ret = | ||||
| 			read(pv_handle, buffer + bytes_read, | ||||
| 			     SECTOR_SIZE - bytes_read)) != -1) | ||||
| 			bytes_read += read_ret; | ||||
|  | ||||
| 		if (read_ret == -1) { | ||||
| 			log_error("%s: read failed: %s", primary->name, | ||||
| 				  strerror(errno)); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (s_buffer[255] == 0xAA55) { | ||||
| 			if (is_whole_disk(dm, d)) | ||||
| 				return -1; | ||||
| 		} else | ||||
| 			return 0; | ||||
|  | ||||
| 		extended_partition = 0; | ||||
|  | ||||
| 		/* Loop through primary partitions */ | ||||
| 		for (p = 0; p < 4; p++) { | ||||
| 			if (part[p].sys_ind == DOS_EXTENDED_PARTITION || | ||||
| 			    part[p].sys_ind == LINUX_EXTENDED_PARTITION | ||||
| 			    || part[p].sys_ind == WIN98_EXTENDED_PARTITION) { | ||||
| 				extended_partition = 1; | ||||
| 				offset = extended_offset + part[p].start_sect; | ||||
| 				if (extended_offset == 0) | ||||
| 					extended_offset = part[p].start_sect; | ||||
| 				if (first_partition == 1) | ||||
| 					part_found++; | ||||
| 			} else if (first_partition == 1) { | ||||
| 				if (p == part_sought) { | ||||
| 					if (part[p].sys_ind == 0) { | ||||
| 						/* missing primary? */ | ||||
| 						return 0; | ||||
| 					} | ||||
| 				} else | ||||
| 					part_found++; | ||||
| 			} else if (!part[p].sys_ind) | ||||
| 				part_found++; | ||||
|  | ||||
| 			if (part_sought == part_found) | ||||
| 				return part[p].sys_ind; | ||||
|  | ||||
| 		} | ||||
| 		first_partition = 0; | ||||
| 	} | ||||
| 	while (extended_partition == 1); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										42
									
								
								lib/device/device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								lib/device/device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_DEVICE_H | ||||
| #define _LVM_DEVICE_H | ||||
|  | ||||
| #include "lvm-types.h" | ||||
|  | ||||
| /* | ||||
|  * All devices in LVM will be represented by one of these. | ||||
|  * pointer comparisons are valid. | ||||
|  */ | ||||
| struct device { | ||||
| 	char *name; | ||||
| 	dev_t dev; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * All io should use these routines, rather than opening the devices | ||||
|  * by hand.  You do not have to call an open routine.  ATM all io is | ||||
|  * immediately flushed. | ||||
|  */ | ||||
| int dev_get_size(struct device *dev, uint64_t *size); | ||||
| int64_t dev_read(struct device *dev, | ||||
| 		 uint64_t offset, int64_t len, void *buffer); | ||||
| int64_t dev_write(struct device *dev, | ||||
| 		  uint64_t offset, int64_t len, void *buffer); | ||||
|  | ||||
|  | ||||
| static inline int is_lvm_partition(const char *name) { | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| #define LVM_DEFAULT_DIR_PREFIX "/dev/" | ||||
| /* FIXME Allow config file override */ | ||||
| static inline char *lvm_dir_prefix(void) { return LVM_DEFAULT_DIR_PREFIX; } | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										94
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								lib/display/display.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001  Sistina Software | ||||
|  * | ||||
|  * This LVM library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Library General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This LVM library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Library General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Library General Public | ||||
|  * License along with this LVM library; if not, write to the Free | ||||
|  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | ||||
|  * MA 02111-1307, USA | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <string.h> | ||||
| #include "display.h" | ||||
| #include "metadata.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #define SIZE_BUF 128 | ||||
|  | ||||
| char *display_size(unsigned long long size, size_len_t sl) | ||||
| { | ||||
| 	int s; | ||||
| 	ulong byte = 1024 * 1024 * 1024; | ||||
| 	char *size_buf = NULL; | ||||
| 	char *size_str[][2] = { | ||||
| 		{"Terabyte", "TB"}, | ||||
| 		{"Gigabyte", "GB"}, | ||||
| 		{"Megabyte", "MB"}, | ||||
| 		{"Kilobyte", "KB"}, | ||||
| 		{"", ""} | ||||
| 	}; | ||||
|  | ||||
| 	if (!(size_buf = dbg_malloc(SIZE_BUF))) { | ||||
| 		log_error("no memory for size display buffer"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (size == 0LL) | ||||
| 		sprintf(size_buf, "0"); | ||||
| 	else { | ||||
| 		s = 0; | ||||
| 		while (size_str[s] && size < byte) | ||||
| 			s++, byte /= 1024; | ||||
| 		snprintf(size_buf, SIZE_BUF - 1, | ||||
| 			 "%.2f %s", (float) size / byte, size_str[s][sl]); | ||||
| 	} | ||||
|  | ||||
| 	/* Caller to deallocate */ | ||||
| 	return size_buf; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * FIXME: this function is badly named, it doesn't display the data it | ||||
|  * creates a new uuid string with -'s in it.  It would be better if | ||||
|  * the destination was passed in as well. EJT | ||||
|  */ | ||||
| char *display_uuid(char *uuidstr) { | ||||
| 	int i, j; | ||||
| 	char *uuid; | ||||
|  | ||||
| 	if ((!uuidstr) || !(uuid = dbg_malloc(NAME_LEN))) { | ||||
| 		log_error("no memory for uuid display buffer"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	memset(uuid, 0, NAME_LEN); | ||||
|  | ||||
| 	i = 6; | ||||
| 	memcpy(uuid, uuidstr, i); | ||||
| 	uuidstr += i; | ||||
|  | ||||
| 	for (j = 0; j < 6; j++) { | ||||
| 		uuid[i++] = '-'; | ||||
| 		memcpy(&uuid[i], uuidstr, 4); | ||||
| 		uuidstr += 4; | ||||
| 		i += 4; | ||||
| 	} | ||||
|  | ||||
| 	memcpy(&uuid[i], uuidstr, 2); | ||||
|  | ||||
| 	/* Caller must free */ | ||||
| 	return uuid; | ||||
| } | ||||
|  | ||||
							
								
								
									
										31
									
								
								lib/display/display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/display/display.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| /* | ||||
|  * 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 | ||||
|  | ||||
| typedef	enum {SIZE_LONG=0, SIZE_SHORT=1} size_len_t; | ||||
|  | ||||
| /* Specify size in KB */ | ||||
| char *display_size(unsigned long long size, size_len_t sl); | ||||
|  | ||||
| char *display_uuid(char *uuidstr); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										278
									
								
								lib/display/metadata.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								lib/display/metadata.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * This LVM library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Library General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This LVM library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Library General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Library General Public | ||||
|  * License along with this LVM library; if not, write to the Free | ||||
|  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | ||||
|  * MA 02111-1307, USA | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <string.h> | ||||
| #include "display.h" | ||||
| #include "metadata.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
|  | ||||
| void pv_display_colons(pv_t * pv) | ||||
| { | ||||
| 	char *uuid; | ||||
|  | ||||
| 	if (!pv) | ||||
| 		return; | ||||
|  | ||||
| 	uuid = display_uuid(pv->pv_uuid); | ||||
|  | ||||
| 	printf("%s:%s:%d:%d:%d:%d:%d:%d:%d:%d:%d:%s\n", | ||||
| 	       pv->pv_name, | ||||
| 	       pv->vg_name, | ||||
| 	       pv->pv_size, | ||||
| 	       pv->pv_number, | ||||
| 	       pv->pv_status, | ||||
| 	       pv->pv_allocatable, | ||||
| 	       pv->lv_cur, | ||||
| 	       pv->pe_size / 2, | ||||
| 	       pv->pe_total, | ||||
| 	       pv->pe_total - pv->pe_allocated, | ||||
| 	       pv->pe_allocated, uuid ? uuid : "none"); | ||||
|  | ||||
| 	dbg_free(uuid); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| void pv_display_full(pv_t * pv) | ||||
| { | ||||
| 	ulong pe_free; | ||||
| 	char *size = NULL; | ||||
| 	char *uuid; | ||||
|  | ||||
| 	if (!pv) | ||||
| 		return; | ||||
|  | ||||
| 	uuid = display_uuid(pv->pv_uuid); | ||||
|  | ||||
| 	printf("--- %sPhysical volume ---\n", pv->pe_size ? "" : "NEW "); | ||||
| 	printf("PV Name               %s\n", pv->pv_name); | ||||
| 	printf("VG Name               %s\n", pv->vg_name); | ||||
|  | ||||
| 	size = display_size(pv->pv_size / 2, SIZE_SHORT); | ||||
| 	printf("PV Size               %s", size); | ||||
| 	dbg_free(size); | ||||
|  | ||||
| 	if (pv->pe_size && pv->pe_total) { | ||||
| 		size = | ||||
| 		    display_size((pv->pv_size - pv->pe_size * pv->pe_total) / 2, | ||||
| 				 SIZE_SHORT); | ||||
| 		printf(" / NOT usable %s ", size); | ||||
| 		dbg_free(size); | ||||
|  | ||||
| 		size = | ||||
| 		    display_size( | ||||
| 				 (pv->pe_on_disk.base + | ||||
| 				  pv->pe_total * sizeof (pe_disk_t)) / 1024, | ||||
| 				 SIZE_SHORT); | ||||
| 		printf("[LVM: %s]", size); | ||||
| 		dbg_free(size); | ||||
| 	} | ||||
|  | ||||
| 	printf("\n"); | ||||
|  | ||||
| 	printf("PV#                   %u\n", pv->pv_number); | ||||
| 	printf("PV Status             %savailable\n", | ||||
| 	       (pv->pv_status & PV_ACTIVE) ? "" : "NOT "); | ||||
|  | ||||
| 	printf("Allocatable           "); | ||||
| 	pe_free = pv->pe_total - pv->pe_allocated; | ||||
| 	if (pv->pe_total > 0 && (pv->pv_allocatable & PV_ALLOCATABLE)) { | ||||
| 		printf("yes"); | ||||
| 		if (pe_free == 0 && pv->pe_total > 0) | ||||
| 			printf(" (but full)"); | ||||
| 		printf("\n"); | ||||
| 	} else | ||||
| 		printf("NO\n"); | ||||
|  | ||||
| 	printf("Cur LV                %u\n", pv->lv_cur); | ||||
| 	printf("PE Size (KByte)       %u\n", pv->pe_size / 2); | ||||
| 	printf("Total PE              %u\n", pv->pe_total); | ||||
| 	printf("Free PE               %lu\n", pe_free); | ||||
| 	printf("Allocated PE          %u\n", pv->pe_allocated); | ||||
|  | ||||
| #ifdef LVM_FUTURE | ||||
| 	printf("Stale PE              %u\n", pv->pe_stale); | ||||
| #endif | ||||
|  | ||||
| 	printf("PV UUID               %s\n", uuid ? uuid : "none"); | ||||
| 	printf("\n"); | ||||
|  | ||||
| 	dbg_free(uuid); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| /******* | ||||
| void pv_display_short(pv_t * pv) | ||||
| { | ||||
|  | ||||
| 	if (pv != NULL) { | ||||
| 		printf("PV Name (#)           %s (%u)\n", pv->pv_name, | ||||
| 		       pv->pv_number); | ||||
| 		printf("PV Status             "); | ||||
| 		if (!(pv->pv_status & PV_ACTIVE)) | ||||
| 			printf("NOT "); | ||||
| 		printf("available / "); | ||||
| 		if (!(pv->pv_allocatable & PV_ALLOCATABLE)) | ||||
| 			printf("NOT "); | ||||
| 		printf("allocatable\n"); | ||||
| 		printf("Total PE / Free PE    %u / %u\n", | ||||
| 		       pv->pe_total, pv->pe_total - pv->pe_allocated); | ||||
| 	} | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| void pv_display_pe(pv_t * pv, pe_disk_t * pe) | ||||
| { | ||||
| 	int p; | ||||
|  | ||||
| 	for (p = 0; p < pv->pe_total; p++) | ||||
| 		printf("pe#: %4d  vg: %s  lv: %d  le: %d\n", | ||||
| 		       p, pv->vg_name, pe[p].lv_num, pe[p].le_num); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
| *******/ | ||||
|  | ||||
| void pv_display_pe_text(pv_t * pv, pe_disk_t * pe, lv_disk_t * lvs) | ||||
| { | ||||
| 	int flag = 0; | ||||
| 	int lv_num_last = 0; | ||||
| 	int p = 0; | ||||
| 	int pe_free = 0; | ||||
| 	int *pe_this_count = NULL; | ||||
| 	int pt = 0; | ||||
| 	int pt_count = 0; | ||||
| 	lv_disk_t *lv; | ||||
| 	char *lv_name_this = NULL; | ||||
| 	char *lv_names = NULL; | ||||
| 	char *lv_names_sav = NULL; | ||||
| 	pe_disk_t *pe_this = NULL; | ||||
|  | ||||
| 	if ((pe_this = dbg_malloc(pv->pe_total * sizeof (pe_disk_t))) == NULL) { | ||||
| 		log_error("pe_this allocation failed"); | ||||
| 		goto pv_display_pe_text_out; | ||||
| 	} | ||||
|  | ||||
| 	if ((pe_this_count = dbg_malloc(pv->pe_total * sizeof (int))) == NULL) { | ||||
| 		log_error("pe_this_count allocation failed"); | ||||
| 		goto pv_display_pe_text_out; | ||||
| 	} | ||||
|  | ||||
| 	memset(pe_this, 0, pv->pe_total * sizeof (pe_disk_t)); | ||||
| 	memset(pe_this_count, 0, pv->pe_total * sizeof (int)); | ||||
|  | ||||
| 	/* get PE and LE summaries */ | ||||
| 	pt_count = 0; | ||||
| 	for (p = pt_count; p < pv->pe_total; p++) { | ||||
| 		if (pe[p].lv_num != 0) { | ||||
| 			flag = 0; | ||||
| 			for (pt = 0; pt < pt_count; pt++) { | ||||
| 				if (pe_this[pt].lv_num == pe[p].lv_num) { | ||||
| 					flag = 1; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			if (flag == 0) { | ||||
| 				pe_this[pt_count].lv_num = pe[p].lv_num; | ||||
| 				for (pt = 0; pt < pv->pe_total; pt++) | ||||
| 					if (pe_this[pt_count].lv_num == | ||||
| 					    pe[pt].lv_num) | ||||
| 						    pe_this_count[pt_count]++; | ||||
| 				pt_count++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	lv = lvs; | ||||
| 	printf("   --- Distribution of physical volume ---\n" | ||||
| 	       "   LV Name                   LE of LV  PE for LV\n"); | ||||
| 	for (pt = 0; pt < pt_count; pt++) { | ||||
| 		printf("   %-25s ", lv->lv_name); | ||||
| 		if (strlen(lv->lv_name) > 25) | ||||
| 			printf("\n                             "); | ||||
| 		printf("%-8u  %-8d\n", | ||||
| 		       lv->lv_allocated_le, | ||||
| 		       pe_this_count[pt]); | ||||
| 		if (pe_this[pt].lv_num > lv_num_last) { | ||||
| 			lv_num_last = pe_this[pt].lv_num; | ||||
| 			lv_names_sav = lv_names; | ||||
| 			if ((lv_names = dbg_realloc(lv_names, | ||||
| 						    lv_num_last * NAME_LEN)) == | ||||
| 			    NULL) { | ||||
| 				log_error("realloc error in %s [line %d]", | ||||
| 					  __FILE__, __LINE__); | ||||
| 				goto pv_display_pe_text_out; | ||||
| 			} else | ||||
| 				lv_names_sav = NULL; | ||||
| 		} | ||||
| 		strcpy(&lv_names[(pe_this[pt].lv_num - 1) * NAME_LEN], | ||||
| 		       lv->lv_name); | ||||
| 		lv++; | ||||
| 	} | ||||
|  | ||||
| 	printf("\n   --- Physical extents ---\n" | ||||
| 	       "   PE    LV                        LE      Disk sector\n"); | ||||
| 	pe_free = -1; | ||||
| 	for (p = 0; p < pv->pe_total; p++) { | ||||
| 		if (pe[p].lv_num != 0) { | ||||
| 			if (pe_free > -1) { | ||||
| 				pv_display_pe_free(pe_free, p); | ||||
| 				pe_free = -1; | ||||
| 			} | ||||
| 			lv_name_this = &lv_names[(pe[p].lv_num - 1) * NAME_LEN]; | ||||
| 			printf("   %05d %-25s ", p, lv_name_this); | ||||
| 			if (strlen(lv_name_this) > 25) | ||||
| 				printf("\n                                  "); | ||||
| 			printf("%05d   %ld\n", pe[p].le_num, | ||||
| 			       get_pe_offset(p, pv)); | ||||
|  | ||||
| 		} else if (pe_free == -1) | ||||
| 			pe_free = p; | ||||
| 	} | ||||
|  | ||||
| 	if (pe_free > 0) | ||||
| 		pv_display_pe_free(pe_free, p); | ||||
|  | ||||
|       pv_display_pe_text_out: | ||||
| 	if (lv_names != NULL) | ||||
| 		dbg_free(lv_names); | ||||
| 	else if (lv_names_sav != NULL) | ||||
| 		dbg_free(lv_names_sav); | ||||
| 	if (pe_this != NULL) | ||||
| 		dbg_free(pe_this); | ||||
| 	if (pe_this_count != NULL) | ||||
| 		dbg_free(pe_this_count); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| void pv_display_pe_free(int pe_free, int p) | ||||
| { | ||||
| 	printf("   %05d free\n", pe_free); | ||||
|  | ||||
| 	if (p - pe_free > 1) | ||||
| 		printf("   .....\n   %05d free\n", p - 1); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
							
								
								
									
										39
									
								
								lib/display/metadata.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								lib/display/metadata.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| /* | ||||
|  * 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_METADATA_H | ||||
| #define _LVM_DISPLAY_METADATA_H | ||||
|  | ||||
| #if 0 | ||||
| #include "metadata/metadata.h" | ||||
|  | ||||
| void pv_display_colons(pv_t * pv); | ||||
| void pv_display_full(pv_t * pv); | ||||
| void pv_show_short(pv_t * pv); | ||||
| void pv_display_pe(pv_t * pv, pe_disk_t * pe); | ||||
| void pv_display_pe_free(int pe_free, int p); | ||||
| void pv_display_pe_text(pv_t * pv, pe_disk_t * pe, lv_disk_t * lvs); | ||||
|  | ||||
| static inline unsigned long get_pe_offset(ulong p, pv_t *pv) | ||||
| { | ||||
|         return pv->pe_start + (p * pv->pe_size); | ||||
| } | ||||
| #endif | ||||
| #endif | ||||
							
								
								
									
										154
									
								
								lib/filters/filter.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								lib/filters/filter.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * lvm is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2, or (at your option) | ||||
|  * any later version. | ||||
|  * | ||||
|  * lvm is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with GNU CC; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
| #include "dev-cache.h" | ||||
| #include "filter.h" | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <dirent.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
| #include <string.h> | ||||
| #include <linux/kdev_t.h> | ||||
|  | ||||
| #define NUMBER_OF_MAJORS 256 | ||||
|  | ||||
| typedef struct { | ||||
| 	char *name; | ||||
| 	int max_partitions; | ||||
| } device_info_t; | ||||
|  | ||||
| static device_info_t device_info[] = { | ||||
| 	{"ide", 16},		/* IDE disk */ | ||||
| 	{"sd", 16},		/* SCSI disk */ | ||||
| 	{"md", 16},		/* Multiple Disk driver (SoftRAID) */ | ||||
| 	{"loop", 16},		/* Loop device */ | ||||
| 	{"dasd", 4},		/* DASD disk (IBM S/390, zSeries) */ | ||||
| 	{"dac960", 8},		/* DAC960 */ | ||||
| 	{"nbd", 16},		/* Network Block Device */ | ||||
| 	{"ida", 16},		/* Compaq SMART2 */ | ||||
| 	{"cciss", 16},		/* Compaq CCISS array */ | ||||
| 	{"ubd", 16},		/* User-mode virtual block device */ | ||||
| 	{NULL, 0} | ||||
| }; | ||||
|  | ||||
| static int *scan_proc_dev(void); | ||||
|  | ||||
| static int passes_config_device_filter(struct dev_filter *f, struct device *dev) | ||||
| { | ||||
| 	/* FIXME Check against config file scan/reject entries */ | ||||
|  | ||||
| 	/* Is this a recognised device type? */ | ||||
| 	if (!(((int *) f->private)[MAJOR(dev->dev)])) | ||||
| 		return 0; | ||||
| 	else | ||||
| 		return 1; | ||||
| } | ||||
|  | ||||
| struct dev_filter *config_filter_create() | ||||
| { | ||||
| 	struct dev_filter *f; | ||||
|  | ||||
| 	if (!(f = dbg_malloc(sizeof (struct dev_filter)))) { | ||||
| 		log_error("lvm_v1_filter allocation failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	f->passes_filter = passes_config_device_filter; | ||||
|  | ||||
| 	if (!(f->private = scan_proc_dev())) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return f; | ||||
| } | ||||
|  | ||||
| void config_filter_destroy(struct dev_filter *f) | ||||
| { | ||||
| 	dbg_free(f->private); | ||||
| 	dbg_free(f); | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| static int *scan_proc_dev(void) | ||||
| { | ||||
| 	char line[80]; | ||||
| 	FILE *procdevices = NULL; | ||||
| 	int ret = 0; | ||||
| 	int i, j = 0; | ||||
| 	int line_maj = 0; | ||||
| 	int blocksection = 0; | ||||
| 	int dev_len = 0; | ||||
|  | ||||
| 	int *max_partitions_by_major; | ||||
|  | ||||
| 	if (!(max_partitions_by_major = dbg_malloc(sizeof (int) * NUMBER_OF_MAJORS))) { | ||||
| 		log_error("Filter failed to allocate max_partitions_by_major"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(procdevices = fopen("/proc/devices", "r"))) { | ||||
| 		log_error("Failed to open /proc/devices: %s", strerror(errno)); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	memset(max_partitions_by_major, 0, sizeof (int) * NUMBER_OF_MAJORS); | ||||
| 	while (fgets(line, 80, procdevices) != 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++; | ||||
|  | ||||
| 		/* Go through the valid device names and if there is a | ||||
|    			match store max number of partitions */ | ||||
| 		for (j = 0; device_info[j].name != NULL; j++) { | ||||
|  | ||||
| 			dev_len = strlen(device_info[j].name); | ||||
| 			if (dev_len <= strlen(line + i) | ||||
| 			    && !strncmp(device_info[j].name, line + i, dev_len) | ||||
| 			    && (line_maj < NUMBER_OF_MAJORS)) { | ||||
| 				max_partitions_by_major[line_maj] = | ||||
| 				    device_info[j].max_partitions; | ||||
| 				ret++; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	fclose(procdevices); | ||||
| 	return max_partitions_by_major; | ||||
| } | ||||
							
								
								
									
										29
									
								
								lib/filters/filter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								lib/filters/filter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software | ||||
|  * | ||||
|  * lvm is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2, or (at your option) | ||||
|  * any later version. | ||||
|  * | ||||
|  * lvm is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with GNU CC; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_FILTER_H | ||||
| #define _LVM_FILTER_H | ||||
|  | ||||
| struct dev_filter *config_filter_create(); | ||||
|  | ||||
| void config_filter_destroy(struct dev_filter *f); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										481
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										481
									
								
								lib/format1/disk-rep.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,481 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "disk-rep.h" | ||||
| #include "pool.h" | ||||
| #include "xlate.h" | ||||
| #include "log.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_pv(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_lv(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_vg(struct vg_disk *disk) | ||||
| { | ||||
|         xx32(vg_number); | ||||
|         xx32(vg_access); | ||||
|         xx32(vg_status); | ||||
|         xx32(lv_max); | ||||
|         xx32(lv_cur); | ||||
|         xx32(lv_open); | ||||
|         xx32(pv_max); | ||||
|         xx32(pv_cur); | ||||
|         xx32(pv_act); | ||||
|         xx32(dummy); | ||||
|         xx32(vgda); | ||||
|         xx32(pe_size); | ||||
|         xx32(pe_total); | ||||
|         xx32(pe_allocated); | ||||
|         xx32(pvg_total); | ||||
| } | ||||
|  | ||||
| static void _xlate_extents(struct pe_disk *extents, int count) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < count; i++) { | ||||
| 		extents[i].lv_num = xlate16(extents[i].lv_num); | ||||
| 		extents[i].le_num = xlate16(extents[i].le_num); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Handle both minor metadata formats. | ||||
|  */ | ||||
| static int _munge_formats(struct pv_disk *pvd) | ||||
| { | ||||
| 	uint32_t pe_start; | ||||
|  | ||||
| 	switch (pvd->version) { | ||||
| 	case 1: | ||||
| 		pvd->pe_start = ((pvd->pe_on_disk.base + | ||||
| 				  pvd->pe_on_disk.size) / SECTOR_SIZE); | ||||
| 		break; | ||||
|  | ||||
| 	case 2: | ||||
| 		pvd->version = 1; | ||||
| 		pe_start = pvd->pe_start * SECTOR_SIZE; | ||||
| 		pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base; | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_pv(struct disk_list *data) | ||||
| { | ||||
| 	struct pv_disk *pvd = &data->pv; | ||||
| 	if (dev_read(data->dev, 0, sizeof(*pvd), pvd) != sizeof(*pvd)) | ||||
| 		fail; | ||||
| 	_xlate_pv(pvd); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_lv(struct device *dev, ulong pos, struct lv_disk *disk) | ||||
| { | ||||
| 	if (dev_read(dev, pos, sizeof(*disk), disk) != sizeof(*disk)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_lv(disk); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_vg(struct disk_list *data) | ||||
| { | ||||
| 	struct vg_disk *vgd = &data->vg; | ||||
| 	unsigned long pos = data->pv.vg_on_disk.base; | ||||
| 	if (dev_read(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_vg(vgd); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_uuids(struct disk_list *data) | ||||
| { | ||||
| 	int num_read = 0; | ||||
| 	struct uuid_list *ul; | ||||
| 	char buffer[NAME_LEN]; | ||||
| 	ulong pos = data->pv.pv_uuidlist_on_disk.base; | ||||
| 	ulong end = pos + data->pv.pv_uuidlist_on_disk.size; | ||||
|  | ||||
| 	while(pos < end && num_read < data->vg.pv_cur) { | ||||
| 		if (dev_read(data->dev, pos, sizeof(buffer), buffer) != | ||||
| 		    sizeof(buffer)) | ||||
| 			fail; | ||||
|  | ||||
| 		if (!(ul = pool_alloc(data->mem, sizeof(*ul)))) | ||||
| 			fail; | ||||
|  | ||||
| 		memcpy(ul->uuid, buffer, NAME_LEN); | ||||
| 		ul->uuid[NAME_LEN] = '\0'; | ||||
|  | ||||
| 		list_add(&ul->list, &data->uuids); | ||||
|  | ||||
| 		pos += NAME_LEN; | ||||
| 		num_read++; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _check_lv(struct lv_disk *lvd) | ||||
| { | ||||
| 	/* FIXME: add more checks */ | ||||
| 	if (lvd->lv_name[0] == '\0') { | ||||
| 		log_debug("lv has no name"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_lvs(struct disk_list *data) | ||||
| { | ||||
| 	int i; | ||||
| 	unsigned long pos; | ||||
| 	struct lvd_list *ll; | ||||
|  | ||||
| 	for(i = 0; i < data->vg.lv_cur; i++) { | ||||
| 		pos = data->pv.lv_on_disk.base + (i * sizeof(struct lv_disk)); | ||||
| 		ll = pool_alloc(data->mem, sizeof(*ll)); | ||||
|  | ||||
| 		if (!ll) | ||||
| 			fail; | ||||
|  | ||||
| 		if (!_read_lv(data->dev, pos, &ll->lv)) | ||||
| 			fail; | ||||
|  | ||||
| 		if (!_check_lv(&ll->lv)) | ||||
| 			fail; | ||||
|  | ||||
| 		list_add(&ll->list, &data->lvs); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _read_extents(struct disk_list *data) | ||||
| { | ||||
| 	size_t len = sizeof(struct pe_disk) * data->pv.pe_total; | ||||
| 	struct pe_disk *extents = pool_alloc(data->mem, len); | ||||
| 	unsigned long pos = data->pv.pe_on_disk.base; | ||||
|  | ||||
| 	if (!extents) | ||||
| 		fail; | ||||
|  | ||||
| 	if (dev_read(data->dev, pos, len, extents) != len) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_extents(extents, data->pv.pe_total); | ||||
| 	data->extents = extents; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| struct disk_list *read_pv(struct device *dev, struct pool *mem, | ||||
| 			  const char *vg_name) | ||||
| { | ||||
| 	struct disk_list *data = pool_alloc(mem, sizeof(*data)); | ||||
| 	data->dev = dev; | ||||
| 	data->mem = mem; | ||||
| 	INIT_LIST_HEAD(&data->uuids); | ||||
| 	INIT_LIST_HEAD(&data->lvs); | ||||
|  | ||||
| 	if (!_read_pv(data)) { | ||||
| 		log_debug("Failed to read PV data from %s", dev->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (data->pv.id[0] != 'H' || data->pv.id[1] != 'M') { | ||||
| 		log_very_verbose("%s does not have a valid PV identifier", | ||||
| 			 dev->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_munge_formats(&data->pv)) { | ||||
| 		log_very_verbose("Unknown metadata version %d found on %s", | ||||
| 			    data->pv.version, dev->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * is it an orphan ? | ||||
| 	 */ | ||||
| 	if (data->pv.vg_name == '\0') { | ||||
| 		log_very_verbose("%s is not a member of any VG", dev->name); | ||||
| 		return data; | ||||
| 	} | ||||
|  | ||||
| 	if (vg_name && strcmp(vg_name, data->pv.vg_name)) { | ||||
| 		log_very_verbose("%s is not a member of the VG %s", | ||||
| 			 dev->name, vg_name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_vg(data)) { | ||||
| 		log_error("Failed to read VG data from PV (%s)", dev->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_uuids(data)) { | ||||
| 		log_error("Failed to read PV uuid list from %s", dev->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_lvs(data)) { | ||||
| 		log_error("Failed to read LV's from %s", dev->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!_read_extents(data)) { | ||||
| 		log_error("Failed to read extents from %s", dev->name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	return data; | ||||
|  | ||||
|  bad: | ||||
| 	pool_free(data->mem, data); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Build a list of pv_d's structures, allocated | ||||
|  * from mem.  We keep track of the first object | ||||
|  * allocated form the pool so we can free off all | ||||
|  * the memory if something goes wrong. | ||||
|  */ | ||||
| int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter, | ||||
| 		   struct pool *mem, struct list_head *head) | ||||
| { | ||||
| 	struct dev_iter *iter = dev_iter_create(filter); | ||||
| 	struct device *dev; | ||||
| 	struct disk_list *data = NULL; | ||||
|  | ||||
| 	for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) { | ||||
| 		if ((data = read_pv(dev, mem, vg_name))) | ||||
| 			list_add(&data->list, head); | ||||
| 	} | ||||
| 	dev_iter_destroy(iter); | ||||
|  | ||||
| 	if (list_empty(head)) | ||||
| 		return 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int _write_vg(struct disk_list *data) | ||||
| { | ||||
| 	struct vg_disk *vgd = &data->vg; | ||||
| 	unsigned long pos = data->pv.vg_on_disk.base; | ||||
|  | ||||
| 	_xlate_vg(vgd); | ||||
| 	if (dev_write(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_vg(vgd); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_uuids(struct disk_list *data) | ||||
| { | ||||
| 	struct uuid_list *ul; | ||||
| 	struct list_head *tmp; | ||||
| 	ulong pos = data->pv.pv_uuidlist_on_disk.base; | ||||
| 	ulong end = pos + data->pv.pv_uuidlist_on_disk.size; | ||||
|  | ||||
| 	list_for_each(tmp, &data->uuids) { | ||||
| 		if (pos >= end) { | ||||
| 			log_error("Too many uuids to fit on %s", | ||||
| 				data->dev->name); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		ul = list_entry(tmp, struct uuid_list, list); | ||||
| 		if (dev_write(data->dev, pos, NAME_LEN, ul->uuid) != NAME_LEN) | ||||
| 			fail; | ||||
|  | ||||
| 		pos += NAME_LEN; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_lv(struct device *dev, ulong pos, struct lv_disk *disk) | ||||
| { | ||||
| 	_xlate_lv(disk); | ||||
| 	if (dev_write(dev, pos, sizeof(*disk), disk) != sizeof(*disk)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_lv(disk); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_lvs(struct disk_list *data) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	unsigned long pos; | ||||
|  | ||||
| 	list_for_each(tmp, &data->lvs) { | ||||
| 		struct lvd_list *ll = list_entry(tmp, struct lvd_list, list); | ||||
| 		pos = data->pv.lv_on_disk.base; | ||||
|  | ||||
| 		if (!_write_lv(data->dev, pos, &ll->lv)) | ||||
| 			fail; | ||||
|  | ||||
| 		pos += sizeof(struct lv_disk); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_extents(struct disk_list *data) | ||||
| { | ||||
| 	size_t len = sizeof(struct pe_disk) * data->pv.pe_total; | ||||
| 	struct pe_disk *extents = data->extents; | ||||
| 	unsigned long pos = data->pv.pe_on_disk.base; | ||||
|  | ||||
| 	_xlate_extents(extents, data->pv.pe_total); | ||||
| 	if (dev_write(data->dev, pos, len, extents) != len) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_extents(extents, data->pv.pe_total); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_pv(struct disk_list *data) | ||||
| { | ||||
| 	struct pv_disk *disk = &data->pv; | ||||
|  | ||||
| 	_xlate_pv(disk); | ||||
| 	if (dev_write(data->dev, 0, sizeof(*disk), disk) != sizeof(*disk)) | ||||
| 		fail; | ||||
|  | ||||
| 	_xlate_pv(disk); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _write_all_pv(struct disk_list *data) | ||||
| { | ||||
| 	const char *pv_name = data->dev->name; | ||||
|  | ||||
| 	if (!_write_pv(data)) { | ||||
| 		log_error("Failed to write PV structure onto %s", pv_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Stop here for orphan pv's. | ||||
| 	 */ | ||||
| 	if (data->pv.vg_name[0] == '\0') | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!_write_vg(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; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Writes all the given pv's to disk.  Does very | ||||
|  * little sanity checking, so make sure correct | ||||
|  * data is passed to here. | ||||
|  */ | ||||
| int write_pvs(struct list_head *pvs) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct disk_list *dl; | ||||
|  | ||||
| 	list_for_each(tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		if (!(_write_all_pv(dl))) | ||||
| 			fail; | ||||
|  | ||||
| 		log_debug("Successfully wrote data to %s", dl->dev->name); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										234
									
								
								lib/format1/disk-rep.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								lib/format1/disk-rep.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef DISK_REP_FORMAT1_H | ||||
| #define DISK_REP_FORMAT1_H | ||||
|  | ||||
| #include "lvm-types.h" | ||||
| #include "metadata.h" | ||||
| #include "pool.h" | ||||
|  | ||||
|  | ||||
| #define SECTOR_SIZE 512 | ||||
|  | ||||
| #define MAX_PV 256 | ||||
| #define MAX_LV 256 | ||||
| #define MAX_VG 99 | ||||
|  | ||||
| #define MIN_PE_SIZE ( 8192L / SECTOR_SIZE)     /* 8 KB in sectors */ | ||||
| #define MAX_PE_SIZE ( 16L * 1024L * 1024L / SECTOR_SIZE * 1024) | ||||
| #define PE_SIZE_PV_SIZE_REL 5   /* PV size must be at least 5 times PE size */ | ||||
|  | ||||
| #define UNMAPPED_EXTENT 0 | ||||
|  | ||||
| /* volume group */ | ||||
| #define	VG_ACTIVE            0x01	/* vg_status */ | ||||
| #define	VG_EXPORTED          0x02	/*     "     */ | ||||
| #define	VG_EXTENDABLE        0x04	/*     "     */ | ||||
|  | ||||
| #define	VG_READ              0x01	/* vg_access */ | ||||
| #define	VG_WRITE             0x02	/*     "     */ | ||||
| #define	VG_CLUSTERED         0x04	/*     "     */ | ||||
| #define	VG_SHARED            0x08	/*     "     */ | ||||
|  | ||||
| /* logical volume */ | ||||
| #define	LV_ACTIVE            0x01	/* lv_status */ | ||||
| #define	LV_SPINDOWN          0x02	/*     "     */ | ||||
|  | ||||
| #define	LV_READ              0x01	/* lv_access */ | ||||
| #define	LV_WRITE             0x02	/*     "     */ | ||||
| #define	LV_SNAPSHOT          0x04	/*     "     */ | ||||
| #define	LV_SNAPSHOT_ORG      0x08	/*     "     */ | ||||
|  | ||||
| #define	LV_BADBLOCK_ON       0x01	/* lv_badblock */ | ||||
|  | ||||
| #define	LV_STRICT            0x01	/* lv_allocation */ | ||||
| #define	LV_CONTIGUOUS        0x02	/*       "       */ | ||||
|  | ||||
| /* physical volume */ | ||||
| #define	PV_ACTIVE            0x01	/* pv_status */ | ||||
| #define	PV_ALLOCATABLE       0x02	/* pv_allocatable */ | ||||
|  | ||||
|  | ||||
| struct data_area { | ||||
| 	uint32_t base; | ||||
| 	uint32_t size; | ||||
| }; | ||||
|  | ||||
| struct pv_disk { | ||||
|         uint8_t id[2]; | ||||
|         uint16_t version;               /* lvm version */ | ||||
|         struct data_area pv_on_disk; | ||||
|         struct data_area vg_on_disk; | ||||
|         struct data_area pv_uuidlist_on_disk; | ||||
|         struct data_area lv_on_disk; | ||||
|         struct data_area pe_on_disk; | ||||
|         uint8_t pv_uuid[NAME_LEN]; | ||||
|         uint8_t vg_name[NAME_LEN]; | ||||
|         uint8_t system_id[NAME_LEN];    /* for vgexport/vgimport */ | ||||
|         uint32_t pv_major; | ||||
|         uint32_t pv_number; | ||||
|         uint32_t pv_status; | ||||
|         uint32_t pv_allocatable; | ||||
|         uint32_t pv_size; | ||||
|         uint32_t lv_cur; | ||||
|         uint32_t pe_size; | ||||
|         uint32_t pe_total; | ||||
|         uint32_t pe_allocated; | ||||
|  | ||||
| 	/* only present on version == 2 pv's */ | ||||
| 	uint32_t pe_start; | ||||
| }; | ||||
|  | ||||
| struct lv_disk { | ||||
|         uint8_t lv_name[NAME_LEN]; | ||||
|         uint8_t vg_name[NAME_LEN]; | ||||
|         uint32_t lv_access; | ||||
|         uint32_t lv_status; | ||||
|         uint32_t lv_open; | ||||
|         uint32_t lv_dev; | ||||
|         uint32_t lv_number; | ||||
|         uint32_t lv_mirror_copies; /* for future use */ | ||||
|         uint32_t lv_recovery;      /*       "        */ | ||||
|         uint32_t lv_schedule;      /*       "        */ | ||||
|         uint32_t lv_size; | ||||
|         uint32_t lv_snapshot_minor; /* minor number of original */ | ||||
|         uint16_t lv_chunk_size;     /* chunk size of snapshot */ | ||||
|         uint16_t dummy; | ||||
|         uint32_t lv_allocated_le; | ||||
|         uint32_t lv_stripes; | ||||
|         uint32_t lv_stripesize; | ||||
|         uint32_t lv_badblock;   /* for future use */ | ||||
|         uint32_t lv_allocation; | ||||
|         uint32_t lv_io_timeout; /* for future use */ | ||||
|         uint32_t lv_read_ahead; | ||||
| }; | ||||
|  | ||||
| struct vg_disk { | ||||
|         uint8_t vg_uuid[ID_LEN]; /* volume group UUID */ | ||||
|         uint8_t vg_name_dummy[NAME_LEN - ID_LEN]; /* rest of v1 VG name */ | ||||
|         uint32_t vg_number;     /* volume group number */ | ||||
|         uint32_t vg_access;     /* read/write */ | ||||
|         uint32_t vg_status;     /* active or not */ | ||||
|         uint32_t lv_max;	/* maximum logical volumes */ | ||||
|         uint32_t lv_cur;	/* current logical volumes */ | ||||
|         uint32_t lv_open;	/* open logical volumes */ | ||||
|         uint32_t pv_max;	/* maximum physical volumes */ | ||||
|         uint32_t pv_cur;	/* current physical volumes FU */ | ||||
|         uint32_t pv_act;	/* active physical volumes */ | ||||
|         uint32_t dummy; | ||||
|         uint32_t vgda;          /* volume group descriptor arrays FU */ | ||||
|         uint32_t pe_size;	/* physical extent size in sectors */ | ||||
|         uint32_t pe_total;	/* total of physical extents */ | ||||
|         uint32_t pe_allocated;  /* allocated physical extents */ | ||||
|         uint32_t pvg_total;     /* physical volume groups FU */ | ||||
| }; | ||||
|  | ||||
| struct pe_disk { | ||||
| 	uint16_t lv_num; | ||||
| 	uint16_t le_num; | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct uuid_list { | ||||
| 	struct list_head list; | ||||
| 	char uuid[NAME_LEN]; | ||||
| }; | ||||
|  | ||||
| struct lvd_list { | ||||
| 	struct list_head list; | ||||
| 	struct lv_disk lv; | ||||
| }; | ||||
|  | ||||
| struct disk_list { | ||||
| 	struct pool *mem; | ||||
| 	struct device *dev; | ||||
| 	struct list_head list; | ||||
|  | ||||
| 	struct pv_disk pv; | ||||
| 	struct vg_disk vg; | ||||
| 	struct list_head uuids; | ||||
| 	struct list_head lvs; | ||||
| 	struct pe_disk *extents; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Layout constants. | ||||
|  */ | ||||
| #define METADATA_ALIGN 4096UL | ||||
| #define	PE_ALIGN (65536UL / SECTOR_SIZE) | ||||
|  | ||||
| #define	METADATA_BASE 0UL | ||||
| #define	PV_SIZE 1024UL | ||||
| #define	VG_SIZE 4096UL | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Functions to calculate layout info. | ||||
|  */ | ||||
| int calculate_layout(struct disk_list *dl); | ||||
| int calculate_extent_count(struct physical_volume *pv); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Low level io routines which read/write | ||||
|  * disk_lists. | ||||
|  */ | ||||
| struct disk_list *read_pv(struct device *dev, struct pool *mem, | ||||
| 			  const char *vg_name); | ||||
|  | ||||
| int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter, | ||||
| 		   struct pool *mem, struct list_head *results); | ||||
|  | ||||
| int write_pvs(struct list_head *pvs); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Functions to translate to between disk and in | ||||
|  * core structures. | ||||
|  */ | ||||
| int import_pv(struct pool *mem, struct device *dev, | ||||
| 	      struct physical_volume *pv, struct pv_disk *pvd); | ||||
| int export_pv(struct pv_disk *pvd, struct physical_volume *pv); | ||||
|  | ||||
| int import_vg(struct pool *mem, | ||||
| 	      struct volume_group *vg, struct disk_list *dl); | ||||
| int export_vg(struct vg_disk *vgd, struct volume_group *vg); | ||||
|  | ||||
| int import_lv(struct pool *mem, struct logical_volume *lv, | ||||
| 	      struct lv_disk *lvd); | ||||
| void export_lv(struct lv_disk *lvd, struct volume_group *vg, | ||||
| 	       struct logical_volume *lv, const char *prefix); | ||||
|  | ||||
| int import_extents(struct pool *mem, struct volume_group *vg, | ||||
| 		   struct list_head *pvs); | ||||
| int export_extents(struct disk_list *dl, int lv_num, | ||||
| 		   struct logical_volume *lv, | ||||
| 		   struct physical_volume *pv); | ||||
|  | ||||
| int import_pvs(struct pool *mem, struct list_head *pvs, | ||||
| 	       struct list_head *results, int *count); | ||||
|  | ||||
| int import_lvs(struct pool *mem, struct volume_group *vg, | ||||
| 	       struct list_head *pvs); | ||||
| int export_lvs(struct disk_list *dl, struct volume_group *vg, | ||||
| 	       struct physical_volume *pv, const char *prefix); | ||||
|  | ||||
| int export_uuids(struct disk_list *dl, struct volume_group *vg); | ||||
|  | ||||
| void export_numbers(struct list_head *pvs, struct volume_group *vg); | ||||
|  | ||||
| void export_pv_act(struct list_head *pvs); | ||||
|  | ||||
| /* blech */ | ||||
| int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg, | ||||
| 		       int *result); | ||||
| int export_vg_number(struct list_head *pvs, const char *vg_name, | ||||
| 		     struct dev_filter *filter); | ||||
|  | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										471
									
								
								lib/format1/format1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										471
									
								
								lib/format1/format1.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,471 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "disk-rep.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "pool.h" | ||||
| #include "hash.h" | ||||
| #include "list.h" | ||||
| #include "log.h" | ||||
| #include "display.h" | ||||
|  | ||||
| /* VG consistency checks */ | ||||
| static int _check_vgs(struct list_head *pvs) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct disk_list *dl; | ||||
| 	struct disk_list *first = NULL; | ||||
|  | ||||
| 	int pv_count = 0; | ||||
|  | ||||
| 	/* check all the vg's are the same */ | ||||
| 	list_for_each(tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
|  | ||||
| 		if (!first) | ||||
| 			first = dl; | ||||
| 		else if (memcmp(&first->vg, &dl->vg, sizeof(first->vg))) { | ||||
| 			log_err("VG data differs between PVs %s and %s", | ||||
| 				first->dev->name, dl->dev->name); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		pv_count++; | ||||
| 	} | ||||
|  | ||||
| 	/* On entry to fn, list known to be non-empty */ | ||||
| 	if (!(pv_count == dl->vg.pv_cur)) { | ||||
| 		log_error("Only %d out of %d PV(s) found for VG %s", | ||||
| 			  pv_count, dl->vg.pv_cur, dl->pv.vg_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct volume_group *_build_vg(struct pool *mem, struct list_head *pvs) | ||||
| { | ||||
| 	struct volume_group *vg = pool_alloc(mem, sizeof(*vg)); | ||||
| 	struct disk_list *dl; | ||||
|  | ||||
| 	if (!vg) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (list_empty(pvs)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	dl = list_entry(pvs->next, struct disk_list, list); | ||||
|  | ||||
| 	memset(vg, 0, sizeof(*vg)); | ||||
|  | ||||
| 	INIT_LIST_HEAD(&vg->pvs); | ||||
| 	INIT_LIST_HEAD(&vg->lvs); | ||||
|  | ||||
| 	if (!_check_vgs(pvs)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!import_vg(mem, vg, dl)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!import_pvs(mem, pvs, &vg->pvs, &vg->pv_count)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!import_lvs(mem, vg, pvs)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!import_extents(mem, vg, pvs)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	return vg; | ||||
|  | ||||
|  bad: | ||||
| 	stack; | ||||
| 	pool_free(mem, vg); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static struct volume_group *_vg_read(struct io_space *is, const char *vg_name) | ||||
| { | ||||
| 	struct pool *mem = pool_create(1024 * 10); | ||||
| 	struct list_head pvs; | ||||
| 	struct volume_group *vg; | ||||
| 	INIT_LIST_HEAD(&pvs); | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
|         /* Strip prefix if present */ | ||||
|         if (!strncmp(vg_name, is->prefix, strlen(is->prefix))) | ||||
|                 vg_name += strlen(is->prefix); | ||||
|  | ||||
| 	if (!read_pvs_in_vg(vg_name, is->filter, mem, &pvs)) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(vg = _build_vg(is->mem, &pvs))) | ||||
| 		stack; | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	return vg; | ||||
| } | ||||
|  | ||||
| static struct disk_list *_flatten_pv(struct pool *mem, struct volume_group *vg, | ||||
| 				     struct physical_volume *pv, | ||||
| 				     const char *prefix) | ||||
| { | ||||
| 	struct disk_list *dl = pool_alloc(mem, sizeof(*dl)); | ||||
|  | ||||
| 	if (!dl) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	dl->mem = mem; | ||||
| 	dl->dev = pv->dev; | ||||
|  | ||||
| 	INIT_LIST_HEAD(&dl->uuids); | ||||
| 	INIT_LIST_HEAD(&dl->lvs); | ||||
|  | ||||
| 	if (!export_pv(&dl->pv, pv) || | ||||
| 	    !export_vg(&dl->vg, vg) || | ||||
| 	    !export_uuids(dl, vg) || | ||||
| 	    !export_lvs(dl, vg, pv, prefix) || | ||||
| 	    !calculate_layout(dl)) { | ||||
| 		stack; | ||||
| 		pool_free(mem, dl); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return dl; | ||||
| } | ||||
|  | ||||
| static int _flatten_vg(struct pool *mem, struct volume_group *vg, | ||||
| 		       struct list_head *pvs, const char *prefix, | ||||
| 		       struct dev_filter *filter) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct pv_list *pvl; | ||||
| 	struct disk_list *data; | ||||
|  | ||||
| 	list_for_each(tmp, &vg->pvs) { | ||||
| 		pvl = list_entry(tmp, struct pv_list, list); | ||||
|  | ||||
| 		if (!(data = _flatten_pv(mem, vg, &pvl->pv, prefix))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		list_add(&data->list, pvs); | ||||
| 	} | ||||
|  | ||||
| 	export_numbers(pvs, vg); | ||||
| 	export_pv_act(pvs); | ||||
|  | ||||
| 	if (!export_vg_number(pvs, vg->name, filter)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _vg_write(struct io_space *is, struct volume_group *vg) | ||||
| { | ||||
| 	struct pool *mem = pool_create(1024 * 10); | ||||
| 	struct list_head pvs; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	INIT_LIST_HEAD(&pvs); | ||||
|  | ||||
| 	r = (_flatten_vg(mem, vg, &pvs, is->prefix, is->filter) && | ||||
| 	     write_pvs(&pvs)); | ||||
| 	pool_destroy(mem); | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static struct physical_volume *_pv_read(struct io_space *is, | ||||
| 					const char *name) | ||||
| { | ||||
| 	struct pool *mem = pool_create(1024); | ||||
| 	struct physical_volume *pv; | ||||
| 	struct disk_list *dl; | ||||
| 	struct device *dev; | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dev = dev_cache_get(name, is->filter))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dl = read_pv(dev, mem, NULL))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!(pv = pool_alloc(is->mem, sizeof(*pv)))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!import_pv(is->mem, dl->dev, pv, &dl->pv)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	return pv; | ||||
|  | ||||
|  bad: | ||||
| 	pool_destroy(mem); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static struct list_head *_get_pvs(struct io_space *is) | ||||
| { | ||||
| 	struct pool *mem = pool_create(1024 * 10); | ||||
| 	struct list_head pvs, *results; | ||||
| 	uint32_t count; | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(results = pool_alloc(is->mem, sizeof(*results)))) { | ||||
| 		stack; | ||||
| 		pool_destroy(mem); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	INIT_LIST_HEAD(&pvs); | ||||
| 	INIT_LIST_HEAD(results); | ||||
|  | ||||
| 	if (!read_pvs_in_vg(NULL, is->filter, mem, &pvs)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!import_pvs(is->mem, &pvs, results, &count)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	return results; | ||||
|  | ||||
|  bad: | ||||
| 	pool_free(mem, results); | ||||
| 	pool_destroy(mem); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int _find_vg_name(struct list_head *names, const char *vg) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct name_list *nl; | ||||
|  | ||||
| 	list_for_each(tmp, names) { | ||||
| 		nl = list_entry(tmp, struct name_list, list); | ||||
| 		if (!strcmp(nl->name, vg)) | ||||
| 			return 1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct list_head *_get_vgs(struct io_space *is) | ||||
| { | ||||
| 	struct list_head *tmp, *pvs; | ||||
| 	struct list_head *names = pool_alloc(is->mem, sizeof(*names)); | ||||
| 	struct name_list *nl; | ||||
|  | ||||
| 	if (!names) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	INIT_LIST_HEAD(names); | ||||
|  | ||||
| 	if (!(pvs = _get_pvs(is))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	list_for_each(tmp, pvs) { | ||||
| 		struct pv_list *pvl = list_entry(tmp, struct pv_list, list); | ||||
|  | ||||
| 		if (!(*pvl->pv.vg_name) ||   | ||||
| 	 	     _find_vg_name(names, pvl->pv.vg_name)) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!(nl = pool_alloc(is->mem, sizeof(*nl)))) { | ||||
| 			stack; | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		if (!(nl->name = pool_strdup(is->mem, pvl->pv.vg_name))) { | ||||
| 			stack; | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		list_add(&nl->list, names); | ||||
| 	} | ||||
|  | ||||
| 	if (list_empty(names)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	return names; | ||||
|  | ||||
|  bad: | ||||
| 	pool_free(is->mem, names); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int _pv_setup(struct io_space *is, struct physical_volume *pv, | ||||
| 		     struct volume_group *vg) | ||||
| { | ||||
| 	/* | ||||
| 	 * This works out pe_start and pe_count. | ||||
| 	 */ | ||||
| 	if (!calculate_extent_count(pv)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _pv_write(struct io_space *is, struct physical_volume *pv) | ||||
| { | ||||
| 	struct pool *mem; | ||||
| 	struct disk_list *dl; | ||||
| 	struct list_head pvs; | ||||
|  | ||||
| 	INIT_LIST_HEAD(&pvs); | ||||
|  | ||||
| 	if (*pv->vg_name) { | ||||
| 		log_error("Assertion failed: can't _pv_write non-orphan PV "  | ||||
| 			  "(in VG %s)", pv->vg_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(mem = pool_create(1024))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	if (!(dl = pool_alloc(mem, sizeof(*dl)))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
| 	dl->mem = mem; | ||||
| 	dl->dev = pv->dev; | ||||
|  | ||||
| 	if (!export_pv(&dl->pv, pv)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	list_add(&dl->list, &pvs); | ||||
| 	if (!write_pvs(&pvs)) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	return 1; | ||||
|  | ||||
|  bad: | ||||
| 	pool_destroy(mem); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int _vg_setup(struct io_space *is, struct volume_group *vg) | ||||
| { | ||||
| 	/* just check max_pv and max_lv */ | ||||
| 	if (vg->max_lv >= MAX_LV) | ||||
| 		vg->max_lv = MAX_LV - 1; | ||||
|  | ||||
|         if (vg->max_pv >= MAX_PV) | ||||
| 		vg->max_pv = MAX_PV - 1; | ||||
|  | ||||
| 	if (vg->extent_size > MAX_PE_SIZE || vg->extent_size < MIN_PE_SIZE) { | ||||
| 		char *dummy, *dummy2; | ||||
|  | ||||
| 		log_error("Extent size must be between %s and %s", | ||||
| 			(dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT)),  | ||||
| 			(dummy2 = display_size(MAX_PE_SIZE / 2, SIZE_SHORT))); | ||||
|  | ||||
| 		dbg_free(dummy); | ||||
| 		dbg_free(dummy2); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (vg->extent_size % MIN_PE_SIZE) { | ||||
| 		char *dummy; | ||||
| 		log_error("Extent size must be multiple of %s", | ||||
| 			(dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT))); | ||||
| 		dbg_free(dummy); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Redundant? */ | ||||
| 	if (vg->extent_size & (vg->extent_size - 1)) { | ||||
| 		log_error("Extent size must be power of 2"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void _destroy(struct io_space *ios) | ||||
| { | ||||
| 	dbg_free(ios->prefix); | ||||
| 	pool_destroy(ios->mem); | ||||
| 	dbg_free(ios); | ||||
| } | ||||
|  | ||||
| struct io_space *create_lvm1_format(const char *prefix, struct pool *mem, | ||||
| 				    struct dev_filter *filter) | ||||
| { | ||||
| 	struct io_space *ios = dbg_malloc(sizeof(*ios)); | ||||
|  | ||||
| 	ios->get_vgs = _get_vgs; | ||||
| 	ios->get_pvs = _get_pvs; | ||||
| 	ios->pv_read = _pv_read; | ||||
| 	ios->pv_setup = _pv_setup; | ||||
| 	ios->pv_write = _pv_write; | ||||
| 	ios->vg_read = _vg_read; | ||||
| 	ios->vg_setup = _vg_setup; | ||||
| 	ios->vg_write = _vg_write; | ||||
| 	ios->destroy = _destroy; | ||||
|  | ||||
| 	ios->prefix = dbg_malloc(strlen(prefix) + 1); | ||||
| 	if (!ios->prefix) { | ||||
| 		stack; | ||||
| 		dbg_free(ios); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	strcpy(ios->prefix, prefix); | ||||
|  | ||||
| 	ios->mem = mem; | ||||
| 	ios->filter = filter; | ||||
| 	ios->private = NULL; | ||||
|  | ||||
| 	return ios; | ||||
| } | ||||
							
								
								
									
										15
									
								
								lib/format1/format1.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/format1/format1.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef LVM_FORMAT1_H | ||||
| #define LVM_FORMAT1_H | ||||
|  | ||||
| #include "metadata.h" | ||||
|  | ||||
| struct io_space *create_lvm1_format(const char *prefix, struct pool *mem, | ||||
| 				    struct dev_filter *filter); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										628
									
								
								lib/format1/import-export.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										628
									
								
								lib/format1/import-export.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,628 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "disk-rep.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "pool.h" | ||||
| #include "hash.h" | ||||
| #include "list.h" | ||||
| #include "log.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); | ||||
| } | ||||
|  | ||||
| static struct logical_volume *_find_lv(struct volume_group *vg, | ||||
| 				       const char *name) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct logical_volume *lv; | ||||
| 	struct lv_list *ll; | ||||
| 	const char *ptr = strrchr(name, '/') + 1; | ||||
|  | ||||
| 	list_for_each(tmp, &vg->lvs) { | ||||
| 		ll = list_entry(tmp, struct lv_list, list); | ||||
| 		lv = &ll->lv; | ||||
|  | ||||
| 		if (!strcmp(ptr, lv->name)) | ||||
| 			return lv; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static struct physical_volume *_find_pv(struct volume_group *vg, | ||||
| 					struct device *dev) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct physical_volume *pv; | ||||
| 	struct pv_list *pl; | ||||
|  | ||||
| 	list_for_each(tmp, &vg->pvs) { | ||||
| 		pl = list_entry(tmp, struct pv_list, list); | ||||
| 		pv = &pl->pv; | ||||
| 		if (dev == pv->dev) | ||||
| 			return pv; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int _fill_lv_array(struct logical_volume **lvs, | ||||
| 			  struct volume_group *vg, struct disk_list *dl) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct logical_volume *lv; | ||||
| 	int i = 0; | ||||
|  | ||||
| 	list_for_each(tmp, &dl->lvs) { | ||||
| 		struct lvd_list *ll = list_entry(tmp, struct lvd_list, list); | ||||
|  | ||||
| 		if (!(lv = _find_lv(vg, ll->lv.lv_name))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		lvs[i] = lv; | ||||
| 		i++; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int import_pv(struct pool *mem, struct device *dev, | ||||
| 	      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; | ||||
| 	} | ||||
|  | ||||
| 	if (pvd->pv_status & PV_ACTIVE) | ||||
| 		pv->status |= ACTIVE; | ||||
|  | ||||
| 	if (pvd->pv_allocatable) | ||||
| 		pv->status |= ALLOCATED_PV; | ||||
|  | ||||
| 	pv->size = pvd->pv_size; | ||||
| 	pv->pe_size = pvd->pe_size; | ||||
| 	pv->pe_start = pvd->pe_start; | ||||
| 	pv->pe_count = pvd->pe_total; | ||||
| 	pv->pe_allocated = pvd->pe_allocated; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _system_id(char *system_id) | ||||
| { | ||||
| 	struct utsname uts; | ||||
|  | ||||
| 	if (uname(&uts) != 0) { | ||||
| 		log_sys_error("uname", "_system_id"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	sprintf(system_id, "%s%lu", uts.nodename, time(NULL)); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int export_pv(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)); | ||||
|  | ||||
| 	//pvd->pv_major = MAJOR(pv->dev); | ||||
|  | ||||
| 	if (pv->status & ACTIVE) | ||||
| 		pvd->pv_status |= PV_ACTIVE; | ||||
|  | ||||
| 	if (pv->status & ALLOCATED_PV) | ||||
| 		pvd->pv_allocatable = PV_ALLOCATABLE; | ||||
|  | ||||
| 	pvd->pv_size = pv->size; | ||||
| 	pvd->lv_cur = 0;	/* this is set when exporting the lv list */ | ||||
| 	pvd->pe_size = pv->pe_size; | ||||
| 	pvd->pe_total = pv->pe_count; | ||||
| 	pvd->pe_allocated = pv->pe_allocated; | ||||
| 	pvd->pe_start = pv->pe_start; | ||||
|  | ||||
| 	if (!_system_id(pvd->system_id)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int import_vg(struct pool *mem, | ||||
| 	      struct volume_group *vg, struct disk_list *dl) | ||||
| { | ||||
| 	struct vg_disk *vgd = &dl->vg; | ||||
| 	memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN); | ||||
|  | ||||
| 	if (!_check_vg_name(dl->pv.vg_name)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(vg->name = pool_strdup(mem, dl->pv.vg_name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (vgd->vg_status & VG_ACTIVE) | ||||
| 		vg->status |= ACTIVE; | ||||
|  | ||||
| 	if (vgd->vg_status & VG_EXPORTED) | ||||
| 		vg->status |= EXPORTED_VG; | ||||
|  | ||||
| 	if (vgd->vg_status & VG_EXTENDABLE) | ||||
| 		vg->status |= EXTENDABLE_VG; | ||||
|  | ||||
| 	if (vgd->vg_access & VG_READ) | ||||
| 		vg->status |= LVM_READ; | ||||
|  | ||||
| 	if (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; | ||||
| 	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 & ACTIVE) | ||||
| 		vgd->vg_status |= VG_ACTIVE; | ||||
|  | ||||
| 	if (vg->status & EXPORTED_VG) | ||||
| 		vgd->vg_status |= VG_EXPORTED; | ||||
|  | ||||
| 	if (vg->status & EXTENDABLE_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) | ||||
| { | ||||
| 	int len; | ||||
| 	memset(&lv->id, 0, sizeof(lv->id)); | ||||
|         if (!(lv->name = _create_lv_name(mem, lvd->lv_name))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lvd->lv_status & LV_ACTIVE) | ||||
| 		lv->status |= ACTIVE; | ||||
|  | ||||
| 	if (lvd->lv_status & LV_SPINDOWN) | ||||
| 		lv->status |= SPINDOWN_LV; | ||||
|  | ||||
| 	if (lvd->lv_access & LV_READ) | ||||
| 		lv->status |= LVM_READ; | ||||
|  | ||||
| 	if (lvd->lv_access & LV_WRITE) | ||||
| 		lv->status |= LVM_WRITE; | ||||
|  | ||||
| 	if (lvd->lv_access & LV_SNAPSHOT) | ||||
| 		lv->status |= SNAPSHOT; | ||||
|  | ||||
| 	if (lvd->lv_access & LV_SNAPSHOT_ORG) | ||||
| 		lv->status |= SNAPSHOT_ORG; | ||||
|  | ||||
| 	if (lvd->lv_badblock) | ||||
| 		lv->status |= BADBLOCK_ON; | ||||
|  | ||||
| 	if (lvd->lv_allocation == LV_STRICT) | ||||
| 		lv->status |= ALLOC_STRICT; | ||||
| 	else | ||||
| 		lv->status |= ALLOC_CONTIGUOUS; | ||||
|  | ||||
|         lv->size = lvd->lv_size; | ||||
|         lv->le_count = lvd->lv_allocated_le; | ||||
|  | ||||
| 	len = sizeof(struct pe_specifier) * lv->le_count; | ||||
| 	if (!(lv->map = pool_alloc(mem, len))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	memset(lv->map, 0, len); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void export_lv(struct lv_disk *lvd, struct volume_group *vg, | ||||
| 	       struct logical_volume *lv, const char *prefix) | ||||
| { | ||||
| 	memset(lvd, 0, sizeof(*lvd)); | ||||
| 	snprintf(lvd->lv_name, sizeof(lvd->lv_name), "%s/%s", | ||||
| 		 prefix, lv->name); | ||||
|  | ||||
| 	_check_vg_name(vg->name); | ||||
| 	strcpy(lvd->vg_name, vg->name); | ||||
|  | ||||
| 	if (lv->status & LVM_READ) | ||||
| 		lvd->lv_access |= LV_READ; | ||||
|  | ||||
| 	if (lv->status & LVM_WRITE) | ||||
| 		lvd->lv_access |= LV_WRITE; | ||||
|  | ||||
| 	if (lv->status & SNAPSHOT) | ||||
| 		lvd->lv_access |= LV_SNAPSHOT; | ||||
|  | ||||
| 	if (lv->status & SNAPSHOT_ORG) | ||||
| 		lvd->lv_access |= LV_SNAPSHOT_ORG; | ||||
|  | ||||
| 	if (lv->status & ACTIVE) | ||||
| 		lvd->lv_status |= LV_ACTIVE; | ||||
|  | ||||
| 	if (lv->status & SPINDOWN_LV) | ||||
| 		lvd->lv_status |= LV_SPINDOWN; | ||||
|  | ||||
|         lvd->lv_size = lv->size; | ||||
|         lvd->lv_allocated_le = lv->le_count; | ||||
|  | ||||
| 	if (lv->status & BADBLOCK_ON) | ||||
| 		lvd->lv_badblock = LV_BADBLOCK_ON; | ||||
|  | ||||
| 	if (lv->status & ALLOC_STRICT) | ||||
| 		lvd->lv_allocation = LV_STRICT; | ||||
| 	else | ||||
| 		lvd->lv_allocation = LV_CONTIGUOUS; | ||||
| } | ||||
|  | ||||
| int import_extents(struct pool *mem, struct volume_group *vg, | ||||
| 		   struct list_head *pvs) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct disk_list *dl; | ||||
| 	struct logical_volume *lv, *lvs[MAX_LV]; | ||||
| 	struct physical_volume *pv; | ||||
| 	struct pe_disk *e; | ||||
| 	int i; | ||||
| 	uint32_t lv_num, le; | ||||
|  | ||||
| 	list_for_each(tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		pv = _find_pv(vg, dl->dev); | ||||
| 		e = dl->extents; | ||||
|  | ||||
| 		/* build an array of lv's for this pv */ | ||||
| 		if (!_fill_lv_array(lvs, vg, dl)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		for (i = 0; i < dl->pv.pe_total; i++) { | ||||
| 			lv_num = e[i].lv_num; | ||||
|  | ||||
| 			if (lv_num == UNMAPPED_EXTENT) | ||||
| 				continue; | ||||
|  | ||||
| 			else if(lv_num > dl->pv.lv_cur) { | ||||
| 				log_err("invalid lv in extent map\n"); | ||||
| 				return 0; | ||||
|  | ||||
| 			} else { | ||||
| 				lv_num--; | ||||
| 				lv = lvs[lv_num]; | ||||
| 				le = e[i].le_num; | ||||
|  | ||||
| 				lv->map[le].pv = pv; | ||||
| 				lv->map[le].pe = i; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int export_extents(struct disk_list *dl, int lv_num, | ||||
| 		   struct logical_volume *lv, | ||||
| 		   struct physical_volume *pv) | ||||
| { | ||||
| 	struct pe_disk *ped; | ||||
| 	int le; | ||||
|  | ||||
| 	for (le = 0; le < lv->le_count; le++) { | ||||
| 		if (lv->map[le].pv == pv) { | ||||
| 			ped = &dl->extents[lv->map[le].pe]; | ||||
| 			ped->lv_num = lv_num; | ||||
| 			ped->le_num = le; | ||||
| 		} | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int import_pvs(struct pool *mem, struct list_head *pvs, | ||||
| 	       struct list_head *results, int *count) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct disk_list *dl; | ||||
| 	struct pv_list *pvl; | ||||
|  | ||||
| 	*count = 0; | ||||
| 	list_for_each(tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		pvl = pool_alloc(mem, sizeof(*pvl)); | ||||
|  | ||||
| 		if (!pvl) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (!import_pv(mem, dl->dev, &pvl->pv, &dl->pv)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		list_add(&pvl->list, results); | ||||
| 		(*count)++; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct logical_volume *_add_lv(struct pool *mem, | ||||
| 				      struct volume_group *vg, | ||||
| 				      struct lv_disk *lvd) | ||||
| { | ||||
| 	struct lv_list *ll = pool_alloc(mem, sizeof(*ll)); | ||||
| 	struct logical_volume *lv; | ||||
|  | ||||
| 	if (!ll) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	lv = &ll->lv; | ||||
|  | ||||
| 	if (!import_lv(mem, &ll->lv, lvd)) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	list_add(&ll->list, &vg->lvs); | ||||
| 	vg->lv_count++; | ||||
|  | ||||
| 	return lv; | ||||
| } | ||||
|  | ||||
| int import_lvs(struct pool *mem, struct volume_group *vg, | ||||
| 	       struct list_head *pvs) | ||||
| { | ||||
| 	struct list_head *tmp, *tmp2; | ||||
| 	struct disk_list *dl; | ||||
| 	struct lvd_list *ll; | ||||
| 	struct lv_disk *lvd; | ||||
|  | ||||
| 	list_for_each(tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		list_for_each(tmp2, &dl->lvs) { | ||||
| 			ll = list_entry(tmp2, struct lvd_list, list); | ||||
| 			lvd = &ll->lv; | ||||
|  | ||||
| 			if (!_find_lv(vg, lvd->lv_name) && | ||||
| 			    !_add_lv(mem, vg, lvd)) { | ||||
| 				stack; | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int export_lvs(struct disk_list *dl, struct volume_group *vg, | ||||
| 	       struct physical_volume *pv, const char *prefix) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct lv_list *ll; | ||||
| 	struct lvd_list *lvdl; | ||||
| 	int lv_num = 1, len; | ||||
|  | ||||
| 	/* | ||||
| 	 * setup the pv's extents array | ||||
| 	 */ | ||||
| 	len = sizeof(struct pe_disk) * dl->pv.pe_total; | ||||
| 	if (!(dl->extents = pool_alloc(dl->mem, len))) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	memset(dl->extents, 0, len); | ||||
|  | ||||
|  | ||||
| 	list_for_each(tmp, &vg->lvs) { | ||||
| 		ll = list_entry(tmp, struct lv_list, list); | ||||
| 		if (!(lvdl = pool_alloc(dl->mem, sizeof(*lvdl)))) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		export_lv(&lvdl->lv, vg, &ll->lv, prefix); | ||||
| 		if (!export_extents(dl, lv_num++, &ll->lv, pv)) { | ||||
| 			stack; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		list_add(&lvdl->list, &dl->lvs); | ||||
| 		dl->pv.lv_cur++; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int export_uuids(struct disk_list *dl, struct volume_group *vg) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct uuid_list *ul; | ||||
| 	struct pv_list *pvl; | ||||
|  | ||||
| 	list_for_each(tmp, &vg->pvs) { | ||||
| 		pvl = list_entry(tmp, struct pv_list, 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(&ul->list, &dl->uuids); | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _get_lv_number(struct volume_group *vg, const char *name) | ||||
| { | ||||
| 	/* FIXME: inefficient */ | ||||
| 	struct list_head *tmp; | ||||
| 	struct lv_list *ll; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	list_for_each (tmp, &vg->lvs) { | ||||
| 		ll = list_entry(tmp, struct lv_list, list); | ||||
| 		if (!strcmp(ll->lv.name, name)) | ||||
| 			break; | ||||
| 		r++; | ||||
| 	} | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * This calculates the nasty pv_number and | ||||
|  * lv_number fields used by LVM1.  Very | ||||
|  * inefficient code. | ||||
|  */ | ||||
| void export_numbers(struct list_head *pvs, struct volume_group *vg) | ||||
| { | ||||
| 	struct list_head *tmp, *tmp2; | ||||
| 	struct disk_list *dl; | ||||
| 	struct lvd_list *ll; | ||||
| 	int pv_num = 1; | ||||
|  | ||||
| 	list_for_each (tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		dl->pv.pv_number = pv_num++; | ||||
|  | ||||
| 		list_for_each (tmp2, &dl->lvs) { | ||||
| 			ll = list_entry(tmp2, struct lvd_list, list); | ||||
| 			ll->lv.lv_number = _get_lv_number(vg, ll->lv.lv_name); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Calculate vg_disk->pv_act. | ||||
|  */ | ||||
| void export_pv_act(struct list_head *pvs) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct disk_list *dl; | ||||
| 	int act = 0; | ||||
|  | ||||
| 	list_for_each (tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		if (dl->pv.pv_status & PV_ACTIVE) | ||||
| 			act++; | ||||
| 	} | ||||
|  | ||||
| 	list_for_each (tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		dl->vg.pv_act = act; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int export_vg_number(struct list_head *pvs, const char *vg_name,  | ||||
| 		     struct dev_filter *filter) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct disk_list *dl; | ||||
| 	int vg_num; | ||||
|  | ||||
| 	if (!get_free_vg_number(filter, vg_name, &vg_num)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	list_for_each (tmp, pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		dl->vg.vg_number = vg_num; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
							
								
								
									
										151
									
								
								lib/format1/layout.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								lib/format1/layout.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "disk-rep.h" | ||||
| #include "log.h" | ||||
| #include "dbg_malloc.h" | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Only works with powers of 2. | ||||
|  */ | ||||
| static inline ulong _round_up(ulong n, ulong size) | ||||
| { | ||||
| 	size--; | ||||
| 	return (n + size) & ~size; | ||||
| } | ||||
|  | ||||
| static inline ulong _div_up(ulong n, ulong size) | ||||
| { | ||||
| 	return _round_up(n, size) / size; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Each chunk of metadata should be aligned to | ||||
|  * METADATA_ALIGN. | ||||
|  */ | ||||
| static uint32_t _next_base(struct data_area *area) | ||||
| { | ||||
| 	return _round_up(area->base + area->size, METADATA_ALIGN); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Quick calculation based on pe_start. | ||||
|  */ | ||||
| static int _adjust_pe_on_disk(struct pv_disk *pvd) | ||||
| { | ||||
| 	uint32_t pe_start = pvd->pe_start * SECTOR_SIZE; | ||||
|  | ||||
| 	if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size) | ||||
| 		return 0; | ||||
|  | ||||
| 	pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _calc_simple_layout(struct pv_disk *pvd) | ||||
| { | ||||
| 	pvd->pv_on_disk.base = METADATA_BASE; | ||||
| 	pvd->pv_on_disk.size = PV_SIZE; | ||||
|  | ||||
|         pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk); | ||||
|         pvd->vg_on_disk.size = VG_SIZE; | ||||
|  | ||||
|         pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk); | ||||
|         pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN; | ||||
|  | ||||
|         pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk); | ||||
|         pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk); | ||||
|  | ||||
|         pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk); | ||||
|         pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk); | ||||
| } | ||||
|  | ||||
| int _check_vg_limits(struct disk_list *dl) | ||||
| { | ||||
| 	if (dl->vg.lv_max >= MAX_LV) { | ||||
| 		log_error("MaxLogicalVolumes of %d exceeds format limit of %d " | ||||
| 			  "for VG '%s'", dl->vg.lv_max, MAX_LV - 1,  | ||||
| 			  dl->pv.vg_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (dl->vg.pv_max >= MAX_PV) { | ||||
| 		log_error("MaxPhysicalVolumes of %d exceeds format limit of %d " | ||||
| 			  "for VG '%s'", dl->vg.pv_max, MAX_PV - 1,  | ||||
| 			  dl->pv.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->pv; | ||||
|  | ||||
| 	_calc_simple_layout(pvd); | ||||
| 	if (!_adjust_pe_on_disk(pvd)) { | ||||
| 		log_error("Insufficient space for metadata and PE's."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!_check_vg_limits(dl)) | ||||
| 		return 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * It may seem strange to have a struct | ||||
|  * physical_volume in here, but the number of | ||||
|  * extents that can fit on a disk *is* metadata | ||||
|  * format dependant. | ||||
|  */ | ||||
| int calculate_extent_count(struct physical_volume *pv) | ||||
| { | ||||
| 	struct pv_disk *pvd = dbg_malloc(sizeof(*pvd)); | ||||
| 	uint32_t end; | ||||
|  | ||||
| 	if (!pvd) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Guess how many extents will fit, | ||||
| 	 * bearing in mind that one is going to be | ||||
| 	 * knocked off at the start of the next | ||||
| 	 * loop. | ||||
| 	 */ | ||||
| 	pvd->pe_total = (pv->size / pv->pe_size); | ||||
|  | ||||
| 	if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) { | ||||
| 		log_error("Insufficient space for extents on %s", | ||||
| 			  pv->dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	do { | ||||
| 		pvd->pe_total--; | ||||
| 		_calc_simple_layout(pvd); | ||||
| 		end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size) / | ||||
| 		       SECTOR_SIZE); | ||||
|  | ||||
| 		pvd->pe_start = _round_up(end, PE_ALIGN); | ||||
|  | ||||
| 	} while((pvd->pe_start + (pvd->pe_total * pv->pe_size)) > pv->size); | ||||
|  | ||||
| 	pv->pe_count = pvd->pe_total; | ||||
| 	pv->pe_start = pvd->pe_start; | ||||
| 	dbg_free(pvd); | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										59
									
								
								lib/format1/vg_number.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								lib/format1/vg_number.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "pool.h" | ||||
| #include "disk-rep.h" | ||||
|  | ||||
| /* | ||||
|  * FIXME: Quick hack.  We can use caching to | ||||
|  * prevent a total re-read, even so vg_number | ||||
|  * causes the tools to check *every* pv.  Yuck. | ||||
|  * Put in seperate file so it wouldn't contaminate | ||||
|  * other code. | ||||
|  */ | ||||
| int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg, | ||||
| 		       int *result) | ||||
| { | ||||
| 	struct list_head all_pvs, *tmp; | ||||
| 	struct disk_list *dl; | ||||
| 	struct pool *mem = pool_create(10 * 1024); | ||||
| 	int numbers[MAX_VG], i, r = 0; | ||||
|  | ||||
| 	INIT_LIST_HEAD(&all_pvs); | ||||
|  | ||||
| 	if (!mem) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!read_pvs_in_vg(NULL, filter, mem, &all_pvs)) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	memset(numbers, 0, sizeof(numbers)); | ||||
|  | ||||
| 	list_for_each (tmp, &all_pvs) { | ||||
| 		dl = list_entry(tmp, struct disk_list, list); | ||||
| 		if (!strcmp(dl->pv.vg_name, candidate_vg)) | ||||
| 			continue; | ||||
|  | ||||
| 		numbers[dl->vg.vg_number] = 1; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < MAX_VG; i++) { | ||||
| 		if (!numbers[i]) { | ||||
| 			r = 1; | ||||
| 			*result = i; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  out: | ||||
| 	pool_destroy(mem); | ||||
| 	return r; | ||||
| } | ||||
							
								
								
									
										128
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								lib/log/log.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include <stdarg.h> | ||||
| #include <syslog.h> | ||||
|  | ||||
| static FILE *_log = 0; | ||||
|  | ||||
| static int _verbose_level = 0; | ||||
| static int _test = 0; | ||||
| static int _debug_level = 0; | ||||
| static int _syslog = 0; | ||||
|  | ||||
| void init_log(FILE *fp) { | ||||
| 	_log = fp; | ||||
| } | ||||
|  | ||||
| void init_syslog(int facility) { | ||||
| 	openlog("lvm", LOG_PID, facility); | ||||
| 	_syslog = 1; | ||||
| } | ||||
|  | ||||
| void fin_log() { | ||||
| 	_log = 0; | ||||
| } | ||||
|  | ||||
| void fin_syslog() { | ||||
| 	closelog(); | ||||
| 	_syslog = 0; | ||||
| } | ||||
|  | ||||
| void init_verbose(int level) { | ||||
| 	_verbose_level = level; | ||||
| } | ||||
|  | ||||
| void init_test(int level) { | ||||
| 	_test = level; | ||||
| } | ||||
|  | ||||
| int test_mode() { | ||||
| 	return _test; | ||||
| } | ||||
|  | ||||
| void init_debug(int level) { | ||||
| 	_debug_level = level; | ||||
| } | ||||
|  | ||||
| int debug_level() { | ||||
| 	return _debug_level; | ||||
| } | ||||
|  | ||||
| void print_log(int level, const char *file, int line, const char *format, ...) { | ||||
| 	va_list ap; | ||||
|  | ||||
| 	va_start(ap, format); | ||||
| 	switch(level) { | ||||
| 	  case _LOG_DEBUG: | ||||
| 		if (_verbose_level > 2 && format[1]) { | ||||
| 			printf("        "); | ||||
| 			vprintf(format, ap); | ||||
| 			putchar('\n'); | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	  case _LOG_INFO: | ||||
| 		if (_verbose_level > 1) { | ||||
| 			printf("      "); | ||||
| 			vprintf(format, ap); | ||||
| 			putchar('\n'); | ||||
| 		} | ||||
| 		break; | ||||
| 	  case _LOG_NOTICE: | ||||
| 		if (_verbose_level) { | ||||
| 			printf("    "); | ||||
| 			vprintf(format, ap); | ||||
| 			putchar('\n'); | ||||
| 		} | ||||
| 		break; | ||||
| 	  case _LOG_WARN: | ||||
| 		printf("  "); | ||||
| 		vprintf(format, ap); | ||||
| 		putchar('\n'); | ||||
| 		break; | ||||
| 	  case _LOG_ERR: | ||||
| 		fprintf(stderr, "  "); | ||||
| 		vfprintf(stderr, format, ap); | ||||
| 		fputc('\n',stderr); | ||||
| 		break; | ||||
| 	  case _LOG_FATAL: | ||||
| 		vfprintf(stderr, format, ap); | ||||
| 		fputc('\n',stderr); | ||||
| 		break; | ||||
| 	  default: | ||||
| 		; | ||||
| 	} | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	if (level > _debug_level) | ||||
| 		return; | ||||
|  | ||||
| 	if (_log) { | ||||
| 		fprintf(_log, "%s:%d ", file, line); | ||||
|  | ||||
| 		va_start(ap, format); | ||||
| 		vfprintf(_log, format, ap); | ||||
| 		va_end(ap); | ||||
|  | ||||
| 		fprintf(_log, "\n"); | ||||
| 		fflush(_log); | ||||
| 	} | ||||
|  | ||||
| 	if (_syslog) { | ||||
|         	va_start(ap, format); | ||||
| 		vsyslog(level, format, ap); | ||||
| 		va_end(ap); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
|  | ||||
							
								
								
									
										79
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								lib/log/log.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_LOG_H | ||||
| #define _LVM_LOG_H | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #define _LOG_DEBUG 7 | ||||
| #define _LOG_INFO 6 | ||||
| #define _LOG_NOTICE 5 | ||||
| #define _LOG_WARN 4 | ||||
| #define _LOG_ERR 3 | ||||
| #define _LOG_FATAL 2 | ||||
|  | ||||
| void init_log(FILE *fp); | ||||
| void fin_log(void); | ||||
|  | ||||
| void init_syslog(int facility); | ||||
| void fin_syslog(void); | ||||
|  | ||||
| void init_verbose(int level); | ||||
| void init_test(int level); | ||||
| void init_debug(int level); | ||||
|  | ||||
| int test_mode(void); | ||||
| int debug_level(void); | ||||
|  | ||||
| void print_log(int level, const char *file, int line, const char *format, ...) | ||||
|      __attribute__ (( format (printf, 4, 5) )); | ||||
|  | ||||
| #define plog(l, x...) print_log(l, __FILE__, __LINE__ , ## x) | ||||
|  | ||||
| #define log_debug(x...) plog(_LOG_DEBUG, x) | ||||
| #define log_info(x...) plog(_LOG_INFO, x) | ||||
| #define log_notice(x...) plog(_LOG_NOTICE, x) | ||||
| #define log_warn(x...) plog(_LOG_WARN, x) | ||||
| #define log_err(x...) plog(_LOG_ERR, x) | ||||
| #define log_fatal(x...) plog(_LOG_FATAL, x) | ||||
|  | ||||
| #define stack log_debug( "s" ) | ||||
|  | ||||
| /* | ||||
|  * Macros to use for messages: | ||||
|  * | ||||
|  *   log_error - always print to stderr | ||||
|  *   log_print - always print to stdout | ||||
|  *   log_verbose - print to stdout if verbose is set (-v) | ||||
|  *   log_very_verbose - print to stdout if verbose is set twice (-vv) | ||||
|  *   log_debug - print to stdout if verbose is set three times (-vvv) | ||||
|  *               (suppressed if single-character string such as with 'stack') | ||||
|  * | ||||
|  * In addition, messages will be logged to file or syslog if they | ||||
|  * are more serious than the log level specified with -d. | ||||
|  */ | ||||
|  | ||||
| #define log_error(fmt, args...) log_err(fmt , ## args) | ||||
| #define log_print(fmt, args...) log_warn(fmt , ## args) | ||||
| #define log_verbose(fmt, args...) log_notice(fmt , ## args) | ||||
| #define log_very_verbose(fmt, args...) log_info(fmt , ## args) | ||||
|  | ||||
| /* System call equivalents */ | ||||
| #define log_sys_error(x, y) \ | ||||
| 		log_err("%s: %s failed: %s", y, x, strerror(errno)) | ||||
| #define log_sys_very_verbose(x, y) \ | ||||
| 		log_info("%s: %s failed: %s", y, x, strerror(errno)) | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
							
								
								
									
										501
									
								
								lib/metadata/lvm_v1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										501
									
								
								lib/metadata/lvm_v1.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,501 @@ | ||||
| /* | ||||
|  * 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 <stdio.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "dev-manager.h" | ||||
| #include "log.h" | ||||
| #include "metadata.h" | ||||
|  | ||||
| /* | ||||
|  * FIXME: these should not allocate memory | ||||
|  */ | ||||
|  | ||||
| pv_t *pv_copy_from_disk(pv_disk_t * pv_disk) | ||||
| { | ||||
| 	pv_t *pv; | ||||
|  | ||||
| 	if (!pv_disk || !(pv = (pv_t *) dbg_malloc(sizeof (*pv)))) | ||||
| 		return 0; | ||||
|  | ||||
| #define xx16(v) pv->v = LVM_TO_CORE16(pv_disk->v) | ||||
| #define xx32(v) pv->v = LVM_TO_CORE32(pv_disk->v) | ||||
|  | ||||
| 	memset(pv, 0, sizeof (*pv)); | ||||
| 	strncpy(pv->id, pv_disk->id, sizeof (pv->id)); | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| 	memset(pv->pv_name, 0, sizeof (pv->pv_name)); | ||||
| 	memset(pv->pv_uuid, 0, sizeof (pv->pv_uuid)); | ||||
| 	memcpy(pv->pv_uuid, pv_disk->pv_uuid, UUID_LEN); | ||||
| 	strncpy(pv->vg_name, pv_disk->vg_name, sizeof (pv->vg_name)); | ||||
| 	strncpy(pv->system_id, pv_disk->system_id, sizeof (pv->system_id)); | ||||
|  | ||||
| 	pv->pv_dev = LVM_TO_CORE32(pv_disk->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); | ||||
| 	pv->pe_stale = 0; | ||||
| 	xx32(pe_start); | ||||
|  | ||||
| #undef xx16 | ||||
| #undef xx32 | ||||
|  | ||||
| 	return pv; | ||||
| } | ||||
|  | ||||
| pv_disk_t *pv_copy_to_disk(pv_t * pv_core) | ||||
| { | ||||
| 	pv_disk_t *pv; | ||||
|  | ||||
| 	if (!pv_core || !(pv = dbg_malloc(sizeof (*pv)))) | ||||
| 		return 0; | ||||
|  | ||||
| #define xx16(v) pv->v = LVM_TO_DISK16(pv_core->v) | ||||
| #define xx32(v) pv->v = LVM_TO_DISK32(pv_core->v) | ||||
|  | ||||
| 	memset(pv, 0, sizeof (*pv)); | ||||
| 	strncpy(pv->id, pv_core->id, sizeof (pv->id)); | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| 	memcpy(pv->pv_uuid, pv_core->pv_uuid, UUID_LEN); | ||||
| 	strncpy(pv->vg_name, pv_core->vg_name, sizeof (pv->vg_name)); | ||||
| 	strncpy(pv->system_id, pv_core->system_id, sizeof (pv->system_id)); | ||||
|  | ||||
| 	/* core type is kdev_t; but no matter what it is, | ||||
| 	   only store major for check in pv_read() */ | ||||
| 	pv->pv_major = LVM_TO_DISK32(MAJOR(pv_core->pv_dev)); | ||||
| 	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); | ||||
|  | ||||
| #undef xx16 | ||||
| #undef xx32 | ||||
|  | ||||
| 	return pv; | ||||
| } | ||||
|  | ||||
| lv_t *lv_copy_from_disk(lv_disk_t * lv_disk) | ||||
| { | ||||
| 	lv_t *lv; | ||||
|  | ||||
| 	if (!lv_disk || !(lv = dbg_malloc(sizeof (*lv)))) | ||||
| 		return 0; | ||||
|  | ||||
| 	memset(lv, 0, sizeof (*lv)); | ||||
|  | ||||
| #define xx16(v) lv->v = LVM_TO_CORE16(lv_disk->v) | ||||
| #define xx32(v) lv->v = LVM_TO_CORE32(lv_disk->v) | ||||
|  | ||||
| 	strncpy(lv->lv_name, lv_disk->lv_name, sizeof (lv->lv_name)); | ||||
| 	strncpy(lv->vg_name, lv_disk->vg_name, sizeof (lv->vg_name)); | ||||
|  | ||||
| 	xx32(lv_access); | ||||
| 	xx32(lv_status); | ||||
|  | ||||
| 	lv->lv_open = 0; | ||||
|  | ||||
| 	xx32(lv_dev); | ||||
| 	xx32(lv_number); | ||||
| 	xx32(lv_mirror_copies); | ||||
| 	xx32(lv_recovery); | ||||
| 	xx32(lv_schedule); | ||||
| 	xx32(lv_size); | ||||
|  | ||||
| 	lv->lv_current_pe = NULL; | ||||
|  | ||||
| 	xx32(lv_allocated_le); | ||||
|  | ||||
| 	lv->lv_current_le = lv->lv_allocated_le; | ||||
|  | ||||
| 	xx32(lv_stripes); | ||||
| 	xx32(lv_stripesize); | ||||
| 	xx32(lv_badblock); | ||||
| 	xx32(lv_allocation); | ||||
| 	xx32(lv_io_timeout); | ||||
| 	xx32(lv_read_ahead); | ||||
|  | ||||
| 	lv->lv_snapshot_org = NULL; | ||||
| 	lv->lv_snapshot_prev = NULL; | ||||
| 	lv->lv_snapshot_next = NULL; | ||||
| 	lv->lv_block_exception = NULL; | ||||
| 	lv->lv_remap_ptr = 0; | ||||
| 	lv->lv_remap_end = 0; | ||||
|  | ||||
| 	xx32(lv_snapshot_minor); | ||||
| 	xx16(lv_chunk_size); | ||||
|  | ||||
| #undef xx32 | ||||
| #undef xx16 | ||||
|  | ||||
| 	return lv; | ||||
| } | ||||
|  | ||||
| lv_disk_t *lv_copy_to_disk(lv_t * lv_core) | ||||
| { | ||||
| 	lv_disk_t *lv; | ||||
|  | ||||
| 	if (!lv_core || !(lv = dbg_malloc(sizeof (*lv)))) | ||||
| 		return 0; | ||||
|  | ||||
| 	memset(lv, 0, sizeof (*lv)); | ||||
|  | ||||
| #define xx16(v) lv->v = LVM_TO_DISK16(lv_core->v) | ||||
| #define xx32(v) lv->v = LVM_TO_DISK32(lv_core->v) | ||||
|  | ||||
| 	strncpy(lv->lv_name, lv_core->lv_name, sizeof (lv->lv_name)); | ||||
| 	strncpy(lv->vg_name, lv_core->vg_name, sizeof (lv->vg_name)); | ||||
|  | ||||
| 	xx32(lv_access); | ||||
| 	xx32(lv_status); | ||||
|  | ||||
| 	lv->lv_open = 0; | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| 	lv->dummy = 0; | ||||
|  | ||||
| 	xx32(lv_allocated_le); | ||||
| 	xx32(lv_stripes); | ||||
| 	xx32(lv_stripesize); | ||||
| 	xx32(lv_badblock); | ||||
| 	xx32(lv_allocation); | ||||
| 	xx32(lv_io_timeout); | ||||
| 	xx32(lv_read_ahead); | ||||
|  | ||||
| #undef xx32 | ||||
| #undef xx16 | ||||
|  | ||||
| 	return lv; | ||||
| } | ||||
|  | ||||
| vg_t *vg_copy_from_disk(vg_disk_t * vg_disk) | ||||
| { | ||||
| 	vg_t *vg; | ||||
|  | ||||
| 	if (!vg_disk || !(vg = dbg_malloc(sizeof (*vg)))) | ||||
| 		return 0; | ||||
|  | ||||
| #define xx32(v) vg->v = LVM_TO_CORE32(vg_disk->v) | ||||
|  | ||||
| 	memset(vg, 0, sizeof (vg_t)); | ||||
|  | ||||
| 	xx32(vg_number); | ||||
| 	xx32(vg_access); | ||||
| 	xx32(vg_status); | ||||
| 	xx32(lv_max); | ||||
| 	xx32(lv_cur); | ||||
|  | ||||
| 	vg->lv_open = 0; | ||||
|  | ||||
| 	xx32(pv_max); | ||||
| 	xx32(pv_cur); | ||||
| 	xx32(pv_act); | ||||
|  | ||||
| 	vg->dummy = 0; | ||||
|  | ||||
| 	xx32(vgda); | ||||
| 	xx32(pe_size); | ||||
| 	xx32(pe_total); | ||||
| 	xx32(pe_allocated); | ||||
| 	xx32(pvg_total); | ||||
|  | ||||
| #undef xx32 | ||||
|  | ||||
| 	memset(&vg->pv, 0, sizeof (vg->pv)); | ||||
| 	memset(&vg->lv, 0, sizeof (vg->lv)); | ||||
|  | ||||
| 	memset(vg->vg_uuid, 0, sizeof (vg->vg_uuid)); | ||||
| 	memcpy(vg->vg_uuid, vg_disk->vg_uuid, UUID_LEN); | ||||
|  | ||||
| 	return vg; | ||||
| } | ||||
|  | ||||
| vg_disk_t *vg_copy_to_disk(vg_t * vg_core) | ||||
| { | ||||
| 	vg_disk_t *vg; | ||||
|  | ||||
| 	if (!vg_core || | ||||
| /* FIXME:	vg_check_consistency(vg_core) || */ | ||||
| 	    !(vg = dbg_malloc(sizeof (*vg)))) | ||||
| 		return 0; | ||||
|  | ||||
| 	memset(vg, 0, sizeof (*vg)); | ||||
|  | ||||
| #define xx32(v) vg->v = LVM_TO_DISK32(vg_core->v) | ||||
|  | ||||
| 	xx32(vg_number); | ||||
| 	xx32(vg_access); | ||||
| 	xx32(vg_status); | ||||
| 	xx32(lv_max); | ||||
| 	xx32(lv_cur); | ||||
|  | ||||
| 	vg->lv_open = 0; | ||||
|  | ||||
| 	xx32(pv_max); | ||||
| 	xx32(pv_cur); | ||||
| 	xx32(pv_act); | ||||
|  | ||||
| 	vg->dummy = 0; | ||||
|  | ||||
| 	xx32(vgda); | ||||
| 	xx32(pe_size); | ||||
| 	xx32(pe_total); | ||||
| 	xx32(pe_allocated); | ||||
| 	xx32(pvg_total); | ||||
|  | ||||
| #undef xx32 | ||||
|  | ||||
| 	memcpy(vg->vg_uuid, vg_core->vg_uuid, UUID_LEN); | ||||
|  | ||||
| 	return vg; | ||||
| } | ||||
|  | ||||
| pe_disk_t *pe_copy_from_disk(pe_disk_t * pe_file, int count) | ||||
| { | ||||
| 	int i; | ||||
| 	pe_disk_t *pe; | ||||
| 	size_t s = sizeof (*pe) * count; | ||||
|  | ||||
| 	if (!pe_file || count <= 0 || !(pe = dbg_malloc(s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	memset(pe, 0, s); | ||||
| 	for (i = 0; i < count; i++) { | ||||
| 		pe[i].lv_num = LVM_TO_CORE16(pe_file[i].lv_num); | ||||
| 		pe[i].le_num = LVM_TO_CORE16(pe_file[i].le_num); | ||||
| 	} | ||||
|  | ||||
| 	return pe; | ||||
| } | ||||
|  | ||||
| pe_disk_t *pe_copy_to_disk(pe_disk_t * pe_core, int count) | ||||
| { | ||||
| 	int i; | ||||
| 	pe_disk_t *pe; | ||||
| 	size_t s = sizeof (*pe) * count; | ||||
|  | ||||
| 	if (!pe_core || count <= 0 || !(pe = dbg_malloc(s))) | ||||
| 		return 0; | ||||
|  | ||||
| 	memset(pe, 0, s); | ||||
| 	for (i = 0; i < count; i++) { | ||||
| 		pe[i].lv_num = LVM_TO_DISK16(pe_core[i].lv_num); | ||||
| 		pe[i].le_num = LVM_TO_DISK16(pe_core[i].le_num); | ||||
| 	} | ||||
|  | ||||
| 	return pe; | ||||
| } | ||||
|  | ||||
| pv_t *pv_read_lvm_v1(struct dev_mgr * dm, const char *pv_name) | ||||
| { | ||||
| 	int pv_handle = -1; | ||||
| 	ssize_t read_ret; | ||||
| 	ssize_t bytes_read = 0; | ||||
| 	static pv_disk_t pv_this; | ||||
| 	struct stat stat_b; | ||||
| 	pv_t *pv = NULL; | ||||
| 	struct device *pv_dev; | ||||
|  | ||||
| 	if ((pv_handle = open(pv_name, O_RDONLY)) == -1) { | ||||
| 		log_error("%s: open failed: %s", pv_name, strerror(errno)); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if ((fstat(pv_handle, &stat_b))) { | ||||
| 		log_error("%s: fstat failed: %s", pv_name, strerror(errno)); | ||||
| 		goto pv_read_lvm_v1_out; | ||||
| 	} | ||||
|  | ||||
| 	while ((bytes_read < sizeof (pv_this) && | ||||
| 		(read_ret = read(pv_handle, &pv_this + bytes_read, | ||||
| 				 sizeof (pv_this) - bytes_read)) != -1)) | ||||
| 		bytes_read += read_ret; | ||||
|  | ||||
| 	if (read_ret == -1) { | ||||
| 		log_error("%s: read failed: %s", pv_name, strerror(errno)); | ||||
| 		goto pv_read_lvm_v1_out; | ||||
| 	} | ||||
|  | ||||
| 	pv = pv_copy_from_disk(&pv_this); | ||||
|  | ||||
| 	/* correct for imported/moved volumes */ | ||||
| 	if (!(pv_dev = dev_by_dev(dm, stat_b.st_rdev))) { | ||||
| 		log_error("Device missing from cache"); | ||||
| 		goto pv_read_lvm_v1_out; | ||||
| 	} | ||||
|  | ||||
| 	memset(pv->pv_name, 0, sizeof (pv->pv_name)); | ||||
| 	strncpy(pv->pv_name, pv_dev->name, sizeof (pv->pv_name) - 1); | ||||
|  | ||||
| 	/* FIXME: Deleted version / consistency / export checks! */ | ||||
|  | ||||
| 	pv->pv_dev = stat_b.st_rdev; | ||||
|  | ||||
|       pv_read_lvm_v1_out: | ||||
| 	if (pv_handle != -1) | ||||
| 		close(pv_handle); | ||||
|  | ||||
| 	return pv; | ||||
| } | ||||
|  | ||||
| pe_disk_t *pv_read_pe_lvm_v1(const char *pv_name, const pv_t * pv) | ||||
| { | ||||
| 	int pv_handle = -1; | ||||
| 	uint size = 0; | ||||
| 	ssize_t read_ret; | ||||
| 	ssize_t bytes_read = 0; | ||||
| 	pe_disk_t *pe = NULL; | ||||
| 	pe_disk_t *pe_this; | ||||
|  | ||||
| 	size = pv->pe_total * sizeof (pe_disk_t); | ||||
| 	if (size > pv->pe_on_disk.size) { | ||||
| 		log_error("PEs extend beyond end of volume group!"); | ||||
| 		return pe; /*NULL*/ | ||||
| 	} | ||||
|  | ||||
| 	if ((pv_handle = open(pv_name, O_RDONLY)) == -1) { | ||||
| 		log_error("%s: open failed: %s", pv_name, strerror(errno)); | ||||
| 		goto pv_read_pe_out; | ||||
| 	} | ||||
|  | ||||
| 	if (lseek(pv_handle, pv->pe_on_disk.base, SEEK_SET) != | ||||
| 		 pv->pe_on_disk.base) { | ||||
| 		log_error("%s: lseek to PE failed: %s", pv_name, strerror(errno)); | ||||
| 		goto pv_read_pe_out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(pe_this = dbg_malloc(size))) { | ||||
| 		log_error("PE malloc failed"); | ||||
| 		goto pv_read_pe_out; | ||||
| 	} | ||||
|  | ||||
| 	while ((bytes_read < size && | ||||
| 		(read_ret = read(pv_handle, (void *)pe_this + bytes_read, | ||||
| 				 size - bytes_read)) != -1)) | ||||
| 		bytes_read += read_ret; | ||||
|  | ||||
| 	if (read_ret == -1) { | ||||
| 		log_error("%s: read failed: %s", pv_name, strerror(errno)); | ||||
| 		goto pv_read_pe_out; | ||||
| 	} | ||||
|  | ||||
| 	pe = pe_copy_from_disk(pe_this, pv->pe_total); | ||||
| 	 | ||||
|       pv_read_pe_out: | ||||
| 	if (pv_handle != -1) | ||||
| 		close(pv_handle); | ||||
|  | ||||
| 	dbg_free(pe_this); | ||||
|  | ||||
| 	return pe; | ||||
| } | ||||
|  | ||||
| lv_disk_t *pv_read_lvs_lvm_v1(const pv_t *pv) | ||||
| { | ||||
| 	int pv_handle = -1; | ||||
| 	uint size = 0; | ||||
| 	ssize_t read_ret; | ||||
| 	ssize_t bytes_read = 0; | ||||
| 	lv_disk_t *lvs; | ||||
|  | ||||
| 	/* FIXME: replace lv_cur? */ | ||||
| 	size = pv->lv_cur * sizeof (lv_disk_t); | ||||
|  | ||||
| 	if ((pv_handle = open(pv->pv_name, O_RDONLY)) == -1) { | ||||
| 		log_error("%s: open failed: %s", pv->pv_name, strerror(errno)); | ||||
| 		goto pv_read_lvs_out; | ||||
| 	} | ||||
|  | ||||
| 	if (lseek(pv_handle, pv->lv_on_disk.base, SEEK_SET) != | ||||
| 		 pv->lv_on_disk.base) { | ||||
| 		log_error("%s: lseek to LV failed: %s", pv->pv_name, strerror(errno)); | ||||
| 		goto pv_read_lvs_out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(lvs = dbg_malloc(size))) { | ||||
| 		log_error("PE malloc failed"); | ||||
| 		goto pv_read_lvs_out; | ||||
| 	} | ||||
|  | ||||
| 	while ((bytes_read < size && | ||||
| 		(read_ret = read(pv_handle, (void *) lvs + bytes_read, | ||||
| 				 size - bytes_read)) != -1)) | ||||
| 		bytes_read += read_ret; | ||||
|  | ||||
| 	if (read_ret == -1) { | ||||
| 		log_error("%s: read failed: %s", pv->pv_name, strerror(errno)); | ||||
| 		goto pv_read_lvs_out; | ||||
| 	} | ||||
|  | ||||
|       pv_read_lvs_out: | ||||
| 	if (pv_handle != -1) | ||||
| 		close(pv_handle); | ||||
|  | ||||
| 	/* Caller frees */ | ||||
| 	return lvs; | ||||
| } | ||||
|  | ||||
							
								
								
									
										205
									
								
								lib/metadata/lvm_v1.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								lib/metadata/lvm_v1.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| /* | ||||
|  * 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_V1_H_INCLUDE | ||||
| #define _LVM_V1_H_INCLUDE | ||||
|  | ||||
| #ifdef __KERNEL__ | ||||
| #include <linux/kdev_t.h> | ||||
| #include <linux/list.h> | ||||
| #else | ||||
| #define __KERNEL__ | ||||
| #include <linux/kdev_t.h> | ||||
| #include <linux/list.h> | ||||
| #undef __KERNEL__ | ||||
| #endif                          /* #ifndef __KERNEL__ */ | ||||
|  | ||||
| #include <linux/major.h> | ||||
| #include <linux/spinlock.h> | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <linux/version.h> | ||||
|  | ||||
| typedef unsigned long blkoff_t; | ||||
|  | ||||
| #ifndef uint8_t | ||||
| #  define uint8_t       unsigned char | ||||
| #endif | ||||
| #ifndef uint16_t | ||||
| #  define uint16_t      unsigned short int | ||||
| #endif | ||||
| #ifndef uint32_t | ||||
| #  define uint32_t      unsigned int | ||||
| #endif | ||||
| #ifndef uint64_t | ||||
| #  define uint64_t      unsigned long long int | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * This is basically the on disk metadata layout version. | ||||
|  */ | ||||
| #define LVM_STRUCT_VERSION 2 | ||||
|  | ||||
| /* | ||||
|  * Limits for VGs, PVs per VG and LVs per VG.  The ABS_* limit's are | ||||
|  * used in defining the data structures, MAX_* are the real limits | ||||
|  * used. | ||||
|  */ | ||||
| #define MAX_VG  99 | ||||
| #define MAX_PV  256 | ||||
| #define MAX_LV  256     /* caused by 8 bit minor */ | ||||
|  | ||||
| #define NAME_LEN 128            /* the maximum length of various names */ | ||||
| #define UUID_LEN 32             /* some components have unique identifiers */ | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * A little struct to hold a sector range on a block device.  Used to | ||||
|  * hold the location of on disk metadata | ||||
|  */ | ||||
| typedef struct { | ||||
|         uint32_t base; | ||||
|         uint32_t size; | ||||
| } lvm_disk_data_t; | ||||
|  | ||||
|  | ||||
| /*********************************************************** | ||||
|  * On disk representation: | ||||
|  * | ||||
|  * The beginning of each PV contains metadata, known int the Volume | ||||
|  * Group Data Area (VGDA).  This metadata has the following structure: | ||||
|  * | ||||
|  *  offset              description                         size | ||||
|  *   ---------------   ----------------------------------  ------------ | ||||
|  *   0                 physical volume structure           ~500 byte | ||||
|  *   1K                volume group structure              ~200 byte | ||||
|  *   6K                namelist of physical volumes        128 byte each | ||||
|  *  + n * ~300byte      n logical volume structures         ~300 byte each | ||||
|  *   + m * 4byte       m physical extent alloc. structs    4 byte each | ||||
|  *  + ~ 1 PE size       physical extents                    total * size | ||||
|  * | ||||
|  * Spaces are left between the structures for later extensions. | ||||
|  ***********************************************************/ | ||||
|  | ||||
| /* | ||||
|  * physical volume - disk | ||||
|  */ | ||||
| typedef struct { | ||||
| 	uint8_t id[2];		             /* identifier */ | ||||
| 	uint16_t version;	             /* lvm struct version */ | ||||
|  | ||||
| 	/* these define the locations of various bits of on disk metadata */ | ||||
| 	lvm_disk_data_t pv_on_disk;          /* pv_disk_t location */ | ||||
| 	lvm_disk_data_t vg_on_disk;          /* vg_disk_t location */ | ||||
| 	lvm_disk_data_t pv_uuidlist_on_disk; /* uuid list location */ | ||||
| 	lvm_disk_data_t lv_on_disk;          /* lv_disk_t locations */ | ||||
| 	lvm_disk_data_t pe_on_disk;          /* pe mapping table location */ | ||||
|  | ||||
| 	uint8_t pv_uuid[NAME_LEN];           /* uuid for this PV */ | ||||
| 	uint8_t vg_name[NAME_LEN];           /* which vg it belongs to */ | ||||
| 	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; | ||||
|  | ||||
| 	/* data_location.base holds the start of the pe's in sectors */ | ||||
| 	uint32_t pe_start; | ||||
| } pv_disk_t; | ||||
|  | ||||
| /* | ||||
|  * logical volume - disk | ||||
|  */ | ||||
| typedef struct { | ||||
| 	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 origin */ | ||||
| 	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; | ||||
| } lv_disk_t; | ||||
|  | ||||
| /* | ||||
|  * volume group - disk | ||||
|  */ | ||||
| typedef struct { | ||||
| 	uint8_t vg_uuid[UUID_LEN];	/* volume group UUID */ | ||||
|  | ||||
|         /* rest of v1 VG name */ | ||||
| 	uint8_t vg_name_dummy[NAME_LEN-UUID_LEN]; | ||||
| 	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 */ | ||||
| } vg_disk_t; | ||||
|  | ||||
| /* | ||||
|  * pe mapping - disk | ||||
|  */ | ||||
| typedef struct { | ||||
| 	uint16_t lv_num; | ||||
| 	uint16_t le_num; | ||||
| } pe_disk_t; | ||||
|  | ||||
| /* | ||||
|  * copy on write tables - disk | ||||
|  */ | ||||
| typedef struct lv_COW_table_disk_v1 { | ||||
| 	uint64_t pv_org_number; | ||||
| 	uint64_t pv_org_rsector; | ||||
| 	uint64_t pv_snap_number; | ||||
| 	uint64_t pv_snap_rsector; | ||||
| } lv_COW_table_disk_t; | ||||
|  | ||||
|  | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										229
									
								
								lib/metadata/metadata.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								lib/metadata/metadata.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "pool.h" | ||||
| #include "device.h" | ||||
| #include "dev-cache.h" | ||||
| #include "metadata.h" | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| int _add_pv_to_vg(struct io_space *ios, struct volume_group *vg, | ||||
| 		  const char *pv_name) | ||||
| { | ||||
| 	struct pv_list *pvl; | ||||
| 	struct physical_volume *pv; | ||||
|  | ||||
| 	log_verbose("Adding physical volume '%s' to volume group '%s'", | ||||
| 		pv_name, vg->name); | ||||
|  | ||||
| 	if (!(pvl = pool_alloc(ios->mem, sizeof (*pvl)))) { | ||||
| 		log_error("pv_list allocation for '%s' failed", pv_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(pv = ios->pv_read(ios, pv_name))) { | ||||
| 		log_error("Failed to read existing physical volume '%s'", | ||||
| 			  pv_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (*pv->vg_name) { | ||||
| 		log_error("Physical volume '%s' is already in volume group " | ||||
| 			  "'%s'", pv_name, pv->vg_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME For LVM2, set on PV creation instead of here? */ | ||||
| 	pv->status |= ALLOCATED_PV; | ||||
|  | ||||
| 	/* FIXME check this */ | ||||
| 	pv->exported = NULL; | ||||
|  | ||||
| 	if (!(pv->vg_name = pool_strdup(ios->mem, vg->name))) { | ||||
| 		log_error("vg->name allocation failed for '%s'", pv_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME Tie this to activation or not? */ | ||||
| 	pv->status |= ACTIVE; | ||||
|  | ||||
| 	/* Units of 512-byte sectors */ | ||||
| 	if (!dev_get_size(pv->dev, &pv->size)) { | ||||
| 		stack; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Units of 512-byte sectors */ | ||||
| 	pv->pe_size = vg->extent_size; | ||||
|  | ||||
| 	/* | ||||
| 	 * The next two fields should be corrected | ||||
| 	 * by ios->pv_setup. | ||||
| 	 */ | ||||
| 	pv->pe_start = 0; | ||||
| 	pv->pe_count = pv->size / pv->pe_size; | ||||
|  | ||||
| 	pv->pe_allocated = 0; | ||||
|  | ||||
| 	if (!ios->pv_setup(ios, pv, vg)) { | ||||
| 		log_debug("Format-specific setup of physical volume '%s' " | ||||
| 			  "failed.", pv_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (find_pv_in_vg(vg, pv_name)) { | ||||
| 		log_error("Physical volume '%s' listed more than once.", | ||||
| 			  pv_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (vg->pv_count == vg->max_pv) { | ||||
| 		log_error("No space for '%s' - volume group '%s' " | ||||
| 			  "holds max %d physical volume(s).", pv_name, | ||||
| 			  vg->name, vg->max_pv); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	memcpy(&pvl->pv, pv, sizeof (struct physical_volume)); | ||||
|  | ||||
| 	list_add(&pvl->list, &vg->pvs); | ||||
| 	vg->pv_count++; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int vg_extend(struct io_space *ios, struct volume_group *vg, int pv_count, | ||||
| 	   char **pv_names) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	/* attach each pv */ | ||||
| 	for (i = 0; i < pv_count; i++) | ||||
| 		if (!_add_pv_to_vg(ios, vg, pv_names[i])) { | ||||
| 			log_error("Unable to add physical volume '%s' to " | ||||
| 				  "volume group '%s'.", pv_names[i], vg->name); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| struct volume_group *vg_create(struct io_space *ios, const char *vg_name, | ||||
| 			       uint64_t extent_size, int max_pv, int max_lv, | ||||
| 			       int pv_count, char **pv_names) | ||||
| { | ||||
| 	struct volume_group *vg; | ||||
|  | ||||
| 	if (!(vg = pool_alloc(ios->mem, sizeof (*vg)))) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* is this vg name already in use ? */ | ||||
| 	if (ios->vg_read(ios, vg_name)) { | ||||
| 		log_err("A volume group called '%s' already exists.", vg_name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!id_create(&vg->id)) { | ||||
| 		log_err("Couldn't create uuid for volume group '%s'.", vg_name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
|         /* Strip prefix if present */ | ||||
|         if (!strncmp(vg_name, ios->prefix, strlen(ios->prefix))) | ||||
|                 vg_name += strlen(ios->prefix); | ||||
|  | ||||
| 	if (!(vg->name = pool_strdup(ios->mem, vg_name))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	vg->status = (ACTIVE | EXTENDABLE_VG | LVM_READ | LVM_WRITE); | ||||
|  | ||||
| 	vg->extent_size = extent_size; | ||||
| 	vg->extent_count = 0; | ||||
| 	vg->free_count = 0; | ||||
|  | ||||
| 	vg->max_lv = max_lv; | ||||
| 	vg->max_pv = max_pv; | ||||
|  | ||||
| 	vg->pv_count = 0; | ||||
| 	INIT_LIST_HEAD(&vg->pvs); | ||||
|  | ||||
| 	vg->lv_count = 0; | ||||
| 	INIT_LIST_HEAD(&vg->lvs); | ||||
|  | ||||
| 	if (!ios->vg_setup(ios, vg)) { | ||||
| 		log_error("Format specific setup of volume group '%s' failed.", | ||||
| 			  vg_name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	/* attach the pv's */ | ||||
| 	if (!vg_extend(ios, vg, pv_count, pv_names)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	return vg; | ||||
|  | ||||
|       bad: | ||||
| 	pool_free(ios->mem, vg); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| struct physical_volume *pv_create(struct io_space *ios, const char *name) | ||||
| { | ||||
| 	struct physical_volume *pv = pool_alloc(ios->mem, sizeof (*pv)); | ||||
|  | ||||
| 	if (!pv) { | ||||
| 		stack; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	id_create(&pv->id); | ||||
| 	if (!(pv->dev = dev_cache_get(name, ios->filter))) { | ||||
| 		log_err("Couldn't find device '%s'", name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!(pv->vg_name = pool_alloc(ios->mem, NAME_LEN))) { | ||||
| 		stack; | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	*pv->vg_name = 0; | ||||
| 	pv->exported = NULL; | ||||
| 	pv->status = 0; | ||||
|  | ||||
| 	if (!dev_get_size(pv->dev, &pv->size)) { | ||||
| 		log_err("Couldn't get size of device '%s'", name); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	pv->pe_size = 0; | ||||
| 	pv->pe_start = 0; | ||||
| 	pv->pe_count = 0; | ||||
| 	pv->pe_allocated = 0; | ||||
| 	return pv; | ||||
|  | ||||
|       bad: | ||||
| 	pool_free(ios->mem, pv); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| struct list_head *find_pv_in_vg(struct volume_group *vg, const char *pv_name) | ||||
| { | ||||
| 	struct list_head *pvh; | ||||
| 	list_for_each(pvh, &vg->pvs) { | ||||
| 		if (!strcmp(list_entry(pvh, struct pv_list, list)->pv.dev->name, | ||||
| 			    pv_name)) return pvh; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
|  | ||||
| } | ||||
							
								
								
									
										247
									
								
								lib/metadata/metadata.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								lib/metadata/metadata.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,247 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  * | ||||
|  * This is the in core representation of a volume group and its | ||||
|  * associated physical and logical volumes. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_METADATA_H | ||||
| #define _LVM_METADATA_H | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include "dev-cache.h" | ||||
| #include "list.h" | ||||
| #include "uuid.h" | ||||
|  | ||||
| #define NAME_LEN 128 | ||||
|  | ||||
| /* Various flags */ | ||||
| /* Note that the bits no longer necessarily correspond to LVM1 disk format */ | ||||
|  | ||||
| #define ACTIVE               	0x00000001  /* PV VG LV */ | ||||
| #define EXPORTED_VG          	0x00000002  /* VG */  /* And PV too perhaps? */ | ||||
| #define EXTENDABLE_VG        	0x00000004  /* VG */ | ||||
| #define ALLOCATED_PV         	0x00000008  /* PV */ | ||||
|  | ||||
| #define SPINDOWN_LV          	0x00000010  /* LV */ | ||||
| #define BADBLOCK_ON       	0x00000020  /* LV */ | ||||
|  | ||||
| #define LVM_READ              	0x00000100  /* LV VG */ | ||||
| #define LVM_WRITE             	0x00000200  /* LV VG */ | ||||
| #define CLUSTERED         	0x00000400  /* VG */ | ||||
| #define SHARED            	0x00000800  /* VG */ | ||||
|  | ||||
| #define ALLOC_STRICT      	0x00001000  /* LV */ | ||||
| #define ALLOC_CONTIGUOUS  	0x00002000  /* LV */ | ||||
| #define SNAPSHOT          	0x00004000  /* LV */ | ||||
| #define SNAPSHOT_ORG      	0x00008000  /* LV */ | ||||
|  | ||||
|  | ||||
| #define EXPORTED_TAG "PV_EXP"  /* Identifier of exported PV */ | ||||
| #define IMPORTED_TAG "PV_IMP"  /* Identifier of imported PV */ | ||||
|  | ||||
|  | ||||
| struct physical_volume { | ||||
|         struct id id; | ||||
| 	struct device *dev; | ||||
| 	char *vg_name; | ||||
| 	char *exported; | ||||
|  | ||||
|         uint32_t status; | ||||
|         uint64_t size; | ||||
|  | ||||
|         /* physical extents */ | ||||
|         uint64_t pe_size; | ||||
|         uint64_t pe_start; | ||||
|         uint32_t pe_count; | ||||
|         uint32_t pe_allocated; | ||||
| }; | ||||
|  | ||||
| struct pe_specifier { | ||||
|         struct physical_volume *pv; | ||||
|         uint32_t pe; | ||||
| }; | ||||
|  | ||||
| struct logical_volume { | ||||
|         /* disk */ | ||||
| 	struct id id; | ||||
|         char *name; | ||||
|  | ||||
|         uint32_t status; | ||||
|  | ||||
|         uint64_t size; | ||||
|         uint32_t le_count; | ||||
|  | ||||
|         /* le -> pe mapping array */ | ||||
|         struct pe_specifier *map; | ||||
| }; | ||||
|  | ||||
| struct volume_group { | ||||
| 	struct id id; | ||||
| 	char *name; | ||||
|  | ||||
|         uint32_t status; | ||||
|  | ||||
|         uint64_t extent_size; | ||||
|         uint32_t extent_count; | ||||
|         uint32_t free_count; | ||||
|  | ||||
|         uint32_t max_lv; | ||||
|         uint32_t max_pv; | ||||
|  | ||||
|         /* physical volumes */ | ||||
|         uint32_t pv_count; | ||||
| 	struct list_head pvs; | ||||
|  | ||||
|         /* logical volumes */ | ||||
|         uint32_t lv_count; | ||||
| 	struct list_head lvs; | ||||
| }; | ||||
|  | ||||
| struct name_list { | ||||
| 	struct list_head list; | ||||
| 	char *name; | ||||
| }; | ||||
|  | ||||
| struct pv_list { | ||||
| 	struct list_head list; | ||||
| 	struct physical_volume pv; | ||||
| }; | ||||
|  | ||||
| struct lv_list { | ||||
| 	struct list_head list; | ||||
| 	struct logical_volume lv; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Ownership of objects passes to caller. | ||||
|  */ | ||||
| struct io_space { | ||||
| 	/* | ||||
| 	 * Returns a name_list of vg's. | ||||
| 	 */ | ||||
| 	struct list_head *(*get_vgs)(struct io_space *is); | ||||
|  | ||||
| 	/* | ||||
| 	 * Returns pv_list of fully-populated pv structures. | ||||
| 	 */ | ||||
| 	struct list_head *(*get_pvs)(struct io_space *is); | ||||
|  | ||||
| 	/* | ||||
| 	 * Return PV with given path. | ||||
| 	 */ | ||||
| 	struct physical_volume *(*pv_read)(struct io_space *is, | ||||
| 					   const char *pv_name); | ||||
|  | ||||
| 	/* | ||||
| 	 * Tweak an already filled out a pv ready | ||||
| 	 * for importing into a vg.  eg. pe_count | ||||
| 	 * is format specific. | ||||
| 	 */ | ||||
| 	int (*pv_setup)(struct io_space *is, struct physical_volume *pv, | ||||
| 			struct volume_group *vg); | ||||
|  | ||||
| 	/* | ||||
| 	 * Write a PV structure to disk. Fails if | ||||
| 	 * the PV is in a VG ie pv->vg_name must | ||||
| 	 * be null. | ||||
| 	 */ | ||||
| 	int (*pv_write)(struct io_space *is, struct physical_volume *pv); | ||||
|  | ||||
| 	/* | ||||
| 	 * Tweak an already filled out vg.  eg, | ||||
| 	 * max_pv is format specific. | ||||
| 	 */ | ||||
| 	int (*vg_setup)(struct io_space *is, struct volume_group *vg); | ||||
|  | ||||
| 	/* | ||||
| 	 * If vg_name doesn't contain any slash, | ||||
| 	 * this function adds prefix. | ||||
| 	 */ | ||||
| 	struct volume_group *(*vg_read)(struct io_space *is, | ||||
| 					const char *vg_name); | ||||
|  | ||||
| 	/* | ||||
| 	 * Write out complete VG metadata.  Ensure | ||||
| 	 * *internal* consistency before writing | ||||
| 	 * anything.  eg. PEs can't refer to PVs | ||||
| 	 * not part of the VG.  Order write sequence | ||||
| 	 * to aid recovery if process is aborted | ||||
| 	 * (eg flush entire set of changes to each | ||||
| 	 * disk in turn) It is the responsibility | ||||
| 	 * of the caller to ensure external | ||||
| 	 * consistency, eg by calling pv_write() | ||||
| 	 * if removing PVs from a VG or calling | ||||
| 	 * vg_write() a second time if splitting a | ||||
| 	 * VG into two.  vg_write() must not read | ||||
| 	 * or write from any PVs not included in | ||||
| 	 * the volume_group structure it is | ||||
| 	 * handed. | ||||
| 	 */ | ||||
| 	int (*vg_write)(struct io_space *is, struct volume_group *vg); | ||||
|  | ||||
| 	/* | ||||
| 	 * Destructor for this object. | ||||
| 	 */ | ||||
| 	void (*destroy)(struct io_space *is); | ||||
|  | ||||
| 	/* | ||||
| 	 * Current volume group prefix. | ||||
| 	 */ | ||||
| 	char *prefix; | ||||
| 	struct pool *mem; | ||||
| 	struct dev_filter *filter; | ||||
| 	void *private; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Utility functions | ||||
|  */ | ||||
| struct volume_group *vg_create(struct io_space *ios, const char *name, | ||||
| 			       uint64_t extent_size, int max_pv, int max_lv, | ||||
| 			       int pv_count, char **pv_names); | ||||
| struct physical_volume *pv_create(struct io_space *ios, const char *name); | ||||
|  | ||||
|  | ||||
|  | ||||
| int vg_extend(struct io_space *ios, struct volume_group *vg, int pv_count, | ||||
|               char **pv_names); | ||||
|  | ||||
|  | ||||
|  | ||||
| /* FIXME: Move to other files */ | ||||
| struct io_space *create_text_format(struct dev_filter *filter, | ||||
| 				    const char *text_file); | ||||
|  | ||||
| int id_eq(struct id *op1, struct id *op2); | ||||
|  | ||||
| /* Create consistent new empty structures, populated with defaults */ | ||||
| struct volume_group *vg_create(); | ||||
|  | ||||
| int vg_destroy(struct volume_group *vg); | ||||
|  | ||||
| /* Manipulate PV structures */ | ||||
| int pv_add(struct volume_group *vg, struct physical_volume *pv); | ||||
| int pv_remove(struct volume_group *vg, struct physical_volume *pv); | ||||
| struct physical_volume *pv_find(struct volume_group *vg, | ||||
| 				const char *pv_name); | ||||
|  | ||||
| /* Add an LV to a given VG */ | ||||
| int lv_add(struct volume_group *vg, struct logical_volume *lv); | ||||
|  | ||||
| /* Remove an LV from a given VG */ | ||||
| int lv_remove(struct volume_group *vg, struct logical_volume *lv); | ||||
|  | ||||
| /* ? Return the VG that contains a given LV (based on path given in lv_name) */ | ||||
| /* (or environment var?) */ | ||||
| struct volume_group *vg_find(const char *lv_name); | ||||
|  | ||||
| /* Find an LV within a given VG */ | ||||
| struct logical_volume *lv_find(struct volume_group *vg, const char *lv_name); | ||||
|  | ||||
| /* Find a PV within a given VG */ | ||||
| struct list_head *find_pv_in_vg(struct volume_group *vg, const char *pv_name); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										192
									
								
								lib/mm/dbg_malloc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								lib/mm/dbg_malloc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
|  | ||||
| struct memblock { | ||||
| 	struct memblock *prev, *next; /* All allocated blocks are linked */ | ||||
| 	size_t length;                /* Size of the requested block */ | ||||
| 	int id;                       /* Index of the block */ | ||||
| 	const char *file;             /* File that allocated */ | ||||
| 	int line;                     /* Line that allocated */ | ||||
| 	void *magic;                  /* Address of this block */ | ||||
| }; | ||||
|  | ||||
| static struct { | ||||
| 	unsigned int blocks, mblocks; | ||||
| 	unsigned int bytes, mbytes; | ||||
|  | ||||
| } _mem_stats = {0, 0, 0, 0}; | ||||
|  | ||||
| static struct memblock *_head = 0; | ||||
| static struct memblock *_tail = 0; | ||||
|  | ||||
| void *malloc_aux(size_t s, const char *file, int line) | ||||
| { | ||||
| 	struct memblock *nb; | ||||
| 	size_t tsize = s + sizeof(*nb) + sizeof(unsigned long); | ||||
|  | ||||
| 	if (!(nb = malloc(tsize))) { | ||||
| 		log_error("couldn't allocate any memory, size = %u", s); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* set up the file and line info */ | ||||
| 	nb->file = file; | ||||
| 	nb->line = line; | ||||
|  | ||||
| #ifdef BOUNDS_CHECK | ||||
| 	bounds_check(); | ||||
| #endif | ||||
|  | ||||
| 	/* setup fields */ | ||||
| 	nb->magic = nb + 1; | ||||
| 	nb->length = s; | ||||
| 	nb->id = ++_mem_stats.blocks; | ||||
| 	nb->next = 0; | ||||
| 	nb->prev = _tail; | ||||
|  | ||||
| 	/* link to tail of the list */ | ||||
| 	if (!_head) | ||||
| 		_head = _tail = nb; | ||||
| 	else { | ||||
| 		_tail->next = nb; | ||||
| 		_tail = nb; | ||||
| 	} | ||||
|  | ||||
| 	/* stomp a pretty pattern across the new memory | ||||
| 	   and fill in the boundary bytes */ | ||||
| 	{ | ||||
| 		char *ptr = (char *) (nb + 1); | ||||
| 		int i; | ||||
| 		for (i = 0; i < s; i++) | ||||
| 			*ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe; | ||||
|  | ||||
| 		for (i = 0; i < sizeof(unsigned long); i++) | ||||
| 			*ptr++ = (char) nb->id; | ||||
| 	} | ||||
|  | ||||
| 	if (_mem_stats.blocks > _mem_stats.mblocks) | ||||
| 		_mem_stats.mblocks = _mem_stats.blocks; | ||||
|  | ||||
| 	_mem_stats.bytes += s; | ||||
| 	if (_mem_stats.bytes > _mem_stats.mbytes) | ||||
| 		_mem_stats.mbytes = _mem_stats.bytes; | ||||
|  | ||||
| 	return nb + 1; | ||||
| } | ||||
|  | ||||
| void free_aux(void *p) | ||||
| { | ||||
| 	char *ptr; | ||||
| 	int i; | ||||
| 	struct memblock *mb = ((struct memblock *) p) - 1; | ||||
| 	if (!p) | ||||
| 		return; | ||||
|  | ||||
| #ifdef BOUNDS_CHECK | ||||
| 	bounds_check(); | ||||
| #endif | ||||
|  | ||||
| 	/* sanity check */ | ||||
| 	assert(mb->magic == p); | ||||
|  | ||||
| 	/* check data at the far boundary */ | ||||
| 	ptr = ((char *) mb) + sizeof(struct memblock) + mb->length; | ||||
| 	for (i = 0; i < sizeof(unsigned long); i++) | ||||
| 		if(*ptr++ != (char) mb->id) | ||||
| 			assert(!"Damage at far end of block"); | ||||
|  | ||||
| 	/* have we freed this before ? */ | ||||
| 	assert(mb->id != 0); | ||||
| 	mb->id = 0; | ||||
|  | ||||
| 	/* stomp a different pattern across the memory */ | ||||
| 	ptr = ((char *) mb) + sizeof(struct memblock); | ||||
| 	for (i = 0; i < mb->length; i++) | ||||
| 		*ptr++ = i & 1 ? (char) 0xde : (char) 0xad; | ||||
|  | ||||
| 	/* unlink */ | ||||
| 	if (mb->prev) | ||||
| 		mb->prev->next = mb->next; | ||||
| 	else | ||||
| 		_head = mb->next; | ||||
|  | ||||
| 	if (mb->next) | ||||
| 		mb->next->prev = mb->prev; | ||||
| 	else | ||||
| 		_tail = mb->prev; | ||||
|  | ||||
| 	assert(_mem_stats.blocks); | ||||
| 	_mem_stats.blocks--; | ||||
| 	_mem_stats.bytes -= mb->length; | ||||
|  | ||||
| 	/* free the memory */ | ||||
| 	free(mb); | ||||
| } | ||||
|  | ||||
| void *realloc_aux(void *p, unsigned int s, const char *file, int line) | ||||
| { | ||||
| 	void *r; | ||||
| 	struct memblock *mb = ((struct memblock *) p) - 1; | ||||
|  | ||||
| 	r = malloc_aux(s, file, line); | ||||
|  | ||||
| 	if (p) { | ||||
| 		memcpy(r, p, mb->length); | ||||
| 		free_aux(p); | ||||
| 	} | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| #ifdef DEBUG_MEM | ||||
| int dump_memory(void) | ||||
| { | ||||
| 	unsigned long tot = 0; | ||||
| 	struct memblock *mb; | ||||
| 	if (_head) | ||||
| 		log_very_verbose("You have a memory leak:"); | ||||
|  | ||||
| 	for (mb = _head; mb; mb = mb->next) { | ||||
| 		print_log(_LOG_INFO, mb->file, mb->line,  | ||||
| 			  "block %d at %p, size %d", | ||||
| 			  mb->id, mb->magic, mb->length); | ||||
| 		tot += mb->length; | ||||
| 	} | ||||
|  | ||||
| 	if (_head) | ||||
| 		log_very_verbose("%ld bytes leaked in total", tot); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void bounds_check(void) | ||||
| { | ||||
| 	struct memblock *mb = _head; | ||||
| 	while (mb) { | ||||
| 		int i; | ||||
| 		char *ptr = ((char *) (mb + 1)) + mb->length; | ||||
| 		for (i = 0; i < sizeof(unsigned long); i++) | ||||
| 			if (*ptr++ != (char) mb->id) | ||||
| 				assert(!"Memory smash"); | ||||
|  | ||||
| 		mb = mb->next; | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
							
								
								
									
										36
									
								
								lib/mm/dbg_malloc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								lib/mm/dbg_malloc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_DBG_MALLOC_H | ||||
| #define _LVM_DBG_MALLOC_H | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #ifdef DEBUG_MEM | ||||
| void *malloc_aux(size_t s, const char *file, int line); | ||||
| void free_aux(void *p); | ||||
| void *realloc_aux(void *p, unsigned int s, const char *file, int line); | ||||
| int dump_memory(void); | ||||
| void bounds_check(void); | ||||
|  | ||||
| #define dbg_malloc(s) malloc_aux((s), __FILE__, __LINE__) | ||||
| #define dbg_free(p) free_aux(p) | ||||
| #define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__) | ||||
| #else | ||||
| #define dbg_malloc(s) malloc(s) | ||||
| #define dbg_free(p) free(p) | ||||
| #define dbg_realloc(p, s) realloc(p, s) | ||||
| #define dump_memory() | ||||
| #define bounds_check() | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  */ | ||||
							
								
								
									
										222
									
								
								lib/mm/pool.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								lib/mm/pool.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "pool.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
|  | ||||
| struct chunk { | ||||
| 	char *begin, *end; | ||||
| 	struct chunk *prev; | ||||
| }; | ||||
|  | ||||
| struct pool { | ||||
| 	struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free | ||||
| 					      list to stop 'bobbling' */ | ||||
| 	size_t chunk_size; | ||||
| 	size_t object_len; | ||||
| 	unsigned  object_alignment; | ||||
| }; | ||||
|  | ||||
| void _align_chunk(struct chunk *c, unsigned alignment); | ||||
| struct chunk *_new_chunk(struct pool *p, size_t s); | ||||
|  | ||||
| /* by default things come out aligned for doubles */ | ||||
| #define DEFAULT_ALIGNMENT __alignof__ (double) | ||||
|  | ||||
| struct pool *pool_create(size_t chunk_hint) | ||||
| { | ||||
| 	size_t new_size = 1024; | ||||
| 	struct pool *p = dbg_malloc(sizeof(*p)); | ||||
|  | ||||
| 	if (!p) { | ||||
| 		log_err("couldn't create pool"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	memset(p, 0, sizeof(*p)); | ||||
|  | ||||
| 	/* round chunk_hint up to the next power of 2 */ | ||||
| 	p->chunk_size = chunk_hint + sizeof(struct chunk); | ||||
| 	while (new_size < p->chunk_size) | ||||
| 		new_size <<= 1; | ||||
| 	p->chunk_size = new_size; | ||||
| 	return p; | ||||
| } | ||||
|  | ||||
| void pool_destroy(struct pool *p) | ||||
| { | ||||
| 	struct chunk *c, *pr; | ||||
| 	dbg_free(p->spare_chunk); | ||||
| 	c = p->chunk; | ||||
| 	while (c) { | ||||
| 		pr = c->prev; | ||||
| 		dbg_free(c); | ||||
| 		c = pr; | ||||
| 	} | ||||
|  | ||||
| 	dbg_free(p); | ||||
| } | ||||
|  | ||||
| void *pool_alloc(struct pool *p, size_t s) | ||||
| { | ||||
| 	return pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT); | ||||
| } | ||||
|  | ||||
| void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment) | ||||
| { | ||||
| 	struct chunk *c = p->chunk; | ||||
| 	void *r; | ||||
|  | ||||
|         /* realign begin */ | ||||
| 	if (c) | ||||
| 		_align_chunk(c, alignment); | ||||
|  | ||||
| 	/* have we got room ? */ | ||||
| 	if(!c || (c->begin > c->end) || (c->end - c->begin < s)) { | ||||
| 		/* allocate new chunk */ | ||||
| 		int needed = s + alignment + sizeof(struct chunk); | ||||
| 		c = _new_chunk(p, (needed > p->chunk_size) ? | ||||
| 			       needed : p->chunk_size); | ||||
| 		_align_chunk(c, alignment); | ||||
| 	} | ||||
|  | ||||
| 	r = c->begin; | ||||
| 	c->begin += s; | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| void pool_empty(struct pool *p) | ||||
| { | ||||
| 	if (p->chunk) | ||||
| 		pool_free(p, p->chunk->begin); | ||||
| } | ||||
|  | ||||
| void pool_free(struct pool *p, void *ptr) | ||||
| { | ||||
| 	struct chunk *c = p->chunk; | ||||
|  | ||||
| 	while (c) { | ||||
| 		if (((char *) c < (char *) ptr) && | ||||
| 		    ((char *) c->end > (char *) ptr)) { | ||||
| 			c->begin = ptr; | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		if (p->spare_chunk) | ||||
| 			dbg_free(p->spare_chunk); | ||||
| 		p->spare_chunk = c; | ||||
| 		c = c->prev; | ||||
| 	} | ||||
|  | ||||
| 	if (!c) | ||||
| 		log_warn("pool_free asked to free a pointer " | ||||
| 			 "that wasn't in the pool, doing nothing"); | ||||
| 	else | ||||
| 		p->chunk = c; | ||||
| } | ||||
|  | ||||
| void *pool_begin_object(struct pool *p, size_t hint, unsigned align) | ||||
| { | ||||
| 	struct chunk *c = p->chunk; | ||||
|  | ||||
| 	p->object_len = 0; | ||||
| 	p->object_alignment = align; | ||||
|  | ||||
| 	_align_chunk(c, align); | ||||
| 	if (c->end - c->begin < hint) { | ||||
| 		/* allocate a new chunk */ | ||||
| 		c = _new_chunk(p, | ||||
| 			       hint > (p->chunk_size - sizeof(struct chunk)) ? | ||||
| 			       hint + sizeof(struct chunk) + align : | ||||
| 			       p->chunk_size); | ||||
| 		_align_chunk(c, align); | ||||
| 	} | ||||
|  | ||||
| 	return c->begin; | ||||
| } | ||||
|  | ||||
| void *pool_grow_object(struct pool *p, unsigned char *buffer, size_t n) | ||||
| { | ||||
| 	struct chunk *c = p->chunk; | ||||
|  | ||||
| 	if (c->end - (c->begin + p->object_len) < n) { | ||||
| 		/* move into a new chunk */ | ||||
| 		if (p->object_len + n > (p->chunk_size / 2)) | ||||
| 			_new_chunk(p, (p->object_len + n) * 2); | ||||
| 		else | ||||
| 			_new_chunk(p, p->chunk_size); | ||||
|  | ||||
| 		_align_chunk(p->chunk, p->object_alignment); | ||||
| 		memcpy(p->chunk->begin, c->begin, p->object_len); | ||||
| 		c = p->chunk; | ||||
| 	} | ||||
|  | ||||
| 	memcpy(c->begin + p->object_len, buffer, n); | ||||
| 	p->object_len += n; | ||||
| 	return c->begin; | ||||
| } | ||||
|  | ||||
| void *pool_end_object(struct pool *p) | ||||
| { | ||||
| 	struct chunk *c = p->chunk; | ||||
| 	void *r = c->begin; | ||||
| 	c->begin += p->object_len; | ||||
| 	p->object_len = 0u; | ||||
| 	p->object_alignment = DEFAULT_ALIGNMENT; | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| void pool_abandon_object(struct pool *p) | ||||
| { | ||||
| 	p->object_len = 0; | ||||
| 	p->object_alignment = DEFAULT_ALIGNMENT; | ||||
| } | ||||
|  | ||||
| char *pool_strdup(struct pool *p, const char *str) | ||||
| { | ||||
| 	char *ret = pool_alloc(p, strlen(str) + 1); | ||||
|  | ||||
| 	if (ret) | ||||
| 		strcpy(ret, str); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void _align_chunk(struct chunk *c, unsigned alignment) | ||||
| { | ||||
| 	c->begin += alignment - ((unsigned long) c->begin & (alignment - 1)); | ||||
| } | ||||
|  | ||||
| struct chunk *_new_chunk(struct pool *p, size_t s) | ||||
| { | ||||
| 	struct chunk *c; | ||||
|  | ||||
| 	if (p->spare_chunk && | ||||
| 	    ((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) { | ||||
| 		/* reuse old chunk */ | ||||
| 		c = p->spare_chunk; | ||||
| 		p->spare_chunk = 0; | ||||
| 	} else { | ||||
| 		c = dbg_malloc(s); | ||||
| 		c->end = (char *) c + s; | ||||
| 	} | ||||
|  | ||||
| 	c->prev = p->chunk; | ||||
| 	c->begin = (char *) (c + 1); | ||||
| 	p->chunk = c; | ||||
|  | ||||
| 	return c; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  * vim:ai cin ts=8 | ||||
|  */ | ||||
							
								
								
									
										50
									
								
								lib/mm/pool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								lib/mm/pool.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_POOL_H | ||||
| #define _LVM_POOL_H | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| struct pool; | ||||
|  | ||||
| /* constructor and destructor */ | ||||
| struct pool *pool_create(size_t chunk_hint); | ||||
| void pool_destroy(struct pool *p); | ||||
|  | ||||
| /* simple allocation/free routines */ | ||||
| void *pool_alloc(struct pool *p, size_t s); | ||||
| void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment); | ||||
| void pool_empty(struct pool *p); | ||||
| void pool_free(struct pool *p, void *ptr); | ||||
|  | ||||
| /* object building routines */ | ||||
| void *pool_begin_object(struct pool *p, size_t hint, unsigned align); | ||||
| void *pool_grow_object(struct pool *p, unsigned char *buffer, size_t n); | ||||
| void *pool_end_object(struct pool *p); | ||||
| void pool_abandon_object(struct pool *p); | ||||
|  | ||||
| /* utilities */ | ||||
| char *pool_strdup(struct pool *p, const char *str); | ||||
|  | ||||
| static inline void *pool_zalloc(struct pool *p, size_t s) { | ||||
| 	void *ptr = pool_alloc(p, s); | ||||
|  | ||||
| 	if (ptr) | ||||
| 		memset(ptr, 0, s); | ||||
|  | ||||
| 	return ptr; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Local variables: | ||||
|  * c-file-style: "linux" | ||||
|  * End: | ||||
|  * vim:ai cin ts=4 | ||||
|  */ | ||||
|  | ||||
							
								
								
									
										32
									
								
								lib/mm/xlate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								lib/mm/xlate.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_XLATE_H | ||||
| #define _LVM_XLATE_H | ||||
|  | ||||
| /* FIXME: finish these as inlines */ | ||||
|  | ||||
| uint16_t shuffle16(uint16_t n); | ||||
| uint32_t shuffle32(uint32_t n); | ||||
| uint64_t shuffle64(uint64_t n); | ||||
|  | ||||
| /* xlate functions move data between core and disk */ | ||||
| #if __BYTE_ORDER == __BIG_ENDIAN | ||||
| #   define xlate16(x) shuffle16(x) | ||||
| #   define xlate32(x) shuffle32(x) | ||||
| #   define xlate64(x) shuffle64(x) | ||||
|  | ||||
| #elif __BYTE_ORDER == __LITTLE_ENDIAN | ||||
| #   define xlate16(x) (x) | ||||
| #   define xlate32(x) (x) | ||||
| #   define xlate64(x) (x) | ||||
|  | ||||
| #else | ||||
| #   error "__BYTE_ORDER must be defined as __LITTLE_ENDIAN or __BIG_ENDIAN" | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										49
									
								
								lib/uuid/uuid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lib/uuid/uuid.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "uuid.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| static unsigned char _c[] = | ||||
|     "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||||
|  | ||||
| int id_create(struct id *id) | ||||
| { | ||||
| 	int random, i, len = sizeof(id->uuid); | ||||
|  | ||||
| 	memset(id->uuid, 0, len); | ||||
| 	if ((random = open("/dev/urandom", O_RDONLY)) < 0) { | ||||
| 		log_sys_error("open", "id_create"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (read(random, id->uuid, len) != len) { | ||||
| 		log_sys_error("read", "id_create"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	close(random); | ||||
|  | ||||
| 	for (i = 0; i < len; i++) | ||||
| 		id->uuid[i] = _c[id->uuid[i] % (sizeof(_c) - 1)]; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int id_valid(struct id *id) | ||||
| { | ||||
| 	log_err("Joe hasn't written id_valid yet"); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int id_cmp(struct id *lhs, struct id *rhs) | ||||
| { | ||||
| 	return memcmp(lhs->uuid, rhs->uuid, sizeof(lhs->uuid)); | ||||
| } | ||||
							
								
								
									
										22
									
								
								lib/uuid/uuid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/uuid/uuid.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_UUID_H | ||||
| #define _LVM_UUID_H | ||||
|  | ||||
| #include "lvm-types.h" | ||||
|  | ||||
| #define ID_LEN 32 | ||||
|  | ||||
| struct id { | ||||
| 	uint8_t uuid[ID_LEN]; | ||||
| }; | ||||
|  | ||||
| int id_create(struct id *id); | ||||
| int id_valid(struct id *id); | ||||
| int id_cmp(struct id *lhs, struct id *rhs); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										110
									
								
								libdm/datastruct/list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								libdm/datastruct/list.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| /* stolen from the Linux kernel. */ | ||||
|  | ||||
| #ifndef _LVM_LIST_H | ||||
| #define _LVM_LIST_H | ||||
|  | ||||
| /* | ||||
|  * Simple doubly linked list implementation. | ||||
|  * | ||||
|  * Some of the internal functions ("__xxx") are useful when | ||||
|  * manipulating whole lists rather than single entries, as | ||||
|  * sometimes we already know the next/prev entries and we can | ||||
|  * generate better code by using them directly rather than | ||||
|  * using the generic single-entry routines. | ||||
|  */ | ||||
|  | ||||
| struct list_head { | ||||
| 	struct list_head *next, *prev; | ||||
| }; | ||||
|  | ||||
| #define LIST_HEAD_INIT(name) { &(name), &(name) } | ||||
|  | ||||
| #define LIST_HEAD(name) \ | ||||
| 	struct list_head name = { &name, &name } | ||||
|  | ||||
| #define INIT_LIST_HEAD(ptr) do { \ | ||||
| 	(ptr)->next = (ptr); (ptr)->prev = (ptr); \ | ||||
| } while (0) | ||||
|  | ||||
| /* | ||||
|  * Insert a new entry between two known consecutive entries.  | ||||
|  * | ||||
|  * This is only for internal list manipulation where we know | ||||
|  * the prev/next entries already! | ||||
|  */ | ||||
| static __inline__ void __list_add(struct list_head *new, | ||||
| 				  struct list_head *prev, | ||||
| 				  struct list_head *next) | ||||
| { | ||||
| 	next->prev = new; | ||||
| 	new->next = next; | ||||
| 	new->prev = prev; | ||||
| 	prev->next = new; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Insert a new entry after the specified head.. | ||||
|  */ | ||||
| static __inline__ void list_add(struct list_head *new, struct list_head *head) | ||||
| { | ||||
| 	__list_add(new, head, head->next); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Insert a new entry at the tail | ||||
|  */ | ||||
| static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) | ||||
| { | ||||
| 	__list_add(new, head->prev, head); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Delete a list entry by making the prev/next entries | ||||
|  * point to each other. | ||||
|  * | ||||
|  * This is only for internal list manipulation where we know | ||||
|  * the prev/next entries already! | ||||
|  */ | ||||
| static __inline__ void __list_del(struct list_head * prev, | ||||
| 				  struct list_head * next) | ||||
| { | ||||
| 	next->prev = prev; | ||||
| 	prev->next = next; | ||||
| } | ||||
|  | ||||
| static __inline__ void list_del(struct list_head *entry) | ||||
| { | ||||
| 	__list_del(entry->prev, entry->next); | ||||
| } | ||||
|  | ||||
| static __inline__ int list_empty(struct list_head *head) | ||||
| { | ||||
| 	return head->next == head; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Splice in "list" into "head" | ||||
|  */ | ||||
| static __inline__ void list_splice(struct list_head *list, struct list_head *head) | ||||
| { | ||||
| 	struct list_head *first = list->next; | ||||
|  | ||||
| 	if (first != list) { | ||||
| 		struct list_head *last = list->prev; | ||||
| 		struct list_head *at = head->next; | ||||
|  | ||||
| 		first->prev = head; | ||||
| 		head->next = first; | ||||
|  | ||||
| 		last->next = at; | ||||
| 		at->prev = last; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #define list_entry(ptr, type, member) \ | ||||
| 	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) | ||||
|  | ||||
| #define list_for_each(pos, head) \ | ||||
|         for (pos = (head)->next; pos != (head); pos = pos->next) | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										112
									
								
								make.tmpl.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								make.tmpl.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| # @configure_input@ | ||||
| # | ||||
| # 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 | ||||
|  | ||||
| @SET_MAKE@ | ||||
|  | ||||
| CC = @CC@ | ||||
| RANLIB = @RANLIB@ | ||||
| SHELL = /bin/sh | ||||
| INSTALL = @INSTALL@ | ||||
| LN_S = @LN_S@ | ||||
|  | ||||
| # Setup directory variables | ||||
| prefix = @prefix@ | ||||
| exec_prefix = @exec_prefix@ | ||||
| bindir = @bindir@ | ||||
| staticlibdir = ${prefix}/lib | ||||
| libdir = @libdir@ | ||||
| sbindir = @sbindir@ | ||||
| infodir = @infodir@ | ||||
| mandir = @mandir@ | ||||
|  | ||||
| # setup misc variables | ||||
| # define the ownership variables for the binaries and man pages | ||||
| OWNER=@OWNER@ | ||||
| GROUP=@GROUP@ | ||||
|  | ||||
| # The number of jobs to run, if blank, defaults to the make standard | ||||
| ifndef MAKEFLAGS | ||||
| MAKEFLAGS = @JOBS@ | ||||
| endif | ||||
|  | ||||
| SUFFIXES= | ||||
| SUFFIXES=.c .d .o | ||||
|  | ||||
| CFLAGS+=-g -Wall -DDEBUG_MEM -DBOUNDS_CHECK -DDEBUG | ||||
| INCLUDES+=-I. -I$(top_srcdir)/include | ||||
| INC_LNS=$(top_srcdir)/include/.symlinks_created | ||||
|  | ||||
| ifeq ("@READLINE@", "yes") | ||||
|   CFLAGS += -DREADLINE_SUPPORT | ||||
|   EXTRA_LIBS += -lreadline | ||||
| endif | ||||
|  | ||||
| OBJECTS=$(SOURCES:%.c=%.o) | ||||
|  | ||||
| SUBDIRS.install := $(SUBDIRS:=.install) | ||||
| SUBDIRS.clean := $(SUBDIRS:=.clean) | ||||
| SUBDIRS.distclean := $(SUBDIRS:=.distclean) | ||||
|  | ||||
| .PHONY: all install distclean clean  | ||||
| .PHONY: $(SUBDIRS) $(SUBDIRS.install) $(SUBDIRS.clean) $(SUBDIRS.distclean) | ||||
|  | ||||
| all: $(SUBDIRS) $(TARGETS) | ||||
|  | ||||
| install: all $(SUBDIRS.install) | ||||
|  | ||||
| $(SUBDIRS): | ||||
| 	$(MAKE) -C $@ | ||||
|  | ||||
| $(SUBDIRS.install):  | ||||
| 	$(MAKE) -C $(@:.install=) install | ||||
|  | ||||
| $(SUBDIRS.clean): | ||||
| 	$(MAKE) -C $(@:.clean=) clean | ||||
|  | ||||
| $(SUBDIRS.distclean): | ||||
| 	$(MAKE) -C $(@:.distclean=) distclean | ||||
|  | ||||
| %.o: %.c | ||||
| 	$(CC) -c $(INCLUDES) $(CFLAGS) $< -o $@ | ||||
|  | ||||
| %.d: %.c | ||||
| 	set -e; FILE=`echo $@ | sed 's/\\//\\\\\\//g;s/\\.d//g'`; \ | ||||
| 		INC_LNS=`echo $(INC_LNS) | sed -e 's/\\//\\\\\\//g'`; \ | ||||
| 		$(CC) -MM $(INCLUDES) $< | \ | ||||
| 		sed "s/\(.*\)\.o[ :]*/$$FILE.o $$FILE.d : $$INC_LNS /g" > $@; \ | ||||
| 		[ -s $@ ] || $(RM) $@ | ||||
|  | ||||
| clean: $(SUBDIRS.clean) | ||||
| 	$(RM) $(OBJECTS) $(TARGETS) $(SOURCES:%.c=%.d) | ||||
|  | ||||
| distclean: $(SUBDIRS.distclean) | ||||
| 	$(RM) $(OBJECTS) $(TARGETS) $(SOURCES:%.c=%.d) | ||||
| 	$(RM) config.cache config.log config.status | ||||
| 	$(RM) Makefile make.tmpl | ||||
|  | ||||
| ifneq ($(MAKECMDGOALS),clean) | ||||
|   ifneq ($(MAKECMDGOALS),distclean) | ||||
|     ifdef SOURCES | ||||
|        -include $(SOURCES:.c=.d) | ||||
|     endif | ||||
|   endif | ||||
| endif | ||||
|  | ||||
							
								
								
									
										35
									
								
								man/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								man/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@ | ||||
|  | ||||
| MANUALS=$(wildcard *.8) | ||||
| MAN8DIR=${mandir}/man8 | ||||
|  | ||||
| include ../make.tmpl | ||||
|  | ||||
| install: | ||||
| 	@echo "*** Installing $(ALL_MANUALS) in $(DESTDIR)$(MAN8DIR) ***" | ||||
| 	@for f in $(MANUALS); \ | ||||
| 	do \ | ||||
| 	   $(RM) $(DESTDIR)$(MAN8DIR)/$$f; \ | ||||
| 	   @INSTALL@ -D -o $(OWNER) -g $(GROUP) -m 444 $$f $(DESTDIR)$(MAN8DIR)/$$f; \ | ||||
| 	done | ||||
|  | ||||
							
								
								
									
										67
									
								
								man/pvdisplay.8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								man/pvdisplay.8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| .TH PVDISPLAY 8 "LVM TOOLS" "Heinz Mauelshagen" \" -*- nroff -*- | ||||
| .SH NAME | ||||
| pvdisplay \- display attributes of a physical volume | ||||
| .SH SYNOPSIS | ||||
| .B pvdisplay | ||||
| [\-c/\-\-colon] [\-d/\-\-debug] [\-h/\-?/\-\-help] [\-s/\-\-short] | ||||
| [\-v[v]/\-\-verbose [\-\-verbose]] | ||||
| PhysicalVolumePath [PhysicalVolumePath...] | ||||
| .SH DESCRIPTION | ||||
| pvdisplay allows you to see the attributes of one or more physical volumes | ||||
| like size, physical extent size, space used for the volume group descriptor | ||||
| area and so on. | ||||
| .SS OPTIONS | ||||
| .TP | ||||
| .I \-c, \-\-colon | ||||
| Generate colon seperated output for easier parsing in scripts or programs. | ||||
| .nf | ||||
|  | ||||
| The values are: | ||||
|  | ||||
| * physical volume device name | ||||
| * volume group name | ||||
| * physical volume size in kilobytes | ||||
| * internal physical volume number | ||||
| * physical volume status | ||||
| * physical volume (not) allocatable | ||||
| * current number of logical volumes on this physical volume | ||||
| * physical extent size in kilobytes | ||||
| * total number of physical extents | ||||
| * free number of physical extents | ||||
| * allocated number of physical extents | ||||
|  | ||||
| .fi | ||||
| .TP | ||||
| .I \-d, \-\-debug | ||||
| Enables additional debugging output (if compiled with DEBUG). | ||||
| .TP | ||||
| .I \-h, \-?, \-\-help | ||||
| Print a usage message on standard output and exit successfully. | ||||
| .TP | ||||
| .I \-s, \-\-short | ||||
| Only display the size of the given physical volumes. | ||||
| .TP | ||||
| .I \-v, \-\-verbose | ||||
| Display the mapping of physical extents to logical volumes and | ||||
| logical extents. | ||||
| .TP | ||||
| .I \-vv, \-\-verbose \-\-verbose | ||||
| Like \-v with verbose runtime information about pvdisplay's activities. | ||||
| .SH DIAGNOSTICS | ||||
| pvdisplay returns an code state of 0 for success and > 0 for error: | ||||
| .nf | ||||
|  | ||||
| 1  no physical volume name on command line | ||||
| 2  error checking consistency of physical volume | ||||
| 3  error reading physical extent information from physical volume | ||||
|  | ||||
| 95 driver/module not in kernel | ||||
| 96 invalid I/O protocol version | ||||
| 97 error locking logical volume manager | ||||
| 98 invalid lvmtab (run vgscan(8)) | ||||
| 99 invalid command line | ||||
| .fi | ||||
| .SH See also | ||||
| lvm(8), pvcreate(8), lvcreate(8), vgcreate(8) | ||||
| .SH AUTHOR | ||||
| Heinz Mauelshagen <Linux-LVM@Sistina.com> | ||||
							
								
								
									
										100
									
								
								old-tests/datastruct/hash_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								old-tests/datastruct/hash_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "hash.h" | ||||
| #include "dbg_malloc.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| static void _help(FILE *fp, const char *prog) | ||||
| { | ||||
| 	fprintf(fp, "Usage : %s <table size> <num_entries>\n", prog); | ||||
| } | ||||
|  | ||||
| struct key_list { | ||||
| 	struct key_list *next; | ||||
| 	char key[1]; | ||||
| }; | ||||
|  | ||||
| static struct key_list *_create_word(int n) | ||||
| { | ||||
| 	struct key_list *kl = dbg_malloc(sizeof(*kl) + 32); | ||||
| 	snprintf(kl->key, 32, "abc%ddef%d", n, n); | ||||
| 	kl->next = 0; | ||||
| 	return kl; | ||||
| } | ||||
|  | ||||
| static struct key_list *_create_word_from_file(int n) | ||||
| { | ||||
| 	char word[128], *ptr; | ||||
| 	struct key_list *kl; | ||||
|  | ||||
| 	if (!fgets(word, sizeof(word), stdin)) | ||||
| 		return 0; | ||||
|  | ||||
| 	for (ptr = word; *ptr; ptr++) { | ||||
| 		if (*ptr == '\n') { | ||||
| 			*ptr = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	kl = dbg_malloc(sizeof(*kl) + 32); | ||||
| 	snprintf(kl->key, 32, "%s", word); | ||||
| 	kl->next = 0; | ||||
| 	return kl; | ||||
| } | ||||
|  | ||||
| static void _do_test(int table_size, int num_entries) | ||||
| { | ||||
| 	int i; | ||||
| 	hash_table_t ht = hash_create(table_size); | ||||
| 	struct key_list *tmp, *key, *all = 0; | ||||
|  | ||||
| 	for (i = 0; i < num_entries; i++) { | ||||
| 		/* make up a word */ | ||||
| 		if (!(key = _create_word_from_file(i))) { | ||||
| 			log_error("Ran out of words !\n"); | ||||
| 			exit(1); | ||||
| 		} | ||||
|  | ||||
| 		/* insert it */ | ||||
| 		hash_insert(ht, key->key, key); | ||||
| 		key->next = all; | ||||
| 		all = key; | ||||
| 	} | ||||
|  | ||||
| 	for (key = all; key; key = key->next) { | ||||
| 		tmp = (struct key_list *) hash_lookup(ht, key->key); | ||||
| 		if (!tmp || (tmp != key)) { | ||||
| 			log_error("lookup failed\n"); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for (key = all; key; key = tmp)	{ | ||||
| 		tmp = key->next; | ||||
| 		dbg_free(key); | ||||
| 	} | ||||
|  | ||||
| 	hash_destroy(ht); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	init_log(); | ||||
|  | ||||
| 	if (argc != 3) { | ||||
| 		_help(stderr, argv[0]); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	_do_test(atoi(argv[1]), atoi(argv[2])); | ||||
|  | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										45
									
								
								old-tests/dev-mgr/dev_cache_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								old-tests/dev-mgr/dev_cache_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "dev-cache.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	int i; | ||||
| 	struct device *dev; | ||||
| 	struct dev_iter *iter; | ||||
|  | ||||
| 	init_log(); | ||||
| 	if (!dev_cache_init()) { | ||||
| 		log_error("couldn't initialise dev_cache_init failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	for (i = 1; i < argc; i++) { | ||||
| 		if (!dev_cache_add_dir(argv[i])) { | ||||
| 			log_error("couldn't add '%s' to dev_cache\n"); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(iter = dev_iter_create(NULL))) { | ||||
| 		log_error("couldn't create iterator\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	while ((dev = dev_iter_next(iter))) | ||||
| 		printf("%s\n", dev->name); | ||||
|  | ||||
| 	dev_iter_destroy(iter): | ||||
| 	dev_cache_exit(); | ||||
|  | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										20
									
								
								old-tests/device/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								old-tests/device/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| # | ||||
| # Copyright (C) 2001 Sistina Software (UK) Limited | ||||
| # | ||||
| # This file is released under the GPL. | ||||
| # | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| VPATH = @srcdir@ | ||||
|  | ||||
| SOURCES=\ | ||||
| 	dev_cache_t.c | ||||
|  | ||||
| TARGETS=dev_cache_t | ||||
|  | ||||
| include ../../make.tmpl | ||||
|  | ||||
| dev_cache_t: dev_cache_t.o $(top_srcdir)/lib/liblvm.a | ||||
| 	$(CC) -o dev_cache_t dev_cache_t.o -L$(top_srcdir)/lib -llvm | ||||
|  | ||||
							
								
								
									
										48
									
								
								old-tests/device/dev_cache_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								old-tests/device/dev_cache_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "dev-cache.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	int i; | ||||
| 	struct device *dev; | ||||
| 	struct dev_iter *iter; | ||||
|  | ||||
| 	init_log(stderr); | ||||
| 	init_debug(_LOG_INFO); | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		log_err("couldn't initialise dev_cache_init failed"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	for (i = 1; i < argc; i++) { | ||||
| 		if (!dev_cache_add_dir(argv[i])) { | ||||
| 			log_err("couldn't add '%s' to dev_cache", argv[i]); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(iter = dev_iter_create(NULL))) { | ||||
| 		log_err("couldn't create iterator"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	while ((dev = dev_iter_get(iter))) | ||||
| 		printf("%s\n", dev->name); | ||||
|  | ||||
| 	dev_iter_destroy(iter); | ||||
| 	dev_cache_exit(); | ||||
|  | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										43
									
								
								old-tests/format1/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								old-tests/format1/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| # | ||||
| # Copyright (C) 2001 Sistina Software (UK) Limited | ||||
| # | ||||
| # This file is released under the GPL. | ||||
| # | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| VPATH = @srcdir@ | ||||
|  | ||||
| SOURCES=\ | ||||
| 	read_vg_t.c \ | ||||
| 	write_vg_t.c \ | ||||
| 	pretty_print.c \ | ||||
| 	get_pvs_t.c \ | ||||
| 	read_pv_t.c \ | ||||
| 	get_vgs_t.c | ||||
|  | ||||
| TARGETS=\ | ||||
| 	read_vg_t \ | ||||
| 	write_vg_t \ | ||||
| 	get_pvs_t \ | ||||
| 	read_pv_t \ | ||||
| 	get_vgs_t | ||||
|  | ||||
| include ../../make.tmpl | ||||
|  | ||||
| read_vg_t: read_vg_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a | ||||
| 	$(CC) -o read_vg_t read_vg_t.o pretty_print.o -L$(top_srcdir)/lib -llvm | ||||
|  | ||||
| write_vg_t: write_vg_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a | ||||
| 	$(CC) -o write_vg_t write_vg_t.o pretty_print.o \ | ||||
| 	-L$(top_srcdir)/lib -llvm | ||||
|  | ||||
| get_pvs_t: get_pvs_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a | ||||
| 	$(CC) -o get_pvs_t get_pvs_t.o pretty_print.o -L$(top_srcdir)/lib -llvm | ||||
|  | ||||
| read_pv_t: read_pv_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a | ||||
| 	$(CC) -o read_pv_t read_pv_t.o pretty_print.o -L$(top_srcdir)/lib -llvm | ||||
|  | ||||
| get_vgs_t: get_vgs_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a | ||||
| 	$(CC) -o get_vgs_t get_vgs_t.o pretty_print.o -L$(top_srcdir)/lib -llvm | ||||
|  | ||||
							
								
								
									
										66
									
								
								old-tests/format1/get_pvs_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								old-tests/format1/get_pvs_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "format1.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "pool.h" | ||||
| #include "pretty_print.h" | ||||
| #include "list.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	struct io_space *ios; | ||||
| 	struct list_head *pvs, *tmp; | ||||
| 	struct pool *mem; | ||||
|  | ||||
| 	init_log(stderr); | ||||
| 	init_debug(_LOG_INFO); | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		fprintf(stderr, "init of dev-cache failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_cache_add_dir("/dev/loop")) { | ||||
| 		fprintf(stderr, "couldn't add /dev to dir-cache\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!(mem = pool_create(10 * 1024))) { | ||||
| 		fprintf(stderr, "couldn't create pool\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	ios = create_lvm1_format("/dev", mem, NULL); | ||||
|  | ||||
| 	if (!ios) { | ||||
| 		fprintf(stderr, "failed to create io_space for format1\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	pvs = ios->get_pvs(ios); | ||||
|  | ||||
| 	if (!pvs) { | ||||
| 		fprintf(stderr, "couldn't read vg %s\n", argv[1]); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	list_for_each(tmp, pvs) { | ||||
| 		struct pv_list *pvl = list_entry(tmp, struct pv_list, list); | ||||
| 		dump_pv(&pvl->pv, stdout); | ||||
| 	} | ||||
|  | ||||
| 	ios->destroy(ios); | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	dev_cache_exit(); | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										62
									
								
								old-tests/format1/get_vgs_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								old-tests/format1/get_vgs_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "format1.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "pool.h" | ||||
| #include "pretty_print.h" | ||||
| #include "list.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	struct io_space *ios; | ||||
| 	struct list_head *vgs; | ||||
| 	struct pool *mem; | ||||
|  | ||||
| 	init_log(stderr); | ||||
| 	init_debug(_LOG_INFO); | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		fprintf(stderr, "init of dev-cache failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_cache_add_dir("/dev/loop")) { | ||||
| 		fprintf(stderr, "couldn't add /dev to dir-cache\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!(mem = pool_create(10 * 1024))) { | ||||
| 		fprintf(stderr, "couldn't create pool\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	ios = create_lvm1_format("/dev", mem, NULL); | ||||
|  | ||||
| 	if (!ios) { | ||||
| 		fprintf(stderr, "failed to create io_space for format1\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	vgs = ios->get_vgs(ios); | ||||
|  | ||||
| 	if (!vgs) { | ||||
| 		fprintf(stderr, "couldn't read vg names\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	dump_vg_names(vgs, stdout); | ||||
| 	ios->destroy(ios); | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	dev_cache_exit(); | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										77
									
								
								old-tests/format1/pretty_print.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								old-tests/format1/pretty_print.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "pretty_print.h" | ||||
|  | ||||
| void dump_pv(struct physical_volume *pv, FILE *fp) | ||||
| { | ||||
| 	fprintf(fp, "physical_volume {\n"); | ||||
| 	fprintf(fp, "\tname = '%s'\n", pv->dev->name); | ||||
| 	fprintf(fp, "\tvg_name = '%s'\n", pv->vg_name); | ||||
| 	fprintf(fp, "\tsize = %llu\n", pv->size); | ||||
| 	fprintf(fp, "\tpe_size = %llu\n", pv->pe_size); | ||||
| 	fprintf(fp, "\tpe_start = %llu\n", pv->pe_start); | ||||
| 	fprintf(fp, "\tpe_count = %u\n", pv->pe_count); | ||||
| 	fprintf(fp, "\tpe_allocated = %u\n", pv->pe_allocated); | ||||
| 	fprintf(fp, "}\n\n"); | ||||
| } | ||||
|  | ||||
| void dump_lv(struct logical_volume *lv, FILE *fp) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	fprintf(fp, "logical_volume {\n"); | ||||
| 	fprintf(fp, "\tname = '%s'\n", lv->name); | ||||
| 	fprintf(fp, "\tsize = %llu\n", lv->size); | ||||
| 	fprintf(fp, "\tle_count = %u\n", lv->le_count); | ||||
|  | ||||
| 	fprintf(fp, "\tmap {\n"); | ||||
| 	for (i = 0; i < lv->le_count; i++) { | ||||
| 		struct physical_volume *pv = lv->map[i].pv; | ||||
|  | ||||
| 		fprintf(fp, "\t\tpv = '%s', ", | ||||
| 			pv ? pv->dev->name : "null ???"); | ||||
| 		fprintf(fp, "\textent = %u\n", lv->map[i].pe); | ||||
| 	} | ||||
| 	fprintf(fp, "\t}\n}\n\n"); | ||||
| } | ||||
|  | ||||
| void dump_vg(struct volume_group *vg, FILE *fp) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
|  | ||||
| 	fprintf(fp, "volume_group {\n"); | ||||
| 	fprintf(fp, "\tname = '%s'\n", vg->name); | ||||
| 	fprintf(fp, "\textent_size = %llu\n", vg->extent_size); | ||||
| 	fprintf(fp, "\textent_count = %d\n", vg->extent_count); | ||||
| 	fprintf(fp, "\tfree_count = %d\n", vg->free_count); | ||||
| 	fprintf(fp, "\tmax_lv = %d\n", vg->max_lv); | ||||
| 	fprintf(fp, "\tmax_pv = %d\n", vg->max_pv); | ||||
| 	fprintf(fp, "\tpv_count = %d\n", vg->pv_count); | ||||
| 	fprintf(fp, "\tlv_count = %d\n", vg->lv_count); | ||||
| 	fprintf(fp, "}\n\n"); | ||||
|  | ||||
| 	list_for_each(tmp, &vg->pvs) { | ||||
| 		struct pv_list *pvl = list_entry(tmp, struct pv_list, list); | ||||
| 		dump_pv(&pvl->pv, fp); | ||||
| 	} | ||||
|  | ||||
| 	list_for_each(tmp, &vg->lvs) { | ||||
| 		struct lv_list *lvl = list_entry(tmp, struct lv_list, list); | ||||
| 		dump_lv(&lvl->lv, fp); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void dump_vg_names(struct list_head *vg_names, FILE *fp) | ||||
| { | ||||
| 	struct list_head *tmp; | ||||
| 	struct name_list *nl; | ||||
|  | ||||
| 	list_for_each(tmp, vg_names) { | ||||
| 		nl = list_entry(tmp, struct name_list, list); | ||||
| 		fprintf(fp, "%s\n", nl->name); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										19
									
								
								old-tests/format1/pretty_print.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								old-tests/format1/pretty_print.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_PRETTY_PRINT | ||||
| #define _LVM_PRETTY_PRINT | ||||
|  | ||||
| #include "metadata.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| void dump_pv(struct physical_volume *pv, FILE *fp); | ||||
| void dump_lv(struct logical_volume *lv, FILE *fp); | ||||
| void dump_vg(struct volume_group *vg, FILE *fp); | ||||
| void dump_vg_names(struct list_head *vg_names, FILE *fp); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										68
									
								
								old-tests/format1/read_pv_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								old-tests/format1/read_pv_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "format1.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "pool.h" | ||||
| #include "pretty_print.h" | ||||
| #include "list.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	struct io_space *ios; | ||||
| 	struct physical_volume *pv; | ||||
| 	struct pool *mem; | ||||
| 	struct device *dev; | ||||
|  | ||||
| 	if (argc != 2) { | ||||
| 		fprintf(stderr, "usage: read_pv_t <device>\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	init_log(stderr); | ||||
| 	init_debug(_LOG_INFO); | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		fprintf(stderr, "init of dev-cache failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_cache_add_dir("/dev/loop")) { | ||||
| 		fprintf(stderr, "couldn't add /dev to dir-cache\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!(mem = pool_create(10 * 1024))) { | ||||
| 		fprintf(stderr, "couldn't create pool\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	ios = create_lvm1_format("/dev", mem, NULL); | ||||
|  | ||||
| 	if (!ios) { | ||||
| 		fprintf(stderr, "failed to create io_space for format1\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	pv = ios->pv_read(ios, argv[1]); | ||||
|  | ||||
| 	if (!pv) { | ||||
| 		fprintf(stderr, "couldn't read pv %s\n", dev->name); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	dump_pv(pv, stdout); | ||||
| 	ios->destroy(ios); | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	dev_cache_exit(); | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										67
									
								
								old-tests/format1/read_vg_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								old-tests/format1/read_vg_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "format1.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "pool.h" | ||||
| #include "pretty_print.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	struct io_space *ios; | ||||
| 	struct volume_group *vg; | ||||
| 	struct pool *mem; | ||||
|  | ||||
| 	if (argc != 2) { | ||||
| 		fprintf(stderr, "usage: read_vg_t <vg_name>\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	init_log(stderr); | ||||
| 	init_debug(_LOG_INFO); | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		fprintf(stderr, "init of dev-cache failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_cache_add_dir("/dev/loop")) { | ||||
| 		fprintf(stderr, "couldn't add /dev to dir-cache\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!(mem = pool_create(10 * 1024))) { | ||||
| 		fprintf(stderr, "couldn't create pool\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	ios = create_lvm1_format("/dev", mem, NULL); | ||||
|  | ||||
| 	if (!ios) { | ||||
| 		fprintf(stderr, "failed to create io_space for format1\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	vg = ios->vg_read(ios, argv[1]); | ||||
|  | ||||
| 	if (!vg) { | ||||
| 		fprintf(stderr, "couldn't read vg %s\n", argv[1]); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	dump_vg(vg, stdout); | ||||
|  | ||||
| 	ios->destroy(ios); | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	dev_cache_exit(); | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										70
									
								
								old-tests/format1/write_vg_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								old-tests/format1/write_vg_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "log.h" | ||||
| #include "format1.h" | ||||
| #include "dbg_malloc.h" | ||||
| #include "pool.h" | ||||
| #include "pretty_print.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	struct io_space *ios; | ||||
| 	struct volume_group *vg; | ||||
| 	struct pool *mem; | ||||
|  | ||||
| 	if (argc != 2) { | ||||
| 		fprintf(stderr, "usage: read_vg_t <vg_name>\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	init_log(stderr); | ||||
| 	init_debug(_LOG_INFO); | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		fprintf(stderr, "init of dev-cache failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_cache_add_dir("/dev/loop")) { | ||||
| 		fprintf(stderr, "couldn't add /dev to dir-cache\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!(mem = pool_create(10 * 1024))) { | ||||
| 		fprintf(stderr, "couldn't create pool\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	ios = create_lvm1_format("/dev", mem, NULL); | ||||
|  | ||||
| 	if (!ios) { | ||||
| 		fprintf(stderr, "failed to create io_space for format1\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	vg = ios->vg_read(ios, argv[1]); | ||||
|  | ||||
| 	if (!vg) { | ||||
| 		fprintf(stderr, "couldn't read vg %s\n", argv[1]); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	if (!ios->vg_write(ios, vg)) { | ||||
| 		fprintf(stderr, "couldn't write vg\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	ios->destroy(ios); | ||||
|  | ||||
| 	pool_destroy(mem); | ||||
| 	dev_cache_exit(); | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										20
									
								
								old-tests/mm/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								old-tests/mm/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| # | ||||
| # Copyright (C) 2001 Sistina Software (UK) Limited | ||||
| # | ||||
| # This file is released under the GPL. | ||||
| # | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| VPATH = @srcdir@ | ||||
|  | ||||
| SOURCES=\ | ||||
| 	dbg_malloc_t.c | ||||
|  | ||||
| TARGETS=dbg_malloc_t | ||||
|  | ||||
| include ../../make.tmpl | ||||
|  | ||||
| dbg_malloc_t: dbg_malloc_t.o | ||||
| 	$(CC) -o dbg_malloc_t dbg_malloc_t.o -L$(top_srcdir)/lib -llvm | ||||
|  | ||||
							
								
								
									
										148
									
								
								old-tests/mm/dbg_malloc_t.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								old-tests/mm/dbg_malloc_t.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "dbg_malloc.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
|  | ||||
| static void _print_help(FILE *out, const char *prog) | ||||
| { | ||||
| 	fprintf(out, "usage : %s [-hlbufd]\n\n", prog); | ||||
| 	fprintf(out, "  h : this message\n"); | ||||
| 	fprintf(out, "  l : cause memory leak\n"); | ||||
| 	fprintf(out, "  b : overrun memory block\n"); | ||||
| 	fprintf(out, "  u : underrun memory block\n"); | ||||
| 	fprintf(out, "  f : free random pointer\n"); | ||||
| 	fprintf(out, "  d : free block twice\n"); | ||||
| } | ||||
|  | ||||
| struct block_list { | ||||
| 	struct block_list *next; | ||||
| 	char dummy[9]; | ||||
| }; | ||||
|  | ||||
| static void _leak_memory(void) | ||||
| { | ||||
| 	int i; | ||||
| 	struct block_list *b, *head, **l = &head, *n; | ||||
|  | ||||
| 	/* allocate a list of blocks */ | ||||
| 	for (i = 0; i < 1000; i++) { | ||||
|  | ||||
| 		if (!(b = dbg_malloc(sizeof(*b)))) { | ||||
| 			log_fatal("Couldn't allocate memory"); | ||||
| 			exit(1); | ||||
| 		} | ||||
|  | ||||
| 		b->next = 0; | ||||
| 		*l = b; | ||||
| 		l = &b->next; | ||||
| 	} | ||||
|  | ||||
| 	/* free off every other block */ | ||||
| 	for (b = head, i = 0; b; b = n, i++) { | ||||
| 		n = b->next; | ||||
| 		if(i & 0x1) | ||||
| 			dbg_free(b); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void _bounds_overrun(void) | ||||
| { | ||||
| 	char *b; | ||||
|  | ||||
| 	/* allocate a block */ | ||||
| 	b = dbg_malloc(534); | ||||
|  | ||||
| 	/* overrun */ | ||||
| 	b[534] = 56; | ||||
|  | ||||
| 	/* free it, which should trigger the bounds error */ | ||||
| 	dbg_free(b); | ||||
| } | ||||
|  | ||||
| static void _bounds_underrun(void) | ||||
| { | ||||
| 	char *b; | ||||
|  | ||||
| 	/* allocate a block */ | ||||
| 	b = dbg_malloc(534); | ||||
|  | ||||
| 	/* underrun */ | ||||
| 	*(b - 1) = 56; | ||||
|  | ||||
| 	/* free it, which should trigger the bounds error */ | ||||
| 	dbg_free(b); | ||||
| } | ||||
|  | ||||
| static void _free_dud(void) | ||||
| { | ||||
| 	char *b; | ||||
|  | ||||
| 	/* allocate a block */ | ||||
| 	b = dbg_malloc(534); | ||||
|  | ||||
| 	/* free it, which should trigger the bounds error */ | ||||
| 	dbg_free(b + 100); | ||||
| } | ||||
|  | ||||
| static void _free_twice(void) | ||||
| { | ||||
| 	char *b; | ||||
|  | ||||
| 	/* allocate a block */ | ||||
| 	b = dbg_malloc(534); | ||||
|  | ||||
| 	/* free it, which should trigger the bounds error */ | ||||
| 	dbg_free(b); | ||||
| 	dbg_free(b); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	char opt; | ||||
|  | ||||
| 	init_log(stderr); | ||||
| 	init_debug(_LOG_DEBUG); | ||||
| 	opt = getopt(argc, argv, "hlbufd"); | ||||
| 	switch(opt) { | ||||
| 	case EOF: | ||||
| 	case 'h': | ||||
| 		_print_help(stdout, argv[0]); | ||||
| 		break; | ||||
|  | ||||
| 	case 'l': | ||||
| 		_leak_memory(); | ||||
| 		break; | ||||
|  | ||||
| 	case 'b': | ||||
| 		_bounds_overrun(); | ||||
| 		break; | ||||
|  | ||||
| 	case 'u': | ||||
| 		_bounds_underrun(); | ||||
| 		break; | ||||
|  | ||||
| 	case 'f': | ||||
| 		_free_dud(); | ||||
| 		break; | ||||
|  | ||||
| 	case 'd': | ||||
| 		_free_twice(); | ||||
| 		break; | ||||
|  | ||||
| 	case '?': | ||||
| 		fprintf(stderr, "Unknown option -%c\n", opt); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										57
									
								
								tools/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								tools/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| # | ||||
| # 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@ | ||||
|  | ||||
| SOURCES=\ | ||||
| 	lvm.c \ | ||||
| 	lvmchange.c \ | ||||
| 	pvcreate.c \ | ||||
| 	pvscan.c \ | ||||
| 	toollib.c \ | ||||
| 	vgck.c \ | ||||
| 	vgchange.c \ | ||||
| 	vgcreate.c \ | ||||
| 	vgextend.c \ | ||||
| 	vgreduce.c \ | ||||
| 	vgrename.c \ | ||||
| 	vgremove.c \ | ||||
| 	vgscan.c | ||||
|  | ||||
| TARGETS=\ | ||||
| 	lvm | ||||
|  | ||||
| include ../make.tmpl | ||||
|  | ||||
| lvm: $(OBJECTS) $(top_srcdir)/lib/liblvm.a | ||||
| 	$(CC) -o lvm $(OBJECTS) -L$(top_srcdir)/lib -llvm $(EXTRA_LIBS) | ||||
|  | ||||
| install: | ||||
| 	$(INSTALL) -c -d $(sbindir); | ||||
| 	$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 555 -s lvm $(sbindir)/lvm | ||||
| 	( cd $(sbindir); \ | ||||
| 	  for v in ${SOURCES:.c=}; do \ | ||||
| 	  	if [ "x$$v" != "xlvm" ]; then \ | ||||
| 	  		$(LN_S) -f lvm $$v; \ | ||||
| 		fi \ | ||||
| 	  done; \ | ||||
| 	) | ||||
|  | ||||
|  | ||||
							
								
								
									
										73
									
								
								tools/args.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								tools/args.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| /* | ||||
|  * 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 LVM; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| xx(available_ARG, 'a', "available", yes_no_arg) | ||||
| xx(all_ARG, 'a', "all", NULL) | ||||
| xx(autobackup_ARG, 'A', "autobackup", yes_no_arg) | ||||
| xx(activevolumegroups_ARG, 'A', "activevolumegroups", NULL) | ||||
| xx(blockdevice_ARG, 'b', "blockdevice", NULL) | ||||
| xx(chunksize_ARG, 'c', "chunksize", size_arg) | ||||
| xx(colon_ARG, 'c', "colon", NULL) | ||||
| xx(contiguous_ARG, 'C', "contiguous", yes_no_arg) | ||||
| xx(debug_ARG, 'd', "debug", NULL) | ||||
| xx(disk_ARG, 'D', "disk", NULL) | ||||
| xx(exported_ARG, 'e', "exported", NULL) | ||||
| xx(physicalextent_ARG, 'E', "physicalextent", NULL) | ||||
| xx(file_ARG, 'f', "file", NULL) | ||||
| xx(force_ARG, 'f', "force", NULL) | ||||
| xx(full_ARG, 'f', "full", NULL) | ||||
| xx(help_ARG, 'h', "help", NULL) | ||||
| xx(stripesize_ARG, 'I', "stripesize", size_arg) | ||||
| xx(stripes_ARG, 'i', "stripes", int_arg) | ||||
| xx(iop_version_ARG, 'i', "iop_version", NULL) | ||||
| xx(logicalvolume_ARG, 'l', "logicalvolume", int_arg) | ||||
| xx(maxlogicalvolumes_ARG, 'l', "maxlogicalvolumes", int_arg) | ||||
| xx(extents_ARG, 'l', "extents", int_arg) | ||||
| xx(lvmpartition_ARG, 'l', "lvmpartition", NULL) | ||||
| xx(list_ARG, 'l', "list", NULL) | ||||
| xx(size_ARG, 'L', "size", size_arg) | ||||
| xx(logicalextent_ARG, 'L', "logicalextent", int_arg) | ||||
| xx(name_ARG, 'n', "name", string_arg) | ||||
| xx(oldpath_ARG, 'n', "oldpath", NULL) | ||||
| xx(nofsck_ARG, 'n', "nofsck", NULL) | ||||
| xx(novolumegroup_ARG, 'n', "novolumegroup", NULL) | ||||
| xx(permission_ARG, 'p', "permission", permission_arg) | ||||
| xx(maxphysicalvolumes_ARG, 'p', "maxphysicalvolumes", int_arg) | ||||
| xx(physicalvolume_ARG, 'P', "physicalvolume", NULL) | ||||
| xx(readahead_ARG, 'r', "readahead", int_arg) | ||||
| xx(reset_ARG, 'R', "reset", NULL) | ||||
| xx(physicalextentsize_ARG, 's', "physicalextentsize", size_arg) | ||||
| xx(stdin_ARG, 's', "stdin", NULL) | ||||
| xx(snapshot_ARG, 's', "snapshot", NULL) | ||||
| xx(short_ARG, 's', "short", NULL) | ||||
| xx(test_ARG, 't', "test", NULL) | ||||
| xx(uuid_ARG, 'u', "uuid", NULL) | ||||
| xx(uuidlist_ARG, 'U', "uuidlist", NULL) | ||||
| xx(verbose_ARG, 'v', "verbose", NULL) | ||||
| xx(volumegroup_ARG, 'V', "volumegroup", NULL) | ||||
| xx(version_ARG, '\0', "version", NULL) | ||||
| xx(allocation_ARG, 'x', "allocation", yes_no_arg) | ||||
| xx(yes_ARG, 'y', "yes", NULL) | ||||
| xx(zero_ARG, 'Z', "zero", yes_no_arg) | ||||
| xx(suspend_ARG, 'z', "suspend", NULL) | ||||
|  | ||||
| /* this should always be last */ | ||||
| xx(ARG_COUNT, '-', "", NULL) | ||||
|  | ||||
							
								
								
									
										488
									
								
								tools/commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										488
									
								
								tools/commands.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,488 @@ | ||||
| /* | ||||
|  * 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 LVM; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| xx(e2fsadm, | ||||
|    "Resize logical volume and ext2 filesystem", | ||||
|    "e2fsadm " | ||||
|    "[-d|--debug] " | ||||
|    "[-h|--help] " | ||||
|    "[-n|--nofsck]\n\t" | ||||
|    "{[-l|--extents] [+|-]LogicalExtentsNumber |\n\t" | ||||
|    " [-L|--size] [+|-]LogicalVolumeSize[kKmMgGtT]}\n\t" | ||||
|    "[-t|--test] " | ||||
|    "[-v|--verbose] " | ||||
|    "[--version] " | ||||
|    "LogicalVolumePath\n", | ||||
|  | ||||
|     extents_ARG, size_ARG, nofsck_ARG, test_ARG) | ||||
|  | ||||
| xx(help, | ||||
|    "Display help for commands", | ||||
|    "help <command>\n") | ||||
|  | ||||
| /********* | ||||
| xx(lvactivate, | ||||
|    "Activate logical volume on given partition(s)", | ||||
|    "lvactivate " | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "Logical Volume(s)\n") | ||||
| ***********/ | ||||
|  | ||||
| xx(lvchange, | ||||
|    "Change the attributes of logical volume(s)", | ||||
|    "lvchange\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-a/--available y/n]\n" | ||||
|    "\t[-C/--contiguous y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-p/--permission r/rw]\n" | ||||
|    "\t[-r/--readahead ReadAheadSectors]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", | ||||
|  | ||||
|    autobackup_ARG, available_ARG, contiguous_ARG, | ||||
|    permission_ARG, readahead_ARG) | ||||
|  | ||||
| xx(lvcreate, | ||||
|    "Create a logical volume", | ||||
|    "lvcreate " | ||||
|    "[-A|--autobackup {y|n}] " | ||||
|    "[-C|--contiguous {y|n}] " | ||||
|    "[-d|--debug]\n" | ||||
|    "\t[-h|--help] " | ||||
|    "[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n\t" | ||||
|    "{-l|--extents LogicalExtentsNumber |\n\t" | ||||
|    " -L|--size LogicalVolumeSize[kKmMgGtT]} " | ||||
|    "[-n|--name LogicalVolumeName]\n\t" | ||||
|    "[-p|--permission {r|rw}] " | ||||
|    "[-r|--readahead ReadAheadSectors]\n\t" | ||||
|    "[-v|--verbose] " | ||||
|    "[-Z|--zero {y|n}] " | ||||
|    "[--version]\n\t" | ||||
|    "VolumeGroupName [PhysicalVolumePath...]\n\n" | ||||
|    "lvcreate " | ||||
|    "-s|--snapshot " | ||||
|    "[-c|--chunksize ChunkSize]\n\t" | ||||
|    "{-l|--extents LogicalExtentsNumber |\n\t" | ||||
|    " -L|--size LogicalVolumeSize[kKmMgGtT]}\n\t" | ||||
|    "-n|--name SnapshotLogicalVolumeName\n\t" | ||||
|    "LogicalVolume[Path] [PhysicalVolumePath...]\n", | ||||
|  | ||||
|    autobackup_ARG, chunksize_ARG, contiguous_ARG, | ||||
|    stripes_ARG, stripesize_ARG, extents_ARG, size_ARG, name_ARG, | ||||
|    permission_ARG, readahead_ARG, snapshot_ARG, zero_ARG) | ||||
|  | ||||
| xx(lvdisplay, | ||||
|    "Display information about a logical volume", | ||||
|    "lvdisplay\n" | ||||
|    "\t[-c/--colon]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-D/--disk]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v[v]/--verbose [--verbose]]\n" | ||||
|    "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", | ||||
|  | ||||
|     colon_ARG, disk_ARG) | ||||
|  | ||||
| xx(lvextend, | ||||
|    "Add space to a logical volume", | ||||
|    "lvextend\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t{-l/--extents [+]LogicalExtentsNumber |\n" | ||||
|    "\t -L/--size [+]LogicalVolumeSize[kKmMgGtT]}\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n", | ||||
|  | ||||
|    autobackup_ARG, extents_ARG, size_ARG) | ||||
|  | ||||
| xx(lvmchange, | ||||
|    "With the device mapper, lvmchange is obsolete and does nothing.", | ||||
|    "lvmchange\n"  | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-f/--force]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-i/-?/--iop_version]\n" | ||||
|    "\t[-R/--reset]\n" | ||||
|    "\t[-v/--verbose]\n", | ||||
|  | ||||
|     force_ARG, reset_ARG) | ||||
|  | ||||
| xx(lvmdiskscan, | ||||
|    "List devices that may be used as physical volumes", | ||||
|    "lvmdiskscan\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-l/--lvmpartition]\n" | ||||
|    "\t[-v/--verbose]\n", | ||||
|  | ||||
|    lvmpartition_ARG) | ||||
|  | ||||
| xx(lvmsadc, | ||||
|    "Collect activity data", | ||||
|    "lvmsadc\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\t[LogFilePath]\n" ) | ||||
|  | ||||
| xx(lvmsar, | ||||
|    "Create activity report", | ||||
|    "lvmsar\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-f/--full]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-s/--stdin]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tLogFilePath\n", | ||||
|  | ||||
|    full_ARG,  stdin_ARG) | ||||
|  | ||||
| xx(lvreduce, | ||||
|    "Reduce the size of a logical volume", | ||||
|    "lvreduce\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-f/--force]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t{-l/--extents [-]LogicalExtentsNumber |\n" | ||||
|    "\t -L/--size [-]LogicalVolumeSize[kKmMgGtT]}\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tLogicalVolume[Path]\n", | ||||
|  | ||||
|    autobackup_ARG, force_ARG,  extents_ARG, | ||||
|    size_ARG, yes_ARG) | ||||
|  | ||||
| xx(lvremove, | ||||
|    "Remove logical volume(s) from the system", | ||||
|    "lvremove\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-f/--force]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", | ||||
|  | ||||
|    autobackup_ARG, force_ARG) | ||||
|  | ||||
| xx(lvrename, | ||||
|    "Rename a logical volume", | ||||
|    "lvrename " | ||||
|    "[-A|--autobackup {y|n}] " | ||||
|    "[-d|--debug] " | ||||
|    "[-h|--help] " | ||||
|    "[-v|--verbose]\n\t" | ||||
|    "[--version] " | ||||
|    "{ OldLogicalVolumePath NewLogicalVolumePath |\n\t" | ||||
|    "  VolumeGroupName OldLogicalVolumeName NewLogicalVolumeName }\n", | ||||
|  | ||||
|    autobackup_ARG) | ||||
|  | ||||
| xx(lvscan, | ||||
|    "List all logical volumes in all volume groups", | ||||
|    "lvscan " | ||||
|    "[-b|--blockdevice] " | ||||
|    "[-d|--debug] " | ||||
|    "[-D|--disk]\n\t" | ||||
|    "[-h|--help] " | ||||
|    "[-v|--verbose] " | ||||
|    "[--version]\n", | ||||
|  | ||||
|    blockdevice_ARG, disk_ARG) | ||||
|  | ||||
| xx(pvchange, | ||||
|    "Change attributes of physical volume(s)", | ||||
|    "pvchange\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\t[-a/--all]\n" | ||||
|    "\t[-x/--allocation y/n]\n" | ||||
|    "\t[PhysicalVolumePath...]\n", | ||||
|  | ||||
|    all_ARG, autobackup_ARG, allocation_ARG) | ||||
|  | ||||
| xx(pvcreate, | ||||
|    "Initialize physical volume(s) for use by LVM", | ||||
|    "pvcreate " | ||||
|    "[-d|--debug]" | ||||
|    "[-f[f]|--force [--force]] " | ||||
|    "[-h|--help] " | ||||
|    "[-y|--yes]\n\t" | ||||
|    "[-v|--verbose] " | ||||
|    "[--version] " | ||||
|    "PhysicalVolume [PhysicalVolume...]\n", | ||||
|  | ||||
|    force_ARG, yes_ARG) | ||||
|  | ||||
| xx(pvdata, | ||||
|    "Display the on-disk metadata for physical volume(s)", | ||||
|    "pvdata " | ||||
|    "[-a|--all] " | ||||
|    "[-d|--debug] " | ||||
|    "[-E|--physicalextent] " | ||||
|    "[-h|--help]\n\t" | ||||
|    "[-L|--logicalvolume] " | ||||
|    "[-P[P]|--physicalvolume [--physicalvolume]]\n\t" | ||||
|    "[-U|--uuidlist] " | ||||
|    "[-v[v]|--verbose [--verbose]] " | ||||
|    "[-V|--volumegroup]\n\t" | ||||
|    "[--version] " | ||||
|    "PhysicalVolume [PhysicalVolume...]\n", | ||||
|  | ||||
|    all_ARG,  logicalextent_ARG, physicalextent_ARG, | ||||
|    physicalvolume_ARG, uuidlist_ARG, volumegroup_ARG) | ||||
|  | ||||
| xx(pvdisplay, | ||||
|    "Display various attributes of logical volume(s)", | ||||
|    "pvdisplay\n" | ||||
|    "\t[-c/--colon]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-s/--short]\n" | ||||
|    "\t[-v[v]/--verbose [--verbose]]\n" | ||||
|    "\tPhysicalVolumePath [PhysicalVolumePath...]\n", | ||||
|  | ||||
|    colon_ARG, short_ARG) | ||||
|  | ||||
| #if 0 | ||||
| xx(pvmove, | ||||
|    "Move extents from one physical volume to another", | ||||
|    "pvmove " | ||||
|    "[-A|--autobackup {y|n}] " | ||||
|    "[-d|--debug] " | ||||
|    "[-f|--force]" | ||||
|    "[-h|--help]\n\t" | ||||
|    "[-t|--test] " | ||||
|    "[-v[v]|--verbose [--verbose]] " | ||||
|    "[--version]\n\t" | ||||
|    "[{-n|--name} LogicalVolume[:LogicalExtent[-LogicalExtent]...]]\n\t" | ||||
|    "SourcePhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]}\n\t" | ||||
|    "[DestinationPhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]...]\n", | ||||
|  | ||||
|    autobackup_ARG, force_ARG,  name_ARG, test_ARG) | ||||
| #endif | ||||
|  | ||||
| xx(pvscan, | ||||
|    "List all physical volumes", | ||||
|    "pvscan " | ||||
|    "[-d|--debug] " | ||||
|    "{-e|--exported | -n/--novolumegroup} " | ||||
|    "[-h|--help]\n\t" | ||||
|    "[-s|--short] " | ||||
|    "[-u|--uuid] " | ||||
|    "[-v[v]|--verbose [--verbose]] " | ||||
|    "[--version]\n", | ||||
|  | ||||
|    exported_ARG,  novolumegroup_ARG, short_ARG, uuid_ARG) | ||||
|  | ||||
| xx(vgcfgbackup, | ||||
|    "Backup volume group configuration(s)", | ||||
|    "vgcfgbackup " | ||||
|    "[-d|--debug] " | ||||
|    "[-h|--help] " | ||||
|    "[-v|--verbose]\n\t" | ||||
|    "[-V|--version] " | ||||
|    "[VolumeGroupName...]\n" ) | ||||
|  | ||||
| xx(vgcfgrestore, | ||||
|    "Restore volume group configuration", | ||||
|    "vgcfgrestore " | ||||
|    "[-d|--debug] " | ||||
|    "[-f|--file VGConfPath] " | ||||
|    "[-l[l]|--list [--list]]\n\t" | ||||
|    "[-n|--name VolumeGroupName] " | ||||
|    "[-h|--help]\n\t" | ||||
|    "[-o|--oldpath OldPhysicalVolumePath] " | ||||
|    "[-t|--test] " | ||||
|    "[-v|--verbose]\n\t" | ||||
|    "[--version] " | ||||
|    "[PhysicalVolumePath]\n", | ||||
|  | ||||
|    file_ARG, list_ARG, name_ARG, oldpath_ARG, test_ARG) | ||||
|  | ||||
| xx(vgchange, | ||||
|    "Change volume group attributes", | ||||
|    "vgchange\n\t" | ||||
|    "[-A|--autobackup {y|n}] " | ||||
|    "[-d|--debug] " | ||||
|    "[-h|--help]\n\t" | ||||
|    "[-v|--verbose] " | ||||
|    "[--version]\n\t" | ||||
|    "{-a|--available {y|n} |\n\t" | ||||
|    " -x|--allocation {y|n} |\n\t" | ||||
|    " -l|--logicalvolume MaxLogicalVolumes}\n\t" | ||||
|    "[VolumeGroupName...]\n", | ||||
|  | ||||
|    autobackup_ARG, available_ARG, logicalvolume_ARG, allocation_ARG ) | ||||
|  | ||||
| xx(vgck, | ||||
|    "Check the consistency of volume group(s)", | ||||
|    "vgck " | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\t[VolumeGroupName...]\n" ) | ||||
|  | ||||
| xx(vgcreate, | ||||
|    "Create a volume group", | ||||
|    "vgcreate" | ||||
|    "[-A|--autobackup {y|n}] " | ||||
|    "[-d|--debug]\n\t" | ||||
|    "[-l|--maxlogicalvolumes MaxLogicalVolumes]\n\t" | ||||
|    "[-p|--maxphysicalvolumes MaxPhysicalVolumes] " | ||||
|    "[-h|--help]\n\t" | ||||
|    "[-s|--physicalextentsize PhysicalExtentSize[kKmMgGtT]] " | ||||
|    "[-v|--verbose]\n\t" | ||||
|    "[--version] " | ||||
|    "VolumeGroupName " | ||||
|    "PhysicalVolume [PhysicalVolume...]\n", | ||||
|  | ||||
|    autobackup_ARG, maxlogicalvolumes_ARG, maxphysicalvolumes_ARG, | ||||
|    physicalextentsize_ARG) | ||||
|  | ||||
| xx(vgdisplay, | ||||
|    "Display volume group information", | ||||
|    "vgdisplay " | ||||
|    "[-c|--colon | -s|--short | -v[v]|--verbose [--verbose]]\n\t" | ||||
|    "[-d|--debug] " | ||||
|    "[-h|--help] " | ||||
|    "[--version]\n\t" | ||||
|    "[-A|--activevolumegroups | [-D|--disk] [VolumeGroupName...] ]\n", | ||||
|  | ||||
|    activevolumegroups_ARG, colon_ARG, disk_ARG, short_ARG) | ||||
|  | ||||
| xx(vgexport, | ||||
|    "Unregister volume group(s) from the system", | ||||
|    "vgexport " | ||||
|    "[-a|--all] " | ||||
|    "[-d|--debug] " | ||||
|    "[-h|--help]\n\t" | ||||
|    "[-v|--verbose] " | ||||
|    "[--version] " | ||||
|    "VolumeGroupName [VolumeGroupName...]\n", | ||||
|  | ||||
|    all_ARG) | ||||
|  | ||||
| xx(vgextend, | ||||
|    "Add physical volumes to a volume group", | ||||
|    "vgextend\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tVolumeGroupName\n" | ||||
|    "\tPhysicalDevicePath [PhysicalDevicePath...]\n", | ||||
|  | ||||
|    autobackup_ARG) | ||||
|  | ||||
| xx(vgimport, | ||||
|    "Register exported volume group with system", | ||||
|    "vgimport " | ||||
|    "[-d|--debug] " | ||||
|    "[-f|--force] " | ||||
|    "[-h|--help] " | ||||
|    "[-v|--verbose]\n\t" | ||||
|    "VolumeGroupName PhysicalVolumePath " | ||||
|    "[PhysicalVolumePath...]\n", | ||||
|  | ||||
|    force_ARG) | ||||
|  | ||||
| xx(vgmerge, | ||||
|    "Merge volume groups", | ||||
|    "vgmerge\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-l/--list]\n" | ||||
|    "\t[-t/--test]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tDestinationVolumeGroupName SourceVolumeGroupName\n", | ||||
|  | ||||
|    autobackup_ARG, list_ARG, test_ARG) | ||||
|  | ||||
| xx(vgmknodes, | ||||
|    "Create the special files for volume group devices in /dev", | ||||
|    "vgmknodes\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\t[VolumeGroupName...]\n" ) | ||||
|  | ||||
| xx(vgreduce, | ||||
|    "Remove physical volume(s) from a volume group", | ||||
|    "vgreduce\n" | ||||
|    "\t[-a/--all]\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tVolumeGroupName\n" | ||||
|    "\t[PhysicalVolumePath...]\n", | ||||
|  | ||||
|    all_ARG, autobackup_ARG) | ||||
|  | ||||
| xx(vgremove, | ||||
|    "Remove volume group(s)", | ||||
|    "vgremove\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tVolumeGroupName [VolumeGroupName...]\n" ) | ||||
|  | ||||
| xx(vgrename, | ||||
|    "Rename a volume group", | ||||
|    "vgrename\n" | ||||
|    "\t[-A/--autobackup y/n]\n" | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-f/--force]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" | ||||
|    "\tOldVolumeGroupPath NewVolumeGroupPath /\n" | ||||
|    "\tOldVolumeGroupName NewVolumeGroupName\n", | ||||
|  | ||||
|    autobackup_ARG, force_ARG) | ||||
|  | ||||
| xx(vgscan, | ||||
|    "Search for all volume groups", | ||||
|    "vgscan " | ||||
|    "\t[-d/--debug]\n" | ||||
|    "\t[-h/-?/--help]\n" | ||||
|    "\t[-v/--verbose]\n" ) | ||||
|  | ||||
| xx(vgsplit, | ||||
|    "Move physical volumes into a new volume group", | ||||
|    "vgsplit " | ||||
|    "[-A|--autobackup {y|n}] " | ||||
|    "[-d|--debug] " | ||||
|    "[-h|--help] " | ||||
|    "[-l|--list]\n\t" | ||||
|    "[-t|--test] " | ||||
|    "[-v|--verbose] " | ||||
|    "[--version]\n\t" | ||||
|    "ExistingVolumeGroupName NewVolumeGroupName\n\t" | ||||
|    "PhysicalVolumePath [PhysicalVolumePath...]\n", | ||||
|  | ||||
|    autobackup_ARG, list_ARG, test_ARG) | ||||
							
								
								
									
										30
									
								
								tools/errors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tools/errors.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| /* | ||||
|  * 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 LVM; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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 /* #ifndef _LVM_ERROR_H_INCLUDE */ | ||||
|  | ||||
							
								
								
									
										95
									
								
								tools/lvactivate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								tools/lvactivate.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  * 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 LVM; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "tools.h" | ||||
|  | ||||
| int lvactivate(int argc, char **argv) | ||||
| { | ||||
| 	int p; | ||||
|  | ||||
| 	struct device *pv_dev; | ||||
|  | ||||
| 	char *lv_name; | ||||
| 	char *pv_name; | ||||
|  | ||||
| 	struct physical_volume *pv = NULL; | ||||
| 	struct logical_volume *lv = NULL; | ||||
|  | ||||
| 	if (argc < 2) { | ||||
| 		log_error("please enter logical volume & physical volume(s)"); | ||||
| 		return EINVALID_CMD_LINE; | ||||
| 	} | ||||
|  | ||||
| 	lv_name = argv[0]; | ||||
| 	argc--; | ||||
| 	argv++; | ||||
|  | ||||
| 	while (argc--) { | ||||
| 		pv_name = argv[argc]; | ||||
| 		if (!(pv_dev = dev_cache_get(pv_name))) { | ||||
| 			log_error("device \"%s\" not found", pv_name); | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		if (!(pv = pv_read(ios, pv_dev))) { | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		if (pv->status & ALLOCATED_PV) { | ||||
| 			if (!(pv->pe = pv_read_pe(pv_name, pv))) | ||||
| 				goto pvdisplay_device_out; | ||||
| 			if (!(lvs = pv_read_lvs(pv))) { | ||||
| 				log_error("Failed to read LVs on %s", | ||||
| 					  pv->pv_name); | ||||
| 				goto pvdisplay_device_out; | ||||
| 			} | ||||
| 		} else | ||||
| 			log_print("no logical volume on physical volume %s", | ||||
| 				  pv_name); | ||||
|  | ||||
| 		for (p = 0; p < pv->pe_total; p++) { | ||||
| 			int l = pv->pe[p].lv_num; | ||||
| 			int le = pv->pe[p].le_num; | ||||
| 			long pe_size_guess = lvs[l - 1].lv_size / | ||||
| 			    lvs[l - 1].lv_allocated_le; | ||||
|  | ||||
| 			if (l && !strcmp(lv, lvs[l - 1].lv_name)) | ||||
| 				printf("%012ld %ld linear %s %012ld\n", | ||||
| 				       pe_size_guess * le, | ||||
| 				       pe_size_guess, | ||||
| 				       pv_name, get_pe_offset(p, pv)); | ||||
| 		} | ||||
|  | ||||
| 		if (pv) | ||||
| 			dbg_free(pv->pe); | ||||
| 		dbg_free(pv); | ||||
| 		dbg_free(lvs); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
|       pvdisplay_device_out: | ||||
| 	if (pv) | ||||
| 		dbg_free(pv->pe); | ||||
| 	dbg_free(pv); | ||||
| 	dbg_free(lvs); | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
							
								
								
									
										865
									
								
								tools/lvm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										865
									
								
								tools/lvm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,865 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001 Sistina Software (UK) Limited. | ||||
|  * | ||||
|  * This file is released under the GPL. | ||||
|  */ | ||||
|  | ||||
| #include "tools.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <getopt.h> | ||||
| #include <signal.h> | ||||
| #include <syslog.h> | ||||
| #include <libgen.h> | ||||
| #include <sys/stat.h> | ||||
| #include <ctype.h> | ||||
|  | ||||
| #include "stub.h" | ||||
|  | ||||
| #ifdef READLINE_SUPPORT | ||||
| #include <readline/readline.h> | ||||
| #include <readline/history.h> | ||||
| #endif | ||||
|  | ||||
| /* define the table of valid switches */ | ||||
| struct arg the_args[ARG_COUNT + 1] = { | ||||
|  | ||||
| #define xx(a, b, c, d) {b, "--" c, d, 0, NULL}, | ||||
| #include "args.h" | ||||
| #undef xx | ||||
|  | ||||
| }; | ||||
|  | ||||
| /* a register of the lvm commands */ | ||||
| struct command { | ||||
| 	const char *name; | ||||
| 	const char *desc; | ||||
| 	const char *usage; | ||||
| 	command_fn fn; | ||||
|  | ||||
| 	int num_args; | ||||
| 	int *valid_args; | ||||
| }; | ||||
|  | ||||
| static int _array_size; | ||||
| static int _num_commands; | ||||
| static struct command *_commands; | ||||
|  | ||||
| /* Exported */ | ||||
| struct io_space *ios; | ||||
|  | ||||
| static struct dev_filter *_filter; | ||||
| static struct config_file *_cf; | ||||
|  | ||||
| static int _interactive; | ||||
| static FILE *_log; | ||||
| static int _debug_level; | ||||
|  | ||||
| /* static functions */ | ||||
| static void register_commands(void); | ||||
| static struct command *find_command(const char *name); | ||||
| static void register_command(const char *name, command_fn fn, | ||||
| 			     const char *desc, const char *usage, ...); | ||||
| static void create_new_command(const char *name, command_fn command, | ||||
| 			       const char *desc, const char *usage, | ||||
| 			       int nargs, int *args); | ||||
|  | ||||
| static void alloc_command(void); | ||||
| static void add_getopt_arg(int arg, char **ptr, struct option **o); | ||||
| static int process_command_line(struct command *com, int *argc, char ***argv); | ||||
| static struct arg *find_arg(struct command *com, int a); | ||||
| static int process_common_commands(struct command *com); | ||||
| static int run_command(int argc, char **argv); | ||||
| static int init(void); | ||||
| static void fin(void); | ||||
| static int run_script(int argc, char **argv); | ||||
|  | ||||
| #ifdef READLINE_SUPPORT | ||||
| static int shell(void); | ||||
| static char **lvm_completion(char *text, int start_pos, int end_pos); | ||||
| static char *list_cmds(char *text, int state); | ||||
| static char *list_args(char *text, int state); | ||||
| #endif | ||||
|  | ||||
| static void display_help(void); | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	char *namebase, *base; | ||||
| 	int ret, alias = 0; | ||||
|  | ||||
| 	if (!init()) | ||||
| 		return -1; | ||||
|  | ||||
| 	namebase = strdup(argv[0]); | ||||
| 	base = basename(namebase); | ||||
| 	if (strcmp(base, "lvm")) | ||||
| 		alias = 1; | ||||
| 	free(namebase); | ||||
|  | ||||
| 	register_commands(); | ||||
|  | ||||
| #ifdef READLINE_SUPPORT | ||||
| 	if (!alias && argc == 1) { | ||||
| 		ret = shell(); | ||||
| 		goto out; | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	if (!alias) { | ||||
| 		if (argc < 2) { | ||||
| 			log_fatal("Please supply an LVM command."); | ||||
| 			display_help(); | ||||
| 			ret = EINVALID_CMD_LINE; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		argc--; | ||||
| 		argv++; | ||||
| 	} | ||||
|  | ||||
| 	ret = run_command(argc, argv); | ||||
| 	if ((ret == ENO_SUCH_CMD) && (!alias)) | ||||
| 		ret = run_script(argc, argv); | ||||
| 	if (ret == ENO_SUCH_CMD) | ||||
| 		log_error("No such command.  Try 'help'."); | ||||
|  | ||||
|       out: | ||||
| 	fin(); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void usage(const char *name) | ||||
| { | ||||
| 	struct command *com = find_command(name); | ||||
|  | ||||
| 	if (!com) | ||||
| 		return; | ||||
|  | ||||
| 	log_error("%s: %s\n\n%s", com->name, com->desc, com->usage); | ||||
| } | ||||
|  | ||||
| int yes_no_arg(struct arg *a) | ||||
| { | ||||
| 	if (!strcmp(a->value, "y")) | ||||
| 		a->i_value = 1; | ||||
|  | ||||
| 	else if (!strcmp(a->value, "n")) | ||||
| 		a->i_value = 0; | ||||
|  | ||||
| 	else | ||||
| 		return 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int size_arg(struct arg *a) | ||||
| { | ||||
| 	static char *suffixes = "kmgt"; | ||||
|  | ||||
| 	char *ptr; | ||||
| 	int i; | ||||
| 	long v = strtol(a->value, &ptr, 10); | ||||
|  | ||||
| 	if (ptr == a->value) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (*ptr) { | ||||
| 		for (i = strlen(suffixes) - 1; i >= 0; i--) | ||||
| 			if (suffixes[i] == tolower((int) *ptr)) | ||||
| 				break; | ||||
|  | ||||
| 		if (i < 0) | ||||
| 			return 0; | ||||
|  | ||||
| 		while (i-- > 0) | ||||
| 			v *= 1024; | ||||
| 	} | ||||
|  | ||||
| 	a->i_value = (int) v; | ||||
| 	return 1; | ||||
|  | ||||
| } | ||||
|  | ||||
| int int_arg(struct arg *a) | ||||
| { | ||||
| 	char *ptr; | ||||
| 	long v = strtol(a->value, &ptr, 10); | ||||
|  | ||||
| 	if (ptr == a->value || *ptr) | ||||
| 		return 0; | ||||
|  | ||||
| 	a->i_value = (int) v; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int string_arg(struct arg *a) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int permission_arg(struct arg *a) | ||||
| { | ||||
| 	if ((!strcmp(a->value, "rw")) || (!strcmp(a->value, "wr"))) | ||||
| 		a->i_value = LVM_READ | LVM_WRITE; | ||||
|  | ||||
| 	else if (!strcmp(a->value, "r")) | ||||
| 		a->i_value = LVM_READ; | ||||
|  | ||||
| 	else | ||||
| 		return 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| char yes_no_prompt(const char *prompt, ...) | ||||
| { | ||||
| 	int c = 0; | ||||
| 	va_list ap; | ||||
|  | ||||
| 	while (c != 'y' && c != 'n') { | ||||
| 		if (c == '\n' || c == 0) { | ||||
| 			va_start(ap, prompt); | ||||
| 			vprintf(prompt, ap); | ||||
| 			va_end(ap); | ||||
| 		} | ||||
| 		c = tolower(getchar()); | ||||
| 	} | ||||
|  | ||||
| 	while (getchar() != '\n') | ||||
| 		; | ||||
|  | ||||
| 	return c; | ||||
| } | ||||
|  | ||||
| static void register_commands() | ||||
| { | ||||
| #define xx(a, b, c...) register_command(# a, a, b, ## c, \ | ||||
|                                         debug_ARG, help_ARG, suspend_ARG, \ | ||||
|                                         version_ARG, verbose_ARG, -1); | ||||
| #include "commands.h" | ||||
| #undef xx | ||||
| } | ||||
|  | ||||
| static void register_command(const char *name, command_fn fn, | ||||
| 			     const char *desc, const char *usage, ...) | ||||
| { | ||||
| 	int nargs = 0, i; | ||||
| 	int *args; | ||||
| 	va_list ap; | ||||
|  | ||||
| 	/* count how many arguments we have */ | ||||
| 	va_start(ap, usage); | ||||
| 	while (va_arg(ap, int) >= 0) | ||||
| 		 nargs++; | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	/* allocate space for them */ | ||||
| 	if (!(args = dbg_malloc(sizeof (*args) * nargs))) { | ||||
| 		log_fatal("Out of memory."); | ||||
| 		exit(ECMD_FAILED); | ||||
| 	} | ||||
|  | ||||
| 	/* fill them in */ | ||||
| 	va_start(ap, usage); | ||||
| 	for (i = 0; i < nargs; i++) | ||||
| 		args[i] = va_arg(ap, int); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	/* enter the command in the register */ | ||||
| 	create_new_command(name, fn, desc, usage, nargs, args); | ||||
| } | ||||
|  | ||||
| static struct command *find_command(const char *name) | ||||
| { | ||||
| 	int i; | ||||
| 	char *namebase, *base; | ||||
|  | ||||
| 	namebase = strdup(name); | ||||
| 	base = basename(namebase); | ||||
|  | ||||
| 	for (i = 0; i < _num_commands; i++) { | ||||
| 		if (!strcmp(base, _commands[i].name)) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	free(namebase); | ||||
|  | ||||
| 	if (i >= _num_commands) | ||||
| 		return 0; | ||||
|  | ||||
| 	return _commands + i; | ||||
| } | ||||
|  | ||||
| static void create_new_command(const char *name, command_fn command, | ||||
| 			       const char *desc, const char *usage, | ||||
| 			       int nargs, int *args) | ||||
| { | ||||
| 	struct command *nc; | ||||
|  | ||||
| 	alloc_command(); | ||||
|  | ||||
| 	nc = _commands + _num_commands++; | ||||
|  | ||||
| 	nc->name = name; | ||||
| 	nc->desc = desc; | ||||
| 	nc->usage = usage; | ||||
| 	nc->fn = command; | ||||
| 	nc->num_args = nargs; | ||||
| 	nc->valid_args = args; | ||||
| } | ||||
|  | ||||
| static void __alloc(int size) | ||||
| { | ||||
| 	if (!(_commands = dbg_realloc(_commands, sizeof (*_commands) * size))) { | ||||
| 		log_fatal("Couldn't allocate memory."); | ||||
| 		exit(ECMD_FAILED); | ||||
| 	} | ||||
|  | ||||
| 	_array_size = size; | ||||
| } | ||||
|  | ||||
| static void alloc_command(void) | ||||
| { | ||||
| 	if (!_array_size) | ||||
| 		__alloc(32); | ||||
|  | ||||
| 	if (_array_size <= _num_commands) | ||||
| 		__alloc(2 * _array_size); | ||||
| } | ||||
|  | ||||
| static void add_getopt_arg(int arg, char **ptr, struct option **o) | ||||
| { | ||||
| 	struct arg *a = the_args + arg; | ||||
|  | ||||
| 	if (a->short_arg) { | ||||
| 		*(*ptr)++ = a->short_arg; | ||||
|  | ||||
| 		if (a->fn) | ||||
| 			*(*ptr)++ = ':'; | ||||
| 	} | ||||
|  | ||||
| 	if (*(a->long_arg + 2)) { | ||||
| 		(*o)->name = a->long_arg + 2; | ||||
| 		(*o)->has_arg = a->fn ? 1 : 0; | ||||
| 		(*o)->flag = NULL; | ||||
| 		(*o)->val = a->short_arg ? a->short_arg : (int) a; | ||||
| 		(*o)++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int process_command_line(struct command *com, int *argc, char ***argv) | ||||
| { | ||||
| 	int i, opt; | ||||
| 	char str[((ARG_COUNT + 1) * 2) + 1], *ptr = str; | ||||
| 	struct option opts[ARG_COUNT + 1], *o = opts; | ||||
| 	struct arg *a; | ||||
|  | ||||
| 	for (i = 0; i < ARG_COUNT; i++) { | ||||
| 		struct arg *a = the_args + i; | ||||
|  | ||||
| 		/* zero the count and arg */ | ||||
| 		a->count = 0; | ||||
| 		a->value = 0; | ||||
| 		a->i_value = 0; | ||||
| 	} | ||||
|  | ||||
| 	/* fill in the short and long opts */ | ||||
| 	for (i = 0; i < com->num_args; i++) | ||||
| 		add_getopt_arg(com->valid_args[i], &ptr, &o); | ||||
|  | ||||
| 	*ptr = '\0'; | ||||
| 	memset(o, 0, sizeof (*o)); | ||||
|  | ||||
| 	/* initialise getopt_long & scan for command line switches */ | ||||
| 	optarg = 0; | ||||
| 	optind = 0; | ||||
| 	while ((opt = getopt_long(*argc, *argv, str, opts, NULL)) >= 0) { | ||||
|  | ||||
| 		a = find_arg(com, opt); | ||||
|  | ||||
| 		if (!a) { | ||||
| 			log_fatal("Unrecognised option."); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (a->fn) { | ||||
| 			if (!optarg) { | ||||
| 				log_error("Option requires argument."); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			a->value = optarg; | ||||
|  | ||||
| 			if (!a->fn(a)) { | ||||
| 				log_error("Invalid argument %s", optarg); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		a->count++; | ||||
| 	} | ||||
|  | ||||
| 	*argc -= optind; | ||||
| 	*argv += optind; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static struct arg *find_arg(struct command *com, int opt) | ||||
| { | ||||
| 	struct arg *a; | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < com->num_args; i++) { | ||||
| 		a = the_args + com->valid_args[i]; | ||||
|  | ||||
| 		if ((opt == a->short_arg) || (opt == (int) a)) | ||||
| 			return a; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int process_common_commands(struct command *com) | ||||
| { | ||||
| 	int l; | ||||
|  | ||||
| 	if (arg_count(suspend_ARG)) | ||||
| 		kill(getpid(), SIGSTOP); | ||||
|  | ||||
| 	l = arg_count(debug_ARG); | ||||
| 	init_debug(l ? l : _debug_level); | ||||
|  | ||||
| 	init_verbose(arg_count(verbose_ARG)); | ||||
|  | ||||
| 	init_test(arg_count(test_ARG)); | ||||
|  | ||||
| 	if (arg_count(help_ARG)) { | ||||
| 		usage(com->name); | ||||
| 		return ECMD_PROCESSED; | ||||
| 	} | ||||
|  | ||||
| 	if (arg_count(version_ARG)) { | ||||
| 		/* FIXME: Add driver and software version */ | ||||
| 		log_error("%s: ", com->name); | ||||
| 		return ECMD_PROCESSED; | ||||
| 	} | ||||
|  | ||||
| 	/* Set autobackup if command takes this option */ | ||||
| 	for (l = 0; l < com->num_args; l++) | ||||
| 		if (com->valid_args[l] == autobackup_ARG) { | ||||
| 			if (init_autobackup()) | ||||
| 				return EINVALID_CMD_LINE; | ||||
| 			else | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int help(int argc, char **argv) | ||||
| { | ||||
| 	if (!argc) | ||||
| 		display_help(); | ||||
| 	else { | ||||
| 		int i; | ||||
| 		for (i = 0; i < argc; i++) | ||||
| 			usage(argv[i]); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void display_help() | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	log_error("Available lvm commands:"); | ||||
| 	log_error("Use 'lvm help <command>' for more information"); | ||||
| 	log_error(" "); | ||||
|  | ||||
| 	for (i = 0; i < _num_commands; i++) { | ||||
| 		struct command *com = _commands + i; | ||||
|  | ||||
| 		log_error("%-16.16s%s", com->name, com->desc); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int run_command(int argc, char **argv) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	struct command *com; | ||||
|  | ||||
| 	if (!(com = find_command(argv[0]))) | ||||
| 		return ENO_SUCH_CMD; | ||||
|  | ||||
| 	if (!process_command_line(com, &argc, &argv)) { | ||||
| 		log_error("Error during parsing of command line."); | ||||
| 		return EINVALID_CMD_LINE; | ||||
| 	} | ||||
|  | ||||
| 	if ((ret = process_common_commands(com))) | ||||
| 		return ret; | ||||
|  | ||||
| 	ret = com->fn(argc, argv); | ||||
|  | ||||
| 	/* | ||||
| 	 * free off any memory the command used. | ||||
| 	 */ | ||||
| 	pool_empty(ios->mem); | ||||
|  | ||||
| 	if (ret == EINVALID_CMD_LINE && !_interactive) | ||||
| 		usage(com->name); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int split(char *str, int *argc, char **argv, int max) | ||||
| { | ||||
| 	char *b = str, *e; | ||||
| 	*argc = 0; | ||||
|  | ||||
| 	while (*b) { | ||||
| 		while (*b && isspace(*b)) | ||||
| 			b++; | ||||
|  | ||||
| 		if ((!*b) || (*b == '#')) | ||||
| 			break; | ||||
|  | ||||
| 		e = b; | ||||
| 		while (*e && !isspace(*e)) | ||||
| 			e++; | ||||
|  | ||||
| 		argv[(*argc)++] = b; | ||||
| 		if (!*e) | ||||
| 			break; | ||||
| 		*e++ = '\0'; | ||||
| 		b = e; | ||||
| 		if (*argc == max) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	return *argc; | ||||
| } | ||||
|  | ||||
| struct config_file *active_config_file(void) | ||||
| { | ||||
| 	return _cf; | ||||
| } | ||||
|  | ||||
| struct dev_filter *active_filter(void) | ||||
| { | ||||
| 	return _filter; | ||||
| } | ||||
|  | ||||
| static void __init_log(struct config_file *cf) | ||||
| { | ||||
| 	const char *log_file = find_config_str(cf->root, "log/file", '/', 0); | ||||
|  | ||||
| 	if (log_file) { | ||||
| 		/* set up the logging */ | ||||
| 		if (!(_log = fopen(log_file, "w"))) | ||||
| 			log_error("couldn't open log file %s\n", log_file); | ||||
| 		else | ||||
| 			init_log(_log); | ||||
| 	} | ||||
|  | ||||
| 	_debug_level = find_config_int(cf->root, "log/level", '/', 0); | ||||
| 	init_debug(_debug_level); | ||||
| } | ||||
|  | ||||
| static int init(void) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	const char *e = getenv("LVM_CONFIG_FILE"); | ||||
| 	struct stat info; | ||||
| 	struct pool *ios_pool; | ||||
|  | ||||
| 	/* FIXME: Override from config file */ | ||||
| 	char *prefix = "/dev/"; | ||||
|  | ||||
| 	if (!(_cf = create_config_file())) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* Use LOG_USER for syslog messages by default */ | ||||
| 	init_syslog(LOG_USER); | ||||
|  | ||||
| 	/* send log messages to stderr for now */ | ||||
| 	init_log(stderr); | ||||
|  | ||||
| 	e = e ? e : "/etc/lvm/lvm.conf"; | ||||
| 	if (stat(e, &info) != -1) { | ||||
| 		/* we've found a config file */ | ||||
| 		if (!read_config(_cf, e)) { | ||||
| 			stack; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		__init_log(_cf); | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_cache_init()) { | ||||
| 		stack; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dev_cache_add_dir("/dev")) { | ||||
| 		log_error("Failed to add %s to internal device cache", prefix); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(_filter = config_filter_create())) { | ||||
| 		/* Add scan & rejects from _cf->root */ | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(ios_pool = pool_create(4 * 1024))) { | ||||
| 		log_error("ios pool creation failed"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(ios = create_lvm1_format(prefix, ios_pool, _filter))) { | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	ret = 1; | ||||
|  | ||||
|       out: | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void __fin_commands(void) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < _num_commands; i++) | ||||
| 		dbg_free(_commands[i].valid_args); | ||||
|  | ||||
| 	dbg_free(_commands); | ||||
| } | ||||
|  | ||||
| static void fin(void) | ||||
| { | ||||
| 	ios->destroy(ios); | ||||
| 	config_filter_destroy(_filter); | ||||
| 	dev_cache_exit(); | ||||
| 	destroy_config_file(_cf); | ||||
| 	__fin_commands(); | ||||
| 	dump_memory(); | ||||
| 	fin_log(); | ||||
|  | ||||
| 	if (_log) | ||||
| 		fclose(_log); | ||||
| } | ||||
|  | ||||
| static int run_script(int argc, char **argv) | ||||
| { | ||||
| 	FILE *script; | ||||
|  | ||||
| 	char buffer[CMD_LEN]; | ||||
| 	int ret = 0; | ||||
| 	int magic_number = 0; | ||||
|  | ||||
| 	if ((script = fopen(argv[0], "r")) == NULL) | ||||
| 		return ENO_SUCH_CMD; | ||||
|  | ||||
| 	while (fgets(buffer, sizeof (buffer), script) != NULL) { | ||||
| 		if (!magic_number) { | ||||
| 			if (buffer[0] == '#' && buffer[1] == '!') | ||||
| 				magic_number = 1; | ||||
| 			else | ||||
| 				return ENO_SUCH_CMD; | ||||
| 		} | ||||
| 		if ((strlen(buffer) == sizeof (buffer) - 1) | ||||
| 		    && (buffer[sizeof (buffer) - 1] - 2 != '\n')) { | ||||
| 			buffer[50] = '\0'; | ||||
| 			log_error("Line too long (max 255) beginning: %s", | ||||
| 				  buffer); | ||||
| 			ret = EINVALID_CMD_LINE; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (split(buffer, &argc, argv, MAX_ARGS) == MAX_ARGS) { | ||||
| 			buffer[50] = '\0'; | ||||
| 			log_error("Too many arguments: %s", buffer); | ||||
| 			ret = EINVALID_CMD_LINE; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (!argc) | ||||
| 			continue; | ||||
| 		if (!strcmp(argv[0], "quit")) | ||||
| 			break; | ||||
| 		run_command(argc, argv); | ||||
| 	} | ||||
|  | ||||
| 	fclose(script); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| #ifdef READLINE_SUPPORT | ||||
| /* Custom completion function */ | ||||
| static char **lvm_completion(char *text, int start_pos, int end_pos) | ||||
| { | ||||
| 	char **match_list = NULL; | ||||
| 	int p = 0; | ||||
|  | ||||
| 	while (isspace((int) *(rl_line_buffer + p))) | ||||
| 		p++; | ||||
|  | ||||
| 	/* First word should be one of our commands */ | ||||
| 	if (start_pos == p) | ||||
| 		match_list = completion_matches(text, list_cmds); | ||||
| 	else if (*text == '-') | ||||
| 		match_list = completion_matches(text, list_args); | ||||
| 	/* else other args */ | ||||
|  | ||||
| 	/* No further completion */ | ||||
| 	rl_attempted_completion_over = 1; | ||||
| 	return match_list; | ||||
| } | ||||
|  | ||||
| /* List matching commands */ | ||||
| static char *list_cmds(char *text, int state) | ||||
| { | ||||
| 	static int i = 0; | ||||
| 	static int len = 0; | ||||
|  | ||||
| 	/* Initialise if this is a new completion attempt */ | ||||
| 	if (!state) { | ||||
| 		i = 0; | ||||
| 		len = strlen(text); | ||||
| 	} | ||||
|  | ||||
| 	while (i < _num_commands) | ||||
| 		if (!strncmp(text, _commands[i++].name, len)) | ||||
| 			return strdup(_commands[i - 1].name); | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* List matching arguments */ | ||||
| static char *list_args(char *text, int state) | ||||
| { | ||||
| 	static int match_no = 0; | ||||
| 	static int len = 0; | ||||
| 	static struct command *com; | ||||
|  | ||||
| 	/* Initialise if this is a new completion attempt */ | ||||
| 	if (!state) { | ||||
| 		char *s = rl_line_buffer; | ||||
| 		int j = 0; | ||||
|  | ||||
| 		match_no = 0; | ||||
| 		com = NULL; | ||||
| 		len = strlen(text); | ||||
|  | ||||
| 		/* Find start of first word in line buffer */ | ||||
| 		while (isspace(*s)) | ||||
| 			s++; | ||||
|  | ||||
| 		/* Look for word in list of commands */ | ||||
| 		for (j = 0; j < _num_commands; j++) { | ||||
| 			char *p; | ||||
| 			char *q = s; | ||||
|  | ||||
| 			p = (char *) _commands[j].name; | ||||
| 			while (*p == *q) { | ||||
| 				p++; | ||||
| 				q++; | ||||
| 			} | ||||
| 			if ((!*p) && *q == ' ') { | ||||
| 				com = _commands + j; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (!com) | ||||
| 			return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Short form arguments */ | ||||
| 	if (len < 3) { | ||||
| 		while (match_no < com->num_args) { | ||||
| 			char s[3]; | ||||
| 			char c; | ||||
| 			if (!(c = (the_args + | ||||
| 				   com->valid_args[match_no++])->short_arg)) | ||||
| 				    continue; | ||||
|  | ||||
| 			sprintf(s, "-%c", c); | ||||
| 			if (!strncmp(text, s, len)) | ||||
| 				return strdup(s); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Long form arguments */ | ||||
| 	if (match_no < com->num_args) | ||||
| 		match_no = com->num_args; | ||||
|  | ||||
| 	while (match_no - com->num_args < com->num_args) { | ||||
| 		char *l; | ||||
| 		l = (the_args + | ||||
| 		     com->valid_args[match_no++ - com->num_args])->long_arg; | ||||
| 		if (!strncmp(text, l, len)) | ||||
| 			return strdup(l); | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static int shell(void) | ||||
| { | ||||
| 	int argc, ret; | ||||
| 	char *input = NULL, *args[MAX_ARGS], **argv; | ||||
|  | ||||
| 	rl_readline_name = "lvm"; | ||||
| 	rl_attempted_completion_function = (CPPFunction *) lvm_completion; | ||||
|  | ||||
| 	_interactive = 1; | ||||
| 	while (1) { | ||||
| 		free(input); | ||||
| 		input = readline("lvm> "); | ||||
|  | ||||
| 		/* EOF */ | ||||
| 		if (!input) { | ||||
| 			printf("\n"); | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		/* empty line */ | ||||
| 		if (!*input) | ||||
| 			continue; | ||||
|  | ||||
| 		add_history(input); | ||||
|  | ||||
| 		argv = args; | ||||
|  | ||||
| 		if (split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) { | ||||
| 			log_error("Too many arguments, sorry."); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (!strcmp(argv[0], "lvm")) { | ||||
| 			argv++; | ||||
| 			argc--; | ||||
| 		} | ||||
|  | ||||
| 		if (!argc) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!strcmp(argv[0], "quit")) { | ||||
| 			log_error("Exiting."); | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		ret = run_command(argc, argv); | ||||
| 		if (ret == ENO_SUCH_CMD) | ||||
| 			log_error("No such command '%s'.  Try 'help'.", | ||||
| 				  argv[0]); | ||||
| 	} | ||||
|  | ||||
| 	free(input); | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										27
									
								
								tools/lvmchange.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tools/lvmchange.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| /* | ||||
|  * 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 LVM; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "tools.h" | ||||
|  | ||||
| int lvmchange(int argc, char **argv) | ||||
| { | ||||
| 	log_print("With the device mapper, this program is obsolete."); | ||||
| 	return 0; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user