1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-08-15 01:49:29 +03:00

Compare commits

...

254 Commits

Author SHA1 Message Date
f8686d0e75 o Update to parser
o Extra checks in symlink routines
2001-10-17 11:09:43 +00:00
549e3c8f9d pvscan 2001-10-16 18:07:54 +00:00
bb56225b95 o Fixed infinite loop in parser
o Fixed error handling whilst creating volumes
 o General tidy up
2001-10-16 17:09:27 +00:00
f00be261ba vgchange 2001-10-16 16:25:28 +00:00
9cd94f26d0 o Get file size correct for table
o Make parser look at the right object
2001-10-16 12:17:54 +00:00
4d8f5c80a7 o Fixes to parsing code 2001-10-16 11:56:55 +00:00
24a23acc3d o Tidy up, removing debugging printk's no longer needed 2001-10-16 10:38:13 +00:00
ca8703cfd8 o More bug fixes 2001-10-15 22:39:14 +00:00
6dcbb5b2f8 vgextend 2001-10-15 22:04:27 +00:00
a84fdddb2a vgcreate basic extent size validation 2001-10-15 20:29:15 +00:00
38bb2f8ceb More vgcreate error trapping 2001-10-15 18:39:40 +00:00
23f5ef4345 o More fixes 2001-10-15 16:40:17 +00:00
ef8a2a9054 o lvm readline error-case tidy-up
o more vgcreate error cases
2001-10-15 12:49:58 +00:00
96103d0e36 o Some bug fixes
o Added symlink ops
 o Some extra sanity checks
2001-10-15 11:31:00 +00:00
ff5f6748df o vgcreate 2001-10-12 14:25:53 +00:00
1c1fd6c366 vgcreate 2001-10-12 12:21:43 +00:00
32d37d00cb o make ios the first argument to pv_create 2001-10-12 10:52:32 +00:00
82f6cda966 o lift call to check out of pvcreate_single 2001-10-12 10:45:04 +00:00
f1ff8ff0d0 o rename _single pvcreate_signle 2001-10-12 10:43:36 +00:00
756c72902f o made _single static 2001-10-12 10:42:31 +00:00
73f8f0bbd0 o pvcreate
o added uuid unit

o stubbed partition stuff
2001-10-12 10:32:06 +00:00
18ed528f5d o Further tidyups and fixes. 2001-10-12 10:06:40 +00:00
8fd2f136bc sync 2001-10-12 09:52:30 +00:00
0524b1bf67 vgreduce, vgremove, vgrename & vgscan 2001-10-11 21:35:55 +00:00
15716f65ce Some more sync ups
o Error file routines (initial idea)
 o Various fixes for bugs
 o Tidy a few things
 o Added a bit of debugging code ready for when this gets tested
 o get_exclusive_write_access() function which will get moved into namei.c
   I hope (and rewritten accordingly), should this become the final version
   used.

Still a few more areas need thinking about, but in general much closer now I
think. Last area to sort out before testing is the symlink code which is
pretty close now... just a few more checks needed and the actual calls to
the core code.
2001-10-11 20:29:00 +00:00
d46bdba332 o try incrementing pv_number from 1 2001-10-11 16:31:09 +00:00
760728110a o if contained &= instad of & 2001-10-11 15:09:31 +00:00
12d0a194ca o initalise list_heads, initialise list_heads, initi .. 2001-10-11 14:21:38 +00:00
4104543508 o a very quick hack to get vg_number right 2001-10-11 14:10:18 +00:00
5c211db015 o set PV_ALLOCATABLE flag correctly 2001-10-11 13:34:17 +00:00
2dc6180f8d o pv->system_id 2001-10-11 13:22:51 +00:00
e222a34b69 o vg->pv_act 2001-10-11 13:05:55 +00:00
ef17d95063 o calculate pv_numbers and lv_numbers for LVM1 support 2001-10-11 10:55:19 +00:00
853502e5d7 o pe_start wasn't being set properly when exporting to disk
o added a check for lv's with null lv_name

o setup pv->lv_cur correctly

o test program for vg_write
2001-10-11 10:08:44 +00:00
c18e297e77 o Everybody needs dm.h
o Fixed to work with highmem
 o Added dmfs private inode struct for lv and table directories
 o Fixed a number of errors/typos
 o Status file read returns 0 so we can leave this until we've actually got
   something to report in this now.
 o New locking on tables.... still some issues to be worked out here but
   closer now I think.
 o Now use mapping of table directory to hold pages rather than mapping of
   table file inode. Need to write a note to myself to fix issues with the
   file length at the same time....

Well thats enough for tonight I think. The error file will be part of
tomorrows work.
2001-10-10 21:49:21 +00:00
c5a49599ba o sync 2001-10-10 17:11:31 +00:00
df9da9edf5 standardise some log messages 2001-10-10 16:36:32 +00:00
e2200fd050 o builds a very sub-optimal table 2001-10-10 15:30:31 +00:00
c6207f5d9c o allocate and zero the extents before exporting the lv's 2001-10-10 14:56:53 +00:00
4302b7ff6b o zero all of uuid 2001-10-10 13:33:20 +00:00
50a7923438 o uuid_list->id should be NAME_LEN wide 2001-10-10 13:30:58 +00:00
ab416445c8 o sizeof(NAME_LEN), don't do that 2001-10-10 13:24:16 +00:00
a54698d43c o forgot to init a list head 2001-10-10 13:09:40 +00:00
c5a77cc1c0 o dev_write 2001-10-10 13:03:10 +00:00
a9ffa811fc Tidy metadata diagnostic messages. 2001-10-10 12:45:20 +00:00
080a2608e0 o return data not 1 in read_ov 2001-10-10 12:42:03 +00:00
57f2e83d6a o check for orphaned pv's when reading 2001-10-10 12:28:10 +00:00
5b030139d3 o pv_setup for format1, this is the last one ! 2001-10-10 10:55:55 +00:00
0da1ff42d1 o AC_INIT was pointing to an old file 2001-10-10 10:11:25 +00:00
2c599b7baa o metadata.h was in here twice 2001-10-10 10:09:12 +00:00
5c8af8d21a o pv_write for orphan pv's 2001-10-10 10:05:29 +00:00
026f3cfde2 o add munging for format1 and 2 2001-10-10 09:36:29 +00:00
f6349180e8 o Code to calculate the metadata layout. 2001-10-10 09:25:04 +00:00
aa6421921c o stub pv_write to stop tools crashing 2001-10-09 17:44:58 +00:00
7d41d2dab2 o fix seg fault while reading extents 2001-10-09 17:36:48 +00:00
f0b4d18f93 o remove another spurious error message 2001-10-09 17:30:20 +00:00
6750f06e10 o vgremove.
o filter devices by major.
2001-10-09 17:20:02 +00:00
b2bd38fa9e o spot empty list in build_vg 2001-10-09 17:09:46 +00:00
3482a01e22 o proposed interface to the kernel driver 2001-10-09 16:44:30 +00:00
6335467552 o dev-mgr disappears 2001-10-09 16:13:12 +00:00
4a39e65b62 o change pv_read to take a name rather than a device 2001-10-09 16:05:34 +00:00
c50a23e918 o remove spurious log message 2001-10-09 14:42:58 +00:00
1e76b72b98 o hack, hack, hack 2001-10-09 14:26:45 +00:00
b94cf39eef o vg_write compiles 2001-10-09 10:47:52 +00:00
fef254ffff o get_vgs works 2001-10-09 09:22:50 +00:00
e5495863a2 o pv_Read works 2001-10-09 08:58:52 +00:00
3b4df2abf0 o get_pvs now works for format 1 2001-10-09 08:11:52 +00:00
aef2aee6a4 vgrename & vgck 2001-10-08 18:44:22 +00:00
d0d9519149 o test program for get_pvs 2001-10-08 18:09:31 +00:00
685df1d2c5 o get_pvs for format 1
o fix vg_read if vg doesn't exist
2001-10-08 17:53:43 +00:00
08e6b6f2e7 o added pretty printing to read_vg_t, run this on your system
to see what vg's you've got

S: ----------------------------------------------------------------------
2001-10-08 17:28:49 +00:00
66c887d0f3 o read_vg works (or so it claims) 2001-10-08 16:08:16 +00:00
22e9960697 o dev_cache_t program works 2001-10-08 13:58:52 +00:00
64aa6e1f2d o sync 2001-10-08 12:11:33 +00:00
7a93ed9d04 o we were stuill building dev-mgr files 2001-10-08 10:35:59 +00:00
a905e922e9 o read_vg_t compiles 2001-10-08 10:20:25 +00:00
f9f08fc720 o makefile for read_vg_t 2001-10-08 09:50:00 +00:00
8d402d76d0 o get things compiling 2001-10-08 09:45:16 +00:00
46fda6281c o test program for reading a vg 2001-10-08 08:47:27 +00:00
a14dbe1ea6 Sync include file changes. 2001-10-05 21:39:30 +00:00
18810a4c16 o end of day sync 2001-10-05 16:36:53 +00:00
147bc80dba o replace {stack; return 0;}'s with a macro (just for this file). 2001-10-05 15:48:05 +00:00
c7a484195a o low level write path 2001-10-05 15:20:40 +00:00
4968eb6503 o finished writing extent reading code 2001-10-05 13:59:44 +00:00
a6f2d698a9 revised flags and comments 2001-10-05 13:03:03 +00:00
ea5ed93ea5 Just a small sync of pending changes before I start looking at this again
more seriously.

 o Odds and ends
2001-10-05 10:00:13 +00:00
e1140134c6 metadata status flags regrouping & comments; misc tool changes 2001-10-04 22:53:37 +00:00
5ed11e012e o vg_read for format1 2001-10-04 17:48:55 +00:00
5380bd39ca makefile support for tests 2001-10-04 12:07:29 +00:00
2ee2685688 o define the uintN_t types 2001-10-04 11:40:13 +00:00
782002245b o forgot to add this before 2001-10-04 10:25:34 +00:00
7fc0905843 o got dbg_malloc_t working, Alasdair could you look at the Makefile.in it
seems to be having trouble with the dependencies.

o removed some files from the lib makefile that don't compile yet.
2001-10-04 10:13:07 +00:00
72ecb99e54 o Use the __alignof__ extension to set DEFAULT_ALIGNMENT to that required
for a 'double'.
2001-10-04 09:10:11 +00:00
c863507d08 vgcreate & lvmchange outlines 2001-10-03 20:38:07 +00:00
cff86c9093 vgrename & pvchange outlines 2001-10-03 17:03:25 +00:00
0479dfcc54 o added dev-cache.c, dev-io and sorted source files alphabetically 2001-10-03 12:46:17 +00:00
68dd67f21c o moved dev-cache to device dir 2001-10-03 12:43:29 +00:00
540f6858b5 o I've moved the dev-cache and dev-io into here since this directory has a
better name.  dev-mgr will be removed at some point.
2001-10-03 12:41:29 +00:00
b61e791a4f lvremove outline 2001-10-03 12:34:08 +00:00
d0986f9482 o code sync for dev-cache.c
o made copyright headers the same

o added __attribute ((format ... to print_log so we'll get better compile errors

o added iterator to the hash table
2001-10-03 11:06:31 +00:00
112cb0dc28 pvscan framework 2001-10-02 17:09:05 +00:00
0d3d7fdcf2 o test program for the device cache 2001-10-02 13:44:44 +00:00
5d6b89ef3b o test program for the hash table. 2001-10-02 12:46:04 +00:00
ed0b26c09e o Test program for dbg_malloc unit.
I'm postfixing test programs with _t, and benchmarks with _b
2001-10-02 12:27:55 +00:00
ae292bd920 Another step towards consistency & compilation. 2001-10-01 22:12:10 +00:00
6c85a90723 Misc structural changes. 2001-10-01 19:36:06 +00:00
852592066c Misc structural changes. 2001-10-01 19:29:52 +00:00
96e1bc9b44 o changed dev-manager to a dev_filter 2001-10-01 16:21:21 +00:00
b41d81ed31 o get block size moved to dev-io.c 2001-10-01 16:07:29 +00:00
e241ec2244 merge partition code 2001-10-01 15:59:40 +00:00
16e1f1a94c Tidy includes 2001-10-01 15:53:21 +00:00
7a68c42b26 o drop the reference counting in the devices. 2001-10-01 15:43:51 +00:00
37ccc2e118 Merge fixes 2001-10-01 15:29:39 +00:00
4192fe1ab2 o missing * 2001-10-01 15:28:28 +00:00
d5c743d7bb o added filter type. 2001-10-01 15:27:16 +00:00
11814d63e8 Tidy include files 2001-10-01 15:14:39 +00:00
b753656d50 Create symlinks to .h files in an include directory 2001-10-01 13:36:54 +00:00
f7e87611fc o I'm splitting dev-manager in two. dev-cache is the bottom layer that
handles devices.  Dev-manager will sit on this filtering the view.
2001-09-28 15:42:25 +00:00
1fb0e1900e o list.h from kernel for userland tools to use. 2001-09-28 13:19:17 +00:00
954a9731e0 o logical data structures 2001-09-28 13:15:30 +00:00
65c3364ad8 o generic hash table to store void *'s, not efficient, but adequate for LVM. 2001-09-28 13:08:44 +00:00
3d72b7dccc o rewrite of dm_user_bmap, not tested though. 2001-09-27 10:15:02 +00:00
13ee569f06 Fix prototype for malloc_aux 2001-09-27 10:01:17 +00:00
d79ef23a75 Don't include asm/* files 2001-09-27 10:00:47 +00:00
5d0797d4ba o Kill write funcs for error/status files
o Redo write logic for table file
 o Relax rules for symlink content by removing the rewriting function

Well I probably won't get a chance to work on this tomorrow, so this is my
changeset to date.
2001-09-26 20:24:39 +00:00
47a8d7475f o table creation works again. 2001-09-26 19:48:20 +00:00
4939053121 o It should build now 2001-09-26 17:32:57 +00:00
b525bf554e o typos 2001-09-26 17:07:10 +00:00
f00285d2b2 o remove steve's insane ramblings from my code. 2001-09-26 14:32:07 +00:00
040f8d6eda o Lunchtime. 2001-09-26 11:47:02 +00:00
66d905325c o More updates 2001-09-26 09:26:10 +00:00
8b0cd95e73 o Beginnings of new interface. 2001-09-26 08:06:46 +00:00
d867cca6d9 fix memory leak 2001-09-25 16:26:38 +00:00
a28f736369 o quick tidy up 2001-09-25 15:23:20 +00:00
5c3a71cc59 lvactivate checkpoint commit 2001-09-25 12:49:28 +00:00
cef6dadb08 Another missing dependency. 2001-09-24 22:44:06 +00:00
36be817a3e o Check in case of setting up volumes before root is mounted. 2001-09-24 15:18:45 +00:00
02f571f081 Well when things start looking so complicated that future direction becomes
non-obvious, its time to simplify :-)

 o Moving towards a simpler and more obviously correct interface
 o Removed some fs operations in directories representing volumes
 o Changed some file names
 o Made things cleaner

more changes to follow...
2001-09-24 15:10:33 +00:00
157159e487 Fix dependencies. 2001-09-24 12:05:04 +00:00
02ada9f800 Makefiles & autoconf. 2001-09-21 12:37:43 +00:00
6fcf9a97bb Initialise root node pointer. 2001-09-21 12:32:37 +00:00
17a5d8799f Unused variables. 2001-09-21 12:31:57 +00:00
31f3fe7a22 o Sync up of todays changes .... nothing very important 2001-09-20 22:58:06 +00:00
89e46d3d83 o Bug fix in error path 2001-09-20 20:22:15 +00:00
885795e67d o Use ERR_PTR and PTR_ERR rather than an extra argument. 2001-09-20 19:25:58 +00:00
92bfb53dd4 o Changed to use table->err_msg rather than passing functions around 2001-09-20 18:22:35 +00:00
4cecbeb115 o Some new files (also part of new fs interface) 2001-09-19 21:28:25 +00:00
5971480f55 o Further changes to new file system interface 2001-09-19 21:27:46 +00:00
05bebea511 o Removed the error reporting function from the target constructor function
arguments. Errors are now reported by setting a pointer in the table to
   point to an error message.
2001-09-19 21:27:15 +00:00
76fa6c5cfb hardsect/blksize handling 2001-09-19 17:46:27 +00:00
633b68b518 o Added ref counting to tables
o Further changes to new fs interface
2001-09-19 16:01:27 +00:00
6913d8e995 o Fixed a bug where we were not holding a reference of the block devices
used by the targets correctly.
2001-09-19 14:54:44 +00:00
f3654e6f8d o Change the deallocation of tables to match the vmalloc changes in my
previous commit
2001-09-19 11:02:02 +00:00
d685dbcf22 o Cut down number of vmallocs to increase speed and efficiency 2001-09-19 10:59:10 +00:00
6a57fa079e o More fs fiddling. Another check point commit. 2001-09-19 10:32:51 +00:00
0a91d145ba o Bug fix to LV_BMAP ioctl()
o Account for I/O against tables rather than logical volume devices
2001-09-19 10:32:09 +00:00
c2866e799d Fix allocation & list-handling. 2001-09-18 20:03:00 +00:00
d8cffcaae7 These files are now a bit closer towards what I'm aiming at. Still a lot
more to do though.
2001-09-18 16:54:14 +00:00
30abca7be2 Should have been included in the previous commit. 2001-09-18 16:53:18 +00:00
edce87f3fb o Changed dm_create() to return a struct mapped_device rather than an int
o Changed dm_remove() to accept a struct mapped_device argument rather than
   a name
 o We no longer have to look up devices by name, the dcache handles that
   nicely for us
 o Fixed a bug where we were freeing a structure before we'd finished with
   it.
 o The name field in struct mapped_device is now only used in a very few
   places in dm.c and will be replaced in future with a back reference to
   the dentry rather than keeping the name in two places.
2001-09-18 16:52:50 +00:00
66bac98fc2 o New file dmfs-super.c
o dmfs-dir.c becomes dmfs-lv.c
 o dmfs-file.c becomes dmfs-table.c
 o A few tweeks and updates

The main reason for the slow progress on these files (which are not yet used
by the device mapper) is that we are working out what this interface should
look like as we go along.

Once this has evolved a bit further and in a state where it can be used we'll
announce it on the lists for further comment.
2001-09-18 15:38:54 +00:00
59156de92b Error checking: only allow block devices & test for 'nodev'. 2001-09-17 21:17:30 +00:00
e0d7d10600 o Again, please ignore this for the time being. 2001-09-17 19:05:49 +00:00
daaf862257 o Arbitrary mount path.
o Name length 128.
2001-09-17 16:55:31 +00:00
9de53d4b59 o Work in progress, please ignore these files for a day or two whilst I
get everything going.
2001-09-17 15:42:59 +00:00
f1571e2d46 o Fixed code where return value of vmalloc wasn't checked 2001-09-17 11:23:13 +00:00
bd28d06298 o Use count should be an atomic_t 2001-09-17 09:01:23 +00:00
a24e4655eb o Targets now get rw passed through so they can do COW for example
o Added error handler (not sure that this is the "correct" way to do
   this at the moment, so its a bit exprimental for now)
2001-09-14 16:22:02 +00:00
20a6c8d8e5 o Support /sbin/hotplug 2001-09-14 15:35:06 +00:00
98d264faf4 o Made pending I/O wait uninterruptible 2001-09-14 14:03:02 +00:00
321902a9b5 o New ioctl(): LV_BMAP which is compatible with LVM so that hopefully LILO
will work. I haven't actually tested that, but this support at least will
   be required.
2001-09-14 13:45:40 +00:00
8df5d06f9a Use dmfs_ function name prefix (in line with other file systems). 2001-09-14 13:27:58 +00:00
e69ea529cc lc->in->f_op->read expects its buffer to be in userspace so surround it in
set_fs() etc calls
2001-09-14 12:27:57 +00:00
15405b1119 o As promised earlier, the device registration is now hashed and the
lists are private to dm-blkdev.c
2001-09-14 11:25:51 +00:00
d2f97ce2da Always truncate error file. 2001-09-14 11:15:54 +00:00
543ca631e9 Don't store things in _devs[-1] - it's not nice. 2001-09-14 10:54:08 +00:00
f184886db1 o Forgot to create slab caches for dm-blkdev.c
o Misc code tidy
2001-09-14 10:40:20 +00:00
8432ab4324 o kmalloc error check
o error file mode
The 1st Jan 1970 date I'm seeing in /dev is a devfs issue I think.
2001-09-14 10:06:22 +00:00
6c05b37ca3 Changes to device handling;
o Only one list of block devices for all tables
 o Locking to ensure that block devices only get opened once
 o Block device handling is now in dm-blkdev.c
 o We open block devices when we create the tables and hold them open until
   the table is destroyed (this prevents the module for the device being
   unloaded after table parsing and before the table is used)
 o We compute the hardsect size when the table is created rather than when
   someone requests it.

Still to fix/change:

 o Probably want to hash the device lists in dm-blkdev.c and also remove refs
   to struct dm_bdev outside this file.
 o Need to ensure that hardsect_size doesn't change when new tables are
   swapped in (maybe this ought to be a per volume parameter and the tables
   will only parse if they match the value for the volume?).

Things are changing fast here, so if you want a stable version of thic code
try checking out yesterdays.
2001-09-14 09:45:35 +00:00
35f4beeb47 o New code for handling block device registration. Not yet used but checked
in for backup purposes.
2001-09-14 08:06:02 +00:00
cbad7caa68 is_identifier characters 2001-09-13 21:50:38 +00:00
b0388a4012 o Two fixes which Alasdair pointed out. 2001-09-13 20:10:14 +00:00
df3fab4d55 o Tidy in dm-fs.c
o Magic number is really magic
 o Check on directory names
2001-09-13 19:41:46 +00:00
da49f88a03 o Forgot to add ref to module. 2001-09-13 19:36:40 +00:00
e28feceb06 Add dm-parse to makefile 2001-09-13 19:09:23 +00:00
50496a164d o Now we handle target modules correctly
o Moved the linear target into its own module (not really because it needs to
   be there, but because its useful to have a simple example so people can see
   what we are doing)

Btw, this needs testing properly.
2001-09-13 18:30:05 +00:00
6f1dce1572 o Remove hard-coded mount point
o Fix macro for compilation
2001-09-13 16:52:50 +00:00
6847776ae7 o Some structures change size with different configs, so always include
<linux/config.h> first.
2001-09-13 14:03:42 +00:00
67bd53bdd8 o Some ioctl() commands. 2001-09-13 14:01:13 +00:00
e735abfdfd Fix includes so that string functions get prototyped 2001-09-13 12:38:31 +00:00
1de93a2d6d Fix includes so that string functions get prototyped.
Fix cast - repeat after me Joe: "I must not cast pointers to ints"!
2001-09-13 12:38:08 +00:00
36f9e7c742 o I'm afraid that wu and wl etc. is just too confusing.... I've changed it
to up_write() and down_write() etc so that you can see what kind of a lock
   it is (otherwise it could be anything.. semaphore, spinlock, spinlock_bh,
   spinlock_irq, br_lock, etc.)
2001-09-13 11:29:38 +00:00
9462763bbb o Use kmem_cache_destroy() to remove slab cache. 2001-09-13 11:07:08 +00:00
4ae0880ea6 Set DEFAULT_ALIGNMENT to 8 for Alpha.
If you think this is wasteful on other arches then stick some ifdefs in.
2001-09-13 09:03:42 +00:00
6ae2b6c835 Add dm-parse 2001-09-12 13:50:26 +00:00
a0f180fd48 o first sattab at custom fs. Very rough ATM.
Mount the dm-fs filesystem on /device-mapper (will fix later).  mkdir
to create a device, inside that directory every file you create is a table
file.  If there are errors <table>.err will appear automagically.  Mv a table
file to ACTIVE to activeate the device.  I'm not happy with mv being the
binding command, symlink would be better.
2001-09-07 11:34:46 +00:00
bf1cf89914 o more tidy ups from Clausen. 2001-09-05 07:48:11 +00:00
297a047fb4 o Added two new functions get_child [Andrew Clausen] and get_node. I think
this makes 'high()' a bit more understandable.
2001-09-04 10:17:28 +00:00
52ffc15ffc o added new constant CHILD_PER_NODE to make things clearer 2001-09-03 08:36:41 +00:00
e478c9c693 o Various tidy ups [Andrew Clausen] 2001-09-02 10:49:20 +00:00
d004f28074 o added global dm_table_lookup_device(path)
o changed linear target to : <device_path> <start>
2001-08-31 18:26:27 +00:00
bc68ed8b1d o added reference counting to the destination devices, make sure that the
destructor for any targets you write call dm_table_remove_device.
2001-08-31 16:36:56 +00:00
04555ae650 o split struct mapped_device into mapped_device and dm_table
o seperated loading of a table from binding a table to the device

These should allow multiple tables to be managed by dm-fs
2001-08-31 15:13:33 +00:00
e8f62085be o tidy ups 2001-08-31 12:49:31 +00:00
f430bffe2a o allocate io_hooks from a slab 2001-08-31 10:25:32 +00:00
1f0520634f o stray return -ENXIO in reuqest [Jens Axboe] 2001-08-31 09:43:35 +00:00
902d4c31fb o rebuilt 00_latest 2001-08-31 09:14:55 +00:00
17364ac09f o split uml part out 2001-08-29 14:23:40 +00:00
0b889f8f81 o various little tidy ups 2001-08-29 13:58:48 +00:00
40e349ff35 o change format of table line to <start> <len> <target> ... 2001-08-28 14:56:47 +00:00
c943b1b1df o Enable building dm modules (called dm-mod)
o split the patches into config and makefile specific.
2001-08-28 14:11:55 +00:00
912bc1d4e1 o more deferred io stuff 2001-08-28 14:05:22 +00:00
cacb1533a3 o added proper suspend/resume support, it now waits for all 'in flight' io's
to complete.

  
  moved comment to dm.h
2001-08-28 13:04:44 +00:00
f0feaca9d7 o ACtual source code patch 2001-08-24 09:50:16 +00:00
b6656f171b o a couple of patches we'll need for deviec-mapper 2001-08-24 09:39:32 +00:00
6206ab3931 o you can now load maps repeatedly without hanging
o tested multiple target map

Driver is now useable
2001-08-23 17:10:05 +00:00
c35fc58b1f o dm_add_target was returning 0 an error when it shouldn't
o reference count was being checked badly
2001-08-23 16:45:43 +00:00
deed8abed7 o map loads ok now
o request function appears to work, but something is segfaulting when i
  mke2fs
2001-08-23 12:35:02 +00:00
7151ad23f0 Tweak permissions - currently root-only. (no support for non-root ownership
in procfs except for PIDs)
2001-08-22 20:10:06 +00:00
0166d938af o chagngesd alloc to return 0 on success 2001-08-22 15:59:56 +00:00
6194aeddb0 o fs_add and fs_remove actually create/remove the device now 2001-08-22 15:33:08 +00:00
903dbf2c30 o added brackets to make t->name = (char *) (t + 1) more explicit 2001-08-22 15:12:31 +00:00
9380f9ff57 Return 0 on success now. 2001-08-22 15:02:55 +00:00
259ed95486 o wu macro was doing a read unlock
o added dm_fs_add/remveove
2001-08-22 15:01:09 +00:00
2ebc92681e o _tok_cpy was broken 2001-08-22 14:30:30 +00:00
195a1ffe13 o fix get_word
o capy name in when registering targets

o change _line_splitter so it expects the process functions to return zero
  on success
2001-08-22 14:13:26 +00:00
a8c2978185 o _get_workd was always returning the end on iput 2001-08-22 13:52:26 +00:00
140f97a457 o Initialisation tweaks.
o Use different major number so it can co-exist with LVM 1.
2001-08-22 13:46:58 +00:00
7f94445a1e o set permissions on /proc/device-mapper/control to -w-w-w 2001-08-22 13:45:28 +00:00
82a89aec65 o call dm_init_fs 2001-08-22 13:41:00 +00:00
7e95110232 o Ok, this seems to be a much better method for caching valid
devices based on /proc/devices
   + The dev_mgr structure now has a 256 element char array that is
     initially all 0s
   + When a match is found, the array element corresponding to the major
     number of the match is set to a non-zero value
   + to check for a match, all one has to do is check that the array
     element at the major number in question is non-zero.
 o I'm wondering if we should do this with bitwise operators instead?  Does
   anyone expect the major numbers to grow larger than 8-bits?
2001-08-21 20:40:37 +00:00
ec4aaaad89 o Quick and dirty *UGLY* hack of a /proc/devices cache using a linked list
o I don't like it, but I'm committing it so I can go back and laugh at
   myself later
 o I have a (hopefully) better idea that i'll try to commit yet today.
2001-08-21 19:51:04 +00:00
1b790fde24 o Quick and dirty hack to get lvm_check_dev code into the dev-manager
o I'm working on caching the /proc/devices entries now, and should have
   that in by the end of today or early tomorrow.
 o There will be much cleanup involved with that...
2001-08-21 18:20:14 +00:00
aaccea731e o quick hack to get the proc entry registering 2001-08-21 15:31:50 +00:00
29e31d7610 o dm-fs compiles, I've forgotten to register the device in /proc though 2001-08-21 15:24:02 +00:00
aa51f4a98f o Added a basic makefile to build liblvm.a again
o Modified source files so that this works
2001-08-21 15:23:45 +00:00
e6ccd12f00 o Brought hash table code over from experimental 2001-08-21 15:22:59 +00:00
b134315df1 o wasn't including dm-fs.o in the build 2001-08-21 14:52:54 +00:00
7f34dffa13 o dm-target compiles 2001-08-21 14:51:41 +00:00
fa239e78c9 o dm-table compiles 2001-08-21 14:47:42 +00:00
707a6c4d6a o Added _basic_ config file support to the device manager 2001-08-21 14:44:18 +00:00
e5da303b43 o make dm.c compile 2001-08-21 14:28:00 +00:00
84ccd66331 o 2.4.9 support
o new uml-lvm patch
2001-08-21 13:45:16 +00:00
ad8cc2baea o Populating with stuff from experimental 2001-08-21 13:22:16 +00:00
7c4cf70309 o Populating with stuff from experimental 2001-08-21 12:56:08 +00:00
c3211e9b4f o dm_activate/dm_close 2001-08-20 16:12:22 +00:00
268d94c983 dec use count on close. corrects a typo.
Really the use counts on these modules should be handled at a higher level,
otherwise there are races I think.
2001-08-20 15:59:22 +00:00
0bcacbba58 o implemeted dm_start_table/dm_add_entry/dm_complete_table as used by
the /proc interface.
2001-08-20 15:22:44 +00:00
8cdc26add9 o changed _dev_lock to a rw_semaphore 2001-08-20 14:06:25 +00:00
e0b2238886 o proc interface is getting there. 2001-08-20 13:45:43 +00:00
369a2e4029 o missed one 2001-08-20 08:05:51 +00:00
c4089e3b51 Just syncing with the office.
o device-mapper.c has split
2001-08-20 08:03:02 +00:00
9e2e9bc5b8 o added a description 2001-08-16 15:14:07 +00:00
a9e44426ed o checked in the new driver, and the uml dir 2001-08-16 08:26:13 +00:00
117 changed files with 20440 additions and 0 deletions

26
Makefile.in Normal file
View 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

2333
configure vendored Executable file

File diff suppressed because it is too large Load Diff

141
configure.in Normal file
View 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
View 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.

View 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:
*/

View 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);
}

View 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

View 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);

View 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);

View 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
View 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
View 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

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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)
+

View 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.

View 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.

View 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
View 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;
}
}

View 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

Binary file not shown.

16
include/.symlinks Normal file
View 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
View 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
View 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
View 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
View 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

View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>

View 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;
}

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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;
}

View 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);
}
}

View 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

View 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;
}

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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