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

Compare commits

...

645 Commits

Author SHA1 Message Date
b6556dce8b Remove stray comma. 2002-01-30 17:25:51 +00:00
aa8d8bc8b5 Propagate volume group read-only setting down to its logical volumes.
(Might sometimes be safe to relax this restriction.)
2002-01-30 17:12:14 +00:00
0800dcfdc4 Basic support for (read-only) partial activation if any PVs are
missing from a VG.  (Linear targets use the device-mapper 'error' target
which returns ioerror; striped targets use '/dev/ioerror' for now - which must
already exist e.g. as a sufficiently large block device version of /dev/zero).
2002-01-30 15:33:12 +00:00
12b0101e94 quotes around names in output 2002-01-30 15:04:48 +00:00
021b391a02 Allocate fixed space for vg->system_id when vg is created, instead of
dynamically.
2002-01-30 12:47:29 +00:00
d11e2b6057 Correct statement order for case when 'stripes' parameter is not supplied. 2002-01-30 12:17:40 +00:00
264d90e7e5 add vgimport 2002-01-29 19:23:46 +00:00
f9f3da9e78 o A vgimport implementation
o Require -a or <list of vgs> parameters with vgexport/vgimport
o Allow pvcreate -ff to destroy exported/partial VGs
2002-01-29 19:19:37 +00:00
6435b49153 o Basic support for exporting (but importing not completed yet).
o When volume group does not have write flag set, prevent changes to it.
o Preparation for partial activation (not completed yet).
2002-01-29 17:23:33 +00:00
f9aa2941cf Display 'exported' status. 2002-01-29 16:30:18 +00:00
5a933d4bee Add list_iterate that's safe with deletions. 2002-01-29 16:28:52 +00:00
5536aea0df Date changed 2002-01-29 15:54:49 +00:00
976666d216 Zero gap after PV structure on write to disk in order to make non LVM tools happier (AED's idea and patch for LVM1) 2002-01-29 15:52:11 +00:00
9a5bcd4392 fixed div bug in calculation of end in calculate_extent_count 2002-01-29 15:43:04 +00:00
812060a118 Check that vgname doesn't already exits in dev_dir 2002-01-28 16:30:42 +00:00
089b5052e6 o There were some alignment problems with pool-debug which I've resolved
by allocating the data block with an additional dbg_malloc.

o  Added an assertion to check that no one is requesting alternate
   alignment for memory allocated from pool.  I can't see us needing this
   for LVM2.
2002-01-28 09:16:09 +00:00
874e5d72f4 *** empty log message *** 2002-01-27 21:48:05 +00:00
6b11de1329 Tweak some error message levels. 2002-01-27 21:30:47 +00:00
2245a7ad8d If lv isn't active, skip reactivation. 2002-01-25 22:58:01 +00:00
ee5ec1b870 Prevent lvextend from adding segments with different stripe characteristics
at the moment because the old LVM format doesn't support this.
2002-01-25 21:14:43 +00:00
74ccfe851b The latest attempt at making extended striped LVs work portably with LVM1. 2002-01-25 20:24:14 +00:00
e1c24bd5a2 Set pv->pe_size when reading in text-file backup.
Otherwise LVM1 decides the PV structure is corrupt.
But do we need to keep both pv->pe_size and vg->extent_size
in internal metadata or can we generate pvd->pe_size when writing out
a PV that belongs to a VG?
2002-01-25 20:21:13 +00:00
1349b79728 Only remove symbolic links when deactivating.
(if this code didn't create it, don't delete it)
2002-01-25 20:17:44 +00:00
d294d7604c o Tidy 2002-01-25 13:41:13 +00:00
01df2cf464 Have a pe_total check using theoretically big number instead of the
unnecessarily small limit LVM1 imposes in vgcreate (but not vgextend)
2002-01-24 23:35:56 +00:00
45b7322488 Don't bother to write out an empty cache. 2002-01-24 23:17:16 +00:00
b3055e992f Fix the device cache to cope reasonably safely with device name changes.
This should be a rare occurrence so the aim is to recover if it's
straightforward to do so, otherwise just to abort the operation.
If people *knowingly* change device names, they should always run vgscan
afterwards.

A few bytes of memory gets leaked inside a pool each time an alias
has to be discarded - it's not worth restructuring the code to reuse it.

More of LVM2 needs updating to pass device objects (or uuids) about
instead of pathnames so that resolution of pathname->object only happens
once per operation.

dev_cache_get() should now always return the *current* device at the path given

dev_name_confirmed() replaces dev_name() whenever it's important to
know that name for the device is still current (ie when opening it).
If the cache doesn't know a current name, the function fails.

dev_open() guarantees that the file descriptor returned is for the dev_t
of the device structure it was passed.
2002-01-24 23:16:19 +00:00
3da548565c Clear a FIXME about checking for identical devices by comparing dev_t
instead of name.
2002-01-24 22:37:24 +00:00
a975e85548 removed ~64limit for PEs per PV agk introduced 2002-01-24 19:20:35 +00:00
c4b5ade752 o Limit for number of extents should be 65534. 2002-01-24 17:32:56 +00:00
a4b61b0794 Improve allocation error messages when PVs in a VG have the allocatable
flag unset.
2002-01-24 17:26:00 +00:00
f6011184b8 Impose max PE limit for each PV. 2002-01-24 17:24:32 +00:00
74de118c6e o Add check for > 65k extents in a single lv. 2002-01-24 17:16:36 +00:00
9c25e77c17 o Add extra parameter to lv_manip fns 2002-01-24 17:15:49 +00:00
440113e67e o extra fid parameter to lv_manip fns 2002-01-24 17:15:24 +00:00
a11b60c445 o Remove pointless calculation. 2002-01-24 14:15:42 +00:00
a6052681ad Ignore all except one PV found with the same UUID. Use one which
has the md major number if there is such.
2002-01-24 13:36:33 +00:00
482d50536a Fix dev_close arg. 2002-01-24 13:31:18 +00:00
25c79a4fcd Remove any core files on distclean. 2002-01-24 13:30:40 +00:00
02946144ac o typo 2002-01-24 09:54:09 +00:00
71a360e9a3 o Cut and paste description of how pvmove works that I was mailing someone. 2002-01-24 09:26:13 +00:00
f7912d88b1 o Remove redundant symlink-handling code.
o When opening device, return error if its cached name is incorrect (eg if
  it's changed since the cache was generated).  This prevents use until
  the cache is rebuilt (eg with vgscan).  Doesn't catch every case.
2002-01-23 18:55:01 +00:00
975101d7d2 Avoid using VG metadata on PVs that are not in VGs. 2002-01-23 15:50:34 +00:00
ef58c5ff55 *** empty log message *** 2002-01-23 12:25:30 +00:00
e10221804a Silently remove any existing symlink before creating a new one. 2002-01-22 19:58:37 +00:00
284ed9ee0e Update with info on how to configure command output to look like LVM1 2002-01-22 19:20:46 +00:00
5d7b961997 Reviewed interaction with lib/activate now that the interface has settled down. 2002-01-22 19:11:12 +00:00
c699a5c4b4 New config options to customise message output. 2002-01-22 15:33:57 +00:00
1f4ceb89cf Customisable message output prefix / indentation. 2002-01-22 15:33:40 +00:00
0934ca0040 o added BUGS file. 2002-01-22 14:40:38 +00:00
4738f892c2 o Fix inverted logic in list_empty test. 2002-01-22 14:16:27 +00:00
33004fcf33 old file 2002-01-22 13:29:34 +00:00
34d214c166 Update. Ready to release? 2002-01-22 13:11:01 +00:00
a56cd92a1e No need for file output to default to stderr now that log file can be
specified in config file.
2002-01-21 19:05:00 +00:00
7251348507 Insert a missing hash_remove. 2002-01-21 19:04:13 +00:00
01cd5c84d6 o Allow fractional parts for size args. eg, lvcreate -L 34.4M
o  Fix a couple of bugs related to the earlier lv_list change
2002-01-21 17:43:10 +00:00
e210599fa6 o Similar changes for lv_list. 2002-01-21 16:49:32 +00:00
dbe7cee7e9 o fail if create_pv_list would produce an empty list. 2002-01-21 16:15:25 +00:00
7370d88ceb o Typo in comment 2002-01-21 16:10:36 +00:00
34458e0c57 o Changed
struct pv_list {
	struct list list;
	struct physical_volume pv;
   };

   to

   struct pv_list {
	struct list list;
	struct physical_volume *pv;
   };


o  New function in toollib 'create_pv_list', which creates a list of pv's
   from a given command line array of pv's.

o  Changed lvcreate/extend to use this (fixes lvextend [pv list] bug).
2002-01-21 16:05:23 +00:00
05c8c3abf2 Is this sufficient to fix make -j? 2002-01-21 16:02:55 +00:00
e9d464b4d3 Fixx OB1 error in max LV and max PV numbers 2002-01-21 14:53:47 +00:00
6968c3ab9b o Changed find_pv_in_vg, and find_lv_in_vg to return a struct pv_list * and
struct lv_list * respectively.
2002-01-21 14:28:12 +00:00
131a8a9650 o names.[hc] 2002-01-21 13:11:03 +00:00
379ecbf9a9 o lvdisplay now gives a segment map for the -m option. 2002-01-21 12:05:39 +00:00
7b9dfa9a28 o removed display_uuid
o use id_write_format from lib/uuid/uuid.h instead
2002-01-21 11:29:06 +00:00
fbd0f5eed2 o move the path building functions to lib/activate/names.c
o  Update activate.c and fs.c to use them

o  device names are now of the form <vg>:<lv>
2002-01-21 11:06:32 +00:00
5970f904ae Allow syslog facility to be set, or turned off, from the config file. 2002-01-18 21:26:37 +00:00
8cbcb2868d Display something in the "hypothetical" unknown log level case. 2002-01-18 19:38:19 +00:00
8ff2a4b026 Use same log levels as LVM2. 2002-01-18 19:37:26 +00:00
ae14d205a5 Allow compilation against a device-mapper that was installed into $DESTDIR
Always check for negative (error) return code from lv_active()
2002-01-18 16:43:19 +00:00
ec5d560ec5 More updates. 2002-01-18 13:45:12 +00:00
fb543b53c0 added before 2.1 item 2002-01-18 11:07:26 +00:00
39c16422e2 beta1-pre1 tagged, but there's still some documentation to update/write. 2002-01-17 18:48:08 +00:00
4d5b273ebe Support --version argument and 'version' shell command. 2002-01-17 16:39:24 +00:00
ed6a860fad Add function that returns the library version. 2002-01-17 14:13:25 +00:00
423e579292 Add another level of symlink to library name (like LVM1) so people who find
themselves running multiple incompatible kernel versions will just need
to swap symlinks at boot.
2002-01-17 13:37:09 +00:00
ee11aa9e75 Use additional version numbers.
Kernel driver has a version number (stored in kernel/VERSION).
  The first two components of this (0.94) give the version number of the
  ioctl interface.  This number must be changed whenever a change is
  made to the ioctl interface that breaks backwards compatibility.

  The library has a version number (stored in VERSION) which is
  used for linking.
  The first and/or second component of this must be changed whenever
  a change is made to the library API that breaks backwards
  compatibility.
2002-01-17 13:19:55 +00:00
c46d20fa92 o pvcreate --uuid to specify the uuid (required before using vgcfgrestore
onto a new device).  uuid specified must not already exist on the system.
o More message tidying.
o When checking for label, only read PV metadata.
o Add ataraid.  [Needs moving into config/defaults files.]
2002-01-16 18:10:08 +00:00
dc8d17574c o save before committing 2002-01-16 15:53:42 +00:00
5c17cd04c8 o lvm.conf file that contains the same settings that would be assumed if it
wasn't there.  A good starting point for tweaking.
2002-01-16 15:52:53 +00:00
67ada02076 Move test flag from log to global section of config file. 2002-01-16 15:20:51 +00:00
32d94c2eaf o Don't update vgcache when (not really) writing in test mode.
o Don't continue iterating through a possibly-deleted list.
2002-01-16 14:43:27 +00:00
687b39a12a Remove a duplicate disk read (can_handle). 2002-01-16 13:09:26 +00:00
705af407bf #include <string.h> 2002-01-16 12:02:06 +00:00
080052da2e o Set the segment counter back to 1, for a new LV. 2002-01-16 11:34:29 +00:00
ef735fd92a o Add pvmove to the stub file. 2002-01-16 11:27:19 +00:00
17c16dcafc o Knock out the "'%s' is not a block device" debug message. 2002-01-16 09:23:28 +00:00
a89b3018fb Reduce 'no label found' message severity to debug level. 2002-01-16 00:01:36 +00:00
851e2ebd32 Fix function typecasts. 2002-01-15 23:47:56 +00:00
1225ce7fe8 o More comprehensive config parameter debugging messages.
o Make /proc configurable.
 o Review hard-coded "/dev"s - made 2 more of them configurable.
2002-01-15 23:34:13 +00:00
7e77a31b96 o missing labeller free
o updated vgcfgrestore args
o change _check_for_open_devices only to check devices present in the hash
  table instead of using dev_iter which triggers a full scan even when only
  displaying command line help
2002-01-15 21:28:04 +00:00
d146b9002f o Actually check in vgcfgrestore. 2002-01-15 18:17:57 +00:00
1f9d567b23 o vgcfgrestore works ! (with the couple of examples I tried). 2002-01-15 17:37:23 +00:00
ed1b3a023c Another ioctl interface update:
Supply offset to start of variable data area (so struct size can change
without breaking backward compatibility)
  Add command that just returns the driver version
2002-01-15 15:21:57 +00:00
1ca18f501a o split the uuid -> device map out from vgcache
o  roll vgcache back to agk's implementation, we'll revisit this as part
   of the cluster integration.

o  change the extra_info field in a label to be a void *
2002-01-15 10:24:48 +00:00
49588ccd98 Some ioctl interface changes. (Do we want these?)
- use status bits (so we can add flags without changing the struct size)
  - use dev_t
2002-01-14 23:07:32 +00:00
098dedc092 o Non-caching implementation of new vgcache interface. 2002-01-14 11:43:52 +00:00
b06f6b9545 o LVM1 labeller. 2002-01-14 10:00:56 +00:00
ba3cb94999 o Reformat comment and correct typo. 2002-01-14 09:59:12 +00:00
74c67fbf4b o Add rename support to dmsetup.
o Add support to use specified minor number to library and dmsetup.
2002-01-11 12:12:46 +00:00
32b46e4910 Couple of typos fixed. 2002-01-11 11:34:53 +00:00
ecf5539ed2 o Put in the pv_hash which stores the pv section name -> pv struct mapping. 2002-01-11 11:09:12 +00:00
ac9db4e4d5 o label.c now compiles. 2002-01-11 10:43:32 +00:00
0eb96094b0 Change lvm2_label to use Joe's new label switch system. 2002-01-11 10:39:56 +00:00
30b3ac7dc5 Support the renaming of active mapped devices (ioctl interface only). 2002-01-10 23:29:16 +00:00
0092790c7d o ACTIVE is no longer a status flag - lv_active() used to check if an LV
is active in the device-mapper.
o Many operations can be carried out regardless of whether the VG is
  active or not.
o vgscan does not activate anything - use vgchange.
o Change lvrename to support renaming of active LVs.
o Remove '//' appearing in some pathnames.
o Dummy lv_check_segments() for compilation.
2002-01-10 23:21:07 +00:00
28909d8a51 o _read_id function for import.c 2002-01-10 18:12:26 +00:00
b20cfbb7b6 More steps towards successful compilation. 2002-01-10 16:48:28 +00:00
97639bd0a8 Add 'get' functions. 2002-01-10 16:47:58 +00:00
161ec73c96 More detail in error msgs. 2002-01-10 16:47:25 +00:00
957d6bea15 Separate constant fields from variable ones. 2002-01-10 16:47:04 +00:00
f8b6e5b414 Clarify terminology:
VG is resizeable  - PVs can be added or removed
  PV is allocatable - free extents on it may be allocated to LVs
2002-01-10 15:09:51 +00:00
1ab450870e o Moved the current label.[hc] sideways to lvm2_label.[hc]
o  First pass at low level labelling switch.  This allows us to
   register different label types (eg, lvm1, lvm2).
2002-01-10 15:01:58 +00:00
de45f4884c Allow for multiple spellings / backwards compatibility of renamed
command line options.
      vgchange --resizeable y
      pvchange --allocatable y
But --allocation is still allowed for both (as LVM1) and --resizable is OK.
2002-01-10 14:46:50 +00:00
5da1f3e7c8 o vgcfgrestore. 2002-01-10 14:27:47 +00:00
983014952b Temporary file creation & renaming. 2002-01-10 12:22:17 +00:00
55298019a3 o First pass at import.c. Still waiting for label code for the uuid->pv
mapping.
2002-01-10 11:18:08 +00:00
a1ffc3f271 o Put in the 'out of memory' log_err for pool. 2002-01-10 09:35:55 +00:00
2fb60aa997 Renamed to archive.c 2002-01-09 19:17:11 +00:00
f5ec76537a o Rename many occurrences of 'backup' to 'archive' to reduce confusion.
o Extract file creation/renaming code into a library and change backup code
  to use it too.
o Support umask.
o Bring lvm.conf man page up-to-date.
2002-01-09 19:16:48 +00:00
728491fd2b Accept octal values for numbers (such as umask). 2002-01-09 18:53:07 +00:00
d9d3f2b9e4 o Let the comment wars begin. 2002-01-09 14:14:07 +00:00
3fe4864f65 o new function backup_remove(const char *vg_name), to be called from vgremove. 2002-01-09 14:07:49 +00:00
0b156f22a4 o Reformat comments. 2002-01-09 13:56:11 +00:00
35b1d93813 Add archiving. 2002-01-09 13:17:14 +00:00
d770851ac0 o Try to improve NFS-safety for temporary file creation (unique name; O_APPEND
+ fcntl lock) and rename (using hard link), avoiding any "real" archive
  files ever being zero length.
o Fix filename parsing & ordered list handling.
2002-01-09 13:16:19 +00:00
989e7b1033 Explicitly close (=>flush) files. 2002-01-09 13:07:03 +00:00
c4e0eb7b49 Allow pool_begin_object in empty pool. 2002-01-09 13:06:02 +00:00
71f5d0dac7 Another attempt to support both readline versions. 2002-01-08 19:17:08 +00:00
561b0c4381 call archive_exit() & backup_exit() on exit 2002-01-08 18:14:04 +00:00
995fbc7330 o Remove anomalous punctuation. 2002-01-08 10:51:13 +00:00
10ab8949c4 o Introduction to pool for those without psychic powers. 2002-01-08 10:47:17 +00:00
c441202fea fixes for compilation 2002-01-07 23:28:25 +00:00
ca261b0bee Sync. 2002-01-07 23:04:48 +00:00
52f3709f67 Sync tidy. 2002-01-07 22:49:04 +00:00
c2ca6187fe If a device somehow became suspended, lvchange -ay now reactivates it. 2002-01-07 22:36:12 +00:00
671a13d295 Support for read-only. 2002-01-07 22:28:36 +00:00
14c3e2eccf Missing close() in error case. 2002-01-07 22:25:57 +00:00
08e5b852c2 tidying 2002-01-07 22:01:50 +00:00
1c9606c824 o vgcreate wasn't setting vg->cmd 2002-01-07 15:27:55 +00:00
3cd47b5c9b o New function 'merge_segments'
o  Call said function at end of lv_extend
2002-01-07 15:08:28 +00:00
aedc729087 o tidy up renaming of archive files. 2002-01-07 14:21:33 +00:00
5f7cfa3fa9 o sync tool changes for backup stuff. 2002-01-07 11:12:11 +00:00
0083f30af5 o Added find_config_bool 2002-01-07 10:23:52 +00:00
4a06f05ef5 o Get format-text.c compiling. 2002-01-07 09:16:20 +00:00
8f37cadce8 o sync laptop to test machine. 2002-01-07 09:05:31 +00:00
a11603ca6c Imported man pages from LVM1 with some quick LVM2 updates. 2002-01-04 20:35:19 +00:00
86274842e9 The start of an lvm man page. 2002-01-04 18:56:56 +00:00
cf9c955a44 Document remaining configuration file parameters. 2002-01-04 17:49:38 +00:00
55d828c35f o Revert to the 6-4-4-4-4-4-6 format for uuid's
o  When reading a uuid all -'s are stripped, wherever they are.
2002-01-04 16:55:14 +00:00
cdff28aca6 Put device name in quotes. 2002-01-03 17:47:48 +00:00
b9da39274f o High level archiving and backup functions.
I've split the old autobackup function into two seperate areas:

'archiving' is performed *before* a vg configuration is changed.  This
produces a numbered backup in /etc/lvm/archive.

A 'backup' is performed *after* a vg change.  So the directory /etc/lvm/backup
will hold the  a copy of the current configuration.
2002-01-03 15:46:48 +00:00
c379aa5782 stub for read-only functions with fs interface 2002-01-03 15:12:02 +00:00
9dcabac9dd Fix final comma in arrays. 2002-01-03 12:43:01 +00:00
794f3a2b9f *** empty log message *** 2002-01-03 12:39:04 +00:00
2066121b7c o Added -r, --read-only switch to dmsetup for use with create and reload. 2002-01-03 10:39:21 +00:00
0c4067f143 o Allow the definition of read-only devices (ioctl interface only) (Joe)
o Add version number to ioctl structure with error on kernel/library mismatch
2002-01-02 19:01:09 +00:00
8aa69243b7 o Added section on the syntax of the config file, with an informal grammar. 2002-01-02 17:54:57 +00:00
8697263bde Fix $DESTDIR support 2002-01-02 14:23:10 +00:00
ea25c4f65c Tidy makefiles - $DESTDIR & shared library version (like LVM1) 2002-01-02 13:40:49 +00:00
82ac3ebd7e Add test mode parm. 2001-12-31 22:12:03 +00:00
13cb94909c o Add autobackup support to tools (follows most vg_write calls).
o Skip autobackup when in test mode.
o Set test mode from config file.
o Create system/backup dirs if not present (unless LVM_SYSTEM_DIR holds "").
2001-12-31 21:27:39 +00:00
b57ca2a763 vgcache.h inclusion (avoid compiler warning) 2001-12-31 19:18:44 +00:00
83c49e9745 o Use lvm_snprintf wherever return value is used
o Add parameters to set retention limits for backups
2001-12-31 19:09:51 +00:00
e15771d78d Remove some old files. 2001-12-31 17:34:51 +00:00
6edc4920ba Redundant. 2001-12-31 17:26:42 +00:00
302bb1bd93 Document lvm.conf fields 2001-12-31 17:20:22 +00:00
529b1bceee Outline docs 2001-12-31 16:12:40 +00:00
42cd47d32e o Allow more default values to be overridden from config file.
o Cope with both the readline versions used around here.
2001-12-31 15:20:18 +00:00
711d884c2e Fix C99 error case handling (snprintf ret value >= buffer size). 2001-12-31 15:17:34 +00:00
183d1c4674 Fixes for compilation. 2001-12-31 15:14:44 +00:00
faed63a0bb Remove unused --with_kernel_dir
Current version of LVM2 instead relies on /usr/include/libdevmapper.h
which gets installed by the device mapper package.
(Should this location now be configurable?)
2001-12-31 15:13:42 +00:00
53bff262f8 Revised ioctl/dmfs merge with fixes for bugs found in tests. 2001-12-20 20:32:14 +00:00
3251a708e4 o Added a quick vgcfgbackup, needs parameters as yet. 2001-12-20 16:05:14 +00:00
b5dbdbf7b2 o Debug version of the pool_grow stuff. 2001-12-20 12:27:41 +00:00
a9649e92c9 o sync backup changes 2001-12-20 11:52:54 +00:00
b561f1fa8b Wipe the first label if writing the second one failed. 2001-12-18 14:39:32 +00:00
cecd7491b5 o sync the backup stuff 2001-12-17 19:46:10 +00:00
55a66322b5 o history is now saved in ~/.lvm_history 2001-12-17 17:59:58 +00:00
155c31a2d7 o Shuffled completion functions around so we dont have to declare them
at the top of the file.

o Changed completion_matches -> rl_completion_matches, and added some consts.

This will probably break things on pre readline 4.2 systems.
2001-12-17 17:18:47 +00:00
a89ce91089 o Changed the macro name in args.h from 'xx' to 'arg'
o  There is now a _default_debug, and _default_verbose level, when
   using lvm interactively -vv and -dd switches just effect the current
   command.

o  Added a --quiet switch which sets both verbose and debug to zero.
2001-12-17 16:58:17 +00:00
b897fe6700 o Use lvm_snprintf 2001-12-17 14:05:43 +00:00
548a351b06 o Add symlink for lvm-string.h 2001-12-17 14:04:33 +00:00
4b1da57ca1 o lvm_snprintf
Could everyone please use this from now on.
2001-12-17 14:04:10 +00:00
529aec7f25 o Remove LVM_CONFIG_FILE environment variable.
o  Introduced the LVM_SYSTEM_DIR variable.

This makes more sense because the persistent cache, and backup directories
are config specific.

eg, I use /etc/lvm for running my real LV's

    but I have another directory /dev/lvm_loops that contains a config
    that allows only loopback devices, I use this for testing.
2001-12-17 12:01:09 +00:00
1661e545cb Typos in error messages 2001-12-17 11:07:33 +00:00
ec71d08878 I had another look at the argument processing code:
o You must list long args with no short option (eg. --version) at the
  front of the args.h file.

o If an argument has no short option, set the short option in args.h to '\0'

o The index into the 'the_args' var is now stored as the option value
  for getopt, iff there is no short opt.
2001-12-17 10:08:27 +00:00
ac258b7dd7 o Include dmsetup man page in build
o Allow pathname in dmsetup device arg
o Generated patches for 0.90.02
2001-12-14 13:30:04 +00:00
0f57876233 Write the location of both labels in the labels so we can check them. I don't do
much with this ATM (apart from check that they all match up).
Use a different CRC routine.
2001-12-14 13:15:15 +00:00
1d25a3693d o I figure if I can't remember how to use my code, then I should add
a comment.  It's quite cool, wish I remember writing it.
2001-12-13 16:09:06 +00:00
45fa428bf1 Handle orphan PVs too, so hints remain valid after vgreduce. 2001-12-13 15:08:58 +00:00
177fa80f1a o Man page for dmsetup 2001-12-13 13:46:21 +00:00
3261261bfe made the hard-coded 512 into BLOCK_SIZE just for neatness sake.
log_error() if writing the label fails so someone knows which was in error.
2001-12-13 08:40:47 +00:00
d4de7934f8 Add internal cache holding a 'hint' list of the PVs belonging to each VG.
A substantial speed-up - particularly in readline mode.
If the hints turn out to be wrong, the relevant parts get thrown away.
vgscan destroys it totally.  In both cases it then rebuilds itself as
required.
2001-12-13 00:07:29 +00:00
c3475af809 fix for clean compilation 2001-12-12 16:25:53 +00:00
b12f707812 o silly bugs 2001-12-12 16:22:38 +00:00
22c0c34d60 o pool-debug version of end_object wasn't returning the object. 2001-12-12 16:05:52 +00:00
a60b66f230 o Add error checking in _new_chunk 2001-12-12 14:54:24 +00:00
83f6e93628 o pool-debug versions of begin_object, grow_object etc. 2001-12-12 14:25:20 +00:00
222b5f0229 Build label code into the library 2001-12-12 09:09:04 +00:00
30aa383e26 Use a proper CRC calculation. 2001-12-12 09:05:44 +00:00
676b401294 - Change label format to include a string disk_type and a version number.
- The iterator can find labels by string and also appropriate version number (==,
  <= or any) if you want.
- Add labels_match() call that compares the two labels and returns an error if
  they do not match.
- Write labels in sector 1 & last rather than 2 & last as per Joe.
2001-12-11 16:49:40 +00:00
ebf57159de Apply make distclean to test subdirs too. 2001-12-11 16:26:34 +00:00
199d2aafec Fix label filter. 2001-12-11 14:17:10 +00:00
81952f56fd o Add output_date 2001-12-11 12:29:25 +00:00
c5bac82b43 o flags.c reads and writes a status bitset 2001-12-11 12:18:56 +00:00
081b86109c o Split import-export.c into two files. 2001-12-11 12:16:58 +00:00
8ac2028a75 o Update sample to a format that supports multiple vg's per file. 2001-12-11 12:15:08 +00:00
264fed1c9f Label reading/writing code.
Not tested the filter yet.
2001-12-11 11:42:30 +00:00
dd59f7b2c7 o Pretty print and read for uuid's 2001-12-11 11:40:34 +00:00
9245a760db Add a dev_get_sectsize call. 2001-12-11 10:18:49 +00:00
b61b32bbc3 Fixes for allocation of striped volumes. 2001-12-07 21:17:12 +00:00
09b3914f5d Fixes for library compilation. 2001-12-07 21:15:33 +00:00
fe644e4c9e Moved across to device-mapper repository. 2001-12-06 14:20:38 +00:00
7b09bf2156 o Updated projects.txt to remove the earlier error which turned out to be
a build error.
2001-12-05 18:04:55 +00:00
987d0aae66 Various fixes & restructure to extract common code. 2001-12-05 16:41:52 +00:00
9cbcbc1c22 o Removed unused MOD_INC/DEC_USE_COUNT 2001-12-05 12:00:01 +00:00
cfc4e2bc60 o Added a few more projects 2001-12-05 11:58:43 +00:00
a03405fa81 o Initial merge attempt
There are still a few odd things going on, so more debugging remains to be
done.
2001-12-05 11:28:41 +00:00
d5c9ccbe6e Correct activation message. 2001-12-05 00:04:18 +00:00
e52772d65f Added more log messages. 2001-12-04 23:20:27 +00:00
1e3259e728 o sync 2001-12-04 14:14:07 +00:00
e905a20a60 Tweaks for make install. -m args replaces verbose to display maps. 2001-12-03 20:23:53 +00:00
88e2be7a33 More striping support & fixes. 2001-12-03 16:27:16 +00:00
8939131600 o Comparison function was sorting things in ascending rather than
descending order.

o free off the sort array when finished with it.
2001-11-30 09:19:46 +00:00
ba37ebff8b o Striped allocator
o  Changed pv_map.c to maintain the list of free areas in size order, which
   is more helpful to the allocators.  If you want to allocate a bit of an
   area call consume_area(area, size), this will adjust the area if there's
   some space left and shuffle it to the correct place in the list.


Not tested.
2001-11-29 18:45:35 +00:00
28f4cb7e07 o I was reading striped volumes incorrectly. 2001-11-29 14:13:43 +00:00
db1e7102cd o Confusingly, dmfs-tdir isn't gone, its now called dmfs-lv.c and its the
old dmfs-lv.c thats gone.
 o Dropped out support for multiple tables in line with ioctl interface
 o Some reordering to better support the userland library
 o Updated to 2.4.16

I'm fairly happy with the way that this is working now, so the next job is
to start the integration with the ioctl interface so there is a single
common dm.[ch] and selectable interfaces (fs or ioctl).

Further improvements can be made even now, but I hope to wait until we've
got this going and integrated and the libdm parts working as well before
investigating other avenues.
2001-11-29 14:00:04 +00:00
07eb7a5830 New patches for 2.4.16 2001-11-29 13:44:46 +00:00
b7c6c685fa configure --with-interface=ioctl (default) or =fs to choose kernel interface 2001-11-28 21:03:50 +00:00
212134df70 Add autoconf & makefile structure like LVM2. 2001-11-28 20:08:11 +00:00
6eeb5528f5 Add -t or --test arg to all tools that update metadata to avoid
committing metadata changes or (de)activating.
2001-11-28 18:03:11 +00:00
54fad845c9 o Output the correct format for the stripe target 2001-11-28 17:52:27 +00:00
d6c0de6fc7 Fix single stripe resizing. 2001-11-28 16:16:44 +00:00
649c8649f7 Make source files depend on makefiles. 2001-11-28 15:00:49 +00:00
da2f53d1b1 o pool_free was leaving one block hanging around. 2001-11-28 14:58:33 +00:00
405139e3b8 o Tool support for segments.
o vgmerge working.
2001-11-28 13:45:50 +00:00
4f8d347171 Use CFLAGS during make rule generation. 2001-11-28 12:28:03 +00:00
bf0db4876c o pool-debug.c contains an alternative implementation of pool that gets
a seperate chunk of memory from dbg_malloc for each pool_alloc.  This
   will allow the bounds checking code in dbg_malloc to do it's stuff.

o  The normal implementation moved to pool-fast.c

o  pool.c now just contains a #ifdef and includes the appropriate .c file.

Alasdair, could you make sure that gcc -MM get's passed all the
CFLAGS please, otherwise the dependencies get calculated incorrectly.
2001-11-28 09:13:00 +00:00
47a14884d6 o Turn on pool debugging by default (-DDEBUG_POOL) 2001-11-28 09:07:53 +00:00
3a7bbc8b08 Fix a memory smash. 2001-11-27 23:12:06 +00:00
1b1d65372c o extra error checking 2001-11-27 20:03:45 +00:00
fd2faaa16e o These now compile. 2001-11-27 17:39:15 +00:00
0609cdb9ea o Get format1 building. 2001-11-27 17:29:56 +00:00
d3bb140f89 vgmerge first cut 2001-11-27 17:02:24 +00:00
b31dc66628 o Sync up todays work on converting to the segmented representation of
logical volumes.  It includes:

   format1 changes.

   metadata.h changes.

   lv_manip.c changed (striped allocation still not done though).

   activate.c changes.

Nothing has been near a compiler as yet.

Alasdair can you look at changing display.c to use to output the mappings
in a more segment oriented format please ?

I haven't put the span list into struct physical_volume to represent allocated
extents.  I think the burden of maintaining it for things like lv_extend may
out weigh it's uses.
2001-11-27 16:37:33 +00:00
09476171a6 Tool support for multiple (striped) segments (incomplete). 2001-11-27 13:42:37 +00:00
33dee813b5 o change chunk_size to stripe_size 2001-11-26 16:30:43 +00:00
bb4e73c40b o More metadata changes. 2001-11-26 16:18:48 +00:00
b1f23ffa94 LV create/extend prototype changes for striping 2001-11-26 15:31:46 +00:00
b0e8cec1e7 o make it obvious that stripe_segment is variable sized. 2001-11-26 13:15:22 +00:00
5077ae19bc o segments will have to be held as an array of pointers since they're now
variable sized.
2001-11-26 13:03:36 +00:00
0d8447bf59 o sync the new in core rep. for Alasdair.
This will break everything !  Hopefully things will be working again by
   this evening.
2001-11-26 12:49:29 +00:00
c6cf08a274 additional patch required 2001-11-23 12:35:31 +00:00
dc49ae519e o Revised seq_file usage after discussions on linux-fsdevel 2001-11-22 15:14:20 +00:00
904539476a o Make sure that every switch has a short option, even if it's
non-displayable so we can remove the pointer mangling that was
   breaking 64bit arch.s
2001-11-22 14:37:07 +00:00
3fbf02dc82 o activation & active status tests
o lvdisplay fields from kernel
o update lv->size on resize
2001-11-21 19:32:35 +00:00
c9392a840d dmdir path 2001-11-21 19:20:41 +00:00
d164e8ab72 o Remove an old debug statement 2001-11-21 18:24:22 +00:00
6dc62c9fb6 o Display major number 2001-11-21 18:12:41 +00:00
87a9684d66 o use the major number returned from dm_ioctl. 2001-11-21 17:57:57 +00:00
94525e2f44 o There's no need to prefix dm_dir() with /dev/ anymore 2001-11-21 17:20:49 +00:00
b408b1b3b9 o You can now specify the dev directory for libdm
o  dm_dir() returns the full path to the device-mapper dir (eg, /dev/device-mapper).

o  put stat in on _rm_node
2001-11-21 17:08:37 +00:00
27c2f09e32 o Removed _check_devfs
o  We now do a stat to see if the device node is there
2001-11-21 16:47:10 +00:00
19bc4d3349 o Remove hard coded path to /dev/device-mapper/control 2001-11-21 15:49:45 +00:00
f2b6c424d6 Tidy makefiles 2001-11-21 15:41:14 +00:00
a49d4453e9 o Change name of libdm.h 2001-11-21 15:15:37 +00:00
65e50087b9 o Use MKDEV to build the dev_t for mknod 2001-11-21 15:14:35 +00:00
2d90f759d9 o Don't use dmt->dmi until it has been initialised. 2001-11-21 14:52:16 +00:00
4230ac7674 o Migration of device-mapper from LVM_WORK to it's own (public) repository.
Please use this one from now on.
2001-11-21 12:47:42 +00:00
d96e9182e9 o Oops, I thought this was checked in ages ago. 2001-11-21 09:21:31 +00:00
68c87b9616 o Sync. only 2001-11-21 09:20:05 +00:00
7f8e9a0b6d o _emit_target wasn't spotting contiguous targets properly. 2001-11-19 15:44:06 +00:00
81a229f2a5 o Use new info interface to dm. 2001-11-19 15:38:39 +00:00
8be7ae2733 vgdisplay 2001-11-19 15:20:50 +00:00
846bca4cb1 file cmgr.h was initially added on branch CLUSTER_TAG. 2001-11-19 14:40:32 +00:00
f36f353789 file cmgr.c was initially added on branch CLUSTER_TAG. 2001-11-19 14:40:32 +00:00
939a2731ed file clvm.h was initially added on branch CLUSTER_TAG. 2001-11-19 14:40:32 +00:00
835dab97ff Zero first 4k of new LVs. 2001-11-16 15:38:52 +00:00
fa904b53be Don't need EXTRA_LIBS as autoconf fills in LIBS for us with all that is needed.
BTW if there are any *real* autoconf experts out there please feel free to flame
me.
2001-11-16 11:39:13 +00:00
0ec52dddce size_ts aren't really pointers but there are no format specifiers for them,
so this will just have to do.
2001-11-16 11:37:45 +00:00
c289355a3a Fix format characters for printing size_ts 2001-11-16 10:56:11 +00:00
02a13a5a18 Do substitution on LIBS so that those platforms that need -lncurses as well as
lreadline will work.
2001-11-16 10:40:16 +00:00
6cf2a0281b lvrename (without reactivation) 2001-11-15 17:27:45 +00:00
120d35f9af Use POSIX defined PRIu64 for formatting 64 bit unsigned integer types 2001-11-15 15:18:53 +00:00
2b15d5e7b3 Use FMT_64 to format 64bit types 2001-11-15 14:27:51 +00:00
fc167bd3f0 define FMT_64 to be the right format string for 64-bit types a la GFS 2001-11-15 14:27:34 +00:00
91b04abf05 Use inttypes.h 2001-11-15 14:14:03 +00:00
77faac8740 #include <string.h> for memset 2001-11-15 11:46:00 +00:00
43b3d54855 More LV-related tidying. lvdisplay without args now shows all LVs. 2001-11-14 18:38:07 +00:00
69e9b85700 Avoid generating duplicate lv names 2001-11-14 14:12:01 +00:00
0b6d132759 Miscellaneous tidying 2001-11-14 13:52:38 +00:00
7c233c6c0c o lvcreate no longer needs the explicit -n flag
o  disabled zeroing of lv until bug's worked out
2001-11-14 12:07:37 +00:00
c35b290fa4 o Prefix static var with '_' 2001-11-14 10:44:14 +00:00
3d95cfb367 o Added dev_open and dev_close functions
o  Changed disk-rep to use these

o  if NDEBUG is not defined the dev_cache will check for open devices on
   teardown.

I was hoping this would speed things up.  But I'm still getting:

reti:/home/joe/sistina/LVM2/tools# time ./lvm vgchange -a n
  Volume group vg0 successfully changed

real    0m5.751s
user    0m0.060s
sys     0m0.070s

even though I have only 1 device with the vg on it passing the filters.
2001-11-14 10:01:52 +00:00
b90fc3a56e o Deal with sparse lv arrays (on disk)
o  new fn. dev_zero which zero's an area of a device
2001-11-13 18:52:52 +00:00
1ef3fdccf5 o lvdisplay now shows LE / PV map
o fix LE allocation when first PV is full
o reduce VG free_count when removing PVs from VG
2001-11-13 17:53:06 +00:00
02b7f77bd8 o Put underscore between vg and lv name. 2001-11-13 16:14:54 +00:00
0ac7ead922 Merge lvreduce & lvextend into lvresize. 2001-11-13 14:17:50 +00:00
da9d0e03ce o Stuff 2001-11-12 19:28:50 +00:00
120f65f672 o Add ALLOC_SIMPLE 2001-11-12 17:55:05 +00:00
200a14caa4 Remove hard-coding and create device-mapper directory if required 2001-11-12 17:21:25 +00:00
35bf6da8e2 o if any pattern rejects a device, and there were no accepts then reject ! 2001-11-12 17:06:33 +00:00
f08f70276c o check result of an allocation 2001-11-12 16:00:52 +00:00
1ae50fd95b iospace restructured 2001-11-12 15:10:01 +00:00
40512beb47 o add fs.c to the Makefile 2001-11-12 13:02:06 +00:00
0d7f9b2c94 o add uplink from vg to cmd_context 2001-11-12 12:23:10 +00:00
52f42140a7 o Plug in fs_(add|del)_lv 2001-11-12 12:20:58 +00:00
3f6c50297f o Split struct io_space into:
struct format_handler - format methods
   struct format_instance - links instance data, methods, and cmd
   struct cmd_context - dev_dir, memory allocator, device filter
2001-11-12 12:16:57 +00:00
f72d80afc5 o Compile errors 2001-11-12 11:48:31 +00:00
7c5cb13b22 o Ready for testing 2001-11-12 11:42:29 +00:00
d728750eb2 o Fix module ref counts so that you can actually unload dm-mod
N.B. This means that you have to take very great care in the event that
   you want to access the dcache tree from in kernel

 o Added extra field to allow out of memory conditions to result in the
   correct error code. (This hasn't received a lot of testing...)

I've ditched the final project (which would have cleared my whole list)
since its got other complications which I don't have time to fix right
now. Still as Meatloaf says, two out of three ain't bad!
2001-11-10 17:11:36 +00:00
02a70e5667 o Added lvextend
o Full signed arguments to lvreduce/lvextend
o Consistent lv_number/pe map use
o Populate pv->pe_allocated
o Fixes for allocation/writing of multiple LVs
2001-11-09 22:01:04 +00:00
44e51ea5fa sync only, not ready yet 2001-11-09 08:48:22 +00:00
87e201460a lvdisplay & lvreduce 2001-11-08 16:15:58 +00:00
039bd945e2 more todo 2001-11-08 08:19:06 +00:00
e9e52d2b4b o Always set LVM_READ.
o Avoid duplicate deallocation.
2001-11-07 22:47:43 +00:00
2bf92e7399 Oops. Forgot to check this in earlier. Changes as per previous check in
comments.
2001-11-07 19:27:17 +00:00
5b0df241f0 o more todo 2001-11-07 17:38:25 +00:00
76f5b05eff o Lot's to do 2001-11-07 17:25:17 +00:00
40fb6c998f o Added lvs_in_vgs_opened 2001-11-07 15:02:07 +00:00
33f50a342d o pool_empty was very wrong 2001-11-07 14:11:20 +00:00
81523ab68a Tidy and changes to make code smaller.
o Created dmfs.h as a private header for the filesystem code
 o Using seq_file.[ch] written by Al Viro as a generic mechanism for /proc
   style files which have one record per line. We use a slight modification
   here, so if you are using a recent -ac kernel you'll need to replace the
   existing seq_file.[ch] with  the ones here and do a bit of editing to make
   it work. I'll submit the changes to Al Viro shortly as they are very
   small and I think make sense generally.
 o Using fail_writepage()
 o Init code for filesystem now all in dmfs-super.c
 o Some common code reduction amoung the dmfs-*.c files
 o Auto allocation of major device number (default). You can specify a
   particular major by using a module argument. If built in then you don't
   get this option at the moment but it could be added if required.
 o Hotplug support
 o General tidying
 o Updated projects.txt file
 o Patches updated to 2.4.14
2001-11-07 12:12:56 +00:00
2bf8cc62cf o Another pass at the activation code 2001-11-07 11:51:42 +00:00
1ae8247af3 Added GPL disclaimer 2001-11-07 08:50:07 +00:00
5ef32227ec lvcreate 2001-11-06 19:02:26 +00:00
6456e773bd o lv_extend 2001-11-06 12:01:46 +00:00
234fe53ca3 o Factor _allocate out for use by lv_extend 2001-11-06 11:31:29 +00:00
7c93e7a7b3 o lv_reduce
o  pv_maps wasn't taking a list of acceptable pvs
2001-11-06 11:19:33 +00:00
8afc6c7f4b o Contiguous allocation 2001-11-06 10:55:01 +00:00
4609d0fa3a o lv_manip.c will contain the code for lv_create, lv_extend and lv_reduce. 2001-11-06 10:29:56 +00:00
d452c035c6 Reinstate size of lv 2001-11-05 18:07:44 +00:00
45113c8f5a o code for building free area lists on a pv. Compiles but not run. 2001-11-05 16:41:38 +00:00
0acdd3c62b o adjacent extents are now merged into a single target when activating. 2001-11-05 13:37:13 +00:00
96d7d0a33e lvcreate prototype 2001-11-05 13:06:03 +00:00
b6b280267b o build lv name from <vg>_<lv> 2001-11-02 16:45:44 +00:00
6e6d253b1a Link in the activation library. 2001-11-02 16:28:04 +00:00
d92c105db2 o First pass at activation 2001-11-02 13:45:05 +00:00
906db728d6 o Changes to activation interface
o Add pointer lv->vg
o Some naming tweaks to improve clarity
2001-10-31 17:59:52 +00:00
c4b7411565 o LGPL list implementation 2001-10-31 12:47:01 +00:00
de06396046 o random little fixes 2001-10-30 17:53:21 +00:00
ee6bfeb8e3 lvchange 2001-10-30 14:32:48 +00:00
058347321f basic lvscan 2001-10-29 18:23:35 +00:00
feefe49324 o Add read_ahead and stripes to struct logical_volume 2001-10-29 15:34:56 +00:00
187381a9a2 prefix & vgname in lvname 2001-10-29 15:28:00 +00:00
993dfa4368 lvremove 2001-10-29 13:52:23 +00:00
7e35a16440 o Added two items which ought to be done when we update to 2.4.14-pre3 or
above.
2001-10-29 11:06:46 +00:00
e4eeb15926 o Added a file containing a TODO list.
Please add to/edit this file as you think of new ideas or discover bugs. The
items in it are in no particular order. They are also only ideas and hence may
never get implemented depending on whether they turn out to be good ideas or
not.
2001-10-29 10:03:05 +00:00
634e0db26d o rfilter was no longer accepting by default 2001-10-25 18:12:44 +00:00
56855c23e1 o log/overwrite=1 in config file to overwrite instead of append to log 2001-10-25 17:25:48 +00:00
0b00f742e3 o was freeing memory from the wrong pool 2001-10-25 15:24:35 +00:00
b7ab3f673c o fopen error message
o debug options in makefile
2001-10-25 15:07:26 +00:00
be04ea1e35 o pfilter stores results for all aliases. 2001-10-25 14:51:51 +00:00
1f8e695802 o It's a bit of a hack, but the regex filter now makes sure a device path
that passed the filter is at the front of the aliases list.
2001-10-25 14:41:28 +00:00
2d82b2c64f o rfilter now checks all aliases for a match 2001-10-25 14:19:39 +00:00
d076caf473 o use dev_name(dev) to get the name of a device, this operation is cheap
since it just get's the first alias.
2001-10-25 14:04:18 +00:00
c7abdefa31 o Remove a couple of warnings, and one bug in ttree. Spotted by the optimiser 2001-10-25 13:08:29 +00:00
ba772c0bca o Shuffle the keys to stop degeneracy. 2001-10-25 12:38:18 +00:00
5bad234119 o Trivial binary tree 2001-10-25 11:38:19 +00:00
c7e7baaf23 o added aliases list to struct device. 2001-10-25 11:34:55 +00:00
36658a671b o Correction in logic for write access to tables 2001-10-25 11:05:29 +00:00
045f2e10ba o Fix typos from yesterday 2001-10-25 10:37:05 +00:00
fb5a7db66d o Merged common code between hash_destroy and hash_wipe. 2001-10-25 08:31:43 +00:00
ba7d33982e persistent cache fully incorporated. Goodbye to scanning /dev/cdrom :-) 2001-10-24 17:53:50 +00:00
c62279a755 o Updated 00_makefile
o 00_bh-async-3 has been merge with vanilla
2001-10-24 10:52:10 +00:00
17fa1a7ffb o Error list handling now part of fs rather than part of table. 2001-10-24 08:26:10 +00:00
e89ceac351 o Fix bug in dmfs-error.c where it could return too many bytes under some
circumstances.
 o Use sscanf() in dmfs-table.c
 o Use do_generic_file_read() instead of original hand made loop in dmfs-table.c
2001-10-24 07:51:42 +00:00
0b8c30c109 persistent filter & some log message changes 2001-10-23 18:20:27 +00:00
9ab0f463cc o removed old files 2001-10-23 14:17:07 +00:00
6433dda7b8 o forgot to use the path passed into _read_array. 2001-10-23 13:12:05 +00:00
fa7a2f4be4 o test program for the new persistent filter. 2001-10-23 13:11:28 +00:00
ba90e16505 deallocations 2001-10-23 12:33:57 +00:00
008f710203 o rethink of the persistent filter 2001-10-23 12:24:55 +00:00
df2740f126 filter integration into tools 2001-10-23 11:50:49 +00:00
2db89d143e o forgot to retry on EINTR or EAGAIN, doh ! 2001-10-23 11:16:30 +00:00
0525d49da3 o forgot 'static' 2001-10-22 14:40:31 +00:00
e2b0745882 o composite filter that allows us to merge filters. Think of it as &&'ing
filters in order.

eg,

	f = composite_filter_create(2, regex_filter, persistent_filter);

  ownership of the filters passes, they will be destroyed when f's
  destroy method is called.
2001-10-22 14:39:12 +00:00
92e804fc50 o Filter which caches valid devices in a file. Pass in init == 1 to the
constructor if you want it to ignore the existing cache and check every
  device again (eg, vgscan, pvscan).
2001-10-22 14:14:00 +00:00
67abf45576 reinstate a removed line 2001-10-22 13:44:09 +00:00
d2c9c814e7 o removed 00_latest since it never is. 2001-10-22 10:03:05 +00:00
22f8881a64 o tidying 2001-10-21 10:24:10 +00:00
4ab20322fe o Filter for the dev cache that takes values from config file:
devices {

        # first match is final, eg.  /dev/ide/cdrom
		        # get's rejected due to the first pattern

					filter=["r/cdrom/",         # don't touch the music !
							"a/hd[a-d][0-9]+/",
							"a/ide/",
							"a/sd/",
							"a/md/",
							"a|loop/[0-9]+|", # accept devfs style loop back
							"r/loop/",        # and reject old style
							"a/dasd/",
							"a/dac960/",
							"a/nbd/",
							"a/ida/",
							"a/cciss/",
							"a/ubd/",
							"r/.*/"] # reject all others
}


Alasdair this is ready to roll into the tools now.
2001-10-19 18:20:37 +00:00
5370eeecea o First pass at the regex code. lib/regex/matcher takes an array of regex's
and builds a *very* efficient engine that will tell you which regex a string
  matches with only a single pass through the string.  To be used in the config
  file when specifying devices.

o Anchor's aren't supported yet (^ and $) but that won't take long.

o Also when we get some realistic config files we may want to consider adding an
  extra level of indirection to the dfa state in order to compress the table.
  It all depends on how large typical tables get.
2001-10-19 14:36:57 +00:00
ba71cb5dd7 pvdisplay 2001-10-18 16:55:19 +00:00
9aad6c2c52 o Remove unused variable. 2001-10-18 15:59:25 +00:00
4d9627f20c pvchange 2001-10-17 15:29:31 +00:00
c142492e91 o Fix crash that Patrick reported 2001-10-17 15:03:00 +00:00
6bf8d9e207 o Fix a typo. This should fix devfs support. 2001-10-17 14:34:53 +00:00
4f9a6168c1 o Patches to go with earlier check in 2001-10-17 13:13:25 +00:00
38397f99aa Ok. this is the big one.... the change to the new fs interface.
Things to note:

 o Changes to the dm-*.c files have been kept as small as possible during
   the development of the new fs interface and there are a few places where
   the new code does odd things to give the original code what it wants. These
   places will gradually go away during the next few days once we are sure the
   new code is sound.
 o I've spent most of my testing time looking at the parser since thats where
   a lot of the changes are, I've not checked the actual I/O very much, but
   then that code hasn't changed at all.
 o The print operation in the target type operations is there to help in
   debugging and will go away eventually
 o There are some other printk's which will also go away once we are sure that
   things are working correctly.
 o I've tagged the old code with PRE_DMFS if you want to use that until this is
   stable.
 o There are no kernel patches for this yet (will fix after lunch... :-)
      o Makefile needs some changes
      o need to EXPORT_SYMBOL(deny_write_access); in ksyms.c

How to use the new interface ?

 mount -t dmfs dmfs /mnt/dm
 cd /mnt/dm
 mkdir fish fish/tank
 cd fish/tank
 cat ~/my.table > table
 cd ..
 ln -s tank ACTIVE

Creates a logical volume called fish and activates a table called tank, if
there is a problem doing the link, look in /mnt/dm/fish/tank/errors to see
what is wrong.

If you see any odd things happening, let me know right away as I'm sure there'll
be one or two things that slipped through my testing.
2001-10-17 11:34:50 +00:00
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
211 changed files with 29836 additions and 7 deletions

1
BUGS Normal file
View File

@ -0,0 +1 @@
LVM2's device-mapper driver and ext3 are incompatible at the moment.

340
COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

483
COPYING.LIB Normal file
View File

@ -0,0 +1,483 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

36
INSTALL Normal file
View File

@ -0,0 +1,36 @@
LVM2 installation
=================
1) Install device-mapper
Ensure the device-mapper has been installed on the machine.
The device-mapper should be in the kernel (look for 'device-mapper'
messages in the kernel logs) and /usr/include/libdevmapper.h should
be present.
The device-mapper is available from:
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
2) Generate custom makefiles.
Run the 'configure' script from the top directory.
If you do not have GNU readline (http://www.gnu.org/directory/readline.html)
installed use
./configure --disable-readline
3) Build and install LVM2.
Run 'make install' from the top directory.
4) Create a configuration file
The tools will work fine without a configuration file being
present, but you ought to review the example file in doc/example.conf.
For example, specifying the devices that LVM2 is to use should
make the tools run more efficiently - and avoid scanning /dev/cdrom!

18
INTRO Normal file
View File

@ -0,0 +1,18 @@
An introduction to LVM2
=======================
Background
Compatibility with LVM1
New features
Missing features
Future enhancements

34
Makefile.in Normal file
View File

@ -0,0 +1,34 @@
#
# Copyright (C) 2001 Sistina Software
#
# This LVM library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This LVM library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this LVM library; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
SUBDIRS = include man lib tools
ifeq ($(MAKECMDGOALS),distclean)
SUBDIRS += test/mm test/device test/format1 test/regex test/filters
endif
include make.tmpl
lib: include
tools: include lib

27
README
View File

@ -1,2 +1,25 @@
This is pretty much empty so far...if you can't see subdirectories,
try 'cvs -f update'
This directory contains a beta release of LMV2, the new version of
the userland LVM tools designed for the new device-mapper for
the Linux kernel.
The device-mapper needs to be installed before compiling these LVM2 tools.
For more information about LVM2 read the INTRO file.
Installation instructions are in INSTALL.
This is beta-quality software, released for testing purposes only.
There is no warranty - see COPYING and COPYING.LIB.
Tarballs are available from:
ftp://ftp.sistina.com/pub/LVM2/tools/
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
To access the CVS tree use:
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs login
CVS password: cvs1
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs checkout LVM2
Mailing list for discussion/bug reports etc.
lvm-devel@sistina.com
Subscribe from http://lists.sistina.com/mailman/listinfo/lvm-devel

29
TODO Normal file
View File

@ -0,0 +1,29 @@
before 2.0
-----------
vgexport
vgimport
snapshots
device-mapper support for 2.5 kernel series
review FIXMEs
extra validation & full consistency checks in format1 with LVM1
partial activation
error message review
locking during metadata changes
format2 with atomic transactions
bidirectional format1/format2 migration tool
persistent minors
stats
pvmove
review tool exit codes for LVM1 compatibility
before 2.1
----------
e2fsadm
lvmdiskscan
lvmsadc
lvmsar
pvdata
vgsplit
vgmknodes

1
VERSION Normal file
View File

@ -0,0 +1 @@
0.95.03-cvs (2002-01-30)

2386
configure vendored Executable file

File diff suppressed because it is too large Load Diff

148
configure.in Normal file
View File

@ -0,0 +1,148 @@
################################################################################
##
## Copyright 1999-2000 Sistina Software, Inc.
##
## This is free software released under the GNU General Public License.
## There is no warranty for this software. See the file COPYING for
## details.
##
## See the file CONTRIBUTORS for a list of contributors.
##
## This file is maintained by:
## AJ Lewis <lewis@sistina.com>
##
## File name: configure.in
##
## Description: Input file for autoconf. Generates the configure script
## that tries to keep everything nice and portable. It also
## simplifies distribution package building considerably.
################################################################################
dnl Process this file with autoconf to produce a configure script.
AC_INIT(lib/device/dev-cache.h)
dnl setup the directory where autoconf has auxilary files
AC_CONFIG_AUX_DIR(autoconf)
dnl Checks for programs.
AC_PROG_AWK
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_RANLIB
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h unistd.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_TYPE_OFF_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_STRUCT_ST_RDEV
AC_HEADER_TIME
dnl -- prefix is /usr by default, the exec_prefix default is setup later
AC_PREFIX_DEFAULT(/usr)
dnl -- setup the ownership of the files
AC_ARG_WITH(user,
[ --with-user=USER Set the owner of installed files ],
[ OWNER="$withval" ],
[ OWNER="root" ])
dnl -- setup the group ownership of the files
AC_ARG_WITH(group,
[ --with-group=GROUP Set the group owner of installed files ],
[ GROUP="$withval" ],
[ GROUP="root" ])
AC_ARG_ENABLE(jobs, [ --enable-jobs=NUM Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=)
dnl Enables staticly linked tools
AC_ARG_ENABLE(static_link, [ --enable-static_link Use this to link the tools to the liblvm library
statically. Default is dynamic linking], STATIC_LINK=$enableval, STATIC_LINK=no)
dnl Disable readline
AC_ARG_ENABLE(readline, [ --disable-readline Disable readline support], \
READLINE=$enableval, READLINE=yes)
dnl Mess with default exec_prefix
if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]];
then exec_prefix="";
fi;
dnl Checks for library functions.
AC_PROG_GCC_TRADITIONAL
AC_TYPE_SIGNAL
AC_FUNC_VPRINTF
AC_CHECK_FUNCS(mkdir rmdir uname)
dnl check for termcap (Shamelessly copied from parted 1.4.17)
if test x$READLINE = xyes; then
AC_SEARCH_LIBS(tgetent, ncurses curses termcap termlib, ,
AC_MSG_ERROR(
termcap could not be found which is required for the
--enable-readline option (which is enabled by default). Either disable readline
support with --disable-readline or download and install termcap from:
ftp.gnu.org/gnu/termcap
Note: if you are using precompiled packages you will also need the development
package as well (which may be called termcap-devel or something similar).
Note: (n)curses also seems to work as a substitute for termcap. This was
not found either - but you could try installing that as well.
)
exit
)
fi
dnl Check for readline (Shamelessly copied from parted 1.4.17)
if test x$READLINE = xyes; then
AC_CHECK_LIB(readline, readline, ,
AC_MSG_ERROR(
GNU Readline could not be found which is required for the
--enable-readline option (which is enabled by default). Either disable readline
support with --disable-readline or download and install readline from:
ftp.gnu.org/gnu/readline
Note: if you are using precompiled packages you will also need the development
package as well (which may be called readline-devel or something similar).
)
exit
)
AC_CHECK_FUNC(rl_completion_matches, HAVE_RL_COMPLETION_MATCHES=yes,
HAVE_RL_COMPLETION_MATCHES=no)
fi
if test "-f VERSION"; then
LVM_VERSION="\"`cat VERSION`\""
else
LVM_VERSION="Unknown"
fi
AC_SUBST(JOBS)
AC_SUBST(STATIC_LINK)
AC_SUBST(READLINE)
AC_SUBST(HAVE_RL_COMPLETION_MATCHES)
AC_SUBST(OWNER)
AC_SUBST(GROUP)
AC_SUBST(LIBS)
AC_SUBST(LVM_VERSION)
dnl First and last lines should not contain files to generate in order to
dnl keep utility scripts running properly
AC_OUTPUT( \
Makefile \
make.tmpl \
include/Makefile \
lib/Makefile \
man/Makefile \
tools/Makefile \
tools/version.h \
test/mm/Makefile \
test/device/Makefile \
test/format1/Makefile \
test/regex/Makefile \
test/filters/Makefile \
)

View File

@ -1 +0,0 @@
Wow! This is really incredible documentation!

137
doc/example.conf Normal file
View File

@ -0,0 +1,137 @@
# This is an example configuration file for the LVM2 system. It
# contains the default settings that would be used if there was no
# /etc/lvm/lvm.conf file.
# Refer to 'man lvm.conf' for further information.
# This section allows the user to configure which block devices should
# be used by the LVM system.
devices {
# where do you want your volume groups to appear ?
dir = "/dev"
# An array of directories that contain the device nodes you wish
# to use with LVM2.
scan = "/dev"
# A very important option, that allows you to tune the LVM2 system
# to just look at a restricted set of devices that you're
# interested in.
# The filter consists of an array of regular expressions. These
# expressions can be delimited by a character of your choice, and
# prefixed with either an 'a' (for accept) or 'r' (for reject).
# ATM you cannot use anchors (^ or $) in your regular expression.
# By default we accept every block device:
filter = "a/.*/"
# When testing I like to work with just loopback devices:
# filter = ["a/loop/", "r/.*/"]
# Or maybe all loops and ide drives except hdc:
# filter =["a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|"]
# The results of all the filtering are cached on disk to avoid
# rescanning dud devices (which can take a very long time). By
# default this cache file is hidden in the /etc/lvm directory, it
# is human readable to aid filter debugging.
cache = "/etc/lvm/.cache"
# You can turn off writing this cache file by setting this to 0.
write_cache_state = 1
}
# A section that allows the user to configure the nature of the
# information that LVM2 reports.
log {
# Where should the log of error and debug messages go ? By
# default there is no log.
#file = "/var/log/lvm2.log"
# Should we overwrite the last log. By default we append.
overwrite = 0
# There are 9 log levels, with 9 being the most verbose.
level = 3
# Controls the messages sent to stdout or stderr while running
# LVM2. There are three levels of verbosity, 3 being the most
# verbose.
verbose = 0
# Should we send log messages through syslog?
# 1 is yes; 0 is no.
syslog = 1
# Choose format of output messages
# Whether or not (1 or 0) to indent messages according to their severity
indent = 1
# Whether or not (1 or 0) to display the command name on each line output
command_names = 0
# A prefix to use before the message text (but after the command name,
# if selected)
prefix = " "
# To make the messages look similar to the original LVM use:
# indent = 0
# command_names = 1
# prefix = " -- "
}
# Configuration of metadata backups and archiving. In LVM2 when we
# talk about a 'backup' we mean making a copy of the metadata for the
# *current* system. The 'archive' contains old metadata configurations.
# Backups are stored in a human readeable text format.
backup {
# Should we maintain a backup of the current metadata configuration ?
# Use 1 for Yes; 0 for No.
# Think very hard before turning this off.
backup = 1
# Where shall we keep it ?
backup_dir = "/etc/lvm/backup"
# Should we maintain an archive of old metadata configurations.
# Use 1 for Yes; 0 for No.
# On by default. Think very hard before turning this off.
archive = 1
# Where should archived files go ?
archive_dir = "/etc/lvm/archive"
# What is the minimum number of archive files you wish to keep ?
retain_min = 10
# What is the minimum time you wish to keep an archive file for ?
retain_days = 30
}
# Settings for the running LVM2 in shell mode.
shell {
# Number of lines of history to store in ~/.lvm_history
history_size = 100
}
# Miscellaneous global settings
global {
# The file creation mask for any files and directories created.
# Interpreted as octal if the first digit is zero.
umask = 077
# Allow other users to read the files
#umask = 022
# Enabling test mode means that no changes to the on disk metadata
# will be made. Equivalent to having the -t option on every
# command. Defaults to off.
test = 0
}

52
doc/pvmove_outline.txt Normal file
View File

@ -0,0 +1,52 @@
Let's say we have an LV, made up of three segments of different PV's,
I've also added in the device major:minor as this will be useful
later:
+-----------------------------+
| PV1 | PV2 | PV3 | 254:3
+----------+---------+--------+
Now our hero decides to PV move PV2 to PV4:
1. Suspend our LV (254:3), this starts queueing all io, and flushes
all pending io. Once the suspend has completed we are free to change
the mapping table.
2. Set up *another* (254:4) device with the mapping table of our LV.
3. Load a new mapping table into (254:3) that has identity targets for
parts that aren't moving, and a mirror target for parts that are.
4. Unsuspend (254:3)
So now we have:
destination of copy
+--------------------->--------------+
| |
+-----------------------------+ + -----------+
| Identity | mirror | Ident. | 254:3 | PV4 |
+----------+---------+--------+ +------------+
| | |
\/ \/ \/
+-----------------------------+
| PV1 | PV2 | PV3 | 254:4
+----------+---------+--------+
Any writes to segment2 of the LV get intercepted by the mirror target
who checks that that chunk has been copied to the new destination, if
it hasn't it queues the initial copy and defers the current io until
it has finished. Then the current io is written to *both* PV2 and the
PV4.
5. When the copying has completed 254:3 is suspended/pending flushed.
6. 254:4 is taken down
7. metadata is updated on disk
8. 254:3 has new mapping table loaded:
+-----------------------------+
| PV1 | PV4 | PV3 | 254:3
+----------+---------+--------+

View File

@ -1 +0,0 @@
The driver directory

29
include/.symlinks Normal file
View File

@ -0,0 +1,29 @@
../lib/activate/activate.h
../lib/config/config.h
../lib/datastruct/bitset.h
../lib/datastruct/btree.h
../lib/datastruct/hash.h
../lib/datastruct/list.h
../lib/datastruct/lvm-types.h
../lib/device/dev-cache.h
../lib/device/device.h
../lib/display/display.h
../lib/filters/filter-composite.h
../lib/filters/filter-persistent.h
../lib/filters/filter-regex.h
../lib/filters/filter.h
../lib/format1/format1.h
../lib/format1/lvm1_label.h
../lib/format_text/format-text.h
../lib/label/label.h
../lib/label/uuid-map.h
../lib/log/log.h
../lib/metadata/metadata.h
../lib/mm/dbg_malloc.h
../lib/mm/pool.h
../lib/mm/xlate.h
../lib/misc/lvm-file.h
../lib/misc/lvm-string.h
../lib/regex/matcher.h
../lib/uuid/uuid.h
../lib/vgcache/vgcache.h

43
include/Makefile.in Normal file
View File

@ -0,0 +1,43 @@
#
# Copyright (C) 2001 Sistina Software
#
# This LVM library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This LVM library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this LVM library; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA
SHELL = /bin/sh
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
LN_S = @LN_S@
all: .symlinks_created
.symlinks_created: .symlinks
find . -maxdepth 1 -type l -exec $(RM) \{\} \;
for i in `cat .symlinks`; do $(LN_S) $$i ; done
touch $@
distclean:
find . -maxdepth 1 -type l -exec $(RM) \{\} \;
$(RM) Makefile .include_symlinks .symlinks_created
clean:
install:
.PHONY: clean distclean all install

63
lib/Makefile.in Normal file
View File

@ -0,0 +1,63 @@
#
# Copyright (C) 2001 Sistina Software (UK) Limited
#
# This file is released under the GPL.
#
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
SOURCES=\
activate/activate.c \
activate/fs.c \
activate/names.c \
config/config.c \
datastruct/bitset.c \
datastruct/btree.c \
datastruct/hash.c \
device/dev-cache.c \
device/dev-io.c \
device/device.c \
display/display.c \
filters/filter-composite.c \
filters/filter-persistent.c \
filters/filter-regex.c \
filters/filter.c \
format1/disk-rep.c \
format1/format1.c \
format1/import-export.c \
format1/import-extents.c \
format1/layout.c \
format1/lvm1_label.c \
format1/vg_number.c \
format_text/archive.c \
format_text/export.c \
format_text/flags.c \
format_text/format-text.c \
format_text/import.c \
label/label.c \
label/uuid-map.c \
log/log.c \
metadata/lv_manip.c \
metadata/merge.c \
metadata/metadata.c \
metadata/pv_map.c \
misc/lvm-file.c \
mm/dbg_malloc.c \
mm/pool.c \
regex/matcher.c \
regex/parse_rx.c \
regex/ttree.c \
uuid/uuid.c \
vgcache/vgcache.c
TARGETS=liblvm.a
include ../make.tmpl
liblvm.a: $(OBJECTS)
$(RM) $@
$(AR) r $@ $(OBJECTS)
$(RANLIB) $@

View File

@ -1 +0,0 @@
Base library directory

452
lib/activate/activate.c Normal file
View File

@ -0,0 +1,452 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "metadata.h"
#include "activate.h"
#include "display.h"
#include "log.h"
#include "fs.h"
#include "lvm-string.h"
#include "names.h"
#include <limits.h>
int library_version(char *version, size_t size)
{
if (!dm_get_library_version(version, size))
return 0;
return 1;
}
static struct dm_task *_setup_task_with_name(struct logical_volume *lv,
const char *lv_name,
int task)
{
char name[128];
struct dm_task *dmt;
if (!(dmt = dm_task_create(task))) {
stack;
return NULL;
}
if (!build_dm_name(name, sizeof(name), lv->vg->name, lv_name)) {
stack;
return NULL;
}
dm_task_set_name(dmt, name);
return dmt;
}
static struct dm_task *_setup_task(struct logical_volume *lv, int task)
{
return _setup_task_with_name(lv, lv->name, task);
}
int driver_version(char *version, size_t size)
{
int r = 0;
struct dm_task *dmt;
log_very_verbose("Getting driver version");
if (!(dmt = dm_task_create(DM_DEVICE_VERSION))) {
stack;
return 0;
}
if (!dm_task_run(dmt))
log_error("Failed to get driver version");
if (!dm_task_get_driver_version(dmt, version, size))
goto out;
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
int lv_info(struct logical_volume *lv, struct dm_info *info)
{
int r = 0;
struct dm_task *dmt;
log_very_verbose("Getting device info for %s", lv->name);
if (!(dmt = _setup_task(lv, DM_DEVICE_INFO))) {
stack;
return 0;
}
if (!dm_task_run(dmt)) {
stack;
goto out;
}
if (!dm_task_get_info(dmt, info)) {
stack;
goto out;
}
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
int lv_rename(const char *old_name, struct logical_volume *lv)
{
int r = 0;
char new_name[PATH_MAX];
struct dm_task *dmt;
if (test_mode())
return 0;
if (!(dmt = _setup_task_with_name(lv, old_name, DM_DEVICE_RENAME))) {
stack;
return 0;
}
if (!build_dm_name(new_name, sizeof(new_name),
lv->vg->name, lv->name)) {
stack;
return 0;
}
if (!dm_task_set_newname(dmt, new_name)) {
stack;
r = 0;
goto end;
}
if (!dm_task_run(dmt)) {
stack;
r = 0;
goto end;
}
fs_rename_lv(old_name, lv);
end:
dm_task_destroy(dmt);
return r;
}
int lv_active(struct logical_volume *lv)
{
int r = -1;
struct dm_info info;
if (!lv_info(lv, &info)) {
stack;
return r;
}
log_very_verbose("%s is%s active", lv->name, info.exists ? "":" not");
return info.exists;
}
int lv_suspended(struct logical_volume *lv)
{
int r = -1;
struct dm_info info;
if (!lv_info(lv, &info)) {
stack;
return r;
}
log_very_verbose("%s is%s suspended", lv->name,
info.suspended ? "":" not");
return info.suspended;
}
int lv_open_count(struct logical_volume *lv)
{
int r = -1;
struct dm_info info;
if (!lv_info(lv, &info)) {
stack;
return r;
}
log_very_verbose("%s is open %d time(s)", lv->name, info.open_count);
return info.open_count;
}
/*
* Emit a target for a given segment.
*/
static int _emit_target(struct dm_task *dmt, struct stripe_segment *seg)
{
char params[1024];
uint64_t esize = seg->lv->vg->extent_size;
uint32_t s, stripes = seg->stripes;
int w = 0, tw = 0, error = 0;
const char *no_space =
"Insufficient space to write target parameters.";
char *filler = "/dev/ioerror";
char *target;
if (stripes == 1) {
if (!seg->area[0].pv) {
target = "error";
error = 1;
}
else
target = "linear";
}
if (stripes > 1) {
target = "striped";
tw = lvm_snprintf(params, sizeof(params), "%u %u ",
stripes, seg->stripe_size);
if (tw < 0) {
log_err(no_space);
return 0;
}
w = tw;
}
if (!error) {
for (s = 0; s < stripes; s++, w += tw) {
if (!seg->area[s].pv)
tw = lvm_snprintf(
params + w, sizeof(params) - w,
"%s 0%s", filler,
s == (stripes - 1) ? "" : " ");
else
tw = lvm_snprintf(
params + w, sizeof(params) - w,
"%s %" PRIu64 "%s",
dev_name(seg->area[s].pv->dev),
(seg->area[s].pv->pe_start +
(esize * seg->area[s].pe)),
s == (stripes - 1) ? "" : " ");
if (tw < 0) {
log_err(no_space);
return 0;
}
}
}
log_very_verbose("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
esize * seg->le, esize * seg->len,
target, params);
if (!dm_task_add_target(dmt, esize * seg->le, esize * seg->len,
target, params)) {
stack;
return 0;
}
return 1;
}
int _load(struct logical_volume *lv, int task)
{
int r = 0;
struct dm_task *dmt;
struct list *segh;
struct stripe_segment *seg;
log_very_verbose("Generating devmapper parameters for %s", lv->name);
if (!(dmt = _setup_task(lv, task))) {
stack;
return 0;
}
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
if (!_emit_target(dmt, seg)) {
log_error("Unable to activate logical volume '%s'",
lv->name);
goto out;
}
}
if (!((lv->status & LVM_WRITE) && (lv->vg->status & LVM_WRITE))) {
if (!dm_task_set_ro(dmt))
log_error("Failed to set %s read-only during "
"activation.", lv->name);
else
log_very_verbose("Activating %s read-only", lv->name);
}
if (!(r = dm_task_run(dmt)))
stack;
log_verbose("Logical volume %s%s activated", lv->name,
r == 1 ? "" : " not");
out:
dm_task_destroy(dmt);
return r;
}
/* FIXME: Always display error msg */
int lv_activate(struct logical_volume *lv)
{
if (test_mode())
return 0;
log_very_verbose("Activating %s", lv->name);
return _load(lv, DM_DEVICE_CREATE) && fs_add_lv(lv);
}
int _suspend(struct logical_volume *lv, int sus)
{
int r;
struct dm_task *dmt;
int task = sus ? DM_DEVICE_SUSPEND : DM_DEVICE_RESUME;
log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", lv->name);
if (!(dmt = _setup_task(lv, task))) {
stack;
return 0;
}
if (!(r = dm_task_run(dmt)))
log_err("Couldn't %s device '%s'", sus ? "suspend" : "resume",
lv->name);
dm_task_destroy(dmt);
return r;
}
int lv_suspend(struct logical_volume *lv)
{
return _suspend(lv, 1);
}
int lv_reactivate(struct logical_volume *lv)
{
int r;
if (test_mode())
return 0;
if (!lv_suspended(lv) && !_suspend(lv, 1)) {
stack;
return 0;
}
r = _load(lv, DM_DEVICE_RELOAD);
if (!_suspend(lv, 0)) {
stack;
return 0;
}
return r;
}
int lv_deactivate(struct logical_volume *lv)
{
int r;
struct dm_task *dmt;
log_very_verbose("Deactivating %s", lv->name);
if (test_mode())
return 0;
if (!(dmt = _setup_task(lv, DM_DEVICE_REMOVE))) {
stack;
return 0;
}
if (!(r = dm_task_run(dmt)))
stack;
dm_task_destroy(dmt);
fs_del_lv(lv);
return r;
}
int activate_lvs_in_vg(struct volume_group *vg)
{
struct list *lvh;
struct logical_volume *lv;
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
count += (!lv_active(lv) && lv_activate(lv));
}
return count;
}
int lv_update_write_access(struct logical_volume *lv)
{
struct dm_info info;
if (!lv_info(lv, &info)) {
stack;
return 0;
}
if (!info.exists || info.suspended)
/* Noop */
return 1;
return lv_reactivate(lv);
}
int deactivate_lvs_in_vg(struct volume_group *vg)
{
struct list *lvh;
struct logical_volume *lv;
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
count += ((lv_active(lv) == 1) && lv_deactivate(lv));
}
return count;
}
int lvs_in_vg_activated(struct volume_group *vg)
{
struct list *lvh;
struct logical_volume *lv;
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
count += (lv_active(lv) == 1);
}
return count;
}
int lvs_in_vg_opened(struct volume_group *vg)
{
struct list *lvh;
struct logical_volume *lv;
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
count += (lv_open_count(lv) == 1);
}
return count;
}

52
lib/activate/activate.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef LVM_ACTIVATE_H
#define LVM_ACTIVATE_H
#include <libdevmapper.h>
/* FIXME Snapshot handling? */
int driver_version(char *version, size_t size);
int library_version(char *version, size_t size);
int lv_active(struct logical_volume *lv);
int lv_suspended(struct logical_volume *lv);
int lv_open_count(struct logical_volume *lv);
int lv_info(struct logical_volume *lv, struct dm_info *info);
int lv_rename(const char *old_name, struct logical_volume *lv);
int lv_activate(struct logical_volume *lv);
int lv_reactivate(struct logical_volume *lv);
int lv_deactivate(struct logical_volume *lv);
int lv_suspend(struct logical_volume *lv);
/*
* Return number of LVs in the VG that are
* active.
*/
int lvs_in_vg_activated(struct volume_group *vg);
int lvs_in_vg_opened(struct volume_group *vg);
/*
* Test for (lv->status & LVM_WRITE)
*/
int lv_update_write_access(struct logical_volume *lv);
/*
* 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

160
lib/activate/fs.c Normal file
View File

@ -0,0 +1,160 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "fs.h"
#include "log.h"
#include "names.h"
#include <libdevmapper.h>
/*
* Lazy programmer: I'm just going to always try
* and create/remove the vg directory, and not say
* anything if it fails.
*/
static int _mk_dir(struct volume_group *vg)
{
char vg_path[PATH_MAX];
if (!build_vg_path(vg_path, sizeof(vg_path),
vg->cmd->dev_dir, vg->name)) {
log_error("Couldn't construct name of volume group directory.");
return 0;
}
log_very_verbose("Creating directory %s", vg_path);
mkdir(vg_path, 0555);
return 1;
}
static int _rm_dir(struct volume_group *vg)
{
char vg_path[PATH_MAX];
if (!build_vg_path(vg_path, sizeof(vg_path),
vg->cmd->dev_dir, vg->name)) {
log_error("Couldn't construct name of volume group dir for %s",
vg->name);
return 0;
}
log_very_verbose("Removing directory %s", vg_path);
rmdir(vg_path);
return 1;
}
static int _mk_link(struct logical_volume *lv)
{
char lv_path[PATH_MAX], link_path[PATH_MAX];
struct stat buf;
if (!build_dm_path(lv_path, sizeof(lv_path), lv->vg->name, lv->name)) {
log_error("Couldn't create destination pathname for "
"logical volume link for %s", lv->name);
return 0;
}
if (!build_lv_link_path(link_path, sizeof(link_path),
lv->vg->cmd->dev_dir,
lv->vg->name, lv->name)) {
log_error("Couldn't create source pathname for "
"logical volume link %s", lv->name);
return 0;
}
if (!lstat(link_path, &buf)) {
if (!S_ISLNK(buf.st_mode)) {
log_error("Symbolic link %s not created: file exists",
link_path);
return 0;
}
if (unlink(link_path) < 0) {
log_sys_error("unlink", link_path);
return 0;
}
}
log_very_verbose("Linking %s to %s", link_path, lv_path);
if (symlink(lv_path, link_path) < 0) {
log_sys_error("symlink", link_path);
return 0;
}
return 1;
}
static int _rm_link(struct logical_volume *lv, const char *lv_name)
{
struct stat buf;
char link_path[PATH_MAX];
if (!lv_name)
lv_name = lv->name;
if (!build_lv_link_path(link_path, sizeof(link_path),
lv->vg->cmd->dev_dir,
lv->vg->name, lv->name)) {
log_error("Couldn't determine link pathname.");
return 0;
}
log_very_verbose("Removing link %s", link_path);
if (lstat(link_path, &buf) || !S_ISLNK(buf.st_mode)) {
log_error("%s not symbolic link - not removing",
link_path);
return 0;
}
if (unlink(link_path) < 0) {
log_sys_error("unlink", link_path);
return 0;
}
return 1;
}
int fs_add_lv(struct logical_volume *lv)
{
if (!_mk_dir(lv->vg) ||
!_mk_link(lv)) {
stack;
return 0;
}
return 1;
}
int fs_del_lv(struct logical_volume *lv)
{
if (!_rm_link(lv, NULL) ||
!_rm_dir(lv->vg)) {
stack;
return 0;
}
return 1;
}
int fs_rename_lv(const char *old_name, struct logical_volume *lv)
{
if (!_rm_link(lv, old_name))
stack;
if (!_mk_link(lv))
stack;
return 1;
}

23
lib/activate/fs.h Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_FS_H
#define _LVM_FS_H
#include "metadata.h"
/*
* These calls, private to the activate unit, set
* up the volume group directory in /dev and the
* symbolic links to the dm device.
*/
int fs_add_lv(struct logical_volume *lv);
int fs_del_lv(struct logical_volume *lv);
int fs_rename_lv(const char *old_name, struct logical_volume *lv);
#endif

88
lib/activate/names.c Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "names.h"
#include "lvm-string.h"
#include "log.h"
#include "limits.h"
#include <libdevmapper.h>
/*
* The volume group name and the logical volume name are
* seperated by a single ':', any colons in the vg name are
* doubled up to form a pair.
*/
int build_dm_name(char *buffer, size_t len,
const char *vg_name, const char *lv_name)
{
char *out;
const char *in;
for (out = buffer, in = vg_name; len && *in; len--) {
if (*in == ':') {
*out++ = ':';
if (!--len)
break;
}
*out++ = *in++;
len--;
}
if (!len)
return 0;
if (lvm_snprintf(out, len, ":%s", lv_name) == -1) {
log_err("Couldn't build logical volume name.");
return 0;
}
return 1;
}
int build_dm_path(char *buffer, size_t len,
const char *vg_name, const char *lv_name)
{
char dev_name[PATH_MAX];
if (!build_dm_name(dev_name, sizeof(dev_name), vg_name, lv_name)) {
stack;
return 0;
}
if (lvm_snprintf(buffer, len, "%s/%s", dm_dir(), dev_name) == -1) {
stack;
return 0;
}
return 1;
}
int build_vg_path(char *buffer, size_t len,
const char *dev_dir, const char *vg_name)
{
if (lvm_snprintf(buffer, len, "%s%s", dev_dir, vg_name) == -1) {
stack;
return 0;
}
return 1;
}
int build_lv_link_path(char *buffer, size_t len, const char *dev_dir,
const char *vg_name, const char *lv_name)
{
if (lvm_snprintf(buffer, len, "%s%s/%s",
dev_dir, vg_name, lv_name) == -1) {
stack;
return 0;
}
return 1;
}

50
lib/activate/names.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_NAMES_H
#define _LVM_NAMES_H
#include <stdlib.h>
/*
* Functions that build up useful paths to devices, sym-links
* etc. Names are passed in as strings, rather than via the
* appropriate metadata structures, so we can use it for renaming
* devices.
*/
/*
* The name of the device-mapper device for a particular LV.
* eg, vg0:music
*/
int build_dm_name(char *buffer, size_t len,
const char *vg_name, const char *lv_name);
/*
* The path of the device-mapper device for a particular LV.
* eg, /dev/device-mapper/vg0:music
*/
int build_dm_path(char *buffer, size_t len,
const char *vg_name, const char *lv_name);
/*
* Path to the volume group directory.
* eg, /dev/vg0
*/
int build_vg_path(char *buffer, size_t len,
const char *dev_dir, const char *vg_name);
/*
* Path to the symbolic link that lives in the volume group
* directory.
* eg, /dev/vg0/music
*/
int build_lv_link_path(char *buffer, size_t len,
const char *dev_dir,
const char *vg_name, const char *lv_name);
#endif

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "table-build.c"
static void _print_run(FILE *fp, struct logical_volume *lv)
{
}
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

674
lib/config/config.c Normal file
View File

@ -0,0 +1,674 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "pool.h"
#include "log.h"
enum {
TOK_INT,
TOK_FLOAT,
TOK_STRING,
TOK_EQ,
TOK_SECTION_B,
TOK_SECTION_E,
TOK_ARRAY_B,
TOK_ARRAY_E,
TOK_IDENTIFIER,
TOK_COMMA,
TOK_EOF
};
struct parser {
const char *fb, *fe; /* file limits */
int t; /* token limits and type */
const char *tb, *te;
int fd; /* descriptor for file being parsed */
int line; /* line number we are on */
struct pool *mem;
};
struct cs {
struct config_file cf;
struct pool *mem;
};
static void _get_token(struct parser *p);
static void _eat_space(struct parser *p);
static struct config_node *_file(struct parser *p);
static struct config_node *_section(struct parser *p);
static struct config_value *_value(struct parser *p);
static struct config_value *_type(struct parser *p);
static int _match_aux(struct parser *p, int t);
static struct config_value *_create_value(struct parser *p);
static struct config_node *_create_node(struct parser *p);
static char *_dup_tok(struct parser *p);
#define MAX_INDENT 32
#define match(t) do {\
if (!_match_aux(p, (t))) {\
log_error("Parse error at line %d: unexpected token", p->line); \
return 0;\
} \
} while(0);
static int _tok_match(const char *str, const char *b, const char *e)
{
while (*str && (b != e)) {
if (*str++ != *b++)
return 0;
}
return !(*str || (b != e));
}
/*
* public interface
*/
struct config_file *create_config_file(void)
{
struct cs *c;
struct pool *mem = pool_create(10 * 1024);
if (!mem) {
stack;
return 0;
}
if (!(c = pool_alloc(mem, sizeof(*c)))) {
stack;
pool_destroy(mem);
return 0;
}
c->mem = mem;
c->cf.root = (struct config_node *)NULL;
return &c->cf;
}
void destroy_config_file(struct config_file *cf)
{
pool_destroy(((struct cs *) cf)->mem);
}
int read_config(struct config_file *cf, const char *file)
{
struct cs *c = (struct cs *) cf;
struct parser *p;
struct stat info;
int r = 1, fd;
if (!(p = pool_alloc(c->mem, sizeof(*p)))) {
stack;
return 0;
}
p->mem = c->mem;
/* memory map the file */
if (stat(file, &info) || S_ISDIR(info.st_mode)) {
log_sys_error("stat", file);
return 0;
}
if ((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 = NULL, *n, *l = NULL;
while (p->t != TOK_EOF) {
if (!(n = _section(p))) {
stack;
return 0;
}
if (!root)
root = n;
else
l->sib = n;
l = n;
}
return root;
}
static struct config_node *_section(struct parser *p)
{
/* IDENTIFIER '{' VALUE* '}' */
struct config_node *root, *n, *l = NULL;
if (!(root = _create_node(p))) {
stack;
return 0;
}
if (!(root->key = _dup_tok(p))) {
stack;
return 0;
}
match (TOK_IDENTIFIER);
if (p->t == TOK_SECTION_B) {
match(TOK_SECTION_B);
while (p->t != TOK_SECTION_E) {
if (!(n = _section(p))) {
stack;
return 0;
}
if (!root->child)
root->child = n;
else
l->sib = n;
l = n;
}
match(TOK_SECTION_E);
} else {
match(TOK_EQ);
if (!(root->v = _value(p))) {
stack;
return 0;
}
}
return root;
}
static struct config_value *_value(struct parser *p)
{
/* '[' TYPE* ']' | TYPE */
struct config_value *h = 0, *l, *ll = 0;
if (p->t == TOK_ARRAY_B) {
match (TOK_ARRAY_B);
while (p->t != TOK_ARRAY_E) {
if (!(l = _type(p))) {
stack;
return 0;
}
if (!h)
h = l;
else
ll->next = l;
ll = l;
if (p->t == TOK_COMMA)
match(TOK_COMMA);
}
match(TOK_ARRAY_E);
} else
h = _type(p);
return h;
}
static struct config_value *_type(struct parser *p)
{
/* [0-9]+ | [0-9]*\.[0-9]* | ".*" */
struct config_value *v = _create_value(p);
switch (p->t) {
case TOK_INT:
v->type = CFG_INT;
v->v.i = strtol(p->tb, 0, 0); /* FIXME: check error */
match(TOK_INT);
break;
case TOK_FLOAT:
v->type = CFG_FLOAT;
v->v.r = strtod(p->tb, 0); /* FIXME: check error */
match(TOK_FLOAT);
break;
case TOK_STRING:
v->type = CFG_STRING;
p->tb++, p->te--; /* strip "'s */
if (!(v->v.str = _dup_tok(p))) {
stack;
return 0;
}
p->te++;
match(TOK_STRING);
break;
default:
log_error("Parse error at line %d: expected a value", p->line);
return 0;
}
return v;
}
static int _match_aux(struct parser *p, int t)
{
if (p->t != t)
return 0;
_get_token(p);
return 1;
}
/*
* tokeniser
*/
static void _get_token(struct parser *p)
{
p->tb = p->te;
_eat_space(p);
if (p->tb == p->fe) {
p->t = TOK_EOF;
return;
}
p->t = TOK_INT; /* fudge so the fall through for
floats works */
switch (*p->te) {
case '{':
p->t = TOK_SECTION_B;
p->te++;
break;
case '}':
p->t = TOK_SECTION_E;
p->te++;
break;
case '[':
p->t = TOK_ARRAY_B;
p->te++;
break;
case ']':
p->t = TOK_ARRAY_E;
p->te++;
break;
case ',':
p->t = TOK_COMMA;
p->te++;
break;
case '=':
p->t = TOK_EQ;
p->te++;
break;
case '"':
p->t = TOK_STRING;
p->te++;
while ((p->te != p->fe) && (*p->te != '"')) {
if ((*p->te == '\\') && (p->te + 1 != p->fe))
p->te++;
p->te++;
}
if (p->te != p->fe)
p->te++;
break;
case '.':
p->t = TOK_FLOAT;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
p->te++;
while (p->te != p->fe) {
if (*p->te == '.') {
if (p->t == TOK_FLOAT)
break;
p->t = TOK_FLOAT;
} else if (!isdigit((int) *p->te))
break;
p->te++;
}
break;
default:
p->t = TOK_IDENTIFIER;
while ((p->te != p->fe) && !isspace(*p->te) &&
(*p->te != '#') && (*p->te != '='))
p->te++;
break;
}
}
static void _eat_space(struct parser *p)
{
while (p->tb != p->fe) {
if (*p->te == '#') {
while ((p->te != p->fe) && (*p->te != '\n'))
p->te++;
p->line++;
}
else if (isspace(*p->te)) {
while ((p->te != p->fe) && isspace(*p->te)) {
if (*p->te == '\n')
p->line++;
p->te++;
}
}
else
return;
p->tb = p->te;
}
}
/*
* memory management
*/
static struct config_value *_create_value(struct parser *p)
{
struct config_value *v = pool_alloc(p->mem, sizeof(*v));
memset(v, 0, sizeof(*v));
return v;
}
static struct config_node *_create_node(struct parser *p)
{
struct config_node *n = pool_alloc(p->mem, sizeof(*n));
memset(n, 0, sizeof(*n));
return n;
}
static char *_dup_tok(struct parser *p)
{
int len = p->te - p->tb;
char *str = pool_alloc(p->mem, len + 1);
if (!str) {
stack;
return 0;
}
strncpy(str, p->tb, len);
str[len] = '\0';
return str;
}
/*
* utility functions
*/
struct config_node *find_config_node(struct config_node *cn,
const char *path, char sep)
{
const char *e;
while (cn) {
/* trim any leading slashes */
while (*path && (*path == sep))
path++;
/* find the end of this segment */
for (e = path; *e && (*e != sep); e++)
;
/* hunt for the node */
while (cn) {
if (_tok_match(cn->key, path, e))
break;
cn = cn->sib;
}
if (cn && *e)
cn = cn->child;
else
break; /* don't move into the last node */
path = e;
}
return cn;
}
const char *
find_config_str(struct config_node *cn,
const char *path, char sep, const char *fail)
{
struct config_node *n = find_config_node(cn, path, sep);
if (n && n->v->type == CFG_STRING) {
log_very_verbose("Setting %s to %s", path, n->v->v.str);
return n->v->v.str;
}
if (fail)
log_very_verbose("%s not found in config: defaulting to %s",
path, fail);
return fail;
}
int find_config_int(struct config_node *cn, const char *path,
char sep, int fail)
{
struct config_node *n = find_config_node(cn, path, sep);
if (n && n->v->type == CFG_INT) {
log_very_verbose("Setting %s to %d", path, n->v->v.i);
return n->v->v.i;
}
log_very_verbose("%s not found in config: defaulting to %d",
path, fail);
return fail;
}
float find_config_float(struct config_node *cn, const char *path,
char sep, float fail)
{
struct config_node *n = find_config_node(cn, path, sep);
if (n && n->v->type == CFG_FLOAT) {
log_very_verbose("Setting %s to %f", path, n->v->v.r);
return n->v->v.r;
}
log_very_verbose("%s not found in config: defaulting to %f",
path, fail);
return fail;
}
static int _str_in_array(const char *str, const char *values[])
{
int i;
for (i = 0; values[i]; i++)
if (!strcasecmp(str, values[i]))
return 1;
return 0;
}
static int _str_to_bool(const char *str, int fail)
{
static const char *_true_values[] = {"y", "yes", "on", "true", NULL};
static const char *_false_values[] = {"n", "no", "off", "false", NULL};
if (_str_in_array(str, _true_values))
return 1;
if (_str_in_array(str, _false_values))
return 0;
return fail;
}
int find_config_bool(struct config_node *cn, const char *path,
char sep, int fail)
{
struct config_node *n = find_config_node(cn, path, sep);
struct config_value *v;
if (!n)
return fail;
v = n->v;
switch (v->type) {
case CFG_INT:
return v->v.i ? 1 : 0;
case CFG_STRING:
return _str_to_bool(v->v.str, fail);
}
return fail;
}
int get_config_uint32(struct config_node *cn, const char *path,
char sep, uint32_t *result)
{
struct config_node *n;
n = find_config_node(cn, path, sep);
if (!n || !n->v || n->v->type != CFG_INT)
return 0;
*result = n->v->v.i;
return 1;
}
int get_config_uint64(struct config_node *cn, const char *path,
char sep, uint64_t *result)
{
struct config_node *n;
n = find_config_node(cn, path, sep);
if (!n || !n->v || n->v->type != CFG_INT)
return 0;
/* FIXME Support 64-bit value! */
*result = (uint64_t) n->v->v.i;
return 1;
}

72
lib/config/config.h Normal file
View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_CONFIG_H
#define _LVM_CONFIG_H
#include <inttypes.h>
enum {
CFG_STRING,
CFG_FLOAT,
CFG_INT,
};
struct config_value {
int type;
union {
int i;
float r;
char *str;
} v;
struct config_value *next; /* for arrays */
};
struct config_node {
char *key;
struct config_node *sib, *child;
struct config_value *v;
};
struct config_file {
struct config_node *root;
};
struct config_file *create_config_file(void);
void destroy_config_file(struct config_file *cf);
int read_config(struct config_file *cf, const char *file);
int write_config(struct config_file *cf, const char *file);
struct config_node *find_config_node(struct config_node *cn,
const char *path, char seperator);
const char *find_config_str(struct config_node *cn,
const char *path, char sep, const char *fail);
int find_config_int(struct config_node *cn, const char *path,
char sep, int fail);
float find_config_float(struct config_node *cn, const char *path,
char sep, float fail);
/*
* Understands (0, ~0), (y, n), (yes, no), (on,
* off), (true, false).
*/
int find_config_bool(struct config_node *cn, const char *path,
char sep, int fail);
int get_config_uint32(struct config_node *cn, const char *path,
char sep, uint32_t *result);
int get_config_uint64(struct config_node *cn, const char *path,
char sep, uint64_t *result);
#endif

79
lib/datastruct/bitset.c Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "bitset.h"
#include "dbg_malloc.h"
#include <stdlib.h>
/* FIXME: calculate this. */
#define INT_SHIFT 5
bitset_t bitset_create(struct pool *mem, unsigned num_bits)
{
int n = (num_bits / BITS_PER_INT) + 2;
int size = sizeof(int) * n;
unsigned *bs = pool_zalloc(mem, size);
if (!bs)
return NULL;
*bs = num_bits;
return bs;
}
void bitset_destroy(bitset_t bs)
{
dbg_free(bs);
}
void bit_union(bitset_t out, bitset_t in1, bitset_t in2)
{
int i;
for(i = (in1[0] / BITS_PER_INT) + 1; i; i--)
out[i] = in1[i] | in2[i];
}
/*
* FIXME: slow
*/
static inline int _test_word(uint32_t test, int bit)
{
while (bit < BITS_PER_INT) {
if (test & (0x1 << bit))
return bit;
bit++;
}
return -1;
}
int bit_get_next(bitset_t bs, int last_bit)
{
int bit, word;
uint32_t test;
last_bit++; /* otherwise we'll return the same bit again */
while(last_bit < bs[0]) {
word = last_bit >> INT_SHIFT;
test = bs[word + 1];
bit = last_bit & (BITS_PER_INT - 1);
if ((bit = _test_word(test, bit)) >= 0)
return (word * BITS_PER_INT) + bit;
last_bit = last_bit - (last_bit & (BITS_PER_INT - 1)) +
BITS_PER_INT;
}
return -1;
}
int bit_get_first(bitset_t bs)
{
return bit_get_next(bs, -1);
}

46
lib/datastruct/bitset.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_BITSET_H
#define _LVM_BITSET_H
#include "lvm-types.h"
#include "pool.h"
#include <limits.h>
#include <string.h>
typedef uint32_t *bitset_t;
bitset_t bitset_create(struct pool *mem, unsigned num_bits);
void bit_union(bitset_t out, bitset_t in1, bitset_t in2);
int bit_get_first(bitset_t bs);
int bit_get_next(bitset_t bs, int last_bit);
#define BITS_PER_INT (sizeof(int) * CHAR_BIT)
#define bit(bs, i) \
(bs[(i / BITS_PER_INT) + 1] & (0x1 << (i & (BITS_PER_INT - 1))))
#define bit_set(bs, i) \
(bs[(i / BITS_PER_INT) + 1] |= (0x1 << (i & (BITS_PER_INT - 1))))
#define bit_clear(bs, i) \
(bs[(i / BITS_PER_INT) + 1] &= ~(0x1 << (i & (BITS_PER_INT - 1))))
#define bit_set_all(bs) \
memset(bs + 1, -1, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
#define bit_clear_all(bs) \
memset(bs + 1, 0, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
#define bit_copy(bs1, bs2) \
memcpy(bs1 + 1, bs2 + 1, ((*bs1 / BITS_PER_INT) + 1) * sizeof(int))
#endif

130
lib/datastruct/btree.c Normal file
View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "btree.h"
#include "log.h"
struct node {
uint32_t key;
struct node *l, *r, *p;
void *data;
};
struct btree {
struct pool *mem;
struct node *root;
};
struct btree *btree_create(struct pool *mem)
{
struct btree *t = pool_alloc(mem, sizeof(*t));
if (t) {
t->mem = mem;
t->root = NULL;
}
return t;
}
/*
* Shuffle the bits in a key, to try and remove
* any ordering.
*/
static uint32_t _shuffle(uint32_t k)
{
#if 1
return ((k & 0xff) << 24 |
(k & 0xff00) << 8 |
(k & 0xff0000) >> 8 |
(k & 0xff000000) >> 24);
#else
return k;
#endif
}
struct node **_lookup(struct node **c, uint32_t key, struct node **p)
{
*p = NULL;
while (*c) {
*p = *c;
if ((*c)->key == key)
break;
if (key < (*c)->key)
c = &(*c)->l;
else
c = &(*c)->r;
}
return c;
}
void *btree_lookup(struct btree *t, uint32_t k)
{
uint32_t key = _shuffle(k);
struct node *p, **c = _lookup(&t->root, key, &p);
return (*c) ? (*c)->data : NULL;
}
int btree_insert(struct btree *t, uint32_t k, void *data)
{
uint32_t key = _shuffle(k);
struct node *p, **c = _lookup(&t->root, key, &p), *n;
if (!*c) {
if (!(n = pool_alloc(t->mem, sizeof(*n)))) {
stack;
return 0;
}
n->key = key;
n->data = data;
n->l = n->r = NULL;
n->p = p;
*c = n;
}
return 1;
}
void *btree_get_data(struct btree_iter *it)
{
return ((struct node *) it)->data;
}
static inline struct node *_left(struct node *n)
{
while (n->l)
n = n->l;
return n;
}
struct btree_iter *btree_first(struct btree *t)
{
if (!t->root)
return NULL;
return (struct btree_iter *) _left(t->root);
}
struct btree_iter *btree_next(struct btree_iter *it)
{
struct node *n = (struct node *) it;
uint32_t k = n->key;
if (n->r)
return (struct btree_iter *) _left(n->r);
do
n = n->p;
while (n && k > n->key);
return (struct btree_iter *) n;
}

26
lib/datastruct/btree.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_BTREE_H
#define _LVM_BTREE_H
#include "lvm-types.h"
#include "pool.h"
struct btree;
struct btree *btree_create(struct pool *mem);
void *btree_lookup(struct btree *t, uint32_t k);
int btree_insert(struct btree *t, uint32_t k, void *data);
struct btree_iter;
void *btree_get_data(struct btree_iter *it);
struct btree_iter *btree_first(struct btree *t);
struct btree_iter *btree_next(struct btree_iter *it);
#endif

226
lib/datastruct/hash.c Normal file
View File

@ -0,0 +1,226 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#include "dbg_malloc.h"
#include "hash.h"
#include "log.h"
struct hash_node {
struct hash_node *next;
void *data;
char key[1];
};
struct hash_table {
int num_nodes;
int num_slots;
struct hash_node **slots;
};
/* Permutation of the Integers 0 through 255 */
static unsigned char _nums[] = {
1, 14,110, 25, 97,174,132,119,138,170,125,118, 27,233,140, 51,
87,197,177,107,234,169, 56, 68, 30, 7,173, 73,188, 40, 36, 65,
49,213,104,190, 57,211,148,223, 48,115, 15, 2, 67,186,210, 28,
12,181,103, 70, 22, 58, 75, 78,183,167,238,157,124,147,172,144,
176,161,141, 86, 60, 66,128, 83,156,241, 79, 46,168,198, 41,254,
178, 85,253,237,250,154,133, 88, 35,206, 95,116,252,192, 54,221,
102,218,255,240, 82,106,158,201, 61, 3, 89, 9, 42,155,159, 93,
166, 80, 50, 34,175,195,100, 99, 26,150, 16,145, 4, 33, 8,189,
121, 64, 77, 72,208,245,130,122,143, 55,105,134, 29,164,185,194,
193,239,101,242, 5,171,126, 11, 74, 59,137,228,108,191,232,139,
6, 24, 81, 20,127, 17, 91, 92,251,151,225,207, 21, 98,113,112,
84,226, 18,214,199,187, 13, 32, 94,220,224,212,247,204,196, 43,
249,236, 45,244,111,182,153,136,129, 90,217,202, 19,165,231, 71,
230,142, 96,227, 62,179,246,114,162, 53,160,215,205,180, 47,109,
44, 38, 31,149,135, 0,216, 52, 63, 23, 37, 69, 39,117,146,184,
163,200,222,235,248,243,219, 10,152,131,123,229,203, 76,120,209
};
static struct hash_node *_create_node(const char *str)
{
/* remember sizeof(n) includes an extra char from key[1],
so not adding 1 to the strlen as you would expect */
struct hash_node *n = dbg_malloc(sizeof(*n) + strlen(str));
if (n)
strcpy(n->key, str);
return n;
}
static unsigned _hash(const char *str)
{
unsigned long int h = 0, g;
while (*str) {
h <<= 4;
h += _nums[(int) *str++];
g = h & ((unsigned long) 0xf << 16u);
if (g) {
h ^= g >> 16u;
h ^= g >> 5u;
}
}
return h;
}
struct hash_table *hash_create(unsigned size_hint)
{
size_t len;
unsigned new_size = 16u;
struct hash_table *hc = dbg_malloc(sizeof(*hc));
if (!hc) {
stack;
return 0;
}
memset(hc, 0, sizeof(*hc));
/* round size hint up to a power of two */
while (new_size < size_hint)
new_size = new_size << 1;
hc->num_slots = new_size;
len = sizeof(*(hc->slots)) * new_size;
if (!(hc->slots = dbg_malloc(len))) {
stack;
goto bad;
}
memset(hc->slots, 0, len);
return hc;
bad:
dbg_free(hc->slots);
dbg_free(hc);
return 0;
}
static void _free_nodes(struct hash_table *t)
{
struct hash_node *c, *n;
int i;
for (i = 0; i < t->num_slots; i++)
for (c = t->slots[i]; c; c = n) {
n = c->next;
dbg_free(c);
}
}
void hash_destroy(struct hash_table *t)
{
_free_nodes(t);
dbg_free(t->slots);
dbg_free(t);
}
static inline struct hash_node **_find(struct hash_table *t, const char *key)
{
unsigned h = _hash(key) & (t->num_slots - 1);
struct hash_node **c;
for(c = &t->slots[h]; *c; c = &((*c)->next))
if(!strcmp(key, (*c)->key))
break;
return c;
}
void *hash_lookup(struct hash_table *t, const char *key)
{
struct hash_node **c = _find(t, key);
return *c ? (*c)->data : 0;
}
int hash_insert(struct hash_table *t, const char *key, void *data)
{
struct hash_node **c = _find(t, key);
if(*c)
(*c)->data = data;
else {
struct hash_node *n = _create_node(key);
if (!n)
return 0;
n->data = data;
n->next = 0;
*c = n;
t->num_nodes++;
}
return 1;
}
void hash_remove(struct hash_table *t, const char *key)
{
struct hash_node **c = _find(t, key);
if (*c) {
struct hash_node *old = *c;
*c = (*c)->next;
dbg_free(old);
t->num_nodes--;
}
}
unsigned hash_get_num_entries(struct hash_table *t)
{
return t->num_nodes;
}
void hash_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_wipe(struct hash_table *t)
{
_free_nodes(t);
memset(t->slots, 0, sizeof(struct hash_node *) * t->num_slots);
t->num_nodes = 0;
}
char *hash_get_key(struct hash_table *t, struct hash_node *n)
{
return n->key;
}
void *hash_get_data(struct hash_table *t, struct hash_node *n)
{
return n->data;
}
static struct hash_node *_next_slot(struct hash_table *t, unsigned int s)
{
struct hash_node *c = NULL;
int i;
for (i = s; i < t->num_slots && !c; i++)
c = t->slots[i];
return c;
}
struct hash_node *hash_get_first(struct hash_table *t)
{
return _next_slot(t, 0);
}
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n)
{
unsigned int h = _hash(n->key) & (t->num_slots - 1);
return n->next ? n->next : _next_slot(t, h + 1);
}

32
lib/datastruct/hash.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the GPL.
*/
#ifndef _LVM_HASH_H
#define _LVM_HASH_H
struct hash_table;
struct hash_node;
typedef void (*iterate_fn)(void *data);
struct hash_table *hash_create(unsigned size_hint);
void hash_destroy(struct hash_table *t);
void hash_wipe(struct hash_table *t);
void *hash_lookup(struct hash_table *t, const char *key);
int hash_insert(struct hash_table *t, const char *key, void *data);
void hash_remove(struct hash_table *t, const char *key);
unsigned hash_get_num_entries(struct hash_table *t);
void hash_iterate(struct hash_table *t, iterate_fn f);
char *hash_get_key(struct hash_table *t, struct hash_node *n);
void *hash_get_data(struct hash_table *t, struct hash_node *n);
struct hash_node *hash_get_first(struct hash_table *t);
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n);
#endif

72
lib/datastruct/list.h Normal file
View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#ifndef _LVM_LIST_H
#define _LVM_LIST_H
#include <assert.h>
struct list {
struct list *n, *p;
};
static inline void list_init(struct list *head) {
head->n = head->p = head;
}
static inline void list_add(struct list *head, struct list *elem) {
assert(head->n);
elem->n = head;
elem->p = head->p;
head->p->n = elem;
head->p = elem;
}
static inline void list_add_h(struct list *head, struct list *elem) {
assert(head->n);
elem->n = head->n;
elem->p = head;
head->n->p = elem;
head->n = elem;
}
static inline void list_del(struct list *elem) {
elem->n->p = elem->p;
elem->p->n = elem->n;
}
static inline int list_empty(struct list *head) {
return head->n == head;
}
#define list_iterate(v, head) \
for (v = (head)->n; v != head; v = v->n)
#define list_iterate_safe(v, t, head) \
for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
static inline int list_size(struct list *head) {
int s = 0;
struct list *v;
list_iterate(v, head)
s++;
return s;
}
#define list_item(v, t) \
((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->list))
/* Given a known element in a known structure, locate the struct list */
#define list_head(v, t, e) \
(((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->list)
#endif

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_TYPES_H
#define _LVM_TYPES_H
#include "list.h"
#include <sys/types.h>
#include <inttypes.h>
struct str_list {
struct list list;
char *str;
};
#endif

402
lib/device/dev-cache.c Normal file
View File

@ -0,0 +1,402 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "dev-cache.h"
#include "log.h"
#include "pool.h"
#include "hash.h"
#include "list.h"
#include "lvm-types.h"
#include "btree.h"
#include "dbg_malloc.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#include <linux/kdev_t.h>
/*
* FIXME: really need to seperate names from the devices since
* multiple names can point to the same device.
*/
struct dev_iter {
struct btree_iter *current;
struct dev_filter *filter;
};
struct dir_list {
struct list list;
char dir[0];
};
static struct {
struct pool *mem;
struct hash_table *names;
struct btree *devices;
int has_scanned;
struct list dirs;
} _cache;
#define _alloc(x) pool_alloc(_cache.mem, (x))
#define _free(x) pool_free(_cache.mem, (x))
static int _insert(const char *path, int rec);
static struct device *_create_dev(dev_t d)
{
struct device *dev;
if (!(dev = _alloc(sizeof(*dev)))) {
stack;
return NULL;
}
list_init(&dev->aliases);
dev->dev = d;
dev->fd = -1;
return dev;
}
static int _add_alias(struct device *dev, const char *path)
{
struct str_list *sl = _alloc(sizeof(*sl));
if (!sl) {
stack;
return 0;
}
if (!(sl->str = pool_strdup(_cache.mem, path))) {
stack;
return 0;
}
list_add(&dev->aliases, &sl->list);
return 1;
}
/*
* Either creates a new dev, or adds an alias to
* an existing dev.
*/
static int _insert_dev(const char *path, dev_t d)
{
struct device *dev;
/* is this device already registered ? */
if (!(dev = (struct device *) btree_lookup(_cache.devices, d))) {
/* create new device */
if (!(dev = _create_dev(d))) {
stack;
return 0;
}
if (!(btree_insert(_cache.devices, d, dev))) {
log_err("Couldn't insert device into binary tree.");
_free(dev);
return 0;
}
}
if (!_add_alias(dev, path)) {
log_err("Couldn't add alias to dev cache.");
return 0;
}
if (!hash_insert(_cache.names, path, dev)) {
log_err("Couldn't add name to hash in dev cache.");
return 0;
}
return 1;
}
static char *_join(const char *dir, const char *name)
{
int len = strlen(dir) + strlen(name) + 2;
char *r = dbg_malloc(len);
if (r)
snprintf(r, len, "%s/%s", dir, name);
return r;
}
/*
* Get rid of extra slashes in the path string.
*/
static void _collapse_slashes(char *str)
{
char *ptr;
int was_slash = 0;
for (ptr = str; *ptr; ptr++) {
if (*ptr == '/') {
if (was_slash)
continue;
was_slash = 1;
} else
was_slash = 0;
*str++ = *ptr;
}
*str = *ptr;
}
static int _insert_dir(const char *dir)
{
int n, dirent_count, r = 1;
struct dirent **dirent;
char *path;
dirent_count = scandir(dir, &dirent, NULL, alphasort);
if (dirent_count > 0) {
for (n = 0; n < dirent_count; n++) {
if (dirent[n]->d_name[0] == '.') {
free(dirent[n]);
continue;
}
if (!(path = _join(dir, dirent[n]->d_name))) {
stack;
return 0;
}
_collapse_slashes(path);
r &= _insert(path, 1);
dbg_free(path);
free(dirent[n]);
}
free(dirent);
}
return r;
}
static int _insert(const char *path, int rec)
{
struct stat info;
int r = 0;
if (stat(path, &info) < 0) {
log_sys_very_verbose("stat", path);
return 0;
}
if (S_ISDIR(info.st_mode)) { /* add a directory */
if (rec)
r = _insert_dir(path);
} else { /* add a device */
if (!S_ISBLK(info.st_mode)) {
log_debug("%s: Not a block device", path);
return 0;
}
if (!_insert_dev(path, info.st_rdev)) {
stack;
return 0;
}
r = 1;
}
return r;
}
static void _full_scan(void)
{
struct list *dh;
if (_cache.has_scanned)
return;
list_iterate(dh, &_cache.dirs) {
struct dir_list *dl = list_item(dh, struct dir_list);
_insert_dir(dl->dir);
};
_cache.has_scanned = 1;
}
int dev_cache_init(void)
{
_cache.names = NULL;
if (!(_cache.mem = pool_create(10 * 1024))) {
stack;
return 0;
}
if (!(_cache.names = hash_create(128))) {
stack;
pool_destroy(_cache.mem);
_cache.mem = 0;
return 0;
}
if (!(_cache.devices = btree_create(_cache.mem))) {
log_err("Couldn't create binary tree for dev-cache.");
goto bad;
}
list_init(&_cache.dirs);
return 1;
bad:
dev_cache_exit();
return 0;
}
void _check_closed(struct device *dev)
{
if (dev->fd >= 0)
log_err("Device '%s' has been left open.", dev_name(dev));
}
static inline void _check_for_open_devices(void)
{
hash_iterate(_cache.names, (iterate_fn)_check_closed);
}
void dev_cache_exit(void)
{
_check_for_open_devices();
pool_destroy(_cache.mem);
if (_cache.names)
hash_destroy(_cache.names);
}
int dev_cache_add_dir(const char *path)
{
struct dir_list *dl;
struct stat st;
if (stat(path, &st)) {
log_error("Ignoring %s: %s", path, strerror(errno));
/* But don't fail */
return 1;
}
if (!S_ISDIR(st.st_mode)) {
log_error("Ignoring %s: Not a directory", path);
return 1;
}
if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1)))
return 0;
strcpy(dl->dir, path);
list_add(&_cache.dirs, &dl->list);
return 1;
}
/* Check cached device name is still valid before returning it */
/* This should be a rare occurrence */
/* FIXME Make rest of code pass/cache struct device instead of dev_name */
const char *dev_name_confirmed(struct device *dev)
{
struct stat buf;
char *name;
int r;
while ((r = stat(name = list_item(dev->aliases.n,
struct str_list)->str, &buf)) ||
(buf.st_rdev != dev->dev)) {
if (r < 0)
log_sys_error("stat", name);
log_error("Path %s no longer valid for device(%d,%d)",
name, (int) MAJOR(dev->dev), (int) MINOR(dev->dev));
/* Remove the incorrect hash entry */
hash_remove(_cache.names, name);
/* Leave list alone if there isn't an alternative name */
/* so dev_name will always find something to return. */
/* Otherwise add the name to the correct device. */
if (list_size(&dev->aliases) > 1) {
list_del(dev->aliases.n);
if (!r)
_insert(name, 0);
continue;
}
log_error("Aborting - please provide new pathname for what "
"used to be %s", name);
return NULL;
}
return dev_name(dev);
}
struct device *dev_cache_get(const char *name, struct dev_filter *f)
{
struct stat buf;
struct device *d = (struct device *) hash_lookup(_cache.names, name);
/* If the entry's wrong, remove it */
if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) {
hash_remove(_cache.names, name);
d = NULL;
}
if (!d) {
_insert(name, 0);
d = (struct device *) hash_lookup(_cache.names, name);
}
return (d && (!f || f->passes_filter(f, d))) ? d : NULL;
}
struct dev_iter *dev_iter_create(struct dev_filter *f)
{
struct dev_iter *di = dbg_malloc(sizeof(*di));
if (!di)
return NULL;
_full_scan();
di->current = btree_first(_cache.devices);
di->filter = f;
return di;
}
void dev_iter_destroy(struct dev_iter *iter)
{
dbg_free(iter);
}
static inline struct device *_iter_next(struct dev_iter *iter)
{
struct device *d = btree_get_data(iter->current);
iter->current = btree_next(iter->current);
return d;
}
struct device *dev_iter_get(struct dev_iter *iter)
{
while (iter->current) {
struct device *d = _iter_next(iter);
if (!iter->filter ||
iter->filter->passes_filter(iter->filter, d))
return d;
}
return NULL;
}

42
lib/device/dev-cache.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_DEV_CACHE_H
#define _LVM_DEV_CACHE_H
#include <sys/types.h>
#include "lvm-types.h"
#include "device.h"
/*
* predicate for devices.
*/
struct dev_filter {
int (*passes_filter)(struct dev_filter *f, struct device *dev);
void (*destroy)(struct dev_filter *f);
void *private;
};
/*
* The global device cache.
*/
int dev_cache_init(void);
void dev_cache_exit(void);
int dev_cache_add_dir(const char *path);
struct device *dev_cache_get(const char *name, struct dev_filter *f);
/*
* Object for iterating through the cache.
*/
struct dev_iter;
struct dev_iter *dev_iter_create(struct dev_filter *f);
void dev_iter_destroy(struct dev_iter *iter);
struct device *dev_iter_get(struct dev_iter *iter);
#endif

235
lib/device/dev-io.c Normal file
View File

@ -0,0 +1,235 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#include "device.h"
#include "lvm-types.h"
#include "log.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h> // UGH!!! for BLKSSZGET
int dev_get_size(struct device *dev, uint64_t *size)
{
int fd;
long s;
const char *name = dev_name(dev);
log_very_verbose("Getting size of %s", name);
if ((fd = open(name, O_RDONLY)) < 0) {
log_sys_error("open", name);
return 0;
}
/* FIXME: add 64 bit ioctl */
if (ioctl(fd, BLKGETSIZE, &s) < 0) {
log_sys_error("ioctl BLKGETSIZE", name);
close(fd);
return 0;
}
close(fd);
*size = (uint64_t) s;
return 1;
}
int dev_get_sectsize(struct device *dev, uint32_t *size)
{
int fd;
int s;
const char *name = dev_name(dev);
log_very_verbose("Getting size of %s", name);
if ((fd = open(name, O_RDONLY)) < 0) {
log_sys_error("open", name);
return 0;
}
if (ioctl(fd, BLKSSZGET, &s) < 0) {
log_sys_error("ioctl BLKSSZGET", name);
close(fd);
return 0;
}
close(fd);
*size = (uint32_t) s;
return 1;
}
int dev_open(struct device *dev, int flags)
{
struct stat buf;
const char *name = dev_name_confirmed(dev);
if (!name) {
stack;
return 0;
}
if (dev->fd >= 0) {
log_error("Device '%s' has already been opened", name);
return 0;
}
if ((stat(name, &buf) < 0) || (buf.st_rdev != dev->dev)) {
log_error("%s: stat failed: Has device name changed?", name);
return 0;
}
if ((dev->fd = open(name, flags)) < 0) {
log_sys_error("open", name);
return 0;
}
if ((fstat(dev->fd, &buf) < 0) || (buf.st_rdev != dev->dev)) {
log_error("%s: fstat failed: Has device name changed?", name);
dev_close(dev);
return 0;
}
return 1;
}
int dev_close(struct device *dev)
{
if (dev->fd < 0) {
log_error("Attempt to close device '%s' "
"which is not open.", dev_name(dev));
return 0;
}
if (close(dev->fd))
log_sys_error("close", dev_name(dev));
dev->fd = -1;
return 1;
}
/*
* FIXME: factor common code out.
*/
int _read(int fd, void *buf, size_t count)
{
size_t n = 0;
int tot = 0;
while (tot < count) {
do
n = read(fd, buf, count - tot);
while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
if (n <= 0)
return tot ? tot : n;
tot += n;
buf += n;
}
return tot;
}
int64_t dev_read(struct device *dev, uint64_t offset,
int64_t len, void *buffer)
{
const char *name = dev_name(dev);
int fd = dev->fd;
if (fd < 0) {
log_err("Attempt to read an unopened device (%s).", name);
return 0;
}
if (lseek(fd, offset, SEEK_SET) < 0) {
log_sys_error("lseek", name);
return 0;
}
return _read(fd, buffer, len);
}
int _write(int fd, const void *buf, size_t count)
{
size_t n = 0;
int tot = 0;
/* Skip all writes */
if (test_mode())
return count;
while (tot < count) {
do
n = write(fd, buf, count - tot);
while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
if (n <= 0)
return tot ? tot : n;
tot += n;
buf += n;
}
return tot;
}
int64_t dev_write(struct device *dev, uint64_t offset,
int64_t len, void *buffer)
{
const char *name = dev_name(dev);
int fd = dev->fd;
if (fd < 0) {
log_error("Attempt to write to unopened device %s", name);
return 0;
}
if (lseek(fd, offset, SEEK_SET) < 0) {
log_sys_error("lseek", name);
return 0;
}
return _write(fd, buffer, len);
}
int dev_zero(struct device *dev, uint64_t offset, int64_t len)
{
int64_t r, s;
char buffer[4096];
const char *name = dev_name(dev);
int fd = dev->fd;
if (fd < 0) {
log_error("Attempt to zero part of an unopened device %s",
name);
return 0;
}
if (lseek(fd, offset, SEEK_SET) < 0) {
log_sys_error("lseek", name);
return 0;
}
memset(buffer, 0, sizeof(buffer));
while (1) {
s = len > sizeof(buffer) ? sizeof(buffer) : len;
r = _write(fd, buffer, s);
if (r <= 0)
break;
len -= r;
if (!len) {
r = 1;
break;
}
}
/* FIXME: Always display error */
return (len == 0);
}

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

58
lib/device/device.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_DEVICE_H
#define _LVM_DEVICE_H
#include "lvm-types.h"
#include "list.h"
/*
* All devices in LVM will be represented by one of these.
* pointer comparisons are valid.
*/
struct device {
struct list aliases; /* struct str_list from lvm-types.h */
dev_t dev;
/* private */
int fd;
};
struct device_list {
struct list list;
struct device *dev;
};
/*
* All io should use these routines.
*/
int dev_get_size(struct device *dev, uint64_t *size);
int dev_get_sectsize(struct device *dev, uint32_t *size);
int dev_open(struct device *dev, int flags);
int dev_close(struct device *dev);
int64_t dev_read(struct device *dev,
uint64_t offset, int64_t len, void *buffer);
int64_t dev_write(struct device *dev,
uint64_t offset, int64_t len, void *buffer);
int dev_zero(struct device *dev, uint64_t offset, int64_t len);
static inline const char *dev_name(struct device *dev) {
return list_item(dev->aliases.n, struct str_list)->str;
}
/* Return a valid device name from the alias list; NULL otherwise */
const char *dev_name_confirmed(struct device *dev);
static inline int is_lvm_partition(const char *name) {
return 1;
}
#endif

521
lib/display/display.c Normal file
View File

@ -0,0 +1,521 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This LVM library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This LVM library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this LVM library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*
*/
#include "metadata.h"
#include "dbg_malloc.h"
#include "log.h"
#include "display.h"
#include "activate.h"
#include "uuid.h"
#include <sys/types.h>
#include <string.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;
}
void pvdisplay_colons(struct physical_volume *pv)
{
char uuid[64];
if (!pv)
return;
if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
stack;
return;
}
log_print("%s:%s:%" PRIu64 ":-1:%u:%u:-1:%" PRIu64 ":%u:%u:%u:%s",
dev_name(pv->dev), pv->vg_name, pv->size,
/* FIXME pv->pv_number, Derive or remove? */
pv->status, /* FIXME Support old or new format here? */
pv->status & ALLOCATABLE_PV, /* FIXME remove? */
/* FIXME pv->lv_cur, Remove? */
pv->pe_size / 2,
pv->pe_count,
pv->pe_count - pv->pe_allocated,
pv->pe_allocated, *uuid ? uuid : "none");
return;
}
void pvdisplay_full(struct physical_volume *pv)
{
char uuid[64];
char *size, *size1; /*, *size2; */
uint64_t pe_free;
if (!pv)
return;
if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
stack;
return;
}
log_print("--- %sPhysical volume ---", pv->pe_size ? "" : "NEW ");
log_print("PV Name %s", dev_name(pv->dev));
log_print("VG Name %s%s", pv->vg_name,
pv->status & EXPORTED_VG ? " (exported)" : "");
size = display_size(pv->size / 2, SIZE_SHORT);
if (pv->pe_size && pv->pe_count) {
size1 = display_size((pv->size - pv->pe_count * pv->pe_size)
/ 2, SIZE_SHORT);
/******** FIXME display LVM on-disk data size
size2 = display_size(pv->size / 2, SIZE_SHORT);
********/
log_print("PV Size %s" " / not usable %s", /* [LVM: %s]", */
size, size1); /* , size2); */
dbg_free(size1);
/* dbg_free(size2); */
} else
log_print("PV Size %s", size);
dbg_free(size);
/*********FIXME Anything use this?
log_print("PV# %u", pv->pv_number);
**********/
pe_free = pv->pe_count - pv->pe_allocated;
if (pv->pe_count && (pv->status & ALLOCATABLE_PV))
log_print("Allocatable yes %s",
(!pe_free && pv->pe_count) ? "(but full)" : "");
else
log_print("Allocatable NO");
/*********FIXME
log_print("Cur LV %u", pv->lv_cur);
*********/
log_print("PE Size (KByte) %" PRIu64, pv->pe_size / 2);
log_print("Total PE %u", pv->pe_count);
log_print("Free PE %" PRIu64, pe_free);
log_print("Allocated PE %u", pv->pe_allocated);
#ifdef LVM_FUTURE
printf("Stale PE %u", pv->pe_stale);
#endif
log_print("PV UUID %s", *uuid ? uuid : "none");
log_print(" ");
return;
}
int pvdisplay_short(struct volume_group *vg, struct physical_volume *pv)
{
if (!pv)
return 0;
log_print("PV Name %s ", dev_name(pv->dev));
/* FIXME pv->pv_number); */
log_print("PV Status %sallocatable",
(pv->status & ALLOCATABLE_PV) ? "" : "NOT ");
log_print("Total PE / Free PE %u / %u",
pv->pe_count, pv->pe_count - pv->pe_allocated);
log_print(" ");
return 0;
}
void lvdisplay_colons(struct logical_volume *lv)
{
int inkernel;
struct dm_info info;
inkernel = lv_info(lv, &info) && info.exists;
log_print("%s%s/%s:%s:%d:%d:-1:%d:%" PRIu64 ":%d:-1:%d:%d:%d:%d",
lv->vg->cmd->dev_dir,
lv->vg->name,
lv->name,
lv->vg->name,
(lv->status & (LVM_READ | LVM_WRITE)) >> 8,
inkernel ? 1 : 0,
/* FIXME lv->lv_number, */
inkernel ? info.open_count : 0, lv->size, lv->le_count,
/* FIXME Add num allocated to struct! lv->lv_allocated_le, */
((lv->status & ALLOC_STRICT) +
(lv->status & ALLOC_CONTIGUOUS) * 2), lv->read_ahead,
inkernel ? info.major : -1,
inkernel ? info.minor : -1
);
return;
}
int lvdisplay_full(struct logical_volume *lv)
{
char *size;
uint32_t alloc;
struct dm_info info;
int inkernel;
inkernel = lv_info(lv, &info) && info.exists;
log_print("--- Logical volume ---");
log_print("LV Name %s%s/%s", lv->vg->cmd->dev_dir,
lv->vg->name, lv->name);
log_print("VG Name %s", lv->vg->name);
log_print("LV Write Access %s",
(lv->status & LVM_WRITE) ? "read/write" : "read only");
/******* FIXME Snapshot
if (lv->status & (LVM_SNAPSHOT_ORG | LVM_SNAPSHOT)) {
if (lvm_tab_vg_read_with_pv_and_lv(vg_name, &vg) < 0) {
ret = -LVM_ELV_SHOW_VG_READ_WITH_PV_AND_LV;
goto lv_show_end;
}
printf("LV snapshot status ");
if (vg_check_active(vg_name) == TRUE) {
vg_t *vg_core;
if ((ret = vg_status_with_pv_and_lv(vg_name, &vg_core)) == 0) {
lv_t *lv_ptr =
vg_core->
lv[lv_get_index_by_name(vg_core, lv->lv_name)];
if (lv_ptr->lv_access & LV_SNAPSHOT) {
if (lv_ptr->lv_status & LV_ACTIVE)
printf("active ");
else
printf("INACTIVE ");
}
if (lv_ptr->lv_access & LV_SNAPSHOT_ORG) {
printf("source of\n");
while (lv_ptr->lv_snapshot_next != NULL) {
lv_ptr = lv_ptr->lv_snapshot_next;
printf(" %s [%s]\n",
lv_ptr->lv_name,
(lv_ptr->
lv_status & LV_ACTIVE) ? "active" :
"INACTIVE");
}
vg_free(vg_core, TRUE);
} else {
printf("destination for %s\n",
lv_ptr->lv_snapshot_org->lv_name);
}
}
} else {
printf("INACTIVE ");
if (lv->lv_access & LV_SNAPSHOT_ORG)
printf("original\n");
else
printf("snapshot\n");
}
}
***********/
if (inkernel && info.suspended)
log_print("LV Status suspended");
else
log_print("LV Status %savailable",
inkernel ? "" : "NOT ");
/********* FIXME lv_number
log_print("LV # %u", lv->lv_number + 1);
************/
if (inkernel)
log_print("# open %u", info.open_count);
/********
#ifdef LVM_FUTURE
printf("Mirror copies %u\n", lv->lv_mirror_copies);
printf("Consistency recovery ");
if (lv->lv_recovery | LV_BADBLOCK_ON)
printf("bad blocks\n");
else
printf("none\n");
printf("Schedule %u\n", lv->lv_schedule);
#endif
********/
size = display_size(lv->size / 2, SIZE_SHORT);
log_print("LV Size %s", size);
dbg_free(size);
log_print("Current LE %u", lv->le_count);
/********** FIXME allocation
log_print("Allocated LE %u", lv->allocated_le);
**********/
/********** FIXME Snapshot
if (lv->lv_access & LV_SNAPSHOT) {
printf("snapshot chunk size %s\n",
(dummy = lvm_show_size(lv->lv_chunk_size / 2, SHORT)));
dbg_free(dummy);
dummy = NULL;
if (lv->lv_remap_end > 0) {
lv_remap_ptr = lv->lv_remap_ptr;
if (lv_remap_ptr > lv->lv_remap_end)
lv_remap_ptr = lv->lv_remap_end;
dummy = lvm_show_size(lv_remap_ptr *
lv->lv_chunk_size / 2, SHORT);
dummy1 = lvm_show_size(lv->lv_remap_end *
lv->lv_chunk_size / 2, SHORT);
printf("Allocated to snapshot %.2f%% [%s/%s]\n",
(float) lv_remap_ptr * 100 / lv->lv_remap_end,
dummy, dummy1);
dbg_free(dummy);
dbg_free(dummy1);
dummy =
lvm_show_size((vg->
lv[lv_get_index_by_number
(vg,
lv->lv_number)]->lv_size -
lv->lv_remap_end * lv->lv_chunk_size) / 2,
SHORT);
printf("Allocated to COW-table %s\n", dummy);
dbg_free(dummy);
}
}
******************/
log_print("Segments %u", list_size(&lv->segments));
/********* FIXME Stripes & stripesize for each segment
log_print("Stripe size (KByte) %u", lv->stripesize / 2);
***********/
/**************
#ifdef LVM_FUTURE
printf("Bad block ");
if (lv->lv_badblock == LV_BADBLOCK_ON)
printf("on\n");
else
printf("off\n");
#endif
***************/
/* FIXME next free == ALLOC_SIMPLE */
alloc = lv->status & (ALLOC_STRICT | ALLOC_CONTIGUOUS);
log_print("Allocation %s%s%s%s",
!(alloc & (ALLOC_STRICT | ALLOC_CONTIGUOUS)) ? "next free" :
"", (alloc == ALLOC_STRICT) ? "strict" : "",
(alloc == ALLOC_CONTIGUOUS) ? "contiguous" : "",
(alloc ==
(ALLOC_STRICT | ALLOC_CONTIGUOUS)) ? "strict/contiguous" :
"");
log_print("Read ahead sectors %u", lv->read_ahead);
/****************
#ifdef LVM_FUTURE
printf("IO Timeout (seconds) ");
if (lv->lv_io_timeout == 0)
printf("default\n\n");
else
printf("%lu\n\n", lv->lv_io_timeout);
#endif
*************/
if (inkernel)
log_print("Block device %d:%d", info.major,
info.minor);
log_print(" ");
return 0;
}
void _display_stripe(struct stripe_segment *seg, int s, const char *pre)
{
uint32_t len = seg->len / seg->stripes;
log_print("%sphysical volume\t%s", pre,
seg->area[s].pv ? dev_name(seg->area[s].pv->dev) : "Missing");
if (seg->area[s].pv)
log_print("%sphysical extents\t%d to %d", pre,
seg->area[s].pe, seg->area[s].pe + len - 1);
}
int lvdisplay_segments(struct logical_volume *lv)
{
int s;
struct list *segh;
struct stripe_segment *seg;
log_print("--- Segments ---");
list_iterate (segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
log_print("logical extent %d to %d:",
seg->le, seg->le + seg->len - 1);
if (seg->stripes == 1)
_display_stripe(seg, 0, " ");
else {
log_print(" stripes\t\t%d", seg->stripes);
log_print(" stripe size\t\t%d", seg->stripe_size);
for (s = 0; s < seg->stripes; s++) {
log_print(" stripe %d:", s);
_display_stripe(seg, s, " ");
}
}
log_print(" ");
}
log_print(" ");
return 1;
}
void vgdisplay_extents(struct volume_group *vg)
{
return;
}
void vgdisplay_full(struct volume_group *vg)
{
uint32_t access;
char *s1;
char uuid[64];
log_print("--- Volume group ---");
log_print("VG Name %s", vg->name);
log_print("System ID %s", vg->system_id);
access = vg->status & (LVM_READ | LVM_WRITE);
log_print("VG Access %s%s%s%s",
access == (LVM_READ | LVM_WRITE) ? "read/write" : "",
access == LVM_READ ? "read" : "",
access == LVM_WRITE ? "write" : "",
access == 0 ? "error" : "");
log_print("VG Status %s%sresizable",
vg->status & EXPORTED_VG ? "exported/" : "",
vg->status & RESIZEABLE_VG ? "" : "NOT ");
/******* FIXME vg number
log_print ("VG # %u\n", vg->vg_number);
********/
if (vg->status & CLUSTERED) {
log_print("Clustered yes");
log_print("Shared %s",
vg->status & SHARED ? "yes" : "no");
}
log_print("MAX LV %u", vg->max_lv);
log_print("Cur LV %u", vg->lv_count);
/****** FIXME Open LVs
log_print ( "Open LV %u", vg->lv_open);
*******/
/****** FIXME Max LV Size
log_print ( "MAX LV Size %s",
( s1 = display_size ( LVM_LV_SIZE_MAX(vg) / 2, SIZE_SHORT)));
free ( s1);
*********/
log_print("Max PV %u", vg->max_pv);
log_print("Cur PV %u", vg->pv_count);
/******* FIXME act PVs
log_print ( "Act PV %u", vg->pv_act);
*********/
s1 = display_size(vg->extent_count * vg->extent_size / 2, SIZE_SHORT);
log_print("VG Size %s", s1);
dbg_free(s1);
s1 = display_size(vg->extent_size / 2, SIZE_SHORT);
log_print("PE Size %s", s1);
dbg_free(s1);
log_print("Total PE %u", vg->extent_count);
s1 =
display_size((vg->extent_count - vg->free_count) *
vg->extent_size / 2, SIZE_SHORT);
log_print("Alloc PE / Size %u / %s",
vg->extent_count - vg->free_count, s1);
dbg_free(s1);
s1 = display_size(vg->free_count * vg->extent_size / 2, SIZE_SHORT);
log_print("Free PE / Size %u / %s", vg->free_count, s1);
dbg_free(s1);
if (!id_write_format(&vg->id, uuid, sizeof(uuid))) {
stack;
return;
}
log_print("VG UUID %s", uuid);
log_print(" ");
return;
}
void vgdisplay_colons(struct volume_group *vg)
{
return;
}
void vgdisplay_short(struct volume_group *vg)
{
char *s1, *s2, *s3;
s1 = display_size(vg->extent_count * vg->extent_size / 2, SIZE_SHORT);
s2 =
display_size((vg->extent_count - vg->free_count) * vg->extent_size /
2, SIZE_SHORT);
s3 = display_size(vg->free_count * vg->extent_size / 2, SIZE_SHORT);
log_print("\"%s\" %-9s [%-9s used / %s free]", vg->name,
/********* FIXME if "open" print "/used" else print "/idle"??? ******/
s1, s2, s3);
dbg_free(s1);
dbg_free(s2);
dbg_free(s3);
return;
}

47
lib/display/display.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This LVM library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This LVM library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this LVM library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*
*/
#ifndef _LVM_DISPLAY_H
#define _LVM_DISPLAY_H
#include "metadata.h"
#include <stdint.h>
typedef enum {SIZE_LONG=0, SIZE_SHORT=1} size_len_t;
/* Specify size in KB */
char *display_size(unsigned long long size, size_len_t sl);
char *display_uuid(char *uuidstr);
void pvdisplay_colons(struct physical_volume *pv);
void pvdisplay_full(struct physical_volume *pv);
int pvdisplay_short(struct volume_group *vg, struct physical_volume *pv);
void lvdisplay_colons(struct logical_volume *lv);
int lvdisplay_segments(struct logical_volume *lv);
int lvdisplay_full(struct logical_volume *lv);
void vgdisplay_extents(struct volume_group *vg);
void vgdisplay_full(struct volume_group *vg);
void vgdisplay_colons(struct volume_group *vg);
void vgdisplay_short(struct volume_group *vg);
#endif

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "filter-composite.h"
#include "dbg_malloc.h"
#include "log.h"
#include <stdarg.h>
static int _and_p(struct dev_filter *f, struct device *dev)
{
struct dev_filter **filters = (struct dev_filter **) f->private;
while (*filters) {
if (!(*filters)->passes_filter(*filters, dev))
return 0;
filters++;
}
return 1;
}
static void _destroy(struct dev_filter *f)
{
struct dev_filter **filters = (struct dev_filter **) f->private;
while (*filters) {
(*filters)->destroy(*filters);
filters++;
}
dbg_free(f->private);
dbg_free(f);
}
struct dev_filter *composite_filter_create(int n, ...)
{
struct dev_filter **filters = dbg_malloc(sizeof(*filters) * (n + 1));
struct dev_filter *cf;
va_list ap;
int i;
if (!filters) {
stack;
return NULL;
}
if (!(cf = dbg_malloc(sizeof(*cf)))) {
stack;
dbg_free(filters);
return NULL;
}
va_start(ap, n);
for (i = 0; i < n; i++) {
struct dev_filter *f = va_arg(ap, struct dev_filter *);
filters[i] = f;
}
filters[i] = NULL;
va_end(ap);
cf->passes_filter = _and_p;
cf->destroy = _destroy;
cf->private = filters;
return cf;
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_FILTER_COMPOSITE_H
#define _LVM_FILTER_COMPOSITE_H
#include "dev-cache.h"
struct dev_filter *composite_filter_create(int n, ...);
#endif

View File

@ -0,0 +1,242 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "config.h"
#include "dev-cache.h"
#include "hash.h"
#include "dbg_malloc.h"
#include "log.h"
#include "filter-persistent.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
struct pfilter {
char *file;
struct hash_table *devices;
struct dev_filter *real;
};
/*
* entries in the table can be in one of these
* states.
*/
#define PF_BAD_DEVICE ((void *) 1)
#define PF_GOOD_DEVICE ((void *) 2)
static int _init_hash(struct pfilter *pf)
{
if (pf->devices)
hash_destroy(pf->devices);
pf->devices = hash_create(128);
return pf->devices ? 1 : 0;
}
int persistent_filter_wipe(struct dev_filter *f)
{
struct pfilter *pf = (struct pfilter *) f->private;
hash_wipe(pf->devices);
return 1;
}
static int _read_array(struct pfilter *pf, struct config_file *cf,
const char *path, void *data)
{
struct config_node *cn;
struct config_value *cv;
if (!(cn = find_config_node(cf->root, path, '/'))) {
log_very_verbose("Couldn't find %s array in '%s'",
path, pf->file);
return 0;
}
/*
* iterate through the array, adding
* devices as we go.
*/
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != CFG_STRING) {
log_verbose("Devices array contains a value "
"which is not a string ... ignoring");
continue;
}
if (!hash_insert(pf->devices, cv->v.str, data))
log_verbose("Couldn't add '%s' to filter ... ignoring",
cv->v.str);
}
return 1;
}
int persistent_filter_load(struct dev_filter *f)
{
struct pfilter *pf = (struct pfilter *) f->private;
int r = 0;
struct config_file *cf;
if (!(cf = create_config_file())) {
stack;
return 0;
}
if (!read_config(cf, pf->file)) {
stack;
goto out;
}
_read_array(pf, cf, "persistent_filter_cache/valid_devices",
PF_GOOD_DEVICE);
_read_array(pf, cf, "persistent_filter_cache/invalid_devices",
PF_BAD_DEVICE);
if (hash_get_num_entries(pf->devices))
r = 1;
out:
destroy_config_file(cf);
return r;
}
static void _write_array(struct pfilter *pf, FILE *fp, const char *path,
void *data)
{
void *d;
int first = 1;
struct hash_node *n;
for (n = hash_get_first(pf->devices); n;
n = hash_get_next(pf->devices, n)) {
d = hash_get_data(pf->devices, n);
if (d != data)
continue;
if (!first)
fprintf(fp, ",\n");
else {
fprintf(fp, "\t%s=[\n", path);
first = 0;
}
fprintf(fp, "\t\t\"%s\"", hash_get_key(pf->devices, n));
}
if (!first)
fprintf(fp, "\n\t]\n");
return;
}
int persistent_filter_dump(struct dev_filter *f)
{
struct pfilter *pf = (struct pfilter *) f->private;
FILE *fp;
if (!hash_get_num_entries(pf->devices)) {
log_very_verbose("Internal persistent device cache empty "
"- not writing to %s", pf->file);
return 0;
}
log_very_verbose("Dumping persistent device cache to %s", pf->file);
fp = fopen(pf->file, "w");
if (!fp) {
log_sys_error("fopen", pf->file);
return 0;
}
fprintf(fp, "# This file is automatically maintained by lvm.\n\n");
fprintf(fp, "persistent_filter_cache {\n");
_write_array(pf, fp, "valid_devices", PF_GOOD_DEVICE);
_write_array(pf, fp, "invalid_devices", PF_BAD_DEVICE);
fprintf(fp, "}\n");
fclose(fp);
return 1;
}
static int _lookup_p(struct dev_filter *f, struct device *dev)
{
struct pfilter *pf = (struct pfilter *) f->private;
void *l = hash_lookup(pf->devices, dev_name(dev));
struct str_list *sl;
struct list *ah;
if (!l) {
l = pf->real->passes_filter(pf->real, dev) ?
PF_GOOD_DEVICE : PF_BAD_DEVICE;
list_iterate(ah, &dev->aliases) {
sl = list_item(ah, struct str_list);
hash_insert(pf->devices, sl->str, l);
}
}
return l == PF_GOOD_DEVICE;
}
static void _destroy(struct dev_filter *f)
{
struct pfilter *pf = (struct pfilter *) f->private;
hash_destroy(pf->devices);
dbg_free(pf->file);
pf->real->destroy(pf->real);
dbg_free(pf);
dbg_free(f);
}
struct dev_filter *persistent_filter_create(struct dev_filter *real,
const char *file)
{
struct pfilter *pf;
struct dev_filter *f = NULL;
if (!(pf = dbg_malloc(sizeof(*pf)))) {
stack;
return NULL;
}
memset(pf, 0, sizeof(*pf));
if (!(pf->file = dbg_malloc(strlen(file) + 1))) {
stack;
goto bad;
}
strcpy(pf->file, file);
pf->real = real;
if (!(_init_hash(pf))) {
log_error("Couldn't create hash table for persistent filter.");
goto bad;
}
if (!(f = dbg_malloc(sizeof(*f)))) {
stack;
goto bad;
}
f->passes_filter = _lookup_p;
f->destroy = _destroy;
f->private = pf;
return f;
bad:
dbg_free(pf->file);
if (pf->devices)
hash_destroy(pf->devices);
dbg_free(pf);
dbg_free(f);
return NULL;
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_FILTER_PERSISTENT_H
#define _LVM_FILTER_PERSISTENT_H
#include "dev-cache.h"
struct dev_filter *persistent_filter_create(struct dev_filter *f,
const char *file);
int persistent_filter_wipe(struct dev_filter *f);
int persistent_filter_load(struct dev_filter *f);
int persistent_filter_dump(struct dev_filter *f);
#endif

228
lib/filters/filter-regex.c Normal file
View File

@ -0,0 +1,228 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "pool.h"
#include "filter-regex.h"
#include "matcher.h"
#include "device.h"
#include "bitset.h"
#include "log.h"
#include "list.h"
struct rfilter {
struct pool *mem;
bitset_t accept;
struct matcher *engine;
};
static int _extract_pattern(struct pool *mem, const char *pat,
char **regex, bitset_t accept, int index)
{
char sep, *r, *ptr;
/*
* is this an accept or reject pattern
*/
switch (*pat) {
case 'a':
bit_set(accept, index);
break;
case 'r':
bit_clear(accept, index);
break;
default:
log_info("pattern must begin with 'a' or 'r'");
return 0;
}
pat++;
/*
* get the seperator
*/
switch (*pat) {
case '(':
sep = ')';
break;
case '[':
sep = ']';
break;
case '{':
sep = '}';
break;
default:
sep = *pat;
}
pat++;
/*
* copy the regex
*/
if (!(r = pool_strdup(mem, pat))) {
stack;
return 0;
}
/*
* trim the trailing character, having checked it's sep.
*/
ptr = r + strlen(r) - 1;
if (*ptr != sep) {
log_info("invalid seperator at end of regex");
return 0;
}
*ptr = '\0';
regex[index] = r;
return 1;
}
static int _build_matcher(struct rfilter *rf, struct config_value *val)
{
struct pool *scratch;
struct config_value *v;
char **regex;
int count = 0, i, r = 0;
if (!(scratch = pool_create(1024))) {
stack;
return 0;
}
/*
* count how many patterns we have.
*/
for (v = val; v; v = v->next) {
if (v->type != CFG_STRING) {
log_info("filter patterns must be enclosed in quotes");
goto out;
}
count++;
}
/*
* allocate space for them
*/
if (!(regex = pool_alloc(scratch, sizeof(*regex) * count))) {
stack;
goto out;
}
/*
* create the accept/reject bitset
*/
rf->accept = bitset_create(rf->mem, count);
/*
* fill the array back to front because we
* want the opposite precedence to what
* the matcher gives.
*/
for (v = val, i = count - 1; v; v = v->next, i--)
if (!_extract_pattern(scratch, v->v.str,
regex, rf->accept, i)) {
log_info("invalid filter pattern");
goto out;
}
/*
* build the matcher.
*/
if (!(rf->engine = matcher_create(rf->mem,
(const char **) regex, count)))
stack;
r = 1;
out:
pool_destroy(scratch);
return r;
}
static int _accept_p(struct dev_filter *f, struct device *dev)
{
struct list *ah;
int m, first = 1, rejected = 0;
struct rfilter *rf = (struct rfilter *) f->private;
struct str_list *sl;
list_iterate(ah, &dev->aliases) {
sl = list_item(ah, struct str_list);
m = matcher_run(rf->engine, sl->str);
if (m >= 0) {
if (bit(rf->accept, m)) {
if (!first) {
list_del(&sl->list);
list_add_h(&dev->aliases, &sl->list);
}
return 1;
}
rejected = 1;
}
first = 0;
}
/*
* pass everything that doesn't match
* anything.
*/
return !rejected;
}
static void _destroy(struct dev_filter *f)
{
struct rfilter *rf = (struct rfilter *) f->private;
pool_destroy(rf->mem);
}
struct dev_filter *regex_filter_create(struct config_value *patterns)
{
struct pool *mem = pool_create(10 * 1024);
struct rfilter *rf;
struct dev_filter *f;
if (!mem) {
stack;
return NULL;
}
if (!(rf = pool_alloc(mem, sizeof(*rf)))) {
stack;
goto bad;
}
rf->mem = mem;
if (!_build_matcher(rf, patterns)) {
stack;
goto bad;
}
if (!(f = pool_zalloc(mem, sizeof(*f)))) {
stack;
goto bad;
}
f->passes_filter = _accept_p;
f->destroy = _destroy;
f->private = rf;
return f;
bad:
pool_destroy(mem);
return NULL;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_FILTER_REGEX_H
#define _LVM_FILTER_REGEX_H
#include "config.h"
#include "dev-cache.h"
/*
* patterns must be an array of strings of the form:
* [ra]<sep><regex><sep>, eg,
* r/cdrom/ - reject cdroms
* a|loop/[0-4]| - accept loops 0 to 4
* r|.*| - reject everything else
*/
struct dev_filter *regex_filter_create(struct config_value *patterns);
#endif

187
lib/filters/filter.c Normal file
View File

@ -0,0 +1,187 @@
/*
* Copyright (C) 2001 Sistina Software
*
* lvm is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* lvm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "dbg_malloc.h"
#include "log.h"
#include "dev-cache.h"
#include "filter.h"
#include "lvm-string.h"
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <linux/kdev_t.h>
#define NUMBER_OF_MAJORS 256
typedef struct {
char *name;
int max_partitions;
} device_info_t;
static int _md_major = -1;
static device_info_t device_info[] = {
{"ide", 16}, /* IDE disk */
{"sd", 16}, /* SCSI disk */
{"md", 16}, /* Multiple Disk driver (SoftRAID) */
{"loop", 16}, /* Loop device */
{"dasd", 4}, /* DASD disk (IBM S/390, zSeries) */
{"dac960", 8}, /* DAC960 */
{"nbd", 16}, /* Network Block Device */
{"ida", 16}, /* Compaq SMART2 */
{"cciss", 16}, /* Compaq CCISS array */
{"ubd", 16}, /* User-mode virtual block device */
{"ataraid", 16}, /* ATA Raid */
{NULL, 0}
};
static int *scan_proc_dev(const char *proc);
static int passes_lvm_type_device_filter(struct dev_filter *f,
struct device *dev)
{
int fd;
const char *name = dev_name(dev);
/* Is this a recognised device type? */
if (!(((int *) f->private)[MAJOR(dev->dev)]))
return 0;
/* Check it's accessible */
if ((fd = open(name, O_RDONLY)) < 0) {
log_debug("Unable to open %s: %s", name, strerror(errno));
return 0;
}
close(fd);
return 1;
}
struct dev_filter *lvm_type_filter_create(const char *proc)
{
struct dev_filter *f;
if (!(f = dbg_malloc(sizeof (struct dev_filter)))) {
log_error("LVM type filter allocation failed");
return NULL;
}
f->passes_filter = passes_lvm_type_device_filter;
f->destroy = lvm_type_filter_destroy;
if (!(f->private = scan_proc_dev(proc)))
return NULL;
return f;
}
int md_major(void)
{
return _md_major;
}
void lvm_type_filter_destroy(struct dev_filter *f)
{
dbg_free(f->private);
dbg_free(f);
return;
}
static int *scan_proc_dev(const char *proc)
{
char line[80];
char proc_devices[PATH_MAX];
FILE *pd = NULL;
int ret = 0;
int i, j = 0;
int line_maj = 0;
int blocksection = 0;
int dev_len = 0;
int *max_partitions_by_major;
if (!(max_partitions_by_major = dbg_malloc(sizeof (int) * NUMBER_OF_MAJORS))) {
log_error("Filter failed to allocate max_partitions_by_major");
return NULL;
}
if (lvm_snprintf(proc_devices, sizeof(proc_devices),
"%s/devices", proc) < 0) {
log_error("Failed to create /proc/devices string");
return NULL;
}
if (!(pd = fopen(proc_devices, "r"))) {
log_sys_error("fopen", proc_devices);
return NULL;
}
memset(max_partitions_by_major, 0, sizeof (int) * NUMBER_OF_MAJORS);
while (fgets(line, 80, pd) != NULL) {
i = 0;
while (line[i] == ' ' && line[i] != '\0')
i++;
/* If it's not a number it may be name of section */
line_maj = atoi(((char *) (line + i)));
if (!line_maj) {
blocksection = (line[i] == 'B') ? 1 : 0;
continue;
}
/* We only want block devices ... */
if (!blocksection)
continue;
/* Find the start of the device major name */
while (line[i] != ' ' && line[i] != '\0')
i++;
while (line[i] == ' ' && line[i] != '\0')
i++;
/* Look for md device */
if (!strncmp("md", line + i, 2) && isspace(*(line + i + 2)))
_md_major = line_maj;
/* Go through the valid device names and if there is a
match store max number of partitions */
for (j = 0; device_info[j].name != NULL; j++) {
dev_len = strlen(device_info[j].name);
if (dev_len <= strlen(line + i)
&& !strncmp(device_info[j].name, line + i, dev_len)
&& (line_maj < NUMBER_OF_MAJORS)) {
max_partitions_by_major[line_maj] =
device_info[j].max_partitions;
ret++;
break;
}
}
}
fclose(pd);
return max_partitions_by_major;
}

31
lib/filters/filter.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2001 Sistina Software
*
* lvm is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* lvm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#ifndef _LVM_FILTER_H
#define _LVM_FILTER_H
struct dev_filter *lvm_type_filter_create();
void lvm_type_filter_destroy(struct dev_filter *f);
int md_major(void);
#endif

656
lib/format1/disk-rep.c Normal file
View File

@ -0,0 +1,656 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "disk-rep.h"
#include "dbg_malloc.h"
#include "pool.h"
#include "xlate.h"
#include "log.h"
#include "vgcache.h"
#include "filter.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/kdev_t.h>
#define fail do {stack; return 0;} while(0)
#define xx16(v) disk->v = xlate16(disk->v)
#define xx32(v) disk->v = xlate32(disk->v)
#define xx64(v) disk->v = xlate64(disk->v)
/*
* Functions to perform the endian conversion
* between disk and core. The same code works
* both ways of course.
*/
static void _xlate_pvd(struct pv_disk *disk)
{
xx16(version);
xx32(pv_on_disk.base);
xx32(pv_on_disk.size);
xx32(vg_on_disk.base);
xx32(vg_on_disk.size);
xx32(pv_uuidlist_on_disk.base);
xx32(pv_uuidlist_on_disk.size);
xx32(lv_on_disk.base);
xx32(lv_on_disk.size);
xx32(pe_on_disk.base);
xx32(pe_on_disk.size);
xx32(pv_major);
xx32(pv_number);
xx32(pv_status);
xx32(pv_allocatable);
xx32(pv_size);
xx32(lv_cur);
xx32(pe_size);
xx32(pe_total);
xx32(pe_allocated);
xx32(pe_start);
}
static void _xlate_lvd(struct lv_disk *disk)
{
xx32(lv_access);
xx32(lv_status);
xx32(lv_open);
xx32(lv_dev);
xx32(lv_number);
xx32(lv_mirror_copies);
xx32(lv_recovery);
xx32(lv_schedule);
xx32(lv_size);
xx32(lv_snapshot_minor);
xx16(lv_chunk_size);
xx16(dummy);
xx32(lv_allocated_le);
xx32(lv_stripes);
xx32(lv_stripesize);
xx32(lv_badblock);
xx32(lv_allocation);
xx32(lv_io_timeout);
xx32(lv_read_ahead);
}
static void _xlate_vgd(struct vg_disk *disk)
{
xx32(vg_number);
xx32(vg_access);
xx32(vg_status);
xx32(lv_max);
xx32(lv_cur);
xx32(lv_open);
xx32(pv_max);
xx32(pv_cur);
xx32(pv_act);
xx32(dummy);
xx32(vgda);
xx32(pe_size);
xx32(pe_total);
xx32(pe_allocated);
xx32(pvg_total);
}
static void _xlate_extents(struct pe_disk *extents, int count)
{
int i;
for (i = 0; i < count; i++) {
extents[i].lv_num = xlate16(extents[i].lv_num);
extents[i].le_num = xlate16(extents[i].le_num);
}
}
/*
* Handle both minor metadata formats.
*/
static int _munge_formats(struct pv_disk *pvd)
{
uint32_t pe_start;
switch (pvd->version) {
case 1:
pvd->pe_start = ((pvd->pe_on_disk.base +
pvd->pe_on_disk.size) / SECTOR_SIZE);
break;
case 2:
pvd->version = 1;
pe_start = pvd->pe_start * SECTOR_SIZE;
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
break;
default:
return 0;
}
return 1;
}
int read_pvd(struct device *dev, struct pv_disk *pvd)
{
if (dev_read(dev, 0, sizeof(*pvd), pvd) != sizeof(*pvd)) {
log_very_verbose("Failed to read PV data from %s",
dev_name(dev));
return 0;
}
_xlate_pvd(pvd);
if (pvd->id[0] != 'H' || pvd->id[1] != 'M') {
log_very_verbose("%s does not have a valid PV identifier",
dev_name(dev));
return 0;
}
if (!_munge_formats(pvd)) {
log_very_verbose("Unknown metadata version %d found on %s",
pvd->version, dev_name(dev));
return 0;
}
return 1;
}
static int _read_lvd(struct device *dev, ulong pos, struct lv_disk *disk)
{
if (dev_read(dev, pos, sizeof(*disk), disk) != sizeof(*disk))
fail;
_xlate_lvd(disk);
return 1;
}
static int _read_vgd(struct disk_list *data)
{
struct vg_disk *vgd = &data->vgd;
ulong pos = data->pvd.vg_on_disk.base;
if (dev_read(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
fail;
_xlate_vgd(vgd);
return 1;
}
static int _read_uuids(struct disk_list *data)
{
int num_read = 0;
struct uuid_list *ul;
char buffer[NAME_LEN];
ulong pos = data->pvd.pv_uuidlist_on_disk.base;
ulong end = pos + data->pvd.pv_uuidlist_on_disk.size;
while (pos < end && num_read < data->vgd.pv_cur) {
if (dev_read(data->dev, pos, sizeof(buffer), buffer) !=
sizeof(buffer))
fail;
if (!(ul = pool_alloc(data->mem, sizeof(*ul))))
fail;
memcpy(ul->uuid, buffer, NAME_LEN);
ul->uuid[NAME_LEN - 1] = '\0';
list_add(&data->uuids, &ul->list);
pos += NAME_LEN;
num_read++;
}
return 1;
}
static inline int _check_lvd(struct lv_disk *lvd)
{
return !(lvd->lv_name[0] == '\0');
}
static int _read_lvs(struct disk_list *data)
{
int i, read = 0;
ulong pos;
struct lvd_list *ll;
struct vg_disk *vgd = &data->vgd;
for (i = 0; (i < vgd->lv_max) && (read < vgd->lv_cur); i++) {
pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
ll = pool_alloc(data->mem, sizeof(*ll));
if (!ll)
fail;
if (!_read_lvd(data->dev, pos, &ll->lvd))
fail;
if (!_check_lvd(&ll->lvd))
continue;
read++;
list_add(&data->lvds, &ll->list);
}
return 1;
}
static int _read_extents(struct disk_list *data)
{
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
struct pe_disk *extents = pool_alloc(data->mem, len);
ulong pos = data->pvd.pe_on_disk.base;
if (!extents)
fail;
if (dev_read(data->dev, pos, len, extents) != len)
fail;
_xlate_extents(extents, data->pvd.pe_total);
data->extents = extents;
return 1;
}
/*
* If exported, remove "PV_EXP" from end of VG name
*/
static void _munge_exported_vg(struct disk_list *data)
{
int l, s;
/* Return if PV not in a VG or VG not exported */
if ((!*data->pvd.vg_name) ||
!(data->vgd.vg_status & VG_EXPORTED))
return;
l = strlen(data->pvd.vg_name);
s = sizeof(EXPORTED_TAG);
if (!strncmp(data->pvd.vg_name + l - s + 1, EXPORTED_TAG, s))
data->pvd.vg_name[l - s + 1] = '\0';
data->pvd.pv_status |= VG_EXPORTED;
}
static struct disk_list *__read_disk(struct device *dev, struct pool *mem,
const char *vg_name)
{
struct disk_list *data = pool_alloc(mem, sizeof(*data));
const char *name = dev_name(dev);
if (!data) {
stack;
return NULL;
}
data->dev = dev;
data->mem = mem;
list_init(&data->uuids);
list_init(&data->lvds);
if (!read_pvd(dev, &data->pvd)) {
stack;
goto bad;
}
/*
* is it an orphan ?
*/
if (!*data->pvd.vg_name) {
log_very_verbose("%s is not a member of any VG", name);
/* Update VG cache */
vgcache_add(data->pvd.vg_name, dev);
return (vg_name) ? NULL : data;
}
if (!_read_vgd(data)) {
log_error("Failed to read VG data from PV (%s)", name);
goto bad;
}
/* If VG is exported, set VG name back to the real name */
_munge_exported_vg(data);
/* Update VG cache with what we found */
vgcache_add(data->pvd.vg_name, dev);
if (vg_name && strcmp(vg_name, data->pvd.vg_name)) {
log_very_verbose("%s is not a member of the VG %s",
name, vg_name);
goto bad;
}
if (!_read_uuids(data)) {
log_error("Failed to read PV uuid list from %s", name);
goto bad;
}
if (!_read_lvs(data)) {
log_error("Failed to read LV's from %s", name);
goto bad;
}
if (!_read_extents(data)) {
log_error("Failed to read extents from %s", name);
goto bad;
}
log_very_verbose("Found %s in %sVG %s", name,
(data->vgd.vg_status & VG_EXPORTED) ? "exported " : "",
data->pvd.vg_name);
return data;
bad:
pool_free(data->mem, data);
return NULL;
}
struct disk_list *read_disk(struct device *dev, struct pool *mem,
const char *vg_name)
{
struct disk_list *r;
if (!dev_open(dev, O_RDONLY)) {
stack;
return NULL;
}
r = __read_disk(dev, mem, vg_name);
if (!dev_close(dev))
stack;
return r;
}
static void _add_pv_to_list(struct list *head, struct disk_list *data)
{
struct list *pvdh;
struct pv_disk *pvd;
list_iterate(pvdh, head) {
pvd = &list_item(pvdh, struct disk_list)->pvd;
if (!strncmp(data->pvd.pv_uuid, pvd->pv_uuid,
sizeof(pvd->pv_uuid))) {
if (MAJOR(data->dev->dev) != md_major()) {
log_very_verbose("Ignoring duplicate PV %s on "
"%s", pvd->pv_uuid,
dev_name(data->dev));
return;
}
log_very_verbose("Duplicate PV %s - using md %s",
pvd->pv_uuid, dev_name(data->dev));
list_del(pvdh);
break;
}
}
list_add(head, &data->list);
}
/*
* Build a list of pv_d's structures, allocated from mem.
* We keep track of the first object allocated form the pool
* so we can free off all the memory if something goes wrong.
*/
int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
struct pool *mem, struct list *head)
{
struct dev_iter *iter;
struct device *dev;
struct disk_list *data = NULL;
struct list *pvdh, *pvdh2;
/* Fast path if we already saw this VG and cached the list of PVs */
if ((pvdh = vgcache_find(vg_name))) {
list_iterate(pvdh2, pvdh) {
dev = list_item(pvdh2, struct pvdev_list)->dev;
if (!(data = read_disk(dev, mem, vg_name)))
break;
_add_pv_to_list(head, data);
}
/* Did we find the whole VG? */
if (!vg_name || !*vg_name ||
(data && *data->pvd.vg_name &&
list_size(head) == data->vgd.pv_cur))
return 1;
/* Something changed. Remove the hints. */
list_init(head);
vgcache_del(vg_name);
}
if (!(iter = dev_iter_create(filter))) {
log_error("read_pvs_in_vg: dev_iter_create failed");
return 0;
}
/* Otherwise do a complete scan */
for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
if ((data = read_disk(dev, mem, vg_name))) {
_add_pv_to_list(head, data);
}
}
dev_iter_destroy(iter);
if (list_empty(head))
return 0;
return 1;
}
static int _write_vgd(struct disk_list *data)
{
struct vg_disk *vgd = &data->vgd;
ulong pos = data->pvd.vg_on_disk.base;
_xlate_vgd(vgd);
if (dev_write(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
fail;
_xlate_vgd(vgd);
return 1;
}
static int _write_uuids(struct disk_list *data)
{
struct uuid_list *ul;
struct list *uh;
ulong pos = data->pvd.pv_uuidlist_on_disk.base;
ulong end = pos + data->pvd.pv_uuidlist_on_disk.size;
list_iterate(uh, &data->uuids) {
if (pos >= end) {
log_error("Too many uuids to fit on %s",
dev_name(data->dev));
return 0;
}
ul = list_item(uh, struct uuid_list);
if (dev_write(data->dev, pos, NAME_LEN, ul->uuid) != NAME_LEN)
fail;
pos += NAME_LEN;
}
return 1;
}
static int _write_lvd(struct device *dev, ulong pos, struct lv_disk *disk)
{
_xlate_lvd(disk);
if (dev_write(dev, pos, sizeof(*disk), disk) != sizeof(*disk))
fail;
_xlate_lvd(disk);
return 1;
}
static int _write_lvs(struct disk_list *data)
{
struct list *lvh;
ulong pos;
pos = data->pvd.lv_on_disk.base;
if (!dev_zero(data->dev, pos, data->pvd.lv_on_disk.size)) {
log_error("Couldn't zero lv area on device '%s'",
dev_name(data->dev));
return 0;
}
list_iterate(lvh, &data->lvds) {
struct lvd_list *ll = list_item(lvh, struct lvd_list);
if (!_write_lvd(data->dev, pos, &ll->lvd))
fail;
pos += sizeof(struct lv_disk);
}
return 1;
}
static int _write_extents(struct disk_list *data)
{
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
struct pe_disk *extents = data->extents;
ulong pos = data->pvd.pe_on_disk.base;
_xlate_extents(extents, data->pvd.pe_total);
if (dev_write(data->dev, pos, len, extents) != len)
fail;
_xlate_extents(extents, data->pvd.pe_total);
return 1;
}
static int _write_pvd(struct disk_list *data)
{
char *buf;
ulong pos = data->pvd.pv_on_disk.base;
ulong size = data->pvd.pv_on_disk.size;
if(size < sizeof(struct pv_disk)) {
log_error("Invalid PV structure size.");
return 0;
}
/* Make sure that the gap between the PV structure and
the next one is zeroed in order to make non LVM tools
happy (idea from AED) */
buf = dbg_malloc(size);
if(!buf) {
log_err("Couldn't allocate temporary PV buffer.");
return 0;
}
memset(buf, 0, size);
memcpy(buf, &data->pvd, sizeof(struct pv_disk));
_xlate_pvd((struct pv_disk *)buf);
if (dev_write(data->dev, pos, size, buf) != size) {
dbg_free(buf);
fail;
}
dbg_free(buf);
return 1;
}
/*
* assumes the device has been opened.
*/
static int __write_all_pvd(struct disk_list *data)
{
const char *pv_name = dev_name(data->dev);
if (!_write_pvd(data)) {
log_error("Failed to write PV structure onto %s", pv_name);
return 0;
}
if (!test_mode())
vgcache_add(data->pvd.vg_name, data->dev);
/*
* Stop here for orphan pv's.
*/
if (data->pvd.vg_name[0] == '\0')
return 1;
if (!_write_vgd(data)) {
log_error("Failed to write VG data to %s", pv_name);
return 0;
}
if (!_write_uuids(data)) {
log_error("Failed to write PV uuid list to %s", pv_name);
return 0;
}
if (!_write_lvs(data)) {
log_error("Failed to write LV's to %s", pv_name);
return 0;
}
if (!_write_extents(data)) {
log_error("Failed to write extents to %s", pv_name);
return 0;
}
return 1;
}
/*
* opens the device and hands to the above fn.
*/
static int _write_all_pvd(struct disk_list *data)
{
int r;
if (!dev_open(data->dev, O_WRONLY)) {
stack;
return 0;
}
r = __write_all_pvd(data);
if (!dev_close(data->dev))
stack;
return r;
}
/*
* Writes all the given pv's to disk. Does very
* little sanity checking, so make sure correct
* data is passed to here.
*/
int write_disks(struct list *pvs)
{
struct list *pvh;
struct disk_list *dl;
list_iterate(pvh, pvs) {
dl = list_item(pvh, struct disk_list);
if (!(_write_all_pvd(dl)))
fail;
log_very_verbose("Successfully wrote data to %s",
dev_name(dl->dev));
}
return 1;
}

245
lib/format1/disk-rep.h Normal file
View File

@ -0,0 +1,245 @@
/*
* 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 MAX_LE_TOTAL 65534 /* 2^16 - 2 */
#define MAX_PE_TOTAL ((uint32_t) -2)
#define UNMAPPED_EXTENT 0
/* volume group */
#define VG_ACTIVE 0x01 /* vg_status */
#define VG_EXPORTED 0x02 /* " */
#define VG_EXTENDABLE 0x04 /* " */
#define VG_READ 0x01 /* vg_access */
#define VG_WRITE 0x02 /* " */
#define VG_CLUSTERED 0x04 /* " */
#define VG_SHARED 0x08 /* " */
/* logical volume */
#define LV_ACTIVE 0x01 /* lv_status */
#define LV_SPINDOWN 0x02 /* " */
#define LV_READ 0x01 /* lv_access */
#define LV_WRITE 0x02 /* " */
#define LV_SNAPSHOT 0x04 /* " */
#define LV_SNAPSHOT_ORG 0x08 /* " */
#define LV_BADBLOCK_ON 0x01 /* lv_badblock */
#define LV_STRICT 0x01 /* lv_allocation */
#define LV_CONTIGUOUS 0x02 /* " */
/* physical volume */
#define PV_ACTIVE 0x01 /* pv_status */
#define PV_ALLOCATABLE 0x02 /* pv_allocatable */
#define EXPORTED_TAG "PV_EXP" /* Identifier for exported PV */
#define IMPORTED_TAG "PV_IMP" /* Identifier for imported PV */
struct data_area {
uint32_t base;
uint32_t size;
};
struct pv_disk {
uint8_t id[2];
uint16_t version; /* lvm version */
struct data_area pv_on_disk;
struct data_area vg_on_disk;
struct data_area pv_uuidlist_on_disk;
struct data_area lv_on_disk;
struct data_area pe_on_disk;
uint8_t pv_uuid[NAME_LEN];
uint8_t vg_name[NAME_LEN];
uint8_t system_id[NAME_LEN]; /* for vgexport/vgimport */
uint32_t pv_major;
uint32_t pv_number;
uint32_t pv_status;
uint32_t pv_allocatable;
uint32_t pv_size;
uint32_t lv_cur;
uint32_t pe_size;
uint32_t pe_total;
uint32_t pe_allocated;
/* only present on version == 2 pv's */
uint32_t pe_start;
};
struct lv_disk {
uint8_t lv_name[NAME_LEN];
uint8_t vg_name[NAME_LEN];
uint32_t lv_access;
uint32_t lv_status;
uint32_t lv_open;
uint32_t lv_dev;
uint32_t lv_number;
uint32_t lv_mirror_copies; /* for future use */
uint32_t lv_recovery; /* " */
uint32_t lv_schedule; /* " */
uint32_t lv_size;
uint32_t lv_snapshot_minor; /* minor number of original */
uint16_t lv_chunk_size; /* chunk size of snapshot */
uint16_t dummy;
uint32_t lv_allocated_le;
uint32_t lv_stripes;
uint32_t lv_stripesize;
uint32_t lv_badblock; /* for future use */
uint32_t lv_allocation;
uint32_t lv_io_timeout; /* for future use */
uint32_t lv_read_ahead;
};
struct vg_disk {
uint8_t vg_uuid[ID_LEN]; /* volume group UUID */
uint8_t vg_name_dummy[NAME_LEN - ID_LEN]; /* rest of v1 VG name */
uint32_t vg_number; /* volume group number */
uint32_t vg_access; /* read/write */
uint32_t vg_status; /* active or not */
uint32_t lv_max; /* maximum logical volumes */
uint32_t lv_cur; /* current logical volumes */
uint32_t lv_open; /* open logical volumes */
uint32_t pv_max; /* maximum physical volumes */
uint32_t pv_cur; /* current physical volumes FU */
uint32_t pv_act; /* active physical volumes */
uint32_t dummy;
uint32_t vgda; /* volume group descriptor arrays FU */
uint32_t pe_size; /* physical extent size in sectors */
uint32_t pe_total; /* total of physical extents */
uint32_t pe_allocated; /* allocated physical extents */
uint32_t pvg_total; /* physical volume groups FU */
};
struct pe_disk {
uint16_t lv_num;
uint16_t le_num;
};
struct uuid_list {
struct list list;
char uuid[NAME_LEN];
};
struct lvd_list {
struct list list;
struct lv_disk lvd;
};
struct disk_list {
struct list list;
struct pool *mem;
struct device *dev;
struct pv_disk pvd;
struct vg_disk vgd;
struct list uuids;
struct list lvds;
struct pe_disk *extents;
};
/*
* Layout constants.
*/
#define METADATA_ALIGN 4096UL
#define 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.
*/
int read_pvd(struct device *dev, struct pv_disk *pvd);
struct disk_list *read_disk(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 *results);
int write_disks(struct list *pvds);
/*
* Functions to translate to between disk and in
* core structures.
*/
int import_pv(struct pool *mem, struct device *dev,
struct volume_group *vg,
struct physical_volume *pv, struct pv_disk *pvd);
int export_pv(struct pool *mem, struct volume_group *vg,
struct pv_disk *pvd, struct physical_volume *pv);
int import_vg(struct pool *mem,
struct volume_group *vg, struct disk_list *dl,
int partial);
int export_vg(struct vg_disk *vgd, struct volume_group *vg);
int import_lv(struct pool *mem, struct logical_volume *lv,
struct lv_disk *lvd);
void export_lv(struct lv_disk *lvd, struct volume_group *vg,
struct logical_volume *lv, const char *dev_dir);
int import_extents(struct pool *mem, struct volume_group *vg,
struct list *pvds);
int export_extents(struct disk_list *dl, int lv_num,
struct logical_volume *lv,
struct physical_volume *pv);
int import_pvs(struct pool *mem, struct volume_group *vg,
struct list *pvds, struct list *results, int *count);
int import_lvs(struct pool *mem, struct volume_group *vg,
struct list *pvds);
int export_lvs(struct disk_list *dl, struct volume_group *vg,
struct physical_volume *pv, const char *dev_dir);
int export_uuids(struct disk_list *dl, struct volume_group *vg);
void export_numbers(struct list *pvds, struct volume_group *vg);
void export_pv_act(struct list *pvds);
/* blech */
int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
int *result);
int export_vg_number(struct list *pvds, const char *vg_name,
struct dev_filter *filter);
#endif

536
lib/format1/format1.c Normal file
View File

@ -0,0 +1,536 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "disk-rep.h"
#include "dbg_malloc.h"
#include "pool.h"
#include "hash.h"
#include "list.h"
#include "log.h"
#include "display.h"
/* VG consistency checks */
static int _check_vgs(struct list *pvs, int *partial)
{
struct list *pvh, *t;
struct disk_list *dl = NULL;
struct disk_list *first = NULL;
int pv_count = 0;
int exported = -1;
*partial = 0;
/*
* If there are exported and unexported PVs, ignore exported ones.
* This means an active VG won't be affected if disks are inserted
* bearing an exported VG with the same name.
*/
list_iterate(pvh, pvs) {
dl = list_item(pvh, struct disk_list);
if (exported < 0) {
exported = dl->pvd.pv_status & VG_EXPORTED;
continue;
}
if (exported != (dl->pvd.pv_status & VG_EXPORTED)) {
/* Remove exported PVs */
list_iterate_safe(pvh, t, pvs) {
dl = list_item(pvh, struct disk_list);
if (dl->pvd.pv_status & VG_EXPORTED)
list_del(pvh);
}
break;
}
}
/* Remove any PVs with VG structs that differ from the first */
list_iterate_safe(pvh, t, pvs) {
dl = list_item(pvh, struct disk_list);
if (!first)
first = dl;
else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) {
log_error("VG data differs between PVs %s and %s",
dev_name(first->dev), dev_name(dl->dev));
list_del(pvh);
if (partial_mode()) {
*partial = 1;
continue;
}
return 0;
}
pv_count++;
}
/* On entry to fn, list known to be non-empty */
if (pv_count != dl->vgd.pv_cur) {
log_error("%d PV(s) found for VG %s: expected %d",
pv_count, dl->pvd.vg_name, dl->vgd.pv_cur);
if (!partial_mode())
return 0;
*partial = 1;
}
return 1;
}
static struct volume_group *_build_vg(struct pool *mem, struct list *pvs)
{
struct volume_group *vg = pool_alloc(mem, sizeof(*vg));
struct disk_list *dl;
int partial;
if (!vg)
goto bad;
if (list_empty(pvs))
goto bad;
memset(vg, 0, sizeof(*vg));
list_init(&vg->pvs);
list_init(&vg->lvs);
if (!_check_vgs(pvs, &partial))
goto bad;
dl = list_item(pvs->n, struct disk_list);
if (!import_vg(mem, vg, dl, partial))
goto bad;
if (!import_pvs(mem, vg, pvs, &vg->pvs, &vg->pv_count))
goto bad;
if (!import_lvs(mem, vg, pvs))
goto bad;
if (!import_extents(mem, vg, pvs))
goto bad;
return vg;
bad:
stack;
pool_free(mem, vg);
return NULL;
}
static struct volume_group *_vg_read(struct format_instance *fi,
const char *vg_name)
{
struct pool *mem = pool_create(1024 * 10);
struct list pvs;
struct volume_group *vg = NULL;
list_init(&pvs);
if (!mem) {
stack;
return NULL;
}
/* Strip dev_dir if present */
vg_name = strip_dir(vg_name, fi->cmd->dev_dir);
if (!read_pvs_in_vg(vg_name, fi->cmd->filter, mem, &pvs)) {
stack;
goto bad;
}
if (!(vg = _build_vg(fi->cmd->mem, &pvs))) {
stack;
goto bad;
}
vg->cmd = fi->cmd;
bad:
pool_destroy(mem);
return vg;
}
static struct disk_list *_flatten_pv(struct pool *mem, struct volume_group *vg,
struct physical_volume *pv,
const char *dev_dir)
{
struct disk_list *dl = pool_alloc(mem, sizeof(*dl));
if (!dl) {
stack;
return NULL;
}
dl->mem = mem;
dl->dev = pv->dev;
list_init(&dl->uuids);
list_init(&dl->lvds);
if (!export_pv(mem, vg, &dl->pvd, pv) ||
!export_vg(&dl->vgd, vg) ||
!export_uuids(dl, vg) ||
!export_lvs(dl, vg, pv, dev_dir) ||
!calculate_layout(dl)) {
stack;
pool_free(mem, dl);
return NULL;
}
return dl;
}
static int _flatten_vg(struct pool *mem, struct volume_group *vg,
struct list *pvds, const char *dev_dir,
struct dev_filter *filter)
{
struct list *pvh;
struct pv_list *pvl;
struct disk_list *data;
list_iterate(pvh, &vg->pvs) {
pvl = list_item(pvh, struct pv_list);
if (!(data = _flatten_pv(mem, vg, pvl->pv, dev_dir))) {
stack;
return 0;
}
list_add(pvds, &data->list);
}
export_numbers(pvds, vg);
export_pv_act(pvds);
if (!export_vg_number(pvds, vg->name, filter)) {
stack;
return 0;
}
return 1;
}
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
{
struct pool *mem = pool_create(1024 * 10);
struct list pvds;
int r = 0;
if (!mem) {
stack;
return 0;
}
if (vg->status & PARTIAL_VG) {
log_error("Cannot change metadata for partial volume group %s",
vg->name);
return 0;
}
list_init(&pvds);
r = (_flatten_vg(mem, vg, &pvds, fi->cmd->dev_dir, fi->cmd->filter) &&
write_disks(&pvds));
pool_destroy(mem);
return r;
}
static struct physical_volume *_pv_read(struct format_instance *fi,
const char *name)
{
struct pool *mem = pool_create(1024);
struct physical_volume *pv = NULL;
struct disk_list *dl;
struct device *dev;
log_very_verbose("Reading physical volume data %s from disk", name);
if (!mem) {
stack;
return NULL;
}
if (!(dev = dev_cache_get(name, fi->cmd->filter))) {
stack;
goto out;
}
if (!(dl = read_disk(dev, mem, NULL))) {
stack;
goto out;
}
if (!(pv = pool_alloc(fi->cmd->mem, sizeof(*pv)))) {
stack;
goto out;
}
if (!import_pv(fi->cmd->mem, dl->dev, NULL, pv, &dl->pvd)) {
stack;
pool_free(fi->cmd->mem, pv);
pv = NULL;
}
out:
pool_destroy(mem);
return pv;
}
static struct list *_get_pvs(struct format_instance *fi)
{
struct pool *mem = pool_create(1024 * 10);
struct list pvs, *results;
uint32_t count;
if (!mem) {
stack;
return NULL;
}
if (!(results = pool_alloc(fi->cmd->mem, sizeof(*results)))) {
stack;
pool_destroy(mem);
return NULL;
}
list_init(&pvs);
list_init(results);
if (!read_pvs_in_vg(NULL, fi->cmd->filter, mem, &pvs)) {
stack;
goto bad;
}
if (!import_pvs(fi->cmd->mem, NULL, &pvs, results, &count)) {
stack;
goto bad;
}
pool_destroy(mem);
return results;
bad:
pool_free(fi->cmd->mem, results);
pool_destroy(mem);
return NULL;
}
static int _find_vg_name(struct list *names, const char *vg)
{
struct list *nh;
struct name_list *nl;
list_iterate(nh, names) {
nl = list_item(nh, struct name_list);
if (!strcmp(nl->name, vg))
return 1;
}
return 0;
}
static struct list *_get_vgs(struct format_instance *fi)
{
struct list *pvh;
struct list *pvs, *names = pool_alloc(fi->cmd->mem, sizeof(*names));
struct name_list *nl;
if (!names) {
stack;
return NULL;
}
list_init(names);
if (!(pvs = _get_pvs(fi))) {
stack;
goto bad;
}
list_iterate(pvh, pvs) {
struct pv_list *pvl = list_item(pvh, struct pv_list);
if (!(*pvl->pv->vg_name) ||
_find_vg_name(names, pvl->pv->vg_name))
continue;
if (!(nl = pool_alloc(fi->cmd->mem, sizeof(*nl)))) {
stack;
goto bad;
}
if (!(nl->name = pool_strdup(fi->cmd->mem,
pvl->pv->vg_name))) {
stack;
goto bad;
}
list_add(names, &nl->list);
}
if (list_empty(names))
goto bad;
return names;
bad:
pool_free(fi->cmd->mem, names);
return NULL;
}
static int _pv_setup(struct format_instance *fi, 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 _lv_setup(struct format_instance *fi, struct logical_volume *lv)
{
if (lv->le_count > MAX_LE_TOTAL) {
log_error("logical volumes cannot contain more than "
"%d extents.", MAX_LE_TOTAL);
return 0;
}
return 1;
}
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
{
struct pool *mem;
struct disk_list *dl;
struct list pvs;
list_init(&pvs);
if (*pv->vg_name || pv->pe_allocated ) {
log_error("Assertion failed: can't _pv_write non-orphan PV "
"(in VG %s)", pv->vg_name);
return 0;
}
/* Ensure any residual PE structure is gone */
pv->pe_size = pv->pe_count = pv->pe_start = 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(mem, NULL, &dl->pvd, pv)) {
stack;
goto bad;
}
/* must be set to be able to zero gap after PV structure in
dev_write in order to make other disk tools happy */
dl->pvd.pv_on_disk.base = METADATA_BASE;
dl->pvd.pv_on_disk.size = PV_SIZE;
list_add(&pvs, &dl->list);
if (!write_disks(&pvs)) {
stack;
goto bad;
}
pool_destroy(mem);
return 1;
bad:
pool_destroy(mem);
return 0;
}
int _vg_setup(struct format_instance *fi, struct volume_group *vg)
{
/* just check max_pv and max_lv */
if (vg->max_lv >= MAX_LV)
vg->max_lv = MAX_LV - 1;
if (vg->max_pv >= MAX_PV)
vg->max_pv = MAX_PV - 1;
if (vg->extent_size > 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 format_instance *fi)
{
dbg_free(fi);
}
static struct format_handler _format1_ops = {
get_vgs: _get_vgs,
get_pvs: _get_pvs,
pv_read: _pv_read,
pv_setup: _pv_setup,
pv_write: _pv_write,
lv_setup: _lv_setup,
vg_read: _vg_read,
vg_setup: _vg_setup,
vg_write: _vg_write,
destroy: _destroy,
};
struct format_instance *create_lvm1_format(struct cmd_context *cmd)
{
struct format_instance *fi = dbg_malloc(sizeof(*fi));
if (!fi) {
stack;
return NULL;
}
fi->cmd = cmd;
fi->ops = &_format1_ops;
fi->private = NULL;
return fi;
}

14
lib/format1/format1.h Normal file
View File

@ -0,0 +1,14 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_FORMAT1_H
#define _LVM_FORMAT1_H
#include "metadata.h"
struct format_instance *create_lvm1_format(struct cmd_context *cmd);
#endif

587
lib/format1/import-export.c Normal file
View File

@ -0,0 +1,587 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* Translates between disk and in-core formats.
*
* This file is released under the LGPL.
*/
#include "disk-rep.h"
#include "dbg_malloc.h"
#include "pool.h"
#include "hash.h"
#include "list.h"
#include "log.h"
#include "lvm-string.h"
#include <time.h>
#include <sys/utsname.h>
static int _check_vg_name(const char *name)
{
return strlen(name) < NAME_LEN;
}
/*
* Extracts the last part of a path.
*/
static char *_create_lv_name(struct pool *mem, const char *full_name)
{
const char *ptr = strrchr(full_name, '/');
if (!ptr)
ptr = full_name;
else
ptr++;
return pool_strdup(mem, ptr);
}
int import_pv(struct pool *mem, struct device *dev,
struct volume_group *vg,
struct physical_volume *pv, struct pv_disk *pvd)
{
memset(pv, 0, sizeof(*pv));
memcpy(&pv->id, pvd->pv_uuid, ID_LEN);
pv->dev = dev;
if (!(pv->vg_name = pool_strdup(mem, pvd->vg_name))) {
stack;
return 0;
}
/* Store system_id from first PV if PV belongs to a VG */
if (vg && !*vg->system_id)
strncpy(vg->system_id, pvd->system_id, NAME_LEN);
if (vg &&
strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id)))
log_very_verbose("System ID %s on %s differs from %s for "
"volume group", pvd->system_id,
dev_name(pv->dev), vg->system_id);
/*
* If exported, we still need to flag in pv->status too because
* we don't always have a struct volume_group when we need this.
*/
if (pvd->pv_status & VG_EXPORTED)
pv->status |= EXPORTED_VG;
if (pvd->pv_allocatable)
pv->status |= ALLOCATABLE_PV;
pv->size = pvd->pv_size;
pv->pe_size = pvd->pe_size;
pv->pe_start = pvd->pe_start;
pv->pe_count = pvd->pe_total;
pv->pe_allocated = pvd->pe_allocated;
return 1;
}
int _system_id(char *s, const char *prefix)
{
struct utsname uts;
if (uname(&uts) != 0) {
log_sys_error("uname", "_system_id");
return 0;
}
if (lvm_snprintf(s, NAME_LEN, "%s%s%lu",
prefix, uts.nodename, time(NULL)) < 0) {
log_error("Generated system_id too long");
return 0;
}
return 1;
}
int export_pv(struct pool *mem, struct volume_group *vg,
struct pv_disk *pvd, struct physical_volume *pv)
{
memset(pvd, 0, sizeof(*pvd));
pvd->id[0] = 'H';
pvd->id[1] = 'M';
pvd->version = 1;
memcpy(pvd->pv_uuid, pv->id.uuid, ID_LEN);
if (!_check_vg_name(pv->vg_name)) {
stack;
return 0;
}
memset(pvd->vg_name, 0, sizeof(pvd->vg_name));
if (pv->vg_name)
strncpy(pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name));
/* Preserve existing system_id if it exists */
if (vg && *vg->system_id)
strncpy(pvd->system_id, vg->system_id, sizeof(pvd->system_id));
/* Is VG already exported or being exported? */
if (vg && (vg->status & EXPORTED_VG)) {
/* Does system_id need setting? */
if (!*vg->system_id ||
strncmp(vg->system_id, EXPORTED_TAG,
sizeof(EXPORTED_TAG) - 1)) {
if (!_system_id(pvd->system_id, EXPORTED_TAG)) {
stack;
return 0;
}
}
if (strlen(pvd->vg_name) + sizeof(EXPORTED_TAG) >
sizeof(pvd->vg_name)) {
log_error("Volume group name %s too long to export",
pvd->vg_name);
return 0;
}
strcat(pvd->vg_name, EXPORTED_TAG);
}
/* Is VG being imported? */
if (vg && !(vg->status & EXPORTED_VG) && *vg->system_id &&
!strncmp(vg->system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) {
if (!_system_id(pvd->system_id, IMPORTED_TAG)) {
stack;
return 0;
}
}
/* Generate system_id if PV is in VG */
if (!pvd->system_id || !*pvd->system_id)
if (!_system_id(pvd->system_id, "")) {
stack;
return 0;
}
/* Update internal system_id if we changed it */
if (vg &&
(!*vg->system_id ||
strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id))))
strncpy(vg->system_id, pvd->system_id, NAME_LEN);
//pvd->pv_major = MAJOR(pv->dev);
if (pv->status & ALLOCATABLE_PV)
pvd->pv_allocatable = PV_ALLOCATABLE;
pvd->pv_size = pv->size;
pvd->lv_cur = 0; /* this is set when exporting the lv list */
pvd->pe_size = pv->pe_size;
pvd->pe_total = pv->pe_count;
pvd->pe_allocated = pv->pe_allocated;
pvd->pe_start = pv->pe_start;
return 1;
}
int import_vg(struct pool *mem,
struct volume_group *vg, struct disk_list *dl,
int partial)
{
struct vg_disk *vgd = &dl->vgd;
memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
if (!_check_vg_name(dl->pvd.vg_name)) {
stack;
return 0;
}
if (!(vg->name = pool_strdup(mem, dl->pvd.vg_name))) {
stack;
return 0;
}
if (!(vg->system_id = pool_alloc(mem, NAME_LEN))) {
stack;
return 0;
}
*vg->system_id = '\0';
if (vgd->vg_status & VG_EXPORTED)
vg->status |= EXPORTED_VG;
if (vgd->vg_status & VG_EXTENDABLE)
vg->status |= RESIZEABLE_VG;
if (partial || (vgd->vg_access & VG_READ))
vg->status |= LVM_READ;
if (!partial && (vgd->vg_access & VG_WRITE))
vg->status |= LVM_WRITE;
if (vgd->vg_access & VG_CLUSTERED)
vg->status |= CLUSTERED;
if (vgd->vg_access & VG_SHARED)
vg->status |= SHARED;
vg->extent_size = vgd->pe_size;
vg->extent_count = vgd->pe_total;
vg->free_count = vgd->pe_total - vgd->pe_allocated;
vg->max_lv = vgd->lv_max;
vg->max_pv = vgd->pv_max;
if (partial)
vg->status |= PARTIAL_VG;
return 1;
}
int export_vg(struct vg_disk *vgd, struct volume_group *vg)
{
memset(vgd, 0, sizeof(*vgd));
memcpy(vgd->vg_uuid, vg->id.uuid, ID_LEN);
if (vg->status & LVM_READ)
vgd->vg_access |= VG_READ;
if (vg->status & LVM_WRITE)
vgd->vg_access |= VG_WRITE;
if (vg->status & CLUSTERED)
vgd->vg_access |= VG_CLUSTERED;
if (vg->status & SHARED)
vgd->vg_access |= VG_SHARED;
if (vg->status & EXPORTED_VG)
vgd->vg_status |= VG_EXPORTED;
if (vg->status & RESIZEABLE_VG)
vgd->vg_status |= VG_EXTENDABLE;
vgd->lv_max = vg->max_lv;
vgd->lv_cur = vg->lv_count;
vgd->pv_max = vg->max_pv;
vgd->pv_cur = vg->pv_count;
vgd->pe_size = vg->extent_size;
vgd->pe_total = vg->extent_count;
vgd->pe_allocated = vg->extent_count - vg->free_count;
return 1;
}
int import_lv(struct pool *mem, struct logical_volume *lv, struct lv_disk *lvd)
{
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_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;
if (lvd->lv_allocation & LV_CONTIGUOUS)
lv->status |= ALLOC_CONTIGUOUS;
else
lv->status |= ALLOC_SIMPLE;
lv->read_ahead = lvd->lv_read_ahead;
lv->size = lvd->lv_size;
lv->le_count = lvd->lv_allocated_le;
list_init(&lv->segments);
return 1;
}
void export_lv(struct lv_disk *lvd, struct volume_group *vg,
struct logical_volume *lv, const char *dev_dir)
{
memset(lvd, 0, sizeof(*lvd));
snprintf(lvd->lv_name, sizeof(lvd->lv_name), "%s%s/%s",
dev_dir, vg->name, lv->name);
/* FIXME: Add 'if' test */
_check_vg_name(vg->name);
strcpy(lvd->vg_name, vg->name);
if (lv->status & LVM_READ)
lvd->lv_access |= LV_READ;
if (lv->status & LVM_WRITE)
lvd->lv_access |= LV_WRITE;
if (lv->status & SNAPSHOT)
lvd->lv_access |= LV_SNAPSHOT;
if (lv->status & SNAPSHOT_ORG)
lvd->lv_access |= LV_SNAPSHOT_ORG;
if (lv->status & SPINDOWN_LV)
lvd->lv_status |= LV_SPINDOWN;
lvd->lv_read_ahead = lv->read_ahead;
lvd->lv_stripes = list_item(lv->segments.n,
struct stripe_segment)->stripes;
lvd->lv_stripesize = list_item(lv->segments.n,
struct stripe_segment)->stripe_size;
lvd->lv_size = lv->size;
lvd->lv_allocated_le = lv->le_count;
if (lv->status & BADBLOCK_ON)
lvd->lv_badblock = LV_BADBLOCK_ON;
if (lv->status & ALLOC_STRICT)
lvd->lv_allocation |= LV_STRICT;
if (lv->status & ALLOC_CONTIGUOUS)
lvd->lv_allocation |= LV_CONTIGUOUS;
}
int export_extents(struct disk_list *dl, int lv_num,
struct logical_volume *lv,
struct physical_volume *pv)
{
struct list *segh;
struct pe_disk *ped;
struct stripe_segment *seg;
uint32_t pe, s;
list_iterate (segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
for (s = 0; s < seg->stripes; s++) {
if (seg->area[s].pv != pv)
continue; /* not our pv */
for (pe = 0; pe < (seg->len / seg->stripes); pe++) {
ped = &dl->extents[pe + seg->area[s].pe];
ped->lv_num = lv_num;
ped->le_num = (seg->le / seg->stripes) + pe +
s * (lv->le_count / seg->stripes);
}
}
}
return 1;
}
int import_pvs(struct pool *mem, struct volume_group *vg,
struct list *pvds, struct list *results, int *count)
{
struct list *pvdh;
struct disk_list *dl;
struct pv_list *pvl;
*count = 0;
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
if (!(pvl = pool_alloc(mem, sizeof(*pvl))) ||
!(pvl->pv = pool_alloc(mem, sizeof(*pvl->pv)))) {
stack;
return 0;
}
if (!import_pv(mem, dl->dev, vg, pvl->pv, &dl->pvd)) {
stack;
return 0;
}
list_add(results, &pvl->list);
(*count)++;
}
return 1;
}
static struct logical_volume *_add_lv(struct pool *mem,
struct volume_group *vg,
struct lv_disk *lvd)
{
struct lv_list *ll;
struct logical_volume *lv;
if (!(ll = pool_zalloc(mem, sizeof(*ll))) ||
!(ll->lv = pool_zalloc(mem, sizeof(*ll->lv)))) {
stack;
return NULL;
}
lv = ll->lv;
if (!import_lv(mem, lv, lvd)) {
stack;
return NULL;
}
list_add(&vg->lvs, &ll->list);
lv->vg = vg;
vg->lv_count++;
return lv;
}
int import_lvs(struct pool *mem, struct volume_group *vg,
struct list *pvds)
{
struct disk_list *dl;
struct lvd_list *ll;
struct lv_disk *lvd;
struct list *pvdh, *lvdh;
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
list_iterate(lvdh, &dl->lvds) {
ll = list_item(lvdh, struct lvd_list);
lvd = &ll->lvd;
if (!find_lv(vg, lvd->lv_name) &&
!_add_lv(mem, vg, lvd)) {
stack;
return 0;
}
}
}
return 1;
}
int export_lvs(struct disk_list *dl, struct volume_group *vg,
struct physical_volume *pv, const char *dev_dir)
{
struct list *lvh;
struct lv_list *ll;
struct lvd_list *lvdl;
int lv_num = 0, len;
/*
* setup the pv's extents array
*/
len = sizeof(struct pe_disk) * dl->pvd.pe_total;
if (!(dl->extents = pool_alloc(dl->mem, len))) {
stack;
return 0;
}
memset(dl->extents, 0, len);
list_iterate(lvh, &vg->lvs) {
ll = list_item(lvh, struct lv_list);
if (!(lvdl = pool_alloc(dl->mem, sizeof(*lvdl)))) {
stack;
return 0;
}
export_lv(&lvdl->lvd, vg, ll->lv, dev_dir);
lvdl->lvd.lv_number = lv_num;
if (!export_extents(dl, lv_num + 1, ll->lv, pv)) {
stack;
return 0;
}
list_add(&dl->lvds, &lvdl->list);
dl->pvd.lv_cur++;
lv_num++;
}
return 1;
}
int export_uuids(struct disk_list *dl, struct volume_group *vg)
{
struct uuid_list *ul;
struct pv_list *pvl;
struct list *pvh;
list_iterate(pvh, &vg->pvs) {
pvl = list_item(pvh, struct pv_list);
if (!(ul = pool_alloc(dl->mem, sizeof(*ul)))) {
stack;
return 0;
}
memset(ul->uuid, 0, sizeof(ul->uuid));
memcpy(ul->uuid, pvl->pv->id.uuid, ID_LEN);
list_add(&dl->uuids, &ul->list);
}
return 1;
}
/*
* This calculates the nasty pv_number and
* lv_number fields used by LVM1. Very
* inefficient code.
*/
void export_numbers(struct list *pvds, struct volume_group *vg)
{
struct list *pvdh;
struct disk_list *dl;
int pv_num = 1;
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
dl->pvd.pv_number = pv_num++;
}
}
/*
* Calculate vg_disk->pv_act.
*/
void export_pv_act(struct list *pvds)
{
struct list *pvdh;
struct disk_list *dl;
int act = 0;
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
if (dl->pvd.pv_status & PV_ACTIVE)
act++;
}
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
dl->vgd.pv_act = act;
}
}
int export_vg_number(struct list *pvds, const char *vg_name,
struct dev_filter *filter)
{
struct list *pvdh;
struct disk_list *dl;
int vg_num;
if (!get_free_vg_number(filter, vg_name, &vg_num)) {
stack;
return 0;
}
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
dl->vgd.vg_number = vg_num;
}
return 1;
}

View File

@ -0,0 +1,369 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "metadata.h"
#include "hash.h"
#include "dbg_malloc.h"
#include "log.h"
#include "pool.h"
#include "disk-rep.h"
/*
* After much thought I have decided it is easier,
* and probably no less efficient, to convert the
* pe->le map to a full le->pe map, and then
* process this to get the segments form that
* we're after. Any code which goes directly from
* the pe->le map to segments would be gladly
* accepted, if it is less complicated than this
* file.
*/
struct pe_specifier {
struct physical_volume *pv;
uint32_t pe;
};
struct lv_map {
struct logical_volume *lv;
uint32_t stripes;
uint32_t stripe_size;
struct pe_specifier *map;
};
static struct hash_table *_create_lv_maps(struct pool *mem,
struct volume_group *vg)
{
struct hash_table *maps = hash_create(32);
struct list *llh;
struct lv_list *ll;
struct lv_map *lvm;
if (!maps) {
log_err("Unable to create hash table for holding "
"extent maps.");
return NULL;
}
list_iterate(llh, &vg->lvs) {
ll = list_item(llh, struct lv_list);
if (!(lvm = pool_alloc(mem, sizeof(*lvm)))) {
stack;
goto bad;
}
lvm->lv = ll->lv;
if (!(lvm->map = pool_zalloc(mem, sizeof(*lvm->map)
* ll->lv->le_count))) {
stack;
goto bad;
}
if (!hash_insert(maps, ll->lv->name, lvm)) {
stack;
goto bad;
}
}
return maps;
bad:
hash_destroy(maps);
return NULL;
}
static int _fill_lv_array(struct lv_map **lvs,
struct hash_table *maps, struct disk_list *dl)
{
struct list *lvh;
struct lv_map *lvm;
memset(lvs, 0, sizeof(*lvs) * MAX_LV);
list_iterate(lvh, &dl->lvds) {
struct lvd_list *ll = list_item(lvh, struct lvd_list);
if (!(lvm = hash_lookup(maps, strrchr(ll->lvd.lv_name, '/')
+ 1))) {
log_err("Physical volume (%s) contains an "
"unknown logical volume (%s).",
dev_name(dl->dev), ll->lvd.lv_name);
return 0;
}
lvm->stripes = ll->lvd.lv_stripes;
lvm->stripe_size = ll->lvd.lv_stripesize;
lvs[ll->lvd.lv_number] = lvm;
}
return 1;
}
static int _fill_maps(struct hash_table *maps, struct volume_group *vg,
struct list *pvds)
{
struct list *pvdh;
struct disk_list *dl;
struct physical_volume *pv;
struct lv_map *lvms[MAX_LV], *lvm;
struct pe_disk *e;
uint32_t i, lv_num, le;
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
pv = find_pv(vg, dl->dev);
e = dl->extents;
/* build an array of lv's for this pv */
if (!_fill_lv_array(lvms, maps, dl)) {
stack;
return 0;
}
for (i = 0; i < dl->pvd.pe_total; i++) {
lv_num = e[i].lv_num;
if (lv_num == UNMAPPED_EXTENT)
continue;
else {
lv_num--;
lvm = lvms[lv_num];
if (!lvm) {
log_err("invalid lv in extent map");
return 0;
}
le = e[i].le_num;
if (le >= lvm->lv->le_count) {
log_err("logical extent number "
"out of bounds");
return 0;
}
if (lvm->map[le].pv) {
log_err("logical extent (%u) "
"already mapped.", le);
return 0;
}
lvm->map[le].pv = pv;
lvm->map[le].pe = i;
}
}
}
return 1;
}
static int _check_single_map(struct lv_map *lvm)
{
uint32_t i;
for (i = 0; i < lvm->lv->le_count; i++) {
if (!lvm->map[i].pv) {
log_err("Logical volume (%s) contains an incomplete "
"mapping table.", lvm->lv->name);
return 0;
}
}
return 1;
}
static int _check_maps_are_complete(struct hash_table *maps)
{
struct hash_node *n;
struct lv_map *lvm;
for (n = hash_get_first(maps); n; n = hash_get_next(maps, n)) {
lvm = (struct lv_map *) hash_get_data(maps, n);
if (!_check_single_map(lvm)) {
stack;
return 0;
}
}
return 1;
}
static struct stripe_segment *_alloc_seg(struct pool *mem, uint32_t stripes)
{
struct stripe_segment *seg;
uint32_t len = sizeof(*seg) + (stripes * sizeof(seg->area[0]));
if (!(seg = pool_zalloc(mem, len))) {
stack;
return NULL;
}
return seg;
}
static int _read_linear(struct pool *mem, struct lv_map *lvm)
{
uint32_t le = 0;
struct stripe_segment *seg;
while (le < lvm->lv->le_count) {
seg = _alloc_seg(mem, 1);
seg->lv = lvm->lv;
seg->le = le;
seg->len = 0;
seg->stripe_size = 0;
seg->stripes = 1;
seg->area[0].pv = lvm->map[le].pv;
seg->area[0].pe = lvm->map[le].pe;
do
seg->len++;
while ((lvm->map[le + seg->len].pv == seg->area[0].pv) &&
(lvm->map[le + seg->len].pe == seg->area[0].pe +
seg->len));
le += seg->len;
list_add(&lvm->lv->segments, &seg->list);
}
return 1;
}
static int _check_stripe(struct lv_map *lvm, struct stripe_segment *seg,
uint32_t base_le, uint32_t len)
{
uint32_t le, st;
le = base_le + seg->len;
/*
* Is the next physical extent in every stripe adjacent to the last?
*/
for (st = 0; st < seg->stripes; st++)
if ((lvm->map[le + st * len].pv != seg->area[st].pv) ||
(lvm->map[le + st * len].pe != seg->area[st].pe + seg->len))
return 0;
return 1;
}
static int _read_stripes(struct pool *mem, struct lv_map *lvm)
{
uint32_t st, le = 0, len;
struct stripe_segment *seg;
/*
* Work out overall striped length
*/
if (lvm->lv->le_count % lvm->stripes) {
log_error("Number of stripes (%u) incompatible "
"with logical extent count (%u) for %s",
lvm->stripes, lvm->lv->le_count,
lvm->lv->name);
}
len = lvm->lv->le_count / lvm->stripes;
while (le < len) {
if (!(seg = _alloc_seg(mem, lvm->stripes))) {
stack;
return 0;
}
seg->lv = lvm->lv;
seg->stripe_size = lvm->stripe_size;
seg->stripes = lvm->stripes;
seg->le = seg->stripes * le;
seg->len = 1;
/*
* Set up start positions of each stripe in this segment
*/
for (st = 0; st < seg->stripes; st++) {
seg->area[st].pv = lvm->map[le + st * len].pv;
seg->area[st].pe = lvm->map[le + st * len].pe;
}
/*
* Find how many blocks are contiguous in all stripes
* and so can form part of this segment
*/
while (_check_stripe(lvm, seg, le, len))
seg->len++;
le += seg->len;
seg->len *= seg->stripes;
list_add(&lvm->lv->segments, &seg->list);
}
return 1;
}
static int _build_segments(struct pool *mem, struct lv_map *lvm)
{
return (lvm->stripes > 1 ? _read_stripes(mem, lvm) :
_read_linear(mem, lvm));
}
static int _build_all_segments(struct pool *mem, struct hash_table *maps)
{
struct hash_node *n;
struct lv_map *lvm;
for (n = hash_get_first(maps); n; n = hash_get_next(maps, n)) {
lvm = (struct lv_map *) hash_get_data(maps, n);
if (!_build_segments(mem, lvm)) {
stack;
return 0;
}
}
return 1;
}
int import_extents(struct pool *mem, struct volume_group *vg, struct list *pvds)
{
int r = 0;
struct pool *scratch = pool_create(10 * 1024);
struct hash_table *maps;
if (!scratch) {
stack;
return 0;
}
if (!(maps = _create_lv_maps(scratch, vg))) {
log_err("Couldn't allocate logical volume maps.");
goto out;
}
if (!_fill_maps(maps, vg, pvds)) {
log_err("Couldn't fill logical volume maps.");
goto out;
}
if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG)) {
stack;
goto out;
}
if (!_build_all_segments(mem, maps)) {
log_err("Couldn't build extent segments.");
goto out;
}
r = 1;
out:
if (maps)
hash_destroy(maps);
pool_destroy(scratch);
return r;
}

162
lib/format1/layout.c Normal file
View File

@ -0,0 +1,162 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "disk-rep.h"
#include "log.h"
#include "dbg_malloc.h"
/*
* Only works with powers of 2.
*/
static inline ulong _round_up(ulong n, ulong size)
{
size--;
return (n + size) & ~size;
}
static inline ulong _div_up(ulong n, ulong size)
{
return _round_up(n, size) / size;
}
/*
* Each chunk of metadata should be aligned to
* METADATA_ALIGN.
*/
static uint32_t _next_base(struct data_area *area)
{
return _round_up(area->base + area->size, METADATA_ALIGN);
}
/*
* Quick calculation based on pe_start.
*/
static int _adjust_pe_on_disk(struct pv_disk *pvd)
{
uint32_t pe_start = pvd->pe_start * SECTOR_SIZE;
if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size)
return 0;
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
return 1;
}
static void _calc_simple_layout(struct pv_disk *pvd)
{
pvd->pv_on_disk.base = METADATA_BASE;
pvd->pv_on_disk.size = PV_SIZE;
pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk);
pvd->vg_on_disk.size = VG_SIZE;
pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk);
pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN;
pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk);
pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk);
pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk);
pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk);
}
int _check_vg_limits(struct disk_list *dl)
{
if (dl->vgd.lv_max > MAX_LV) {
log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
"for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
dl->pvd.vg_name);
return 0;
}
if (dl->vgd.pv_max > MAX_PV) {
log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
"for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
dl->pvd.vg_name);
return 0;
}
return 1;
}
/*
* This assumes pe_count and pe_start have already
* been calculated correctly.
*/
int calculate_layout(struct disk_list *dl)
{
struct pv_disk *pvd = &dl->pvd;
_calc_simple_layout(pvd);
if (!_adjust_pe_on_disk(pvd)) {
log_error("Insufficient space for metadata and PE's.");
return 0;
}
if (!_check_vg_limits(dl))
return 0;
return 1;
}
/*
* It may seem strange to have a struct
* physical_volume in here, but the number of
* extents that can fit on a disk *is* metadata
* format dependant.
*/
int calculate_extent_count(struct physical_volume *pv)
{
struct pv_disk *pvd = dbg_malloc(sizeof(*pvd));
uint32_t end;
if (!pvd) {
stack;
dbg_free(pvd);
return 0;
}
/*
* Guess how many extents will fit,
* bearing in mind that one is going to be
* knocked off at the start of the next
* loop.
*/
pvd->pe_total = (pv->size / pv->pe_size);
if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
log_error("Insufficient space for extents on %s",
dev_name(pv->dev));
dbg_free(pvd);
return 0;
}
do {
pvd->pe_total--;
_calc_simple_layout(pvd);
end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size + \
SECTOR_SIZE - 1) / SECTOR_SIZE);
pvd->pe_start = _round_up(end, PE_ALIGN);
} while((pvd->pe_start + (pvd->pe_total * pv->pe_size)) > pv->size);
if (pvd->pe_total > MAX_PE_TOTAL) {
log_error("Metadata extent limit (%u) exceeded for %s - "
"%u required", MAX_PE_TOTAL, dev_name(pv->dev),
pvd->pe_total);
dbg_free(pvd);
return 0;
}
pv->pe_count = pvd->pe_total;
pv->pe_start = pvd->pe_start;
dbg_free(pvd);
return 1;
}

145
lib/format1/lvm1_label.c Normal file
View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "lvm1_label.h"
#include "dbg_malloc.h"
#include "disk-rep.h"
#include "log.h"
#include "label.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static void _not_supported(const char *op)
{
log_err("The '%s' operation is not supported for the lvm1 labeller.",
op);
}
static int _can_handle(struct labeller *l, struct device *dev)
{
struct pv_disk pvd;
int r;
if (!dev_open(dev, O_RDONLY)) {
stack;
return 0;
}
r = read_pvd(dev, &pvd);
if (!dev_close(dev))
stack;
return r;
}
static int _write(struct labeller *l,
struct device *dev, struct label *label)
{
_not_supported("write");
return 0;
}
static int _remove(struct labeller *l, struct device *dev)
{
_not_supported("remove");
return 0;
}
static struct label *_to_label(struct pv_disk *pvd)
{
struct label *l;
struct lvm_label_info *info;
if (!(l = dbg_malloc(sizeof(*l)))) {
log_err("Couldn't allocate label.");
return NULL;
}
if (!(info = (struct lvm_label_info *) dbg_strdup(pvd->vg_name))) {
dbg_free(l);
return NULL;
}
memcpy(&l->id, &pvd->pv_uuid, sizeof(l->id));
strcpy(l->volume_type, "lvm");
l->version[0] = 1;
l->version[0] = 0;
l->version[0] = 0;
l->extra_info = info;
return l;
}
static int _read(struct labeller *l, struct device *dev, struct label **label)
{
struct pv_disk pvd;
int r = 0;
if (!dev_open(dev, O_RDONLY)) {
stack;
return 0;
}
r = read_pvd(dev, &pvd);
if (!dev_close(dev))
stack;
if (!r) {
stack;
return 0;
}
/*
* Convert the disk_list into a label structure.
*/
if (!(*label = _to_label(&pvd))) {
stack;
return 0;
}
return 1;
}
static void _destroy_label(struct labeller *l, struct label *label)
{
dbg_free(label->extra_info);
dbg_free(label);
}
static void _destroy(struct labeller *l)
{
dbg_free(l);
}
struct label_ops _lvm1_ops = {
can_handle: _can_handle,
write: _write,
remove: _remove,
read: _read,
verify: _can_handle,
destroy_label: _destroy_label,
destroy: _destroy
};
struct labeller *lvm1_labeller_create(void)
{
struct labeller *l;
if (!(l = dbg_malloc(sizeof(*l)))) {
log_err("Couldn't allocate labeller object.");
return NULL;
}
l->ops = &_lvm1_ops;
l->private = NULL;
return l;
}

21
lib/format1/lvm1_label.h Normal file
View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_LVM1_LABEL_H
#define _LVM_LVM1_LABEL_H
/*
* This is what the 'extra_info' field of the label will point to
* if the label type is lvm1.
*/
struct lvm_label_info {
char volume_group[0];
};
struct labeller *lvm1_labeller_create(void);
#endif

61
lib/format1/vg_number.c Normal file
View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "log.h"
#include "pool.h"
#include "disk-rep.h"
/*
* FIXME: Quick hack. We can use caching to
* prevent a total re-read, even so vg_number
* causes the tools to check *every* pv. Yuck.
* Put in separate file so it wouldn't contaminate
* other code.
*/
int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
int *result)
{
struct list *pvh;
struct list all_pvs;
struct disk_list *dl;
struct pool *mem = pool_create(10 * 1024);
int numbers[MAX_VG], i, r = 0;
list_init(&all_pvs);
if (!mem) {
stack;
return 0;
}
if (!read_pvs_in_vg(NULL, filter, mem, &all_pvs)) {
stack;
goto out;
}
memset(numbers, 0, sizeof(numbers));
list_iterate(pvh, &all_pvs) {
dl = list_item(pvh, struct disk_list);
if (!*dl->pvd.vg_name ||
!strcmp(dl->pvd.vg_name, candidate_vg))
continue;
numbers[dl->vgd.vg_number] = 1;
}
for (i = 0; i < MAX_VG; i++) {
if (!numbers[i]) {
r = 1;
*result = i;
break;
}
}
out:
pool_destroy(mem);
return r;
}

421
lib/format_text/archive.c Normal file
View File

@ -0,0 +1,421 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "format-text.h"
#include "log.h"
#include "pool.h"
#include "config.h"
#include "hash.h"
#include "import-export.h"
#include "lvm-string.h"
#include "lvm-file.h"
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
/*
* The format instance is given a directory path upon creation.
* Each file in this directory whose name is of the form
* '(.*)_[0-9]*.vg' is a config file (see lib/config.[hc]), which
* contains a description of a single volume group.
*
* The prefix ($1 from the above regex) of the config file gives
* the volume group name.
*
* Backup files that have expired will be removed.
*/
struct archive_c {
uint32_t retain_days;
uint32_t min_retains;
char *dir;
/*
* An ordered list of previous archives. Each list
* entered against the vg name. Most recent first.
*/
struct hash_table *vg_archives;
/*
* Scratch pool. Contents of vg_archives come from here.
*/
struct pool *mem;
};
/*
* A list of these is built up for each volume group. Ordered
* with the least recent at the head.
*/
struct archive_file {
struct list list;
char *path;
char *vg;
int index;
};
/*
* This format is write only.
*/
static void _unsupported(const char *cmd)
{
log_err("The archive format doesn't support '%s'", cmd);
}
static struct list *_get_vgs(struct format_instance *fi)
{
_unsupported("get_vgs");
return NULL;
}
static struct list *_get_pvs(struct format_instance *fi)
{
_unsupported("get_pvs");
return NULL;
}
static struct physical_volume *_pv_read(struct format_instance *fi,
const char *pv_name)
{
_unsupported("pv_read");
return NULL;
}
static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
struct volume_group *vg)
{
_unsupported("pv_setup");
return 0;
}
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
{
_unsupported("pv_write");
return 0;
}
static int _vg_setup(struct format_instance *fi, struct volume_group *vg)
{
_unsupported("vg_setup");
return 0;
}
static struct volume_group *_vg_read(struct format_instance *fi,
const char *vg_name)
{
_unsupported("vg_read");
return NULL;
}
static void _destroy(struct format_instance *fi)
{
struct archive_c *bc = (struct archive_c *) fi->private;
if (bc->vg_archives)
hash_destroy(bc->vg_archives);
pool_destroy(bc->mem);
}
/*
* Extract vg name and version number from a filename.
*/
static int _split_vg(const char *filename, char *vg, size_t vg_size,
uint32_t *index)
{
int len, vg_len;
char *dot, *underscore;
len = strlen(filename);
if (len < 7)
return 0;
dot = (char *) (filename + len - 3);
if (strcmp(".vg", dot))
return 0;
if (!(underscore = rindex(filename, '_')))
return 0;
if (sscanf(underscore + 1, "%u", index) != 1)
return 0;
vg_len = underscore - filename;
if (vg_len + 1 > vg_size)
return 0;
strncpy(vg, filename, vg_len);
vg[vg_len] = '\0';
return 1;
}
static void _insert_file(struct list *head, struct archive_file *b)
{
struct list *bh;
struct archive_file *bf;
if (list_empty(head)) {
list_add(head, &b->list);
return;
}
/* index increases through list */
list_iterate (bh, head) {
bf = list_item(bh, struct archive_file);
if (bf->index > b->index) {
list_add(&bf->list, &b->list);
return;
}
}
list_add_h(&bf->list, &b->list);
}
static int _scan_vg(struct archive_c *bc, const char *file,
const char *vg_name, int index)
{
struct archive_file *b;
struct list *files;
/*
* Do we need to create a new list of archive files for
* this vg ?
*/
if (!(files = hash_lookup(bc->vg_archives, vg_name))) {
if (!(files = pool_alloc(bc->mem, sizeof(*files)))) {
stack;
return 0;
}
list_init(files);
if (!hash_insert(bc->vg_archives, vg_name, files)) {
log_err("Couldn't insert archive file "
"into hash table.");
return 0;
}
}
/*
* Create a new archive file.
*/
if (!(b = pool_alloc(bc->mem, sizeof(*b)))) {
log_err("Couldn't create new archive file.");
return 0;
}
b->index = index;
b->path = (char *)file;
b->vg = (char *)vg_name;
/*
* Insert it to the correct part of the list.
*/
_insert_file(files, b);
return 1;
}
static char *_join(struct pool *mem, const char *dir, const char *name)
{
if (!pool_begin_object(mem, 32) ||
!pool_grow_object(mem, dir, strlen(dir)) ||
!pool_grow_object(mem, "/", 1) ||
!pool_grow_object(mem, name, strlen(name)) ||
!pool_grow_object(mem, "\0", 1)) {
stack;
return NULL;
}
return pool_end_object(mem);
}
static int _scan_dir(struct archive_c *bc)
{
int r = 0, i, count, index;
char vg_name[64], *path;
struct dirent **dirent;
if ((count = scandir(bc->dir, &dirent, NULL, alphasort)) < 0) {
log_err("Couldn't scan archive directory.");
return 0;
}
for (i = 0; i < count; i++) {
if ((dirent[i]->d_name[0] == '.') ||
!_split_vg(dirent[i]->d_name, vg_name,
sizeof(vg_name), &index))
continue;
if (!(path = _join(bc->mem, bc->dir, dirent[i]->d_name))) {
stack;
goto out;
}
_scan_vg(bc, path, vg_name, index);
}
r = 1;
out:
for (i = 0; i < count; i++)
free(dirent[i]);
free(dirent);
return r;
}
static int _scan_archives(struct archive_c *bc)
{
pool_empty(bc->mem);
if (bc->vg_archives)
hash_destroy(bc->vg_archives);
if (!(bc->vg_archives = hash_create(128))) {
log_err("Couldn't create hash table for scanning archives.");
return 0;
}
if (!_scan_dir(bc)) {
stack;
return 0;
}
return 1;
}
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
{
int r = 0, i, fd;
unsigned int index = 0;
struct archive_c *bc = (struct archive_c *) fi->private;
struct archive_file *last;
FILE *fp = NULL;
char temp_file[PATH_MAX], archive_name[PATH_MAX];
if (!create_temp_name(bc->dir, temp_file, sizeof(temp_file), &fd)) {
log_err("Couldn't create temporary archive name.");
return 0;
}
if (!(fp = fdopen(fd, "w"))) {
log_err("Couldn't create FILE object for archive.");
close(fd);
return 0;
}
if (!text_vg_export(fp, vg)) {
stack;
fclose(fp);
return 0;
}
fclose(fp);
/*
* Now we want to rename this file to <vg>_index.vg.
*/
if (!_scan_archives(bc)) {
log_err("Couldn't scan the archive directory (%s).", bc->dir);
goto out;
}
if ((last = (struct archive_file *) hash_lookup(bc->vg_archives,
vg->name))) {
/* move to the last in the list */
last = list_item(last->list.p, struct archive_file);
index = last->index + 1;
}
for (i = 0; i < 10; i++) {
if (lvm_snprintf(archive_name, sizeof(archive_name),
"%s/%s_%05d.vg",
bc->dir, vg->name, index) < 0) {
log_err("archive file name too long.");
goto out;
}
if (lvm_rename(temp_file, archive_name)) {
r = 1;
break;
}
index++;
}
out:
return r;
}
void archive_expire(struct format_instance *fi)
{
/* FIXME: finish */
}
static struct format_handler _archive_handler = {
get_vgs: _get_vgs,
get_pvs: _get_pvs,
pv_read: _pv_read,
pv_setup: _pv_setup,
pv_write: _pv_write,
vg_setup: _vg_setup,
vg_read: _vg_read,
vg_write: _vg_write,
destroy: _destroy
};
struct format_instance *archive_format_create(struct cmd_context *cmd,
const char *dir,
uint32_t retain_days,
uint32_t min_retains)
{
struct format_instance *fi;
struct archive_c *bc = NULL;
struct pool *mem = cmd->mem;
if (!(bc = pool_zalloc(mem, sizeof(*bc)))) {
stack;
return NULL;
}
if (!(bc->mem = pool_create(1024))) {
stack;
goto bad;
}
if (!(bc->dir = pool_strdup(mem, dir))) {
stack;
goto bad;
}
bc->retain_days = retain_days;
bc->min_retains = min_retains;
if (!(fi = pool_alloc(mem, sizeof(*fi)))) {
stack;
goto bad;
}
fi->cmd = cmd;
fi->ops = &_archive_handler;
fi->private = bc;
return fi;
bad:
if (bc->mem)
pool_destroy(bc->mem);
pool_free(mem, bc);
return NULL;
}

487
lib/format_text/export.c Normal file
View File

@ -0,0 +1,487 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "import-export.h"
#include "metadata.h"
#include "log.h"
#include "hash.h"
#include "pool.h"
#include "dbg_malloc.h"
#include "lvm-string.h"
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
/*
* The first half of this file deals with
* exporting the vg, ie. writing it to a file.
*/
struct formatter {
struct pool *mem; /* pv names allocated from here */
struct hash_table *pv_names; /* dev_name -> pv_name (eg, pv1) */
FILE *fp; /* where we're writing to */
int indent; /* current level of indentation */
int error;
};
/*
* Formatting functions.
*/
static void _out_size(struct formatter *f, uint64_t size,
const char *fmt, ...)
__attribute__ (( format (printf, 3, 4) ));
static void _out_hint(struct formatter *f, const char *fmt, ...)
__attribute__ (( format (printf, 2, 3) ));
static void _out(struct formatter *f, const char *fmt, ...)
__attribute__ (( format (printf, 2, 3) ));
#define MAX_INDENT 5
static void _inc_indent(struct formatter *f)
{
if (++f->indent > MAX_INDENT)
f->indent = MAX_INDENT;
}
static void _dec_indent(struct formatter *f)
{
if (!f->indent--) {
log_error("Internal error tracking indentation");
f->indent = 0;
}
}
/*
* Newline function for prettier layout.
*/
static void _nl(struct formatter *f)
{
fprintf(f->fp, "\n");
}
#define COMMENT_TAB 6
static void _out_with_comment(struct formatter *f, const char *comment,
const char *fmt, va_list ap)
{
int i;
char white_space[MAX_INDENT + 1];
if (ferror(f->fp))
return;
for (i = 0; i < f->indent; i++)
white_space[i] = '\t';
white_space[i] = '\0';
fprintf(f->fp, white_space);
i = vfprintf(f->fp, fmt, ap);
if (comment) {
/*
* line comments up if possible.
*/
i += 8 * f->indent;
i /= 8;
i++;
do
fputc('\t', f->fp);
while (++i < COMMENT_TAB);
fprintf(f->fp, comment);
}
fputc('\n', f->fp);
}
/*
* Formats a string, converting a size specified
* in 512-byte sectors to a more human readable
* form (eg, megabytes). We may want to lift this
* for other code to use.
*/
static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
{
static char *_units[] = {
"Kilobytes",
"Megabytes",
"Gigabytes",
"Terrabytes",
NULL
};
int i;
double d = (double) sectors;
/* to convert to K */
d /= 2.0;
for (i = 0; (d > 1024.0) && _units[i]; i++)
d /= 1024.0;
return lvm_snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
}
/*
* Appends a comment giving a size in more easily
* readable form (eg, 4M instead of 8096).
*/
static void _out_size(struct formatter *f, uint64_t size,
const char *fmt, ...)
{
char buffer[64];
va_list ap;
_sectors_to_units(size, buffer, sizeof(buffer));
va_start(ap, fmt);
_out_with_comment(f, buffer, fmt, ap);
va_end(ap);
}
/*
* Appends a comment indicating that the line is
* only a hint.
*/
static void _out_hint(struct formatter *f, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_out_with_comment(f, "# Hint only", fmt, ap);
va_end(ap);
}
/*
* The normal output function.
*/
static void _out(struct formatter *f, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_out_with_comment(f, NULL, fmt, ap);
va_end(ap);
}
static int _print_header(struct formatter *f, struct volume_group *vg)
{
time_t t;
t = time(NULL);
_out(f,
"# This file was originally generated by the LVM2 library\n"
"# Generated: %s\n", ctime(&t));
return 1;
}
static int _print_vg(struct formatter *f, struct volume_group *vg)
{
char buffer[256];
if (!id_write_format(&vg->id, buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "id = \"%s\"", buffer);
if (!print_flags(vg->status, VG_FLAGS, buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "status = %s", buffer);
if (vg->system_id && *vg->system_id)
_out(f, "system_id = \"%s\"", vg->system_id);
_out_size(f, vg->extent_size, "extent_size = %u", vg->extent_size);
_out(f, "max_lv = %u", vg->max_lv);
_out(f, "max_pv = %u", vg->max_pv);
return 1;
}
/*
* Get the pv%d name from the formatters hash
* table.
*/
static inline const char *
_get_pv_name(struct formatter *f, struct physical_volume *pv)
{
return (pv) ? (const char *)
hash_lookup(f->pv_names, dev_name(pv->dev)) :
"Missing";
}
static int _print_pvs(struct formatter *f, struct volume_group *vg)
{
struct list *pvh;
struct physical_volume *pv;
char buffer[256];
const char *name;
_out(f, "physical_volumes {");
_inc_indent(f);
list_iterate (pvh, &vg->pvs) {
pv = list_item(pvh, struct pv_list)->pv;
if (!(name = _get_pv_name(f, pv))) {
stack;
return 0;
}
_nl(f);
_out(f, "%s {", name);
_inc_indent(f);
if (!id_write_format(&pv->id, buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "id = \"%s\"", buffer);
_out_hint(f, "device = \"%s\"", dev_name(pv->dev));
_nl(f);
if (!print_flags(pv->status, PV_FLAGS,
buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "status = %s", buffer);
_out(f, "pe_start = %llu", pv->pe_start);
_out_size(f, vg->extent_size * (uint64_t) pv->pe_count,
"pe_count = %u", pv->pe_count);
_dec_indent(f);
_out(f, "}");
}
_dec_indent(f);
_out(f, "}");
return 1;
}
static int _print_segment(struct formatter *f, struct volume_group *vg,
int count, struct stripe_segment *seg)
{
int s;
const char *name;
_out(f, "segment%u {", count);
_inc_indent(f);
_out(f, "start_extent = %u", seg->le);
_out_size(f, seg->len * vg->extent_size, "extent_count = %u", seg->len);
_out(f, "stripes = %u", seg->stripes);
if (seg->stripes > 1)
_out_size(f, seg->stripe_size,
"stripe_size = %u", seg->stripe_size);
_nl(f);
_out(f, "areas = [");
_inc_indent(f);
for (s = 0; s < seg->stripes; s++) {
if (!(name = _get_pv_name(f, seg->area[s].pv))) {
stack;
return 0;
}
_out(f, "\"%s\", %u%s", name, seg->area[s].pe,
(s == seg->stripes - 1) ? "" : ",");
}
_dec_indent(f);
_out(f, "]");
_dec_indent(f);
_out(f, "}");
return 1;
}
static int _count_segments(struct logical_volume *lv)
{
int r = 0;
struct list *segh;
list_iterate (segh, &lv->segments)
r++;
return r;
}
static int _print_lvs(struct formatter *f, struct volume_group *vg)
{
struct list *lvh, *segh;
struct logical_volume *lv;
struct stripe_segment *seg;
char buffer[256];
int seg_count;
_out(f, "logical_volumes {");
_nl(f);
_inc_indent(f);
list_iterate (lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
_out(f, "%s {", lv->name);
_inc_indent(f);
if (!print_flags(lv->status, LV_FLAGS,
buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "status = %s", buffer);
_out(f, "read_ahead = %u", lv->read_ahead);
_out(f, "segment_count = %u", _count_segments(lv));
_nl(f);
seg_count = 1;
list_iterate (segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
if (!_print_segment(f, vg, seg_count++, seg)) {
stack;
return 0;
}
}
_dec_indent(f);
_out(f, "}");
}
_dec_indent(f);
_out(f, "}");
return 1;
}
/*
* In the text format we refer to pv's as 'pv1',
* 'pv2' etc. This function builds a hash table
* to enable a quick lookup from device -> name.
*/
static int _build_pv_names(struct formatter *f,
struct volume_group *vg)
{
int count = 0;
struct list *pvh;
struct physical_volume *pv;
char buffer[32], *name;
if (!(f->mem = pool_create(512))) {
stack;
goto bad;
}
if (!(f->pv_names = hash_create(128))) {
stack;
goto bad;
}
list_iterate (pvh, &vg->pvs) {
pv = list_item(pvh, struct pv_list)->pv;
if (lvm_snprintf(buffer, sizeof(buffer),
"pv%d", count++) < 0) {
stack;
goto bad;
}
if (!(name = pool_strdup(f->mem, buffer))) {
stack;
goto bad;
}
if (!hash_insert(f->pv_names, dev_name(pv->dev), name)) {
stack;
goto bad;
}
}
return 1;
bad:
if (f->mem)
pool_destroy(f->mem);
if (f->pv_names)
hash_destroy(f->pv_names);
return 0;
}
int text_vg_export(FILE *fp, struct volume_group *vg)
{
int r = 0;
struct formatter *f;
if (!(f = dbg_malloc(sizeof(*f)))) {
stack;
return 0;
}
memset(f, 0, sizeof(*f));
f->fp = fp;
f->indent = 0;
if (!_build_pv_names(f, vg)) {
stack;
goto out;
}
#define fail do {stack; goto out;} while(0)
if (!_print_header(f, vg))
fail;
_out(f, "%s {", vg->name);
_inc_indent(f);
if (!_print_vg(f, vg))
fail;
_nl(f);
if (!_print_pvs(f, vg))
fail;
_nl(f);
if (!_print_lvs(f, vg))
fail;
#undef fail
_dec_indent(f);
_out(f, "}");
r = !ferror(f->fp);
out:
if (f->mem)
pool_destroy(f->mem);
if (f->pv_names)
hash_destroy(f->pv_names);
dbg_free(f);
return r;
}

161
lib/format_text/flags.c Normal file
View File

@ -0,0 +1,161 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "log.h"
#include "metadata.h"
#include "import-export.h"
#include "lvm-string.h"
/*
* Bitsets held in the 'status' flags get
* converted into arrays of strings.
*/
struct flag {
int mask;
char *description;
};
static struct flag _vg_flags[] = {
{EXPORTED_VG, "EXPORTED"},
{RESIZEABLE_VG, "RESIZEABLE"},
{PARTIAL_VG, "PARTIAL"},
{LVM_READ, "READ"},
{LVM_WRITE, "WRITE"},
{CLUSTERED, "CLUSTERED"},
{SHARED, "SHARED"},
{0, NULL}
};
static struct flag _pv_flags[] = {
{ALLOCATABLE_PV, "ALLOCATABLE"},
{EXPORTED_VG, "EXPORTED"},
{0, NULL}
};
static struct flag _lv_flags[] = {
{LVM_READ, "READ"},
{LVM_WRITE, "WRITE"},
{ALLOC_SIMPLE, "ALLOC_SIMPLE"},
{ALLOC_STRICT, "ALLOC_STRICT"},
{ALLOC_CONTIGUOUS, "ALLOC_CONTIGUOUS"},
{SNAPSHOT, "SNASHOT"},
{SNAPSHOT_ORG, "SNAPSHOT_ORIGIN"},
{0, NULL}
};
static struct flag *_get_flags(int type)
{
switch (type) {
case VG_FLAGS:
return _vg_flags;
case PV_FLAGS:
return _pv_flags;
case LV_FLAGS:
return _lv_flags;
}
log_err("Unknown flag set requested.");
return NULL;
}
static int _emit(char **buffer, size_t *size, const char *fmt, ...)
{
size_t n;
va_list ap;
va_start(ap, fmt);
n = vsnprintf(*buffer, *size, fmt, ap);
va_end(ap);
if (n < 0 || (n == *size))
return 0;
*buffer += n;
*size -= n;
return 1;
}
/*
* Converts a bitset to an array of string values,
* using one of the tables defined at the top of
* the file.
*/
int print_flags(uint32_t status, int type, char *buffer, size_t size)
{
int f, first = 1;
struct flag *flags;
if (!(flags = _get_flags(type))) {
stack;
return 0;
}
if (!_emit(&buffer, &size, "["))
return 0;
for (f = 0; flags[f].mask; f++) {
if (status & flags[f].mask) {
if (!first) {
if (!_emit(&buffer, &size, ", "))
return 0;
} else
first = 0;
if (!_emit(&buffer, &size, "\"%s\"",
flags[f].description))
return 0;
status &= ~flags[f].mask;
}
}
if (!_emit(&buffer, &size, "]"))
return 0;
if (status)
log_error("Metadata inconsistency: Not all flags successfully "
"exported.");
return 1;
}
int read_flags(uint32_t *status, int type, struct config_value *cv)
{
int f;
uint32_t s = 0;
struct flag *flags;
if (!(flags = _get_flags(type))) {
stack;
return 0;
}
while (cv) {
if (cv->type != CFG_STRING) {
log_err("Status value is not a string.");
return 0;
}
for (f = 0; flags[f].description; f++)
if (!strcmp(flags[f].description, cv->v.str)) {
s |= flags[f].mask;
break;
}
if (!flags[f].description) {
log_err("Unknown status flag '%s'.", cv->v.str);
return 0;
}
cv = cv->next;
}
*status = s;
return 1;
}

View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "format-text.h"
#include "import-export.h"
#include "lvm-file.h"
#include "log.h"
#include "pool.h"
#include "config.h"
#include "hash.h"
#include "dbg_malloc.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/file.h>
#include <limits.h>
/*
* NOTE: Currently there can be only one vg per file.
*/
struct text_c {
char *path;
struct uuid_map *um;
};
static void _not_written(const char *cmd)
{
log_err("The text format is lacking an implementation for '%s'", cmd);
}
static struct list *_get_vgs(struct format_instance *fi)
{
_not_written("_get_vgs");
return NULL;
}
static struct list *_get_pvs(struct format_instance *fi)
{
_not_written("_get_vgs");
return NULL;
}
static struct physical_volume *_pv_read(struct format_instance *fi,
const char *pv_name)
{
_not_written("_get_vgs");
return NULL;
}
static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
struct volume_group *vg)
{
_not_written("_get_vgs");
return 0;
}
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
{
_not_written("_get_vgs");
return 0;
}
static int _vg_setup(struct format_instance *fi, struct volume_group *vg)
{
_not_written("_get_vgs");
return 0;
}
static struct volume_group *_vg_read(struct format_instance *fi,
const char *vg_name)
{
struct text_c *tc = (struct text_c *) fi->private;
struct volume_group *vg;
if (!(vg = text_vg_import(fi->cmd, tc->path, tc->um))) {
stack;
return NULL;
}
/*
* Currently you can only have a single volume group per
* text file (this restriction may remain). We need to
* check that it contains the correct volume group.
*/
if (strcmp(vg_name, vg->name)) {
pool_free(fi->cmd->mem, vg);
log_err("'%s' does not contain volume group '%s'.",
tc->path, vg_name);
return NULL;
}
return vg;
}
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
{
struct text_c *tc = (struct text_c *) fi->private;
FILE *fp;
int fd;
char *slash;
char temp_file[PATH_MAX], temp_dir[PATH_MAX];
slash = rindex(tc->path, '/');
if (slash == 0)
strcpy(temp_dir, ".");
else if (slash - tc->path < PATH_MAX) {
strncpy(temp_dir, tc->path, slash - tc->path);
temp_dir[slash - tc->path] = '\0';
} else {
log_error("Text format failed to determine directory.");
return 0;
}
if (!create_temp_name(temp_dir, temp_file, sizeof(temp_file), &fd)) {
log_err("Couldn't create temporary text file name.");
return 0;
}
if (!(fp = fdopen(fd, "w"))) {
log_sys_error("fdopen", temp_file);
close(fd);
return 0;
}
if (!text_vg_export(fp, vg)) {
log_error("Failed to write metadata to %s.", temp_file);
fclose(fp);
return 0;
}
if (fclose(fp)) {
log_sys_error("fclose", tc->path);
return 0;
}
if (rename(temp_file, tc->path)) {
log_error("%s: rename to %s failed: %s", temp_file, tc->path,
strerror(errno));
return 0;
}
return 1;
}
static void _destroy(struct format_instance *fi)
{
struct text_c *tc = (struct text_c *) fi->private;
dbg_free(tc->path);
dbg_free(tc);
dbg_free(fi);
}
static struct format_handler _text_handler = {
get_vgs: _get_vgs,
get_pvs: _get_pvs,
pv_read: _pv_read,
pv_setup: _pv_setup,
pv_write: _pv_write,
vg_setup: _vg_setup,
vg_read: _vg_read,
vg_write: _vg_write,
destroy: _destroy
};
struct format_instance *text_format_create(struct cmd_context *cmd,
const char *file,
struct uuid_map *um)
{
const char *no_alloc = "Couldn't allocate text format object.";
struct format_instance *fi;
char *path;
struct text_c *tc;
if (!(fi = dbg_malloc(sizeof(*fi)))) {
log_err(no_alloc);
return NULL;
}
if (!(path = dbg_strdup(file))) {
dbg_free(fi);
log_err(no_alloc);
return NULL;
}
if (!(tc = dbg_malloc(sizeof(*tc)))) {
dbg_free(fi);
dbg_free(path);
log_err(no_alloc);
return NULL;
}
tc->path = path;
tc->um = um;
fi->cmd = cmd;
fi->ops = &_text_handler;
fi->private = tc;
return fi;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_FORMAT_TEXT_H
#define _LVM_FORMAT_TEXT_H
#include "lvm-types.h"
#include "metadata.h"
#include "uuid-map.h"
/*
* The archive format is used to maintain a set of metadata backup files
* in an archive directory.
* 'retain_days' is the minimum number of days that an archive file must
* be held for.
*
* 'min_archives' is the minimum number of archives required to be kept
* for each volume group.
*/
struct format_instance *archive_format_create(struct cmd_context *cmd,
const char *dir,
uint32_t retain_days,
uint32_t min_archives);
void backup_expire(struct format_instance *fi);
/*
* The text format can read and write a volume_group to a file.
*/
struct format_instance *text_format_create(struct cmd_context *cmd,
const char *file,
struct uuid_map *um);
#endif

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_TEXT_IMPORT_EXPORT_H
#define _LVM_TEXT_IMPORT_EXPORT_H
#include "config.h"
#include "lvm-types.h"
#include "metadata.h"
#include "uuid-map.h"
#include <stdio.h>
enum {
VG_FLAGS,
PV_FLAGS,
LV_FLAGS
};
int print_flags(uint32_t status, int type, char *buffer, size_t size);
int read_flags(uint32_t *status, int type, struct config_value *cv);
int text_vg_export(FILE *fp, struct volume_group *vg);
struct volume_group *text_vg_import(struct cmd_context *cmd, const char *file,
struct uuid_map *um);
#endif

552
lib/format_text/import.c Normal file
View File

@ -0,0 +1,552 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "metadata.h"
#include "import-export.h"
#include "pool.h"
#include "log.h"
#include "uuid.h"
#include "hash.h"
typedef int (*section_fn)(struct pool *mem,
struct volume_group *vg, struct config_node *pvn,
struct config_node *vgn, struct hash_table *pv_hash,
struct uuid_map *um);
#define _read_int32(root, path, result) \
get_config_uint32(root, path, '/', result)
#define _read_int64(root, path, result) \
get_config_uint64(root, path, '/', result)
static int _read_id(struct id *id, struct config_node *cn, const char *path)
{
struct config_value *cv;
if (!(cn = find_config_node(cn, path, '/'))) {
log_err("Couldn't find uuid.");
return 0;
}
cv = cn->v;
if (!cv || !cv->v.str) {
log_err("uuid must be a string.");
return 0;
}
if (!id_read_format(id, cv->v.str)) {
log_err("Invalid uuid.");
return 0;
}
return 1;
}
static int _read_pv(struct pool *mem,
struct volume_group *vg, struct config_node *pvn,
struct config_node *vgn,
struct hash_table *pv_hash,
struct uuid_map *um)
{
struct physical_volume *pv;
struct pv_list *pvl;
struct config_node *cn;
if (!(pvl = pool_zalloc(mem, sizeof(*pvl))) ||
!(pvl->pv = pool_zalloc(mem, sizeof(*pvl->pv)))) {
stack;
return 0;
}
pv = pvl->pv;
/*
* Add the pv to the pv hash for quick lookup when we read
* the lv segments.
*/
if (!hash_insert(pv_hash, pvn->key, pv)) {
stack;
return 0;
}
if (!(pvn = pvn->child)) {
log_err("Empty pv section.");
return 0;
}
if (!_read_id(&pv->id, pvn, "id")) {
log_err("Couldn't read uuid for volume group.");
return 0;
}
/*
* Use the uuid map to convert the uuid into a device.
*/
if (!(pv->dev = uuid_map_lookup(um, &pv->id))) {
char buffer[64];
if (!id_write_format(&pv->id, buffer, sizeof(buffer))) {
log_err("Couldn't find device.");
return 0;
}
log_err("Couldn't find device with uuid '%s'.", buffer);
return 0;
}
if (!(pv->vg_name = pool_strdup(mem, vg->name))) {
stack;
return 0;
}
if (!(cn = find_config_node(pvn, "status", '/'))) {
log_err("Couldn't find status flags for physical volume.");
return 0;
}
if (!(read_flags(&pv->status, PV_FLAGS, cn->v))) {
log_err("Couldn't read status flags for physical volume.");
return 0;
}
if (!_read_int64(pvn, "pe_start", &pv->pe_start)) {
log_err("Couldn't read extent size for volume group.");
return 0;
}
if (!_read_int32(pvn, "pe_count", &pv->pe_count)) {
log_err("Couldn't find extent count (pe_count) for "
"physical volume.");
return 0;
}
/* adjust the volume group. */
vg->extent_count += pv->pe_count;
vg->free_count += pv->pe_count;
pv->pe_size = vg->extent_size;
pv->size = pv->pe_size * (uint64_t) pv->pe_count;
pv->pe_allocated = 0;
vg->pv_count++;
list_add(&vg->pvs, &pvl->list);
return 1;
}
static void _insert_segment(struct logical_volume *lv,
struct stripe_segment *seg)
{
struct list *segh;
struct stripe_segment *comp;
list_iterate (segh, &lv->segments) {
comp = list_item(segh, struct stripe_segment);
if (comp->le > seg->le) {
list_add(&comp->list, &seg->list);
return;
}
}
lv->le_count += seg->len;
list_add(&lv->segments, &seg->list);
}
static int _read_segment(struct pool *mem, struct volume_group *vg,
struct logical_volume *lv, struct config_node *sn,
struct hash_table *pv_hash)
{
int s;
uint32_t stripes;
struct stripe_segment *seg;
struct config_node *cn;
struct config_value *cv;
const char *seg_name = sn->key;
if (!(sn = sn->child)) {
log_err("Empty segment section.");
return 0;
}
if (!_read_int32(sn, "stripes", &stripes)) {
log_err("Couldn't read 'stripes' for segment '%s'.",
sn->key);
return 0;
}
if (!(seg = pool_zalloc(mem, sizeof(*seg) +
(sizeof(seg->area[0]) * stripes)))) {
stack;
return 0;
}
seg->stripes = stripes;
if (!_read_int32(sn, "start_extent", &seg->le)) {
log_err("Couldn't read 'start_extent' for segment '%s'.",
sn->key);
return 0;
}
if (!_read_int32(sn, "extent_count", &seg->len)) {
log_err("Couldn't read 'extent_count' for segment '%s'.",
sn->key);
return 0;
}
if (seg->stripes == 0) {
log_err("Zero stripes is *not* allowed for segment '%s'.",
sn->key);
return 0;
}
if ((seg->stripes != 1) &&
!_read_int32(sn, "stripe_size", &seg->stripe_size)) {
log_err("Couldn't read 'stripe_size' for segment '%s'.",
sn->key);
return 0;
}
if (!(cn = find_config_node(sn, "areas", '/'))) {
log_err("Couldn't find 'areas' array for segment '%s'.",
sn->key);
return 0;
}
/*
* Read the stripes from the 'areas' array.
* FIXME: we could move this to a separate function.
*/
for (cv = cn->v, s = 0; cv && s < seg->stripes; s++, cv = cv->next) {
/* first we read the pv */
const char *bad = "Badly formed areas array for segment '%s'.";
struct physical_volume *pv;
uint32_t allocated;
if (cv->type != CFG_STRING) {
log_err(bad, sn->key);
return 0;
}
if (!(pv = hash_lookup(pv_hash, cv->v.str))) {
log_err("Couldn't find physical volume '%s' for "
"segment '%s'.",
cn->v->v.str ? cn->v->v.str : "NULL",
seg_name);
return 0;
}
seg->area[s].pv = pv;
if (!(cv = cv->next)) {
log_err(bad, sn->key);
return 0;
}
if (cv->type != CFG_INT) {
log_err(bad, sn->key);
return 0;
}
seg->area[s].pe = cv->v.i;
/*
* Adjust the extent counts in the pv and vg.
*/
allocated = seg->len / seg->stripes;
pv->pe_allocated += allocated;
vg->free_count -= allocated;
}
/*
* Check we read the correct number of stripes.
*/
if (cv || (s < seg->stripes)) {
log_err("Incorrect number of stripes in 'area' array "
"for segment '%s'.", seg_name);
return 0;
}
/*
* Insert into correct part of segment list.
*/
_insert_segment(lv, seg);
return 1;
}
static int _read_segments(struct pool *mem, struct volume_group *vg,
struct logical_volume *lv, struct config_node *lvn,
struct hash_table *pv_hash)
{
struct config_node *sn;
int count = 0, seg_count;
for (sn = lvn; sn; sn = sn->sib) {
/*
* All sub-sections are assumed to be segments.
*/
if (!sn->v) {
if (!_read_segment(mem, vg, lv, sn, pv_hash)) {
stack;
return 0;
}
count++;
}
}
if (!_read_int32(lvn, "segment_count", &seg_count)) {
log_err("Couldn't read segment count for logical volume.");
return 0;
}
if (seg_count != count) {
log_err("segment_count and actual number of segments "
"disagree.");
return 0;
}
/*
* Check there are no gaps or overlaps in the lv.
*/
if (!lv_check_segments(lv)) {
stack;
return 0;
}
/*
* Merge segments in case someones been editing things by hand.
*/
if (!lv_merge_segments(lv)) {
stack;
return 0;
}
return 1;
}
static int _read_lv(struct pool *mem,
struct volume_group *vg, struct config_node *lvn,
struct config_node *vgn, struct hash_table *pv_hash,
struct uuid_map *um)
{
struct logical_volume *lv;
struct lv_list *lvl;
struct config_node *cn;
if (!(lvl = pool_zalloc(mem, sizeof(*lvl))) ||
!(lvl->lv = pool_zalloc(mem, sizeof(*lvl->lv)))) {
stack;
return 0;
}
lv = lvl->lv;
if (!(lv->name = pool_strdup(mem, lvn->key))) {
stack;
return 0;
}
if (!(lvn = lvn->child)) {
log_err("Empty logical volume section.");
return 0;
}
lv->vg = vg;
if (!(cn = find_config_node(lvn, "status", '/'))) {
log_err("Couldn't find status flags for logical volume.");
return 0;
}
if (!(read_flags(&lv->status, LV_FLAGS, cn->v))) {
log_err("Couldn't read status flags for logical volume.");
return 0;
}
if (!_read_int32(lvn, "read_ahead", &lv->read_ahead)) {
log_err("Couldn't read 'read_ahead' value for "
"logical volume.");
return 0;
}
list_init(&lv->segments);
if (!_read_segments(mem, vg, lv, lvn, pv_hash)) {
stack;
return 0;
}
lv->size = (uint64_t) lv->le_count * (uint64_t) vg->extent_size;
vg->lv_count++;
list_add(&vg->lvs, &lvl->list);
return 1;
}
static int _read_sections(const char *section, section_fn fn,
struct pool *mem,
struct volume_group *vg, struct config_node *vgn,
struct hash_table *pv_hash,
struct uuid_map *um)
{
struct config_node *n;
if (!(n = find_config_node(vgn, section, '/'))) {
log_err("Couldn't find section '%s'.", section);
return 0;
}
for (n = n->child; n; n = n->sib) {
if (!fn(mem, vg, n, vgn, pv_hash, um)) {
stack;
return 0;
}
}
return 1;
}
static struct volume_group *_read_vg(struct pool *mem, struct config_file *cf,
struct uuid_map *um)
{
struct config_node *vgn = cf->root, *cn;
struct volume_group *vg;
struct hash_table *pv_hash = NULL;
if (!vgn) {
log_err("Couldn't find volume group.");
return NULL;
}
if (!(vgn = cf->root)) {
log_err("Couldn't find volume group in file.");
return NULL;
}
if (!(vg = pool_zalloc(mem, sizeof(*vg)))) {
stack;
return NULL;
}
if (!(vg->name = pool_strdup(mem, vgn->key))) {
stack;
goto bad;
}
vgn = vgn->child;
if (!_read_id(&vg->id, vgn, "id")) {
log_err("Couldn't read uuid for volume group %s.",
vg->name);
goto bad;
}
if (!(cn = find_config_node(vgn, "status", '/'))) {
log_err("Couldn't find status flags for volume group %s.",
vg->name);
goto bad;
}
if (!(read_flags(&vg->status, VG_FLAGS, cn->v))) {
log_err("Couldn't read status flags for volume group %s.",
vg->name);
goto bad;
}
if (!_read_int32(vgn, "extent_size", &vg->extent_size)) {
log_err("Couldn't read extent size for volume group %s.",
vg->name);
goto bad;
}
/*
* 'extent_count' and 'free_count' get filled in
* implicitly when reading in the pv's and lv's.
*/
if (!_read_int32(vgn, "max_lv", &vg->max_lv)) {
log_err("Couldn't read 'max_lv' for volume group %s.",
vg->name);
goto bad;
}
if (!_read_int32(vgn, "max_pv", &vg->max_pv)) {
log_err("Couldn't read 'max_pv' for volume group %s.",
vg->name);
goto bad;
}
/*
* The pv hash memoises the pv section names -> pv
* structures.
*/
if (!(pv_hash = hash_create(32))) {
log_err("Couldn't create hash table.");
goto bad;
}
list_init(&vg->pvs);
if (!_read_sections("physical_volumes", _read_pv, mem, vg,
vgn, pv_hash, um)) {
log_err("Couldn't find all physical volumes for volume "
"group %s.", vg->name);
goto bad;
}
list_init(&vg->lvs);
if (!_read_sections("logical_volumes", _read_lv, mem, vg,
vgn, pv_hash, um)) {
log_err("Couldn't read all logical volumes for volume "
"group %s.", vg->name);
goto bad;
}
hash_destroy(pv_hash);
/*
* Finished.
*/
return vg;
bad:
if (pv_hash)
hash_destroy(pv_hash);
pool_free(mem, vg);
return NULL;
}
struct volume_group *text_vg_import(struct cmd_context *cmd,
const char *file,
struct uuid_map *um)
{
struct volume_group *vg = NULL;
struct config_file *cf;
if (!(cf = create_config_file())) {
stack;
goto out;
}
if (!read_config(cf, file)) {
log_error("Couldn't read volume group file.");
goto out;
}
if (!(vg = _read_vg(cmd->mem, cf, um))) {
stack;
goto out;
}
vg->cmd = cmd;
out:
destroy_config_file(cf);
return vg;
}

67
lib/format_text/sample.vg Normal file
View File

@ -0,0 +1,67 @@
# An example volume group
# YYYY-MM-DD HH:MM:SS
output_date = "2001-12-11 11:35:12"
sample_volume_group {
id = "ksjdlfksjldskjlsk"
status = ["ACTIVE"]
extent_size = 8192 # 4 Megabytes
max_lv = 99
max_pv = 255
physical_volumes {
pv1 {
id = "lksjdflksdlsk"
device = "/dev/hda1" # Hint only
status = ["ALLOCATABLE"]
pe_start = 8192
pe_count = 2048 # 8 Gigabytes
}
pv2 {
id = "lksjdflksdlsk"
device = "/dev/hda2" # Hint only
status = ["ALLOCATABLE"]
pe_start = 8192
pe_count = 1024 # 4 Gigabytes
}
}
logical_volumes {
music {
status = ["ACTIVE"]
read_ahead = 1024
segment_count = 2
segment1 {
start_extent = 0
extent_count = 1024 # 4 Gigabytes
stripes = 1
areas = [
"pv1", 0
]
}
segment2 {
start_extent = 1024
extent_count = 2048 # 8 Gigabytes
stripes = 2
stripe_size = 32 # 16 Kilobytes
areas = [
"pv1", 1024,
"pv2", 0
]
}
}
}

154
lib/label/label.c Normal file
View File

@ -0,0 +1,154 @@
/*
* Copyright (C) 2002 Sistina Software
*
* This file is released under the LGPL.
*/
#include "label.h"
#include "list.h"
#include "dbg_malloc.h"
#include "log.h"
/*
* Internal labeller struct.
*/
struct labeller_i {
struct list list;
struct labeller *l;
char name[0];
};
static struct list _labellers;
static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
{
struct labeller_i *li;
size_t len;
len = sizeof(*li) + strlen(name) + 1;
if (!(li = dbg_malloc(len))) {
log_error("Couldn't allocate memory for labeller list object.");
return NULL;
}
li->l = l;
strcpy(li->name, name);
return li;
}
static void _free_li(struct labeller_i *li)
{
dbg_free(li);
}
int label_init(void)
{
list_init(&_labellers);
return 1;
}
void label_exit(void)
{
struct list *c, *n;
struct labeller_i *li;
for (c = _labellers.n; c != &_labellers; c = n) {
n = c->n;
li = list_item(c, struct labeller_i);
_free_li(li);
}
}
int label_register_handler(const char *name, struct labeller *handler)
{
struct labeller_i *li;
if (!(li = _alloc_li(name, handler))) {
stack;
return 0;
}
list_add(&_labellers, &li->list);
return 1;
}
struct labeller *label_get_handler(const char *name)
{
struct list *lih;
struct labeller_i *li;
list_iterate (lih, &_labellers) {
li = list_item(lih, struct labeller_i);
if (!strcmp(li->name, name))
return li->l;
}
return NULL;
}
static struct labeller *_find_labeller(struct device *dev)
{
struct list *lih;
struct labeller_i *li;
list_iterate (lih, &_labellers) {
li = list_item(lih, struct labeller_i);
if (li->l->ops->can_handle(li->l, dev))
return li->l;
}
log_debug("No label on device '%s'.", dev_name(dev));
return NULL;
}
int label_remove(struct device *dev)
{
struct labeller *l;
if (!(l = _find_labeller(dev))) {
stack;
return 0;
}
return l->ops->remove(l, dev);
}
int label_read(struct device *dev, struct label **result)
{
int r;
struct list *lih;
struct labeller_i *li;
list_iterate (lih, &_labellers) {
li = list_item(lih, struct labeller_i);
if ((r = li->l->ops->read(li->l, dev, result))) {
(*result)->labeller = li->l;
return r;
}
}
log_debug("No label on device '%s'.", dev_name(dev));
return 0;
}
int label_verify(struct device *dev)
{
struct labeller *l;
if (!(l = _find_labeller(dev))) {
stack;
return 0;
}
return l->ops->verify(l, dev);
}
void label_destroy(struct label *lab)
{
lab->labeller->ops->destroy_label(lab->labeller, lab);
}

90
lib/label/label.h Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_LABEL_H
#define _LVM_LABEL_H
#include "uuid.h"
#include "device.h"
struct label {
struct id id;
char volume_type[32];
uint32_t version[3];
void *extra_info;
struct labeller *labeller;
};
struct labeller;
struct label_ops {
/*
* Is the device labelled with this format ?
*/
int (*can_handle)(struct labeller *l, struct device *dev);
/*
* Write a label to a volume.
*/
int (*write)(struct labeller *l,
struct device *dev, struct label *label);
/*
* Remove a label from a device.
*/
int (*remove)(struct labeller *l, struct device *dev);
/*
* Read a label from a volume.
*/
int (*read)(struct labeller *l,
struct device *dev, struct label **label);
/*
* Additional consistency checks for the paranoid.
*/
int (*verify)(struct labeller *l, struct device *dev);
/*
* Destroy a previously read label.
*/
void (*destroy_label)(struct labeller *l, struct label *label);
/*
* Destructor.
*/
void (*destroy)(struct labeller *l);
};
struct labeller {
struct label_ops *ops;
void *private;
};
int label_init(void);
void label_exit(void);
int label_register_handler(const char *name, struct labeller *handler);
struct labeller *label_get_handler(const char *name);
int label_remove(struct device *dev);
int label_read(struct device *dev, struct label **result);
int label_verify(struct device *dev);
void label_destroy(struct label *lab);
/*
* We'll support two label types: the 'pretend the
* LVM1 pv structure at the begining of the disk
* is a label' hack, and pjc's 1 sector labels at
* the front and back of the device.
*/
#endif

561
lib/label/lvm2_label.c Normal file
View File

@ -0,0 +1,561 @@
/*
* Copyright (C) 2001-2002 Sistina Software
*
* This file is released under the LGPL.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "device.h"
#include "dev-cache.h"
#include "log.h"
#include "pool.h"
#include "dbg_malloc.h"
#include "filter.h"
#include "label.h"
#include "lvm2_label.h"
#include "xlate.h"
/* Label Magic is "LnXl" - error: imagination failure */
#define LABEL_MAGIC 0x6c586e4c
/* Size of blocks that dev_get_size() returns the number of */
#define BLOCK_SIZE 512
/* This is just the "struct lvm2_label" with the data pointer removed */
struct label_ondisk
{
uint32_t magic;
uint32_t crc;
uint64_t label1_loc;
uint64_t label2_loc;
uint16_t datalen;
uint16_t pad;
uint32_t version[3];
char disk_type[32];
};
struct filter_private
{
void *mem;
char disk_type[32];
uint32_t version[3];
int version_match;
};
/* Calculate CRC32 of a buffer */
static uint32_t crc32(uint32_t initial, const unsigned char *databuf, size_t datalen)
{
static const u_int crctab[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
uint32_t idx, crc = initial;
for (idx = 0; idx < datalen; idx++) {
crc ^= *databuf++;
crc = (crc >> 4) ^ crctab[crc & 0xf];
crc = (crc >> 4) ^ crctab[crc & 0xf];
}
return crc;
}
/* Calculate crc */
static uint32_t calc_crc(struct label_ondisk *label, char *data)
{
uint32_t crcval = 0xffffffff;
crcval = crc32(crcval, (char *)&label->magic, sizeof(label->magic));
crcval = crc32(crcval, (char *)&label->label1_loc, sizeof(label->label1_loc));
crcval = crc32(crcval, (char *)&label->label2_loc, sizeof(label->label2_loc));
crcval = crc32(crcval, (char *)&label->datalen, sizeof(label->datalen));
crcval = crc32(crcval, (char *)&label->version, sizeof(label->version));
crcval = crc32(crcval, (char *)label->disk_type, strlen(label->disk_type));
crcval = crc32(crcval, (char *)data, label->datalen);
return crcval;
}
/* Calculate the locations we should find the labels in */
static inline void get_label_locations(uint64_t size, uint32_t sectsize, long *first, long *second)
{
*first = sectsize;
*second = size*BLOCK_SIZE - sectsize;
}
/* Read a label off disk */
static int lvm2_label_read(struct labeller *l, struct device *dev, struct label **label)
{
uint64_t size;
uint32_t sectsize;
char *block;
struct label_ondisk *ondisk;
int status;
int iter;
long offset[2];
if (!dev_get_size(dev, &size))
return 0;
if (!dev_get_sectsize(dev, &sectsize))
return 0;
if (!dev_open(dev, O_RDONLY))
return 0;
block = dbg_malloc(sectsize);
if (!block)
{
stack;
return 0;
}
ondisk = (struct label_ondisk *)block;
get_label_locations(size, sectsize, &offset[0], &offset[1]);
/* If the first label is bad then use the second */
for (iter = 0; iter <= 1; iter++)
{
status = dev_read(dev, offset[iter], sectsize, block);
if (status)
{
struct label *incore;
int i;
int found_nul;
/* If the MAGIC doesn't match there's no point in
carrying on */
if (xlate32(ondisk->magic) != LABEL_MAGIC)
continue;
/* Look for a NUL in the disk_type string so we don't
SEGV is something has gone horribly wrong */
found_nul = 0;
for (i=0; i<sizeof(ondisk->disk_type); i++)
if (ondisk->disk_type[i] == '\0')
found_nul = 1;
if (!found_nul)
continue;
incore = dbg_malloc(sizeof(struct label));
if (incore == NULL)
{
return 0;
}
/* Copy and convert endianness */
strncpy(incore->volume_type, ondisk->disk_type, sizeof(incore->volume_type));
incore->version[0] = xlate32(ondisk->version[0]);
incore->version[1] = xlate32(ondisk->version[1]);
incore->version[2] = xlate32(ondisk->version[2]);
incore->extra_len = xlate16(ondisk->datalen);
incore->extra_info = block + sizeof(struct label_ondisk);
/* Make sure datalen is a sensible size too */
if (incore->extra_len > sectsize)
continue;
/* Check Crc */
if (xlate32(ondisk->crc) != calc_crc(ondisk, incore->extra_info))
{
log_error("Crc %d on device %s does not match. got %x, expected %x",
iter, dev_name(dev), xlate32(ondisk->crc), calc_crc(ondisk, incore->extra_info));
continue;
}
/* Check label locations match our view of the device */
if (xlate64(ondisk->label1_loc) != offset[0])
log_error("Label 1 location is wrong in label %d - check block size of the device\n",
iter);
if (xlate64(ondisk->label2_loc) != offset[1])
log_error("Label 2 location is wrong in label %d - the size of the device must have changed\n",
iter);
/* Copy to user's data area */
*label = incore;
incore->extra_info = dbg_malloc(incore->extra_len);
if (!incore->extra_info)
{
stack;
return 0;
}
memcpy(incore->extra_info, block + sizeof(struct label_ondisk), incore->extra_len);
dbg_free(block);
dev_close(dev);
return 1;
}
}
dbg_free(block);
dev_close(dev);
return 0;
}
/* Write a label to a device */
static int lvm2_label_write(struct labeller *l, struct device *dev, struct label *label)
{
uint64_t size;
uint32_t sectsize;
char *block;
struct label_ondisk *ondisk;
int status1, status2;
long offset[2];
if (!dev_get_size(dev, &size))
return 0;
if (!dev_get_sectsize(dev, &sectsize))
return 0;
/* Can the metata fit in the remaining space ? */
if (label->extra_len > sectsize - sizeof(struct label_ondisk))
return 0;
block = dbg_malloc(sizeof(struct label_ondisk) + label->extra_len);
if (!block)
{
stack;
return 0;
}
ondisk = (struct label_ondisk *)block;
get_label_locations(size, sectsize, &offset[0], &offset[1]);
/* Make into ondisk format */
ondisk->magic = xlate32(LABEL_MAGIC);
ondisk->version[0] = xlate32(label->version[0]);
ondisk->version[1] = xlate32(label->version[1]);
ondisk->version[2] = xlate32(label->version[2]);
ondisk->label1_loc = xlate64(offset[0]);
ondisk->label2_loc = xlate64(offset[1]);
ondisk->datalen = xlate16(label->extra_len);
strncpy(ondisk->disk_type, label->volume_type, sizeof(ondisk->disk_type));
memcpy(block+sizeof(struct label_ondisk), label->extra_info, label->extra_len);
ondisk->crc = xlate32(calc_crc(ondisk, label->extra_info));
/* Write metadata to disk */
if (!dev_open(dev, O_RDWR))
{
dbg_free(block);
return 0;
}
status1 = dev_write(dev, offset[0], sizeof(struct label_ondisk) + label->extra_len, block);
if (!status1)
log_error("Error writing label 1\n");
/* Write another at the end of the device */
status2 = dev_write(dev, offset[1], sizeof(struct label_ondisk) + label->extra_len, block);
if (!status2)
{
char zerobuf[sizeof(struct label_ondisk)];
log_error("Error writing label 2\n");
/* Wipe the first label so it doesn't get confusing */
memset(zerobuf, 0, sizeof(struct label_ondisk));
if (!dev_write(dev, offset[0], sizeof(struct label_ondisk), zerobuf))
log_error("Error erasing label 1\n");
}
dbg_free(block);
dev_close(dev);
return ((status1 != 0) && (status2 != 0));
}
/* Return 1 for Yes, 0 for No */
static int lvm2_is_labelled(struct labeller *l, struct device *dev)
{
struct label *label;
int status;
status = lvm2_label_read(l, dev, &label);
if (status) label_free(label);
return status;
}
/* Check the device is labelled and has the right format_type */
static int _accept_format(struct dev_filter *f, struct device *dev)
{
struct label *l;
int status;
struct filter_private *fp = (struct filter_private *) f->private;
status = lvm2_label_read(NULL, dev, &l);
if (status)
{
if (strcmp(l->volume_type, fp->disk_type) == 0)
{
switch (fp->version_match)
{
case VERSION_MATCH_EQUAL:
if (l->version[0] == fp->version[0] &&
l->version[1] == fp->version[1] &&
l->version[2] == fp->version[2])
return 1;
break;
case VERSION_MATCH_LESSTHAN:
if (l->version[0] == fp->version[0] &&
l->version[1] < fp->version[1])
return 1;
break;
case VERSION_MATCH_LESSEQUAL:
if (l->version[0] == fp->version[0] &&
l->version[1] <= fp->version[1])
return 1;
break;
case VERSION_MATCH_ANY:
return 1;
}
}
label_free(l);
}
return 0;
}
/* We just want to know if it's labelled or not */
static int _accept_label(struct dev_filter *f, struct device *dev)
{
return lvm2_is_labelled(NULL, dev);
}
static void _destroy(struct dev_filter *f)
{
struct filter_private *fp = (struct filter_private *) f->private;
}
/* A filter to find devices with a particular label type on them */
struct dev_filter *lvm2_label_format_filter_create(char *disk_type, uint32_t version[3], int match_type)
{
struct pool *mem;
struct filter_private *fp;
struct dev_filter *f;
/* Validate the match type */
if (match_type != VERSION_MATCH_EQUAL &&
match_type != VERSION_MATCH_LESSTHAN &&
match_type != VERSION_MATCH_LESSEQUAL &&
match_type != VERSION_MATCH_ANY)
return 0;
mem = pool_create(10 * 1024);
if (!mem) {
stack;
return NULL;
}
if (!(f = pool_zalloc(mem, sizeof(*f)))) {
stack;
goto bad;
}
if (!(fp = pool_zalloc(mem, sizeof(*fp)))) {
stack;
goto bad;
}
fp->mem = mem;
strcpy(fp->disk_type, disk_type);
fp->version[0] = version[0];
fp->version[1] = version[1];
fp->version[2] = version[2];
fp->version_match = match_type;
f->passes_filter = _accept_format;
f->destroy = _destroy;
f->private = fp;
return f;
bad:
pool_destroy(mem);
return NULL;
}
/* A filter to find devices with any label on them */
struct dev_filter *lvm2_label_filter_create()
{
struct pool *mem = pool_create(10 * 1024);
struct filter_private *fp;
struct dev_filter *f;
if (!mem) {
stack;
return NULL;
}
if (!(f = pool_zalloc(mem, sizeof(*f)))) {
stack;
goto bad;
}
if (!(fp = pool_zalloc(mem, sizeof(*fp)))) {
stack;
goto bad;
}
fp->mem = mem;
f->passes_filter = _accept_label;
f->destroy = _destroy;
f->private = fp;
return f;
bad:
pool_destroy(mem);
return NULL;
}
/* Return 1 if both labels are identical, 0 if not or there was an error */
static int lvm2_labels_match(struct labeller *l, struct device *dev)
{
uint64_t size;
uint32_t sectsize;
char *block1;
char *block2;
struct label_ondisk *ondisk1;
struct label_ondisk *ondisk2;
int status = 0;
long offset[2];
if (!dev_get_size(dev, &size))
return 0;
if (!dev_get_sectsize(dev, &sectsize))
return 0;
/* Allocate some space for the blocks we are going to read in */
block1 = dbg_malloc(sectsize);
if (!block1)
{
stack;
return 0;
}
block2 = dbg_malloc(sectsize);
if (!block2)
{
stack;
dbg_free(block1);
return 0;
}
ondisk1 = (struct label_ondisk *)block1;
ondisk2 = (struct label_ondisk *)block2;
get_label_locations(size, sectsize, &offset[0], &offset[1]);
/* Fetch em */
if (!dev_open(dev, O_RDONLY))
goto finish;
if (!dev_read(dev, offset[0], sectsize, block1))
goto finish;
if (!dev_read(dev, offset[1], sectsize, block2))
goto finish;
dev_close(dev);
/* Is it labelled? */
if (xlate32(ondisk1->magic) != LABEL_MAGIC)
goto finish;
/* Compare the whole structs */
if (memcmp(ondisk1, ondisk2, sizeof(struct label_ondisk)) != 0)
goto finish;
/* OK, check the data area */
if (memcmp(block1 + sizeof(struct label_ondisk),
block2 + sizeof(struct label_ondisk),
xlate16(ondisk1->datalen)) != 0)
goto finish;
/* They match !! */
status = 1;
finish:
dbg_free(block2);
dbg_free(block1);
return status;
}
static int lvm2_label_remove(struct labeller *l, struct device *dev)
{
uint64_t size;
uint32_t sectsize;
char block[BLOCK_SIZE];
int status1, status2;
long offset[2];
if (!dev_get_size(dev, &size))
return 0;
if (!dev_get_sectsize(dev, &sectsize))
return 0;
if (!dev_open(dev, O_RDWR))
{
dbg_free(block);
return 0;
}
get_label_locations(size, sectsize, &offset[0], &offset[1]);
memset(block, 0, BLOCK_SIZE);
/* Blank out the first label */
status1 = dev_write(dev, offset[0], BLOCK_SIZE, block);
if (!status1)
log_error("Error erasing label 1\n");
/* ...and the other at the end of the device */
status2 = dev_write(dev, offset[1], BLOCK_SIZE, block);
if (!status2)
log_error("Error erasing label 2\n");
dev_close(dev);
return ((status1 != 0) && (status2 != 0));
}
static void lvm2_label_destroy(struct labeller *l)
{
}
static struct label_ops handler_ops =
{
can_handle: lvm2_is_labelled,
write: lvm2_label_write,
remove: lvm2_label_remove,
read: lvm2_label_read,
verify: lvm2_labels_match,
destroy: lvm2_label_destroy,
};
static struct labeller this_labeller =
{
private : NULL,
ops : &handler_ops,
};
/* Don't know how this gets called... */
void lvm2_label_init()
{
label_register_handler("LVM2", &this_labeller);
}

27
lib/label/lvm2_label.h Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
struct lvm2_label
{
uint32_t magic;
uint32_t crc;
uint64_t label1_loc;
uint64_t label2_loc;
uint16_t datalen;
char disk_type[32];
uint32_t version[3];
char *data;
};
#define VERSION_MATCH_EQUAL 1
#define VERSION_MATCH_LESSTHAN 2
#define VERSION_MATCH_LESSEQUAL 3
#define VERSION_MATCH_ANY 4
extern struct dev_filter *lvm2_label_filter_create();
extern struct dev_filter *lvm2_label_format_filter_create(char *disk_type, uint32_t version[3], int match_type);

70
lib/label/uuid-map.c Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_UUID_MAP_H
#define _LVM_UUID_MAP_H
#include "uuid-map.h"
#include "dev-cache.h"
#include "dbg_malloc.h"
#include "log.h"
#include "label.h"
struct uuid_map {
struct dev_filter *filter;
};
struct uuid_map *uuid_map_create(struct dev_filter *devices)
{
struct uuid_map *um;
if (!(um = dbg_malloc(sizeof(*um)))) {
log_err("Couldn't allocate uuid_map object.");
return NULL;
}
um->filter = devices;
return um;
}
void uuid_map_destroy(struct uuid_map *um)
{
dbg_free(um);
}
/*
* Simple, non-caching implementation to start with.
*/
struct device *uuid_map_lookup(struct uuid_map *um, struct id *id)
{
struct dev_iter *iter;
struct device *dev;
struct label *lab;
if (!(iter = dev_iter_create(um->filter))) {
stack;
return NULL;
}
while ((dev = dev_iter_get(iter))) {
if (!label_read(dev, &lab))
continue;
if (id_equal(id, &lab->id)) {
label_destroy(lab);
break;
}
label_destroy(lab);
}
dev_iter_destroy(iter);
return dev;
}
#endif

26
lib/label/uuid-map.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_UUID_MAP_H
#define _LVM_UUID_MAP_H
#include "uuid.h"
#include "dev-cache.h"
/*
* Holds a mapping from uuid -> device.
*/
struct uuid_map;
struct uuid_map *uuid_map_create(struct dev_filter *devices);
void uuid_map_destroy(struct uuid_map *um);
/*
* Find the device with a particular uuid.
*/
struct device *uuid_map_lookup(struct uuid_map *um, struct id *id);
#endif

166
lib/log/log.c Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "log.h"
#include <stdarg.h>
#include <syslog.h>
static FILE *_log = 0;
static int _verbose_level = 0;
static int _test = 0;
static int _partial = 0;
static int _debug_level = 0;
static int _syslog = 0;
static int _indent = 1;
static int _log_cmd_name = 0;
static char _cmd_name[30] = "";
static char _msg_prefix[30] = " ";
void init_log(FILE *fp) {
_log = fp;
}
void init_syslog(int facility) {
openlog("lvm", LOG_PID, facility);
_syslog = 1;
}
void fin_log() {
_log = 0;
}
void fin_syslog() {
if (_syslog)
closelog();
_syslog = 0;
}
void init_verbose(int level) {
_verbose_level = level;
}
void init_test(int level) {
_test = level;
if (_test)
log_print("Test mode. Metadata will NOT be updated.");
}
void init_partial(int level) {
_partial = level;
}
void init_cmd_name(int status) {
_log_cmd_name = status;
}
void set_cmd_name(const char *cmd) {
if (!_log_cmd_name)
return;
strncpy(_cmd_name, cmd, sizeof(_cmd_name));
_cmd_name[sizeof(_cmd_name) - 1] = '\0';
}
void init_msg_prefix(const char *prefix) {
strncpy(_msg_prefix, prefix, sizeof(_msg_prefix));
_msg_prefix[sizeof(_msg_prefix) - 1] = '\0';
}
void init_indent(int indent) {
_indent = indent;
}
int test_mode() {
return _test;
}
int partial_mode() {
return _partial;
}
void init_debug(int level) {
_debug_level = level;
}
int debug_level() {
return _debug_level;
}
void print_log(int level, const char *file, int line, const char *format, ...) {
va_list ap;
va_start(ap, format);
switch(level) {
case _LOG_DEBUG:
if (_verbose_level > 2 && format[1]) {
printf("%s%s", _cmd_name, _msg_prefix);
if (_indent)
printf(" ");
vprintf(format, ap);
putchar('\n');
}
break;
case _LOG_INFO:
if (_verbose_level > 1) {
printf("%s%s", _cmd_name, _msg_prefix);
if (_indent)
printf(" ");
vprintf(format, ap);
putchar('\n');
}
break;
case _LOG_NOTICE:
if (_verbose_level) {
printf("%s%s", _cmd_name, _msg_prefix);
if (_indent)
printf(" ");
vprintf(format, ap);
putchar('\n');
}
break;
case _LOG_WARN:
printf("%s%s", _cmd_name, _msg_prefix);
vprintf(format, ap);
putchar('\n');
break;
case _LOG_ERR:
fprintf(stderr, "%s%s", _cmd_name, _msg_prefix);
vfprintf(stderr, format, ap);
fputc('\n',stderr);
break;
case _LOG_FATAL:
default:
fprintf(stderr, "%s%s", _cmd_name, _msg_prefix);
vfprintf(stderr, format, ap);
fputc('\n',stderr);
break;
;
}
va_end(ap);
if (level > _debug_level)
return;
if (_log) {
fprintf(_log, "%s:%d %s%s", file, line, _cmd_name,
_msg_prefix);
va_start(ap, format);
vfprintf(_log, format, ap);
va_end(ap);
fprintf(_log, "\n");
fflush(_log);
}
if (_syslog) {
va_start(ap, format);
vsyslog(level, format, ap);
va_end(ap);
}
}

94
lib/log/log.h Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_LOG_H
#define _LVM_LOG_H
/*
* printf()-style macros to use for messages:
*
* log_error - always print to stderr.
* log_print - always print to stdout. Use this instead of printf.
* log_verbose - print to stdout if verbose is set (-v)
* log_very_verbose - print to stdout if verbose is set twice (-vv)
* log_debug - print to stdout if verbose is set three times (-vvv)
* (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 the log/debug_level
* parameter in the configuration file. These messages get the file
* and line number prepended. 'stack' (without arguments) can be used
* to log this information at debug level.
*
* log_sys_error and log_sys_very_verbose are for errors from system calls
* e.g. log_sys_error("stat", filename);
* /dev/fd/7: stat failed: No such file or directory
*
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define _LOG_DEBUG 7
#define _LOG_INFO 6
#define _LOG_NOTICE 5
#define _LOG_WARN 4
#define _LOG_ERR 3
#define _LOG_FATAL 2
void init_log(FILE *fp);
void fin_log(void);
void init_syslog(int facility);
void fin_syslog(void);
void init_verbose(int level);
void init_test(int level);
void init_partial(int level);
void init_debug(int level);
void init_cmd_name(int status);
void init_msg_prefix(const char *prefix);
void init_indent(int indent);
void set_cmd_name(const char *cmd_name);
int test_mode(void);
int partial_mode(void);
int debug_level(void);
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" ) /* Backtrace on error */
#define log_error(fmt, args...) log_err(fmt , ## args)
#define log_print(fmt, args...) log_warn(fmt , ## args)
#define log_verbose(fmt, args...) log_notice(fmt , ## args)
#define log_very_verbose(fmt, args...) log_info(fmt , ## args)
/* Two System call equivalents */
#define log_sys_error(x, y) \
log_err("%s: %s failed: %s", y, x, strerror(errno))
#define log_sys_very_verbose(x, y) \
log_info("%s: %s failed: %s", y, x, strerror(errno))
#endif
/*
* Local variables:
* c-file-style: "linux"
* End:
*/

547
lib/metadata/lv_manip.c Normal file
View File

@ -0,0 +1,547 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#include "metadata.h"
#include "pv_map.h"
#include "log.h"
#include "dbg_malloc.h"
#include "lvm-string.h"
#include <assert.h>
/*
* These functions adjust the pe counts in pv's
* after we've added or removed segments.
*/
static void _get_extents(struct stripe_segment *seg)
{
int s, count;
struct physical_volume *pv;
for (s = 0; s < seg->stripes; s++) {
pv = seg->area[s].pv;
count = seg->len / seg->stripes;
pv->pe_allocated += count;
}
}
static void _put_extents(struct stripe_segment *seg)
{
int s, count;
struct physical_volume *pv;
for (s = 0; s < seg->stripes; s++) {
pv = seg->area[s].pv;
count = seg->len / seg->stripes;
assert(pv->pe_allocated >= count);
pv->pe_allocated -= count;
}
}
static struct stripe_segment *_alloc_segment(struct pool *mem, int stripes)
{
struct stripe_segment *seg;
uint32_t len = sizeof(*seg) + (stripes * sizeof(seg->area[0]));
if (!(seg = pool_zalloc(mem, len))) {
stack;
return NULL;
}
return seg;
}
static int _alloc_stripe_area(struct logical_volume *lv, uint32_t stripes,
uint32_t stripe_size,
struct pv_area **areas, uint32_t *index)
{
uint32_t count = lv->le_count - *index;
uint32_t per_area = count / stripes;
uint32_t smallest = areas[stripes - 1]->count;
uint32_t s;
struct stripe_segment *seg;
if (smallest < per_area)
per_area = smallest;
if (!(seg = _alloc_segment(lv->vg->cmd->mem, stripes))) {
log_err("Couldn't allocate new stripe segment.");
return 0;
}
seg->lv = lv;
seg->le = *index;
seg->len = per_area * stripes;
seg->stripes = stripes;
seg->stripe_size = stripe_size;
for (s = 0; s < stripes; s++) {
struct pv_area *pva = areas[s];
seg->area[s].pv = pva->map->pv;
seg->area[s].pe = pva->start;
consume_pv_area(pva, per_area);
}
list_add(&lv->segments, &seg->list);
*index += seg->len;
return 1;
}
static int _comp_area(const void *l, const void *r)
{
struct pv_area *lhs = *((struct pv_area **) l);
struct pv_area *rhs = *((struct pv_area **) r);
if (lhs->count < rhs->count)
return 1;
else if (lhs->count > rhs->count)
return -1;
return 0;
}
static int _alloc_striped(struct logical_volume *lv,
struct list *pvms, uint32_t allocated,
uint32_t stripes, uint32_t stripe_size)
{
int r = 0;
struct list *pvmh;
struct pv_area **areas;
int pv_count = 0, index;
struct pv_map *pvm;
size_t len;
list_iterate (pvmh, pvms)
pv_count++;
/* allocate an array of pv_areas, one candidate per pv */
len = sizeof(*areas) * pv_count;
if (!(areas = dbg_malloc(sizeof(*areas) * pv_count))) {
log_err("Couldn't allocate areas array.");
return 0;
}
while (allocated != lv->le_count) {
index = 0;
list_iterate (pvmh, pvms) {
pvm = list_item(pvmh, struct pv_map);
if (list_empty(&pvm->areas))
continue;
areas[index++] = list_item(pvm->areas.n,
struct pv_area);
}
if (index < stripes) {
log_error("Insufficient allocatable extents suitable "
"for striping for logical volume "
"%s: %u required",
lv->name, lv->le_count);
goto out;
}
/* sort the areas so we allocate from the biggest */
qsort(areas, index, sizeof(*areas), _comp_area);
if (!_alloc_stripe_area(lv, stripes, stripe_size, areas,
&allocated)) {
stack;
goto out;
}
}
r = 1;
out:
dbg_free(areas);
return r;
}
/*
* The heart of the allocation code. This function takes a
* pv_area and allocates it to the lv. If the lv doesn't need
* the complete area then the area is split, otherwise the area
* is unlinked from the pv_map.
*/
static int _alloc_linear_area(struct logical_volume *lv, uint32_t *index,
struct pv_map *map, struct pv_area *pva)
{
uint32_t count, remaining;
struct stripe_segment *seg;
count = pva->count;
remaining = lv->le_count - *index;
if (count > remaining)
count = remaining;
if (!(seg = _alloc_segment(lv->vg->cmd->mem, 1))) {
log_err("Couldn't allocate new stripe segment.");
return 0;
}
seg->lv = lv;
seg->le = *index;
seg->len = count;
seg->stripe_size = 0;
seg->stripes = 1;
seg->area[0].pv = map->pv;
seg->area[0].pe = pva->start;
list_add(&lv->segments, &seg->list);
consume_pv_area(pva, count);
*index += count;
return 1;
}
/*
* Only one area per pv is allowed, so we search
* for the biggest area, or the first area that
* can complete the allocation.
*/
/*
* FIXME: subsequent lvextends may not be contiguous.
*/
static int _alloc_contiguous(struct logical_volume *lv,
struct list *pvms, uint32_t allocated)
{
struct list *tmp1;
struct pv_map *pvm;
struct pv_area *pva;
list_iterate(tmp1, pvms) {
pvm = list_item(tmp1, struct pv_map);
if (list_empty(&pvm->areas))
continue;
/* first item in the list is the biggest */
pva = list_item(pvm->areas.n, struct pv_area);
if (!_alloc_linear_area(lv, &allocated, pvm, pva)) {
stack;
return 0;
}
if (allocated == lv->le_count)
break;
}
if (allocated != lv->le_count) {
log_error("Insufficient allocatable extents (%u) "
"for logical volume %s: %u required",
allocated, lv->name, lv->le_count);
return 0;
}
return 1;
}
/*
* Areas just get allocated in order until the lv
* is full.
*/
static int _alloc_simple(struct logical_volume *lv,
struct list *pvms, uint32_t allocated)
{
struct list *tmp1, *tmp2;
struct pv_map *pvm;
struct pv_area *pva;
list_iterate(tmp1, pvms) {
pvm = list_item(tmp1, struct pv_map);
list_iterate(tmp2, &pvm->areas) {
pva = list_item(tmp2, struct pv_area);
if (!_alloc_linear_area(lv, &allocated, pvm, pva) ||
(allocated == lv->le_count))
goto done;
}
}
done:
if (allocated != lv->le_count) {
log_error("Insufficient allocatable logical extents (%u) "
"for logical volume %s: %u required",
allocated, lv->name, lv->le_count);
return 0;
}
return 1;
}
/*
* Chooses a correct allocation policy.
*/
static int _allocate(struct volume_group *vg, struct logical_volume *lv,
struct list *acceptable_pvs, uint32_t allocated,
uint32_t stripes, uint32_t stripe_size)
{
int r = 0;
struct pool *scratch;
struct list *pvms, *old_tail = lv->segments.p, *segh;
if (!(scratch = pool_create(1024))) {
stack;
return 0;
}
/*
* Build the sets of available areas on the pv's.
*/
if (!(pvms = create_pv_maps(scratch, vg, acceptable_pvs)))
goto out;
if (stripes > 1)
r = _alloc_striped(lv, pvms, allocated, stripes, stripe_size);
else if (lv->status & ALLOC_CONTIGUOUS)
r = _alloc_contiguous(lv, pvms, allocated);
else if (lv->status & ALLOC_SIMPLE)
r = _alloc_simple(lv, pvms, allocated);
else {
log_error("Unknown allocation policy: "
"unable to setup logical volume.");
goto out;
}
if (r) {
vg->free_count -= lv->le_count - allocated;
/*
* Iterate through the new segments, updating pe
* counts in pv's.
*/
for (segh = lv->segments.p; segh != old_tail; segh = segh->p)
_get_extents(list_item(segh, struct stripe_segment));
} else {
/*
* Put the segment list back how we found it.
*/
old_tail->n = &lv->segments;
lv->segments.p = old_tail;
}
out:
pool_destroy(scratch);
return r;
}
static char *_generate_lv_name(struct volume_group *vg,
char *buffer, size_t len)
{
struct list *lvh;
struct logical_volume *lv;
int high = -1, i;
list_iterate(lvh, &vg->lvs) {
lv = (list_item(lvh, struct lv_list)->lv);
if (sscanf(lv->name, "lvol%d", &i) != 1)
continue;
if (i > high)
high = i;
}
if (lvm_snprintf(buffer, len, "lvol%d", high + 1) < 0)
return NULL;
return buffer;
}
struct logical_volume *lv_create(struct format_instance *fi,
const char *name,
uint32_t status,
uint32_t stripes,
uint32_t stripe_size,
uint32_t extents,
struct volume_group *vg,
struct list *acceptable_pvs)
{
struct cmd_context *cmd = vg->cmd;
struct lv_list *ll = NULL;
struct logical_volume *lv;
char dname[32];
if (!extents) {
log_error("Unable to create logical volume %s with no extents",
name);
return NULL;
}
if (vg->free_count < extents) {
log_error("Insufficient free extents (%u) in volume group %s: "
"%u required", vg->free_count, vg->name, extents);
return NULL;
}
if (vg->max_lv == vg->lv_count) {
log_error("Maximum number of logical volumes (%u) reached "
"in volume group %s", vg->max_lv, vg->name);
return NULL;
}
if (stripes > list_size(acceptable_pvs)) {
log_error("Number of stripes (%u) must not exceed "
"number of physical volumes (%d)", stripes,
list_size(acceptable_pvs));
return NULL;
}
if (!name && !(name = _generate_lv_name(vg, dname, sizeof(dname)))) {
log_error("Failed to generate unique name for the new "
"logical volume");
return NULL;
}
log_verbose("Creating logical volume %s", name);
if (!(ll = pool_zalloc(cmd->mem, sizeof(*ll))) ||
!(ll->lv = pool_zalloc(cmd->mem, sizeof(*ll->lv)))) {
stack;
return NULL;
}
lv = ll->lv;
strcpy(lv->id.uuid, "");
if (!(lv->name = pool_strdup(cmd->mem, name))) {
stack;
goto bad;
}
lv->status = status;
lv->read_ahead = 0;
lv->size = extents * vg->extent_size;
lv->le_count = extents;
lv->vg = vg;
list_init(&lv->segments);
if (!_allocate(vg, lv, acceptable_pvs, 0u, stripes, stripe_size)) {
stack;
goto bad;
}
if (fi->ops->lv_setup && !fi->ops->lv_setup(fi, lv)) {
stack;
goto bad;
}
vg->lv_count++;
list_add(&vg->lvs, &ll->list);
return lv;
bad:
if (ll)
pool_free(cmd->mem, ll);
return NULL;
}
int lv_reduce(struct format_instance *fi,
struct logical_volume *lv, uint32_t extents)
{
struct list *segh;
struct stripe_segment *seg;
uint32_t count = extents;
for (segh = lv->segments.p;
(segh != &lv->segments) && count;
segh = segh->p) {
seg = list_item(segh, struct stripe_segment);
if (seg->len <= count) {
/* remove this segment completely */
count -= seg->len;
_put_extents(seg);
list_del(segh);
} else {
/* reduce this segment */
_put_extents(seg);
seg->len -= count;
_get_extents(seg);
count = 0;
}
}
lv->le_count -= extents;
lv->size = lv->le_count * lv->vg->extent_size;
if (fi->ops->lv_setup && !fi->ops->lv_setup(fi, lv)) {
stack;
return 0;
}
return 1;
}
int lv_extend(struct format_instance *fi,
struct logical_volume *lv,
uint32_t stripes, uint32_t stripe_size,
uint32_t extents,
struct list *acceptable_pvs)
{
uint32_t old_le_count = lv->le_count;
uint64_t old_size = lv->size;
lv->le_count += extents;
lv->size += extents * lv->vg->extent_size;
/* FIXME: Format1 must ensure stripes is consistent with 1st seg */
if (!_allocate(lv->vg, lv, acceptable_pvs, old_le_count,
stripes, stripe_size)) {
lv->le_count = old_le_count;
lv->size = old_size;
return 0;
}
if (!lv_merge_segments(lv)) {
log_err("Couldn't merge segments after extending "
"logical volume.");
return 0;
}
if (fi->ops->lv_setup && !fi->ops->lv_setup(fi, lv)) {
stack;
return 0;
}
return 1;
}
int lv_remove(struct volume_group *vg, struct logical_volume *lv)
{
struct list *segh;
struct lv_list *lvl;
/* find the lv list */
if (!(lvl = find_lv_in_vg(vg, lv->name))) {
stack;
return 0;
}
/* iterate through the lv's segments freeing off the pe's */
list_iterate (segh, &lv->segments)
_put_extents(list_item(segh, struct stripe_segment));
vg->lv_count--;
vg->free_count += lv->le_count;
list_del(&lvl->list);
return 1;
}

59
lib/metadata/merge.c Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#include "log.h"
#include "metadata.h"
/*
* Returns success if the segments were
* successfully merged. If the do merge, 'first'
* will be adjusted to contain both areas.
*/
static int _merge(struct stripe_segment *first, struct stripe_segment *second)
{
int s;
uint32_t width;
if (!first ||
(first->stripes != second->stripes) ||
(first->stripe_size != second->stripe_size))
return 0;
for (s = 0; s < first->stripes; s++) {
width = first->len / first->stripes;
if ((first->area[s].pv != second->area[s].pv) ||
(first->area[s].pe + width != second->area[s].pe))
return 0;
}
/* we should merge */
first->len += second->len;
return 1;
}
int lv_merge_segments(struct logical_volume *lv)
{
struct list *segh;
struct stripe_segment *current, *prev = NULL;
list_iterate (segh, &lv->segments) {
current = list_item(segh, struct stripe_segment);
if (_merge(prev, current))
list_del(&current->list);
else
prev = current;
}
return 1;
}
int lv_check_segments(struct logical_volume *lv)
{
return 1;
}

289
lib/metadata/metadata.c Normal file
View File

@ -0,0 +1,289 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "log.h"
#include "pool.h"
#include "device.h"
#include "dev-cache.h"
#include "metadata.h"
#include <string.h>
int _add_pv_to_vg(struct format_instance *fi, struct volume_group *vg,
const char *pv_name)
{
struct pv_list *pvl;
struct physical_volume *pv;
struct pool *mem = fi->cmd->mem;
log_verbose("Adding physical volume '%s' to volume group '%s'",
pv_name, vg->name);
if (!(pvl = pool_alloc(mem, sizeof (*pvl)))) {
log_error("pv_list allocation for '%s' failed", pv_name);
return 0;
}
if (!(pv = fi->ops->pv_read(fi, pv_name))) {
log_error("Failed to read existing physical volume '%s'",
pv_name);
return 0;
}
if (*pv->vg_name) {
log_error("Physical volume '%s' is already in volume group "
"'%s'", pv_name, pv->vg_name);
return 0;
}
if (!(pv->vg_name = pool_strdup(mem, vg->name))) {
log_error("vg->name allocation failed for '%s'", pv_name);
return 0;
}
/* Units of 512-byte sectors */
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 fi->pv_setup.
*/
pv->pe_start = 0;
pv->pe_count = pv->size / pv->pe_size;
pv->pe_allocated = 0;
if (!fi->ops->pv_setup(fi, pv, vg)) {
log_error("Format-specific setup of physical volume '%s' "
"failed.", pv_name);
return 0;
}
if (find_pv_in_vg(vg, pv_name)) {
log_error("Physical volume '%s' listed more than once.",
pv_name);
return 0;
}
if (vg->pv_count == vg->max_pv) {
log_error("No space for '%s' - volume group '%s' "
"holds max %d physical volume(s).", pv_name,
vg->name, vg->max_pv);
return 0;
}
pvl->pv = pv;
list_add(&vg->pvs, &pvl->list);
vg->pv_count++;
vg->extent_count += pv->pe_count;
vg->free_count += pv->pe_count;
return 1;
}
int vg_extend(struct format_instance *fi,
struct volume_group *vg, int pv_count, char **pv_names)
{
int i;
/* attach each pv */
for (i = 0; i < pv_count; i++)
if (!_add_pv_to_vg(fi, vg, pv_names[i])) {
log_error("Unable to add physical volume '%s' to "
"volume group '%s'.", pv_names[i], vg->name);
return 0;
}
return 1;
}
const char *strip_dir(const char *vg_name, const char *dev_dir)
{
int len = strlen(dev_dir);
if (!strncmp(vg_name, dev_dir, len))
vg_name += len;
return vg_name;
}
struct volume_group *vg_create(struct format_instance *fi, const char *vg_name,
uint32_t extent_size, int max_pv, int max_lv,
int pv_count, char **pv_names)
{
struct volume_group *vg;
struct pool *mem = fi->cmd->mem;
if (!(vg = pool_alloc(mem, sizeof (*vg)))) {
stack;
return NULL;
}
/* is this vg name already in use ? */
init_partial(1);
if (fi->ops->vg_read(fi, vg_name)) {
log_err("A volume group called '%s' already exists.", vg_name);
goto bad;
}
init_partial(0);
if (!id_create(&vg->id)) {
log_err("Couldn't create uuid for volume group '%s'.",
vg_name);
goto bad;
}
/* Strip dev_dir if present */
vg_name = strip_dir(vg_name, fi->cmd->dev_dir);
vg->cmd = fi->cmd;
if (!(vg->name = pool_strdup(mem, vg_name))) {
stack;
goto bad;
}
vg->status = (RESIZEABLE_VG | LVM_READ | LVM_WRITE);
vg->system_id = pool_alloc(mem, NAME_LEN);
*vg->system_id = '\0';
vg->extent_size = extent_size;
vg->extent_count = 0;
vg->free_count = 0;
vg->max_lv = max_lv;
vg->max_pv = max_pv;
vg->pv_count = 0;
list_init(&vg->pvs);
vg->lv_count = 0;
list_init(&vg->lvs);
if (!fi->ops->vg_setup(fi, vg)) {
log_error("Format specific setup of volume group '%s' failed.",
vg_name);
goto bad;
}
/* attach the pv's */
if (!vg_extend(fi, vg, pv_count, pv_names))
goto bad;
return vg;
bad:
pool_free(mem, vg);
return NULL;
}
struct physical_volume *pv_create(struct format_instance *fi,
const char *name,
struct id *id)
{
struct pool *mem = fi->cmd->mem;
struct physical_volume *pv = pool_alloc(mem, sizeof (*pv));
if (!pv) {
stack;
return NULL;
}
if (!id)
id_create(&pv->id);
else
memcpy(&pv->id, id, sizeof(*id));
if (!(pv->dev = dev_cache_get(name, fi->cmd->filter))) {
log_err("Couldn't find device '%s'", name);
goto bad;
}
if (!(pv->vg_name = pool_alloc(mem, NAME_LEN))) {
stack;
goto bad;
}
*pv->vg_name = 0;
pv->status = ALLOCATABLE_PV;
if (!dev_get_size(pv->dev, &pv->size)) {
log_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(mem, pv);
return NULL;
}
struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name)
{
struct list *pvh;
struct pv_list *pvl;
list_iterate(pvh, &vg->pvs) {
pvl = list_item(pvh, struct pv_list);
if (pvl->pv->dev == dev_cache_get(pv_name, vg->cmd->filter))
return pvl;
}
return NULL;
}
struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name)
{
struct list *lvh;
struct lv_list *lvl;
const char *ptr;
/* Use last component */
if ((ptr = strrchr(lv_name, '/')))
ptr++;
else
ptr = lv_name;
list_iterate(lvh, &vg->lvs) {
lvl = list_item(lvh, struct lv_list);
if (!strcmp(lvl->lv->name, ptr))
return lvl;
}
return NULL;
}
struct logical_volume *find_lv(struct volume_group *vg, const char *lv_name)
{
struct lv_list *lvl = find_lv_in_vg(vg, lv_name);
return lvl ? lvl->lv : NULL;
}
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev)
{
struct list *pvh;
struct physical_volume *pv;
list_iterate(pvh, &vg->pvs) {
pv = list_item(pvh, struct pv_list)->pv;
if (dev == pv->dev)
return pv;
}
return NULL;
}

326
lib/metadata/metadata.h Normal file
View File

@ -0,0 +1,326 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*
* This is the in core representation of a volume group and its
* associated physical and logical volumes.
*/
#ifndef _LVM_METADATA_H
#define _LVM_METADATA_H
#include <sys/types.h>
#include <asm/page.h>
#include "dev-cache.h"
#include "list.h"
#include "uuid.h"
#define NAME_LEN 128
#define MAX_STRIPES 128
#define SECTOR_SIZE 512
#define STRIPE_SIZE_DEFAULT 16 /* 16KB */
#define STRIPE_SIZE_MIN ( PAGE_SIZE/SECTOR_SIZE) /* PAGESIZE in sectors */
#define STRIPE_SIZE_MAX ( 512L * 1024 / SECTOR_SIZE) /* 512 KB in sectors */
/* Various flags */
/* Note that the bits no longer necessarily correspond to LVM1 disk format */
#define EXPORTED_VG 0x00000002 /* VG PV */
#define RESIZEABLE_VG 0x00000004 /* VG */
#define PARTIAL_VG 0x00000040 /* VG */
/* May any free extents on this PV be used or must they be left free? */
#define ALLOCATABLE_PV 0x00000008 /* PV */
#define SPINDOWN_LV 0x00000010 /* LV */
#define BADBLOCK_ON 0x00000020 /* LV */
/* FIXME: do we really set read/write for a whole vg ? */
#define LVM_READ 0x00000100 /* LV VG */
#define LVM_WRITE 0x00000200 /* LV VG */
#define CLUSTERED 0x00000400 /* VG */
#define SHARED 0x00000800 /* VG */
/* FIXME: This should be an enum rather than a bitset,
remove from status - EJT */
#define ALLOC_SIMPLE 0x00001000 /* LV */
#define ALLOC_STRICT 0x00002000 /* LV */
#define ALLOC_CONTIGUOUS 0x00004000 /* LV */
#define SNAPSHOT 0x00010000 /* LV */
#define SNAPSHOT_ORG 0x00020000 /* LV */
struct physical_volume {
struct id id;
struct device *dev;
char *vg_name;
uint32_t status;
uint64_t size;
/* physical extents */
uint64_t pe_size;
uint64_t pe_start;
uint32_t pe_count;
uint32_t pe_allocated; /* FIXME: change the name to alloc_count ? */
};
struct cmd_context;
struct volume_group {
struct cmd_context *cmd;
struct id id;
char *name;
char *system_id;
uint32_t status;
uint32_t extent_size;
uint32_t extent_count;
uint32_t free_count;
uint32_t max_lv;
uint32_t max_pv;
/* physical volumes */
uint32_t pv_count;
struct list pvs;
/* logical volumes */
uint32_t lv_count;
struct list lvs;
};
struct stripe_segment {
struct list list;
struct logical_volume *lv;
uint32_t le;
uint32_t len;
uint32_t stripe_size;
uint32_t stripes;
/* There will be one area for each stripe */
struct {
struct physical_volume *pv;
uint32_t pe;
} area[0];
};
struct logical_volume {
struct id id;
char *name;
struct volume_group *vg;
uint32_t status;
uint32_t read_ahead;
uint64_t size;
uint32_t le_count;
struct list segments;
};
struct name_list {
struct list list;
char *name;
};
struct pv_list {
struct list list;
struct physical_volume *pv;
};
struct lv_list {
struct list list;
struct logical_volume *lv;
};
struct cmd_context {
/* format handler allocates all objects from here */
struct pool *mem;
/* misc. vars needed by format handler */
char *dev_dir;
struct dev_filter *filter;
struct config_file *cf;
};
struct format_instance {
struct cmd_context *cmd;
struct format_handler *ops;
void *private;
};
/*
* Ownership of objects passes to caller.
*/
struct format_handler {
/*
* Returns a name_list of vg's.
*/
struct list *(*get_vgs)(struct format_instance *fi);
/*
* Returns pv_list of fully-populated pv structures.
*/
struct list *(*get_pvs)(struct format_instance *fi);
/*
* Return PV with given path.
*/
struct physical_volume *(*pv_read)(struct format_instance *fi,
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 format_instance *fi, struct physical_volume *pv,
struct volume_group *vg);
/*
* Write a PV structure to disk. Fails if the PV is in a VG ie
* pv->vg_name must be null.
*/
int (*pv_write)(struct format_instance *fi,
struct physical_volume *pv);
/*
* Tweak an already filled out a lv eg, check there
* aren't too many extents.
*/
int (*lv_setup)(struct format_instance *fi, struct logical_volume *lv);
/*
* Tweak an already filled out vg. eg, max_pv is format
* specific.
*/
int (*vg_setup)(struct format_instance *fi, struct volume_group *vg);
/*
* The name may be prefixed with the dev_dir from the
* job_context.
*/
struct volume_group *(*vg_read)(struct format_instance *fi,
const char *vg_name);
/*
* Write out complete VG metadata. You must ensure internal
* consistency before calling. eg. PEs can't refer to PVs not
* part of the VG.
*
* It is also the responsibility of the caller to ensure external
* consistency, eg by calling pv_write() if removing PVs from
* a VG or calling vg_write() a second time if splitting a VG
* into two.
*
* vg_write() must not read or write from any PVs not included
* in the volume_group structure it is handed. Note: format1
* does read all pv's currently.
*/
int (*vg_write)(struct format_instance *fi, struct volume_group *vg);
/*
* Destructor for this object.
*/
void (*destroy)(struct format_instance *fi);
};
/*
* Utility functions
*/
struct physical_volume *pv_create(struct format_instance *fi,
const char *name,
struct id *id);
struct volume_group *vg_create(struct format_instance *fi, const char *name,
uint32_t extent_size, int max_pv, int max_lv,
int pv_count, char **pv_names);
/*
* This needs the format instance to check the
* pv's are orphaned.
*/
int vg_extend(struct format_instance *fi,
struct volume_group *vg, int pv_count, char **pv_names);
/*
* Create a new LV within a given volume group.
*
*/
struct logical_volume *lv_create(struct format_instance *fi,
const char *name,
uint32_t status,
uint32_t stripes,
uint32_t stripe_size,
uint32_t extents,
struct volume_group *vg,
struct list *acceptable_pvs);
int lv_reduce(struct format_instance *fi,
struct logical_volume *lv, uint32_t extents);
int lv_extend(struct format_instance *fi,
struct logical_volume *lv,
uint32_t stripes,
uint32_t stripe_size,
uint32_t extents,
struct list *allocatable_pvs);
/* lv must be part of vg->lvs */
int lv_remove(struct volume_group *vg, struct logical_volume *lv);
/* FIXME: Move to other files */
int id_eq(struct id *op1, struct id *op2);
/* Manipulate PV structures */
int pv_add(struct volume_group *vg, struct physical_volume *pv);
int pv_remove(struct volume_group *vg, struct physical_volume *pv);
struct physical_volume *pv_find(struct volume_group *vg,
const char *pv_name);
/* Find a PV within a given VG */
struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name);
/* Find an LV within a given VG */
struct lv_list *find_lv_in_vg(struct volume_group *vg, const char *lv_name);
/* Return the VG that contains a given LV (based on path given in lv_name) */
/* or environment var */
struct volume_group *find_vg_with_lv(const char *lv_name);
/* FIXME Merge these functions with ones above */
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev);
struct logical_volume *find_lv(struct volume_group *vg, const char *lv_name);
/*
* Remove a dev_dir if present.
*/
const char *strip_dir(const char *vg_name, const char *dir);
/*
* Checks that an lv has no gaps or overlapping segments.
*/
int lv_check_segments(struct logical_volume *lv);
/*
* Sometimes (eg, after an lvextend), it is possible to merge two
* adjacent segments into a single segment. This function trys
* to merge as many segments as possible.
*/
int lv_merge_segments(struct logical_volume *lv);
#endif

255
lib/metadata/pv_map.c Normal file
View File

@ -0,0 +1,255 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#include "pv_map.h"
#include "log.h"
#include "hash.h"
#include <assert.h>
static int _create_maps(struct pool *mem, struct list *pvs, struct list *maps)
{
struct list *tmp;
struct physical_volume *pv;
struct pv_map *pvm;
list_iterate(tmp, pvs) {
pv = list_item(tmp, struct pv_list)->pv;
if (!(pv->status & ALLOCATABLE_PV))
continue;
if (!(pvm = pool_zalloc(mem, sizeof(*pvm)))) {
stack;
return 0;
}
pvm->pv = pv;
if (!(pvm->allocated_extents =
bitset_create(mem, pv->pe_count))) {
stack;
return 0;
}
list_init(&pvm->areas);
list_add(maps, &pvm->list);
}
return 1;
}
static int _set_allocated(struct hash_table *hash,
struct physical_volume *pv, int pe)
{
struct pv_map *pvm;
if (!(pvm = (struct pv_map *) hash_lookup(hash, dev_name(pv->dev)))) {
/*
* it doesn't matter that this fails, it just
* means this part of the lv is on a pv that
* we're not interested in allocating to.
*/
return 1;
}
/* sanity check */
if (bit(pvm->allocated_extents, pe)) {
log_error("Physical extent %d of %s referenced by more than "
"one logical volume", pe, dev_name(pv->dev));
return 0;
}
bit_set(pvm->allocated_extents, pe);
return 1;
}
static int _fill_bitsets(struct volume_group *vg, struct list *maps)
{
struct list *lvh, *pvmh, *segh;
struct logical_volume *lv;
struct pv_map *pvm;
uint32_t s, pe;
struct hash_table *hash;
struct stripe_segment *seg;
int r = 0;
if (!(hash = hash_create(128))) {
log_err("Couldn't create hash table for pv maps.");
return 0;
}
/* populate the hash table */
list_iterate (pvmh, maps) {
pvm = list_item(pvmh, struct pv_map);
if (!hash_insert(hash, dev_name(pvm->pv->dev), pvm)) {
stack;
goto out;
}
}
/* iterate through all the lv's setting bit's for used pe's */
list_iterate (lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
list_iterate (segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
for (s = 0; s < seg->stripes; s++) {
for (pe = 0; pe < (seg->len / seg->stripes);
pe++) {
if (!_set_allocated(hash,
seg->area[s].pv,
seg->area[s].pe
+ pe)) {
stack;
goto out;
}
}
}
}
}
r = 1;
out:
hash_destroy(hash);
return r;
}
/*
* Areas are maintained in size order.
*/
static void _insert_area(struct list *head, struct pv_area *a)
{
struct list *pvah;
struct pv_area *pva;
if (list_empty(head)) {
list_add(head, &a->list);
return;
}
list_iterate (pvah, head) {
pva = list_item(pvah, struct pv_area);
if (pva->count < a->count)
break;
}
list_add_h(&pva->list, &a->list);
}
static int _create_single_area(struct pool *mem, struct pv_map *pvm,
uint32_t *extent)
{
uint32_t e = *extent, b, count = pvm->pv->pe_count;
struct pv_area *pva;
while (e < count && bit(pvm->allocated_extents, e))
e++;
if (e == count) {
*extent = e;
return 1;
}
b = e++;
while (e < count && !bit(pvm->allocated_extents, e))
e++;
if (!(pva = pool_zalloc(mem, sizeof(*pva)))) {
stack;
return 0;
}
pva->map = pvm;
pva->start = b;
pva->count = e - b;
_insert_area(&pvm->areas, pva);
*extent = e;
return 1;
}
static int _create_areas(struct pool *mem, struct pv_map *pvm)
{
uint32_t pe = 0;
while (pe < pvm->pv->pe_count)
if (!_create_single_area(mem, pvm, &pe)) {
stack;
return 0;
}
return 1;
}
static int _create_all_areas(struct pool *mem, struct list *maps)
{
struct list *tmp;
struct pv_map *pvm;
list_iterate(tmp, maps) {
pvm = list_item(tmp, struct pv_map);
if (!_create_areas(mem, pvm)) {
stack;
return 0;
}
}
return 1;
}
struct list *create_pv_maps(struct pool *mem, struct volume_group *vg,
struct list *pvs)
{
struct list *maps = pool_zalloc(mem, sizeof(*maps));
if (!maps) {
stack;
return NULL;
}
list_init(maps);
if (!_create_maps(mem, pvs, maps)) {
log_error("Couldn't create physical volume maps in %s",
vg->name);
goto bad;
}
if (!_fill_bitsets(vg, maps)) {
log_error("Couldn't fill extent allocation bitmaps in %s",
vg->name);
goto bad;
}
if (!_create_all_areas(mem, maps)) {
log_error("Couldn't create area maps in %s", vg->name);
goto bad;
}
return maps;
bad:
pool_free(mem, maps);
return NULL;
}
void consume_pv_area(struct pv_area *pva, uint32_t to_go)
{
list_del(&pva->list);
assert(to_go <= pva->count);
if (to_go < pva->count) {
/* split the area */
pva->start += to_go;
pva->count -= to_go;
_insert_area(&pva->map->areas, pva);
}
}

43
lib/metadata/pv_map.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#ifndef _LVM_PV_MAP_H
#define _LVM_PV_MAP_H
#include "metadata.h"
#include "bitset.h"
#include "pool.h"
/*
* The in core rep. only stores a mapping from
* logical extents to physical extents against an
* lv. Sometimes, when allocating a new lv for
* instance, it is useful to have the inverse
* mapping available.
*/
struct pv_area {
struct pv_map *map;
uint32_t start;
uint32_t count;
struct list list;
};
struct pv_map {
struct physical_volume *pv;
bitset_t allocated_extents;
struct list areas;
struct list list;
};
struct list *create_pv_maps(struct pool *mem,
struct volume_group *vg, struct list *pvs);
void consume_pv_area(struct pv_area *area, uint32_t to_go);
#endif

101
lib/misc/lvm-file.c Normal file
View File

@ -0,0 +1,101 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "log.h"
#include "lvm-file.h"
#include "lvm-string.h"
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
/*
* Creates a temporary filename, and opens a descriptor to the
* file. Both the filename and descriptor are needed so we can
* rename the file after successfully writing it. Grab
* NFS-supported exclusive fcntl discretionary lock.
*/
int create_temp_name(const char *dir, char *buffer, size_t len,
int *fd)
{
int i, num;
pid_t pid;
char hostname[255];
struct flock lock = {
l_type: F_WRLCK,
l_whence: 0,
l_start: 0,
l_len: 0
};
num = rand();
pid = getpid();
if (gethostname(hostname, sizeof(hostname)) < 0) {
log_sys_error("gethostname", "");
strcpy(hostname, "nohostname");
}
for (i = 0; i < 20; i++, num++) {
if (lvm_snprintf(buffer, len, "%s/.lvm_%s_%d_%d",
dir, hostname, pid, num) == -1) {
log_err("Not enough space to build temporary file "
"string.");
return 0;
}
*fd = open(buffer, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
S_IRUSR | S_IRGRP | S_IROTH |
S_IWUSR | S_IWGRP | S_IWOTH);
if (*fd < 0)
continue;
if (!fcntl(*fd, F_SETLK, &lock))
return 1;
close(*fd);
}
return 0;
}
/*
* NFS-safe rename of a temporary file to a common name, designed
* to avoid race conditions and not overwrite the destination if
* it exists.
*
* Try to create the new filename as a hard link to the original.
* Check the link count of the original file to see if it worked.
* (Assumes nothing else touches our temporary file!) If it
* worked, unlink the old filename.
*/
int lvm_rename(const char *old, const char *new)
{
struct stat buf;
link(old, new);
if (stat(old, &buf)) {
log_sys_error("stat", old);
return 0;
}
if (buf.st_nlink != 2) {
log_error("%s: rename to %s failed", old, new);
return 0;
}
if (unlink(old)) {
log_sys_error("unlink", old);
return 0;
}
return 1;
}

18
lib/misc/lvm-file.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
/*
* Create a temporary filename, and opens a descriptor to the file.
*/
int create_temp_name(const char *dir, char *buffer, size_t len, int *fd);
/*
* NFS-safe rename of a temporary file to a common name, designed
* to avoid race conditions and not overwrite the destination if
* it exists.
*/
int lvm_rename(const char *old, const char *new);

36
lib/misc/lvm-string.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_STRING_H
#define _LVM_STRING_H
#include <stdio.h>
#include <stdarg.h>
/*
* On error, up to glibc 2.0.6, snprintf returned -1 if buffer was too small;
* From glibc 2.1 it returns number of chars (excl. trailing null) that would
* have been written had there been room.
*
* lvm_snprintf reverts to the old behaviour.
*/
static inline int lvm_snprintf(char *buf, size_t bufsize,
const char *format, ...)
{
int n;
va_list ap;
va_start(ap, format);
n = vsnprintf(buf, bufsize, format, ap);
va_end(ap);
if (n < 0 || (n > bufsize - 1))
return -1;
return n;
}
#endif

193
lib/mm/dbg_malloc.c Normal file
View File

@ -0,0 +1,193 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.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 = %" PRIuPTR, 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 %" PRIdPTR,
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:
*/

42
lib/mm/dbg_malloc.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_DBG_MALLOC_H
#define _LVM_DBG_MALLOC_H
#include <stdlib.h>
#include <string.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
static inline char *dbg_strdup(const char *str)
{
char *ret = dbg_malloc(strlen(str) + 1);
if (ret)
strcpy(ret, str);
return ret;
}
#endif

210
lib/mm/pool-debug.c Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "pool.h"
#include "dbg_malloc.h"
#include "log.h"
#include <assert.h>
struct block {
struct block *next;
size_t size;
void *data;
};
struct pool {
int begun;
struct block *object;
struct block *blocks;
struct block *tail;
};
/* by default things come out aligned for doubles */
#define DEFAULT_ALIGNMENT __alignof__ (double)
struct pool *pool_create(size_t chunk_hint)
{
struct pool *mem = dbg_malloc(sizeof(*mem));
if (!mem) {
log_error("Couldn't create memory pool (size %u)",
sizeof(*mem));
return NULL;
}
mem->begun = 0;
mem->object = 0;
mem->blocks = mem->tail = NULL;
return mem;
}
static void _free_blocks(struct block *b)
{
struct block *n;
while (b) {
n = b->next;
dbg_free(b->data);
dbg_free(b);
b = n;
}
}
void pool_destroy(struct pool *p)
{
_free_blocks(p->blocks);
dbg_free(p);
}
void *pool_alloc(struct pool *p, size_t s)
{
return pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
}
static void _append_block(struct pool *p, struct block *b)
{
if (p->tail) {
p->tail->next = b;
p->tail = b;
} else
p->blocks = p->tail = b;
}
static struct block *_new_block(size_t s, unsigned alignment)
{
static char *_oom = "Out of memory";
/* FIXME: I'm currently ignoring the alignment arg. */
size_t len = sizeof(struct block) + s;
struct block *b = dbg_malloc(len);
/*
* Too lazy to implement alignment for debug version, and
* I don't think LVM will use anything but default
* align.
*/
assert(alignment == DEFAULT_ALIGNMENT);
if (!b) {
log_err(_oom);
return NULL;
}
if (!(b->data = dbg_malloc(s))) {
log_err(_oom);
dbg_free(b);
return NULL;
}
b->next = NULL;
b->size = s;
return b;
}
void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment)
{
struct block *b = _new_block(s, alignment);
if (!b)
return NULL;
_append_block(p, b);
return b->data;
}
void pool_empty(struct pool *p)
{
_free_blocks(p->blocks);
p->blocks = p->tail = NULL;
}
void pool_free(struct pool *p, void *ptr)
{
struct block *b, *prev = NULL;
for (b = p->blocks; b; b = b->next) {
if (b->data == ptr)
break;
prev = b;
}
/*
* If this fires then you tried to free a
* pointer that either wasn't from this
* pool, or isn't the start of a block.
*/
assert(b);
_free_blocks(b);
if (prev) {
p->tail = prev;
prev->next = NULL;
} else
p->blocks = p->tail = NULL;
}
int pool_begin_object(struct pool *p, size_t init_size)
{
assert(!p->begun);
p->begun = 1;
return 1;
}
int pool_grow_object(struct pool *p, const void *buffer, size_t delta)
{
struct block *new;
size_t size = delta;
assert(p->begun);
if (p->object)
size += p->object->size;
if (!(new = _new_block(size, DEFAULT_ALIGNMENT))) {
log_err("Couldn't extend object.");
return 0;
}
if (p->object) {
memcpy(new->data, p->object->data, p->object->size);
dbg_free(p->object);
}
p->object = new;
return 1;
}
void *pool_end_object(struct pool *p)
{
assert(p->begun);
_append_block(p, p->object);
p->begun = 0;
p->object = NULL;
return p->tail->data;
}
void pool_abandon_object(struct pool *p)
{
assert(p->begun);
dbg_free(p->object);
p->begun = 0;
p->object = NULL;
}
char *pool_strdup(struct pool *p, const char *str)
{
char *ret = pool_alloc(p, strlen(str) + 1);
if (ret)
strcpy(ret, str);
return ret;
}

240
lib/mm/pool-fast.c Normal file
View File

@ -0,0 +1,240 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#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_error("Couldn't create memory pool (size %u)",
sizeof(*p));
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);
if (!c)
return NULL;
_align_chunk(c, alignment);
}
r = c->begin;
c->begin += s;
return r;
}
void pool_empty(struct pool *p)
{
struct chunk *c;
for (c = p->chunk; c && c->prev; c = c->prev)
;
if (p->chunk)
pool_free(p, (char *) (p->chunk + 1));
}
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_error("Internal error: pool_free asked to free pointer "
"not in pool");
else
p->chunk = c;
}
int pool_begin_object(struct pool *p, size_t hint)
{
struct chunk *c = p->chunk;
const size_t align = DEFAULT_ALIGNMENT;
p->object_len = 0;
p->object_alignment = align;
if (c)
_align_chunk(c, align);
if (!c || (c->begin > c->end) || (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);
if (!c)
return 0;
_align_chunk(c, align);
}
return 1;
}
int pool_grow_object(struct pool *p, const void *extra, size_t n)
{
struct chunk *c = p->chunk, *nc;
if (c->end - (c->begin + p->object_len) < n) {
/* move into a new chunk */
if (p->object_len + n > (p->chunk_size / 2))
nc = _new_chunk(p, (p->object_len + n) * 2);
else
nc = _new_chunk(p, p->chunk_size);
if (!nc)
return 0;
_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, extra, n);
p->object_len += n;
return 1;
}
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 {
if (!(c = dbg_malloc(s))) {
log_err("Out of memory. Requested %u bytes.", s);
return NULL;
}
c->end = (char *) c + s;
}
c->prev = p->chunk;
c->begin = (char *) (c + 1);
p->chunk = c;
return c;
}

11
lib/mm/pool.c Normal file
View File

@ -0,0 +1,11 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifdef DEBUG_POOL
#include "pool-debug.c"
#else
#include "pool-fast.c"
#endif

121
lib/mm/pool.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_POOL_H
#define _LVM_POOL_H
#include <string.h>
#include <stdlib.h>
/*
* The pool allocator is useful when you are going to allocate
* lots of memory, use the memory for a bit, and then free the
* memory in one go. A surprising amount of code has this usage
* profile.
*
* You should think of the pool as an infinite, contiguous chunk
* of memory. The front of this chunk of memory contains
* allocated objects, the second half is free. pool_alloc grabs
* the next 'size' bytes from the free half, in effect moving it
* into the allocated half. This operation is very efficient.
*
* pool_free frees the allocated object *and* all objects
* allocated after it. It is important to note this semantic
* difference from malloc/free. This is also extremely
* efficient, since a single pool_free can dispose of a large
* complex object.
*
* pool_destroy frees all allocated memory.
*
* eg, If you are building a binary tree in your program, and
* know that you are only ever going to insert into your tree,
* and not delete (eg, maintaining a symbol table for a
* compiler). You can create yourself a pool, allocate the nodes
* from it, and when the tree becomes redundant call pool_destroy
* (no nasty iterating through the tree to free nodes).
*
* eg, On the other hand if you wanted to repeatedly insert and
* remove objects into the tree, you would be better off
* allocating the nodes from a free list; you cannot free a
* single arbitrary node with pool.
*/
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:
*
* These allow you to 'grow' an object, useful for
* building strings, or filling in dynamic
* arrays.
*
* It's probably best explained with an example:
*
* char *build_string(struct pool *mem)
* {
* int i;
* char buffer[16];
*
* if (!pool_begin_object(mem, 128))
* return NULL;
*
* for (i = 0; i < 50; i++) {
* snprintf(buffer, sizeof(buffer), "%d, ", i);
* if (!pool_grow_object(mem, buffer, strlen(buffer)))
* goto bad;
* }
*
* // add null
* if (!pool_grow_object(mem, "\0", 1))
* goto bad;
*
* return pool_end_object(mem);
*
* bad:
*
* pool_abandon_object(mem);
* return NULL;
*}
*
* So start an object by calling pool_begin_object
* with a guess at the final object size - if in
* doubt make the guess too small.
*
* Then append chunks of data to your object with
* pool_grow_object. Finally get your object with
* a call to pool_end_object.
*
*/
int pool_begin_object(struct pool *p, size_t hint);
int pool_grow_object(struct pool *p, const void *extra, size_t delta);
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

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

349
lib/regex/matcher.c Normal file
View File

@ -0,0 +1,349 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "matcher.h"
#include "parse_rx.h"
#include "log.h"
#include "ttree.h"
#include "bitset.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
struct dfa_state {
int final;
struct dfa_state *lookup[256];
};
struct state_queue {
struct dfa_state *s;
bitset_t bits;
struct state_queue *next;
};
struct matcher { /* Instance variables for the lexer */
struct dfa_state *start;
int num_nodes, nodes_entered;
struct rx_node **nodes;
struct pool *scratch, *mem;
};
#define TARGET_TRANS '\0'
static int _count_nodes(struct rx_node *rx)
{
int r = 1;
if(rx->left)
r += _count_nodes(rx->left);
if(rx->right)
r += _count_nodes(rx->right);
return r;
}
static void _fill_table(struct matcher *m, struct rx_node *rx)
{
assert((rx->type != OR) || (rx->left && rx->right));
if(rx->left)
_fill_table(m, rx->left);
if(rx->right)
_fill_table(m, rx->right);
m->nodes[m->nodes_entered++] = rx;
}
static void _create_bitsets(struct matcher *m)
{
int i;
for(i = 0; i < m->num_nodes; i++) {
struct rx_node *n = m->nodes[i];
n->firstpos = bitset_create(m->scratch, m->num_nodes);
n->lastpos = bitset_create(m->scratch, m->num_nodes);
n->followpos = bitset_create(m->scratch, m->num_nodes);
}
}
static void _calc_functions(struct matcher *m)
{
int i, j, final = 1;
struct rx_node *rx, *c1, *c2;
for(i = 0; i < m->num_nodes; i++) {
rx = m->nodes[i];
c1 = rx->left;
c2 = rx->right;
if(bit(rx->charset, TARGET_TRANS))
rx->final = final++;
switch(rx->type) {
case CAT:
if(c1->nullable)
bit_union(rx->firstpos,
c1->firstpos, c2->firstpos);
else
bit_copy(rx->firstpos, c1->firstpos);
if(c2->nullable)
bit_union(rx->lastpos,
c1->lastpos, c2->lastpos);
else
bit_copy(rx->lastpos, c2->lastpos);
rx->nullable = c1->nullable && c2->nullable;
break;
case PLUS:
bit_copy(rx->firstpos, c1->firstpos);
bit_copy(rx->lastpos, c1->lastpos);
rx->nullable = c1->nullable;
break;
case OR:
bit_union(rx->firstpos, c1->firstpos, c2->firstpos);
bit_union(rx->lastpos, c1->lastpos, c2->lastpos);
rx->nullable = c1->nullable || c2->nullable;
break;
case QUEST:
case STAR:
bit_copy(rx->firstpos, c1->firstpos);
bit_copy(rx->lastpos, c1->lastpos);
rx->nullable = 1;
break;
case CHARSET:
bit_set(rx->firstpos, i);
bit_set(rx->lastpos, i);
rx->nullable = 0;
break;
default:
log_error("Internal error: Unknown calc node type");
}
/*
* followpos has it's own switch
* because PLUS and STAR do the
* same thing.
*/
switch(rx->type) {
case CAT:
for(j = 0; j < m->num_nodes; j++) {
if(bit(c1->lastpos, j)) {
struct rx_node *n = m->nodes[j];
bit_union(n->followpos,
n->followpos, c2->firstpos);
}
}
break;
case PLUS:
case STAR:
for(j = 0; j < m->num_nodes; j++) {
if(bit(rx->lastpos, j)) {
struct rx_node *n = m->nodes[j];
bit_union(n->followpos,
n->followpos, rx->firstpos);
}
}
break;
}
}
}
static inline struct dfa_state *_create_dfa_state(struct pool *mem)
{
return pool_zalloc(mem, sizeof(struct dfa_state));
}
static struct state_queue *_create_state_queue(struct pool *mem,
struct dfa_state *dfa,
bitset_t bits)
{
struct state_queue *r = pool_alloc(mem, sizeof(*r));
if (!r) {
stack;
return NULL;
}
r->s = dfa;
r->bits = bitset_create(mem, bits[0]); /* first element is the size */
bit_copy(r->bits, bits);
r->next = 0;
return r;
}
static int _calc_states(struct matcher *m, struct rx_node *rx)
{
int iwidth = (m->num_nodes / BITS_PER_INT) + 1;
struct ttree *tt = ttree_create(m->scratch, iwidth);
struct state_queue *h, *t, *tmp;
struct dfa_state *dfa, *ldfa;
int i, a, set_bits = 0, count = 0;
bitset_t bs = bitset_create(m->scratch, m->num_nodes), dfa_bits;
if (!tt) {
stack;
return 0;
}
if (!bs) {
stack;
return 0;
}
/* create first state */
dfa = _create_dfa_state(m->mem);
m->start = dfa;
ttree_insert(tt, rx->firstpos + 1, dfa);
/* prime the queue */
h = t = _create_state_queue(m->scratch, dfa, rx->firstpos);
while (h) {
/* pop state off front of the queue */
dfa = h->s;
dfa_bits = h->bits;
h = h->next;
/* iterate through all the inputs for this state */
bit_clear_all(bs);
for(a = 0; a < 256; a++) {
/* iterate through all the states in firstpos */
for(i = bit_get_first(dfa_bits);
i >=0;
i = bit_get_next(dfa_bits, i)) {
if(bit(m->nodes[i]->charset, a)) {
if(a == TARGET_TRANS)
dfa->final = m->nodes[i]->final;
bit_union(bs, bs, m->nodes[i]->followpos);
set_bits = 1;
}
}
if(set_bits) {
ldfa = ttree_lookup(tt, bs + 1);
if(!ldfa) {
/* push */
ldfa = _create_dfa_state(m->mem);
ttree_insert(tt, bs + 1, ldfa);
tmp = _create_state_queue(m->scratch, ldfa, bs);
if(!h)
h = t = tmp;
else {
t->next = tmp;
t = tmp;
}
count++;
}
dfa->lookup[a] = ldfa;
set_bits = 0;
bit_clear_all(bs);
}
}
}
log_debug("Matcher built with %d dfa states", count);
return 1;
}
struct matcher *matcher_create(struct pool *mem,
const char **patterns, int num)
{
char *all, *ptr;
int i, len = 0;
struct rx_node *rx;
struct pool *scratch = pool_create(10 * 1024);
struct matcher *m;
if (!scratch) {
stack;
return NULL;
}
if (!(m = pool_alloc(mem, sizeof(*m)))) {
stack;
return NULL;
}
memset(m, 0, sizeof(*m));
/* join the regexps together, delimiting with zero */
for(i = 0; i < num; i++)
len += strlen(patterns[i]) + 8;
ptr = all = pool_alloc(scratch, len + 1);
if (!all) {
stack;
goto bad;
}
for(i = 0; i < num; i++) {
ptr += sprintf(ptr, "(.*(%s)%c)", patterns[i],
TARGET_TRANS);
if(i < (num - 1))
*ptr++ = '|';
}
/* parse this expression */
if(!(rx = rx_parse_tok(scratch, all, ptr))) {
log_error("Couldn't parse regex");
goto bad;
}
m->mem = mem;
m->scratch = scratch;
m->num_nodes = _count_nodes(rx);
m->nodes = pool_alloc(scratch, sizeof(*m->nodes) * m->num_nodes);
if (!m->nodes) {
stack;
goto bad;
}
_fill_table(m, rx);
_create_bitsets(m);
_calc_functions(m);
_calc_states(m, rx);
pool_destroy(scratch);
m->scratch = NULL;
return m;
bad:
pool_destroy(scratch);
pool_destroy(mem);
return NULL;
}
int matcher_run(struct matcher *m, const char *b)
{
struct dfa_state *cs = m->start;
int r = 0;
for (; *b; b++) {
if (!(cs = cs->lookup[(int) (unsigned char) *b]))
break;
if (cs->final && (cs->final > r))
r = cs->final;
}
/* subtract 1 to get back to zero index */
return r - 1;
}

18
lib/regex/matcher.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_MATCHER_H
#define _LVM_MATCHER_H
#include "pool.h"
struct matcher;
struct matcher *matcher_create(struct pool *mem,
const char **patterns, int num);
int matcher_run(struct matcher *m, const char *begin);
#endif

333
lib/regex/parse_rx.c Normal file
View File

@ -0,0 +1,333 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "parse_rx.h"
#include "bitset.h"
#include "log.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct parse_sp { /* scratch pad for the parsing process */
struct pool *mem;
int type; /* token type, 0 indicates a charset */
bitset_t charset; /* The current charset */
const char *cursor; /* where we are in the regex */
const char *rx_end; /* 1pte for the expression being parsed */
};
static struct rx_node *_or_term(struct parse_sp *ps);
/*
* Get the next token from the regular expression.
* Returns: 1 success, 0 end of input, -1 error.
*/
static int _get_token(struct parse_sp *ps)
{
int neg = 0, range = 0;
char c, lc = 0;
const char *ptr = ps->cursor;
if(ptr == ps->rx_end) { /* end of input ? */
ps->type = -1;
return 0;
}
switch(*ptr) {
/* charsets and ncharsets */
case '[':
ptr++;
if(*ptr == '^') {
bit_set_all(ps->charset);
/* never transition on zero */
bit_clear(ps->charset, 0);
neg = 1;
ptr++;
} else
bit_clear_all(ps->charset);
while((ptr < ps->rx_end) && (*ptr != ']')) {
if(*ptr == '\\') {
/* an escaped character */
ptr++;
switch(*ptr) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
default:
c = *ptr;
}
} else if(*ptr == '-' && lc) {
/* we've got a range on our hands */
range = 1;
ptr++;
if(ptr == ps->rx_end) {
log_error("Incomplete range"
"specification");
return -1;
}
c = *ptr;
} else
c = *ptr;
if(range) {
/* add lc - c into the bitset */
if(lc > c) {
char tmp = c;
c = lc;
lc = tmp;
}
for(; lc <= c; lc++) {
if(neg)
bit_clear(ps->charset, lc);
else
bit_set(ps->charset, lc);
}
range = 0;
} else {
/* add c into the bitset */
if(neg)
bit_clear(ps->charset, c);
else
bit_set(ps->charset, c);
}
ptr++;
lc = c;
}
if(ptr >= ps->rx_end) {
ps->type = -1;
return -1;
}
ps->type = 0;
ps->cursor = ptr + 1;
break;
/* These characters are special, we just return their ASCII
codes as the type. Sorted into ascending order to help the
compiler */
case '(':
case ')':
case '*':
case '+':
case '?':
case '|':
case '^':
case '$':
ps->type = (int) *ptr;
ps->cursor = ptr + 1;
break;
case '.':
/* The 'all but newline' character set */
ps->type = 0;
ps->cursor = ptr + 1;
bit_set_all(ps->charset);
bit_clear(ps->charset, (int) '\n');
bit_clear(ps->charset, (int) '\r');
bit_clear(ps->charset, 0);
break;
case '\\':
/* escaped character */
ptr++;
if(ptr >= ps->rx_end) {
log_error("Badly quoted character at end "
"of expression");
ps->type = -1;
return -1;
}
ps->type = 0;
ps->cursor = ptr + 1;
bit_clear_all(ps->charset);
switch(*ptr) {
case 'n': bit_set(ps->charset, (int) '\n'); break;
case 'r': bit_set(ps->charset, (int) '\r'); break;
case 't': bit_set(ps->charset, (int) '\t'); break;
default:
bit_set(ps->charset, (int) *ptr);
}
break;
default:
/* add a single character to the bitset */
ps->type = 0;
ps->cursor = ptr + 1;
bit_clear_all(ps->charset);
bit_set(ps->charset, (int) *ptr);
break;
}
return 1;
}
static struct rx_node *_node(struct pool *mem, int type,
struct rx_node *l, struct rx_node *r)
{
struct rx_node *n = pool_zalloc(mem, sizeof(*n));
if (n) {
if (!(n->charset = bitset_create(mem, 256))) {
pool_free(mem, n);
return NULL;
}
n->type = type;
n->left = l;
n->right = r;
}
return n;
}
static struct rx_node *_term(struct parse_sp *ps)
{
struct rx_node *n;
switch(ps->type) {
case 0:
if (!(n = _node(ps->mem, CHARSET, NULL, NULL))) {
stack;
return NULL;
}
bit_copy(n->charset, ps->charset);
_get_token(ps); /* match charset */
break;
case '(':
_get_token(ps); /* match '(' */
n = _or_term(ps);
if(ps->type != ')') {
log_error("missing ')' in regular expression");
return 0;
}
_get_token(ps); /* match ')' */
break;
default:
n = 0;
}
return n;
}
static struct rx_node *_closure_term(struct parse_sp *ps)
{
struct rx_node *l, *n;
if(!(l = _term(ps)))
return NULL;
for (;;) {
switch(ps->type) {
case '*':
n = _node(ps->mem, STAR, l, NULL);
break;
case '+':
n = _node(ps->mem, PLUS, l, NULL);
break;
case '?':
n = _node(ps->mem, QUEST, l, NULL);
break;
default:
return l;
}
if (!n) {
stack;
return NULL;
}
_get_token(ps);
l = n;
}
return n;
}
static struct rx_node *_cat_term(struct parse_sp *ps)
{
struct rx_node *l, *r, *n;
if (!(l = _closure_term(ps)))
return NULL;
if (ps->type == '|')
return l;
if (!(r = _cat_term(ps)))
return l;
if (!(n = _node(ps->mem, CAT, l, r)))
stack;
return n;
}
static struct rx_node *_or_term(struct parse_sp *ps)
{
struct rx_node *l, *r, *n;
if (!(l = _cat_term(ps)))
return NULL;
if (ps->type != '|')
return l;
_get_token(ps); /* match '|' */
if (!(r = _or_term(ps))) {
log_error("Badly formed 'or' expression");
return NULL;
}
if (!(n = _node(ps->mem, OR, l, r)))
stack;
return n;
}
struct rx_node *rx_parse_tok(struct pool *mem,
const char *begin, const char *end)
{
struct rx_node *r;
struct parse_sp *ps = pool_zalloc(mem, sizeof(*ps));
if (!ps) {
stack;
return NULL;
}
ps->mem = mem;
ps->charset = bitset_create(mem, 256);
ps->cursor = begin;
ps->rx_end = end;
_get_token(ps); /* load the first token */
if (!(r = _or_term(ps))) {
log_error("Parse error in regex");
pool_free(mem, ps);
}
return r;
}
struct rx_node *rx_parse_str(struct pool *mem, const char *str)
{
return rx_parse_tok(mem, str, str + strlen(str));
}

39
lib/regex/parse_rx.h Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_PARSE_REGEX_H
#define _LVM_PARSE_REGEX_H
#include "bitset.h"
enum {
CAT,
STAR,
PLUS,
OR,
QUEST,
CHARSET,
HAT,
DOLLAR
};
struct rx_node {
int type;
bitset_t charset;
struct rx_node *left, *right;
/* used to build the dfa for the toker */
int nullable, final;
bitset_t firstpos;
bitset_t lastpos;
bitset_t followpos;
};
struct rx_node *rx_parse_str(struct pool *mem, const char *str);
struct rx_node *rx_parse_tok(struct pool *mem,
const char *begin, const char *end);
#endif

110
lib/regex/ttree.c Normal file
View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "ttree.h"
#include "pool.h"
#include "log.h"
struct node {
unsigned k;
struct node *l, *m, *r;
void *data;
};
struct ttree {
int klen;
struct pool *mem;
struct node *root;
};
struct node **_lookup_single(struct node **c, unsigned int k)
{
while (*c) {
if (k < (*c)->k)
c = &((*c)->l);
else if (k > (*c)->k)
c = &((*c)->r);
else {
c = &((*c)->m);
break;
}
}
return c;
}
void *ttree_lookup(struct ttree *tt, unsigned *key)
{
struct node **c = &tt->root;
int count = tt->klen;
while (*c && count) {
c = _lookup_single(c, *key++);
count--;
}
return *c ? (*c)->data : NULL;
}
static struct node *_node(struct pool *mem, unsigned int k)
{
struct node *n = pool_zalloc(mem, sizeof(*n));
if (n)
n->k = k;
return n;
}
int ttree_insert(struct ttree *tt, unsigned int *key, void *data)
{
struct node **c = &tt->root;
int count = tt->klen;
unsigned int k;
do {
k = *key++;
c = _lookup_single(c, k);
count--;
} while (*c && count);
if (!*c) {
count++;
while (count--) {
if (!(*c = _node(tt->mem, k))) {
stack;
return 0;
}
k = *key++;
if (count)
c = &((*c)->m);
}
}
(*c)->data = data;
return 1;
}
struct ttree *ttree_create(struct pool *mem, unsigned int klen)
{
struct ttree *tt;
if (!(tt = pool_zalloc(mem, sizeof(*tt)))) {
stack;
return NULL;
}
tt->klen = klen;
tt->mem = mem;
return tt;
}

Some files were not shown because too many files have changed in this diff Show More