1/*	$NetBSD: format1.c,v 1.1.1.2 2009/12/02 00:26:48 haad Exp $	*/
2
3/*
4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6 *
7 * This file is part of LVM2.
8 *
9 * This copyrighted material is made available to anyone wishing to use,
10 * modify, copy, or redistribute it subject to the terms and conditions
11 * of the GNU Lesser General Public License v.2.1.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17
18#include "lib.h"
19#include "disk-rep.h"
20#include "limits.h"
21#include "display.h"
22#include "toolcontext.h"
23#include "lvm1-label.h"
24#include "format1.h"
25#include "segtype.h"
26
27/* VG consistency checks */
28static int _check_vgs(struct dm_list *pvs)
29{
30	struct dm_list *pvh, *t;
31	struct disk_list *dl = NULL;
32	struct disk_list *first = NULL;
33
34	uint32_t pv_count = 0;
35	uint32_t exported = 0;
36	int first_time = 1;
37
38	/*
39	 * If there are exported and unexported PVs, ignore exported ones.
40	 * This means an active VG won't be affected if disks are inserted
41	 * bearing an exported VG with the same name.
42	 */
43	dm_list_iterate_items(dl, pvs) {
44		if (first_time) {
45			exported = dl->pvd.pv_status & VG_EXPORTED;
46			first_time = 0;
47			continue;
48		}
49
50		if (exported != (dl->pvd.pv_status & VG_EXPORTED)) {
51			/* Remove exported PVs */
52			dm_list_iterate_safe(pvh, t, pvs) {
53				dl = dm_list_item(pvh, struct disk_list);
54				if (dl->pvd.pv_status & VG_EXPORTED)
55					dm_list_del(pvh);
56			}
57			break;
58		}
59	}
60
61	/* Remove any PVs with VG structs that differ from the first */
62	dm_list_iterate_safe(pvh, t, pvs) {
63		dl = dm_list_item(pvh, struct disk_list);
64
65		if (!first)
66			first = dl;
67
68		else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) {
69			log_error("VG data differs between PVs %s and %s",
70				  dev_name(first->dev), dev_name(dl->dev));
71			log_debug("VG data on %s: %s %s %" PRIu32 " %" PRIu32
72				  "  %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
73				  PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
74				  " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
75				  PRIu32 " %" PRIu32 " %" PRIu32,
76				  dev_name(first->dev), first->vgd.vg_uuid,
77				  first->vgd.vg_name_dummy,
78				  first->vgd.vg_number, first->vgd.vg_access,
79				  first->vgd.vg_status, first->vgd.lv_max,
80				  first->vgd.lv_cur, first->vgd.lv_open,
81				  first->vgd.pv_max, first->vgd.pv_cur,
82				  first->vgd.pv_act, first->vgd.dummy,
83				  first->vgd.vgda, first->vgd.pe_size,
84				  first->vgd.pe_total, first->vgd.pe_allocated,
85				  first->vgd.pvg_total);
86			log_debug("VG data on %s: %s %s %" PRIu32 " %" PRIu32
87				  "  %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
88				  PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
89				  " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
90				  PRIu32 " %" PRIu32 " %" PRIu32,
91				  dev_name(dl->dev), dl->vgd.vg_uuid,
92				  dl->vgd.vg_name_dummy, dl->vgd.vg_number,
93				  dl->vgd.vg_access, dl->vgd.vg_status,
94				  dl->vgd.lv_max, dl->vgd.lv_cur,
95				  dl->vgd.lv_open, dl->vgd.pv_max,
96				  dl->vgd.pv_cur, dl->vgd.pv_act, dl->vgd.dummy,
97				  dl->vgd.vgda, dl->vgd.pe_size,
98				  dl->vgd.pe_total, dl->vgd.pe_allocated,
99				  dl->vgd.pvg_total);
100			dm_list_del(pvh);
101			return 0;
102		}
103		pv_count++;
104	}
105
106	/* On entry to fn, list known to be non-empty */
107	if (pv_count != first->vgd.pv_cur) {
108		log_error("%d PV(s) found for VG %s: expected %d",
109			  pv_count, first->pvd.vg_name, first->vgd.pv_cur);
110	}
111
112	return 1;
113}
114
115static struct volume_group *_build_vg(struct format_instance *fid,
116				      struct dm_list *pvs,
117				      struct dm_pool *mem)
118{
119	struct volume_group *vg = dm_pool_alloc(mem, sizeof(*vg));
120	struct disk_list *dl;
121
122	if (!vg)
123		goto_bad;
124
125	if (dm_list_empty(pvs))
126		goto_bad;
127
128	memset(vg, 0, sizeof(*vg));
129
130	vg->cmd = fid->fmt->cmd;
131	vg->vgmem = mem;
132	vg->fid = fid;
133	vg->seqno = 0;
134	dm_list_init(&vg->pvs);
135	dm_list_init(&vg->lvs);
136	dm_list_init(&vg->tags);
137	dm_list_init(&vg->removed_pvs);
138
139	if (!_check_vgs(pvs))
140		goto_bad;
141
142	dl = dm_list_item(pvs->n, struct disk_list);
143
144	if (!import_vg(mem, vg, dl))
145		goto_bad;
146
147	if (!import_pvs(fid->fmt, mem, vg, pvs, &vg->pvs, &vg->pv_count))
148		goto_bad;
149
150	if (!import_lvs(mem, vg, pvs))
151		goto_bad;
152
153	if (!import_extents(fid->fmt->cmd, vg, pvs))
154		goto_bad;
155
156	if (!import_snapshots(mem, vg, pvs))
157		goto_bad;
158
159	return vg;
160
161      bad:
162	dm_pool_free(mem, vg);
163	return NULL;
164}
165
166static struct volume_group *_format1_vg_read(struct format_instance *fid,
167				     const char *vg_name,
168				     struct metadata_area *mda __attribute((unused)))
169{
170	struct dm_pool *mem = dm_pool_create("lvm1 vg_read", VG_MEMPOOL_CHUNK);
171	struct dm_list pvs;
172	struct volume_group *vg = NULL;
173	dm_list_init(&pvs);
174
175	if (!mem)
176		return_NULL;
177
178	/* Strip dev_dir if present */
179	vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir);
180
181	if (!read_pvs_in_vg
182	    (fid->fmt, vg_name, fid->fmt->cmd->filter, mem, &pvs))
183		goto_bad;
184
185	if (!(vg = _build_vg(fid, &pvs, mem)))
186		goto_bad;
187
188	return vg;
189bad:
190	dm_pool_destroy(mem);
191	return NULL;
192}
193
194static struct disk_list *_flatten_pv(struct format_instance *fid,
195				     struct dm_pool *mem, struct volume_group *vg,
196				     struct physical_volume *pv,
197				     const char *dev_dir)
198{
199	struct disk_list *dl = dm_pool_alloc(mem, sizeof(*dl));
200
201	if (!dl)
202		return_NULL;
203
204	dl->mem = mem;
205	dl->dev = pv->dev;
206
207	dm_list_init(&dl->uuids);
208	dm_list_init(&dl->lvds);
209
210	if (!export_pv(fid->fmt->cmd, mem, vg, &dl->pvd, pv) ||
211	    !export_vg(&dl->vgd, vg) ||
212	    !export_uuids(dl, vg) ||
213	    !export_lvs(dl, vg, pv, dev_dir) || !calculate_layout(dl)) {
214		dm_pool_free(mem, dl);
215		return_NULL;
216	}
217
218	return dl;
219}
220
221static int _flatten_vg(struct format_instance *fid, struct dm_pool *mem,
222		       struct volume_group *vg,
223		       struct dm_list *pvds, const char *dev_dir,
224		       struct dev_filter *filter)
225{
226	struct pv_list *pvl;
227	struct disk_list *data;
228
229	dm_list_iterate_items(pvl, &vg->pvs) {
230		if (!(data = _flatten_pv(fid, mem, vg, pvl->pv, dev_dir)))
231			return_0;
232
233		dm_list_add(pvds, &data->list);
234	}
235
236	export_numbers(pvds, vg);
237	export_pv_act(pvds);
238
239	if (!export_vg_number(fid, pvds, vg->name, filter))
240		return_0;
241
242	return 1;
243}
244
245static int _format1_vg_write(struct format_instance *fid, struct volume_group *vg,
246		     struct metadata_area *mda __attribute((unused)))
247{
248	struct dm_pool *mem = dm_pool_create("lvm1 vg_write", VG_MEMPOOL_CHUNK);
249	struct dm_list pvds;
250	int r = 0;
251
252	if (!mem)
253		return_0;
254
255	dm_list_init(&pvds);
256
257	r = (_flatten_vg(fid, mem, vg, &pvds, fid->fmt->cmd->dev_dir,
258			 fid->fmt->cmd->filter) &&
259	     write_disks(fid->fmt, &pvds));
260
261	lvmcache_update_vg(vg, 0);
262	dm_pool_destroy(mem);
263	return r;
264}
265
266static int _format1_pv_read(const struct format_type *fmt, const char *pv_name,
267		    struct physical_volume *pv, struct dm_list *mdas __attribute((unused)),
268		    int scan_label_only __attribute((unused)))
269{
270	struct dm_pool *mem = dm_pool_create("lvm1 pv_read", 1024);
271	struct disk_list *dl;
272	struct device *dev;
273	int r = 0;
274
275	log_very_verbose("Reading physical volume data %s from disk", pv_name);
276
277	if (!mem)
278		return_0;
279
280	if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
281		goto_out;
282
283	if (!(dl = read_disk(fmt, dev, mem, NULL)))
284		goto_out;
285
286	if (!import_pv(fmt, fmt->cmd->mem, dl->dev, NULL, pv, &dl->pvd, &dl->vgd))
287		goto_out;
288
289	pv->fmt = fmt;
290
291	r = 1;
292
293      out:
294	dm_pool_destroy(mem);
295	return r;
296}
297
298static int _format1_pv_setup(const struct format_type *fmt,
299		     uint64_t pe_start, uint32_t extent_count,
300		     uint32_t extent_size,
301		     unsigned long data_alignment __attribute((unused)),
302		     unsigned long data_alignment_offset __attribute((unused)),
303		     int pvmetadatacopies __attribute((unused)),
304		     uint64_t pvmetadatasize __attribute((unused)), struct dm_list *mdas __attribute((unused)),
305		     struct physical_volume *pv, struct volume_group *vg __attribute((unused)))
306{
307	if (pv->size > MAX_PV_SIZE)
308		pv->size--;
309	if (pv->size > MAX_PV_SIZE) {
310		log_error("Physical volumes cannot be bigger than %s",
311			  display_size(fmt->cmd, (uint64_t) MAX_PV_SIZE));
312		return 0;
313	}
314
315	/* Nothing more to do if extent size isn't provided */
316	if (!extent_size)
317		return 1;
318
319	/*
320	 * This works out pe_start and pe_count.
321	 */
322	if (!calculate_extent_count(pv, extent_size, extent_count, pe_start))
323		return_0;
324
325	/* Retain existing extent locations exactly */
326	if (((pe_start || extent_count) && (pe_start != pv->pe_start)) ||
327	    (extent_count && (extent_count != pv->pe_count))) {
328		log_error("Metadata would overwrite physical extents");
329		return 0;
330	}
331
332	return 1;
333}
334
335static int _format1_lv_setup(struct format_instance *fid, struct logical_volume *lv)
336{
337	uint64_t max_size = UINT_MAX;
338
339	if (!*lv->lvid.s)
340		lvid_from_lvnum(&lv->lvid, &lv->vg->id, find_free_lvnum(lv));
341
342	if (lv->le_count > MAX_LE_TOTAL) {
343		log_error("logical volumes cannot contain more than "
344			  "%d extents.", MAX_LE_TOTAL);
345		return 0;
346	}
347	if (lv->size > max_size) {
348		log_error("logical volumes cannot be larger than %s",
349			  display_size(fid->fmt->cmd, max_size));
350		return 0;
351	}
352
353	return 1;
354}
355
356static int _format1_pv_write(const struct format_type *fmt, struct physical_volume *pv,
357		     struct dm_list *mdas __attribute((unused)), int64_t sector __attribute((unused)))
358{
359	struct dm_pool *mem;
360	struct disk_list *dl;
361	struct dm_list pvs;
362	struct label *label;
363	struct lvmcache_info *info;
364
365	if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev,
366				  pv->vg_name, NULL, 0)))
367		return_0;
368	label = info->label;
369	info->device_size = pv->size << SECTOR_SHIFT;
370	info->fmt = fmt;
371
372	dm_list_init(&info->mdas);
373
374	dm_list_init(&pvs);
375
376	/* Ensure any residual PE structure is gone */
377	pv->pe_size = pv->pe_count = 0;
378	pv->pe_start = LVM1_PE_ALIGN;
379
380	if (!(mem = dm_pool_create("lvm1 pv_write", 1024)))
381		return_0;
382
383	if (!(dl = dm_pool_alloc(mem, sizeof(*dl))))
384		goto_bad;
385
386	dl->mem = mem;
387	dl->dev = pv->dev;
388
389	if (!export_pv(fmt->cmd, mem, NULL, &dl->pvd, pv))
390		goto_bad;
391
392	/* must be set to be able to zero gap after PV structure in
393	   dev_write in order to make other disk tools happy */
394	dl->pvd.pv_on_disk.base = METADATA_BASE;
395	dl->pvd.pv_on_disk.size = PV_SIZE;
396	dl->pvd.pe_on_disk.base = LVM1_PE_ALIGN << SECTOR_SHIFT;
397
398	dm_list_add(&pvs, &dl->list);
399	if (!write_disks(fmt, &pvs))
400		goto_bad;
401
402	dm_pool_destroy(mem);
403	return 1;
404
405      bad:
406	dm_pool_destroy(mem);
407	return 0;
408}
409
410static int _format1_vg_setup(struct format_instance *fid, struct volume_group *vg)
411{
412	/* just check max_pv and max_lv */
413	if (!vg->max_lv || vg->max_lv >= MAX_LV)
414		vg->max_lv = MAX_LV - 1;
415
416	if (!vg->max_pv || vg->max_pv >= MAX_PV)
417		vg->max_pv = MAX_PV - 1;
418
419	if (vg->extent_size > MAX_PE_SIZE || vg->extent_size < MIN_PE_SIZE) {
420		log_error("Extent size must be between %s and %s",
421			  display_size(fid->fmt->cmd, (uint64_t) MIN_PE_SIZE),
422			  display_size(fid->fmt->cmd, (uint64_t) MAX_PE_SIZE));
423
424		return 0;
425	}
426
427	if (vg->extent_size % MIN_PE_SIZE) {
428		log_error("Extent size must be multiple of %s",
429			  display_size(fid->fmt->cmd, (uint64_t) MIN_PE_SIZE));
430		return 0;
431	}
432
433	/* Redundant? */
434	if (vg->extent_size & (vg->extent_size - 1)) {
435		log_error("Extent size must be power of 2");
436		return 0;
437	}
438
439	return 1;
440}
441
442static int _format1_segtype_supported(struct format_instance *fid __attribute((unused)),
443				      const struct segment_type *segtype)
444{
445	if (!(segtype->flags & SEG_FORMAT1_SUPPORT))
446		return_0;
447
448	return 1;
449}
450
451static struct metadata_area_ops _metadata_format1_ops = {
452	.vg_read = _format1_vg_read,
453	.vg_write = _format1_vg_write,
454};
455
456static struct format_instance *_format1_create_instance(const struct format_type *fmt,
457						const char *vgname __attribute((unused)),
458						const char *vgid __attribute((unused)),
459						void *private __attribute((unused)))
460{
461	struct format_instance *fid;
462	struct metadata_area *mda;
463
464	if (!(fid = dm_pool_alloc(fmt->cmd->mem, sizeof(*fid))))
465		return_NULL;
466
467	fid->fmt = fmt;
468	dm_list_init(&fid->metadata_areas);
469
470	/* Define a NULL metadata area */
471	if (!(mda = dm_pool_alloc(fmt->cmd->mem, sizeof(*mda)))) {
472		dm_pool_free(fmt->cmd->mem, fid);
473		return_NULL;
474	}
475
476	mda->ops = &_metadata_format1_ops;
477	mda->metadata_locn = NULL;
478	dm_list_add(&fid->metadata_areas, &mda->list);
479
480	return fid;
481}
482
483static void _format1_destroy_instance(struct format_instance *fid __attribute((unused)))
484{
485	return;
486}
487
488static void _format1_destroy(const struct format_type *fmt)
489{
490	dm_free((void *) fmt);
491}
492
493static struct format_handler _format1_ops = {
494	.pv_read = _format1_pv_read,
495	.pv_setup = _format1_pv_setup,
496	.pv_write = _format1_pv_write,
497	.lv_setup = _format1_lv_setup,
498	.vg_setup = _format1_vg_setup,
499	.segtype_supported = _format1_segtype_supported,
500	.create_instance = _format1_create_instance,
501	.destroy_instance = _format1_destroy_instance,
502	.destroy = _format1_destroy,
503};
504
505#ifdef LVM1_INTERNAL
506struct format_type *init_lvm1_format(struct cmd_context *cmd)
507#else				/* Shared */
508struct format_type *init_format(struct cmd_context *cmd);
509struct format_type *init_format(struct cmd_context *cmd)
510#endif
511{
512	struct format_type *fmt = dm_malloc(sizeof(*fmt));
513
514	if (!fmt)
515		return_NULL;
516
517	fmt->cmd = cmd;
518	fmt->ops = &_format1_ops;
519	fmt->name = FMT_LVM1_NAME;
520	fmt->alias = NULL;
521	fmt->orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME;
522	fmt->features = FMT_RESTRICTED_LVIDS | FMT_ORPHAN_ALLOCATABLE |
523			FMT_RESTRICTED_READAHEAD;
524	fmt->private = NULL;
525
526	if (!(fmt->labeller = lvm1_labeller_create(fmt))) {
527		log_error("Couldn't create lvm1 label handler.");
528		return NULL;
529	}
530
531	if (!(label_register_handler(FMT_LVM1_NAME, fmt->labeller))) {
532		log_error("Couldn't register lvm1 label handler.");
533		return NULL;
534	}
535
536	log_very_verbose("Initialised format: %s", fmt->name);
537
538	return fmt;
539}
540