1 /*
2  * Copyright (c) 2008, XenSource Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of XenSource Inc. nor the names of its contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 #include <stdio.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "lvm-util.h"
34 
35 #define _NAME "%255s"
36 static char line[1024];
37 
38 static inline int
lvm_read_line(FILE * scan)39 lvm_read_line(FILE *scan)
40 {
41 	memset(line, 0, sizeof(line));
42 	return (fscanf(scan, "%1023[^\n]", line) != 1);
43 }
44 
45 static inline int
lvm_next_line(FILE * scan)46 lvm_next_line(FILE *scan)
47 {
48 	return (fscanf(scan, "%1023[\n]", line) != 1);
49 }
50 
51 static int
lvm_copy_name(char * dst,const char * src,size_t size)52 lvm_copy_name(char *dst, const char *src, size_t size)
53 {
54 	if (strnlen(src, size) == size)
55 		return -ENAMETOOLONG;
56 
57 	strcpy(dst, src);
58 	return 0;
59 }
60 
61 static int
lvm_parse_pv(struct vg * vg,const char * name,int pvs,uint64_t start)62 lvm_parse_pv(struct vg *vg, const char *name, int pvs, uint64_t start)
63 {
64 	int i, err;
65 	struct pv *pv;
66 
67 	pv = NULL;
68 
69 	if (!vg->pvs) {
70 		vg->pvs = calloc(pvs, sizeof(struct pv));
71 		if (!vg->pvs)
72 			return -ENOMEM;
73 	}
74 
75 	for (i = 0; i < pvs; i++) {
76 		pv = vg->pvs + i;
77 
78 		if (!pv->name[0])
79 			break;
80 
81 		if (!strcmp(pv->name, name))
82 			return -EEXIST;
83 	}
84 
85 	if (!pv)
86 		return -ENOENT;
87 
88 	if (i == pvs)
89 		return -ENOMEM;
90 
91 	err = lvm_copy_name(pv->name, name, sizeof(pv->name) - 1);
92 	if (err)
93 		return err;
94 
95 	pv->start = start;
96 	return 0;
97 }
98 
99 static int
lvm_open_vg(const char * vgname,struct vg * vg)100 lvm_open_vg(const char *vgname, struct vg *vg)
101 {
102 	FILE *scan;
103 	int i, err, pvs, lvs;
104 	char *cmd, pvname[256];
105 	uint64_t size, pv_start;
106 
107 	memset(vg, 0, sizeof(*vg));
108 
109 	err = asprintf(&cmd, "/usr/sbin/vgs %s --noheadings --nosuffix --units=b "
110 		       "--options=vg_name,vg_extent_size,lv_count,pv_count,"
111 		       "pv_name,pe_start --unbuffered 2> /dev/null", vgname);
112 	if (err == -1)
113 		return -ENOMEM;
114 
115 	errno = 0;
116 	scan  = popen(cmd, "r");
117 	if (!scan) {
118 		err = (errno ? -errno : ENOMEM);
119 		goto out;
120 	}
121 
122 	for (;;) {
123 		if (lvm_read_line(scan))
124 			break;
125 
126 		err = -EINVAL;
127                 if (sscanf(line, _NAME" %"SCNu64" %d %d "_NAME" %"SCNu64,
128 			   vg->name, &size, &lvs, &pvs, pvname, &pv_start) != 6)
129 			goto out;
130 
131 		if (strcmp(vg->name, vgname))
132 			goto out;
133 
134 		err = lvm_parse_pv(vg, pvname, pvs, pv_start);
135 		if (err)
136 			goto out;
137 
138 		if (lvm_next_line(scan))
139 			break;
140 	}
141 
142 	err = -EINVAL;
143 	if (strcmp(vg->name, vgname))
144 		goto out;
145 
146 	for (i = 0; i < pvs; i++)
147 		if (!vg->pvs[i].name[0])
148 			goto out;
149 
150 	err = -ENOMEM;
151 	vg->lvs = calloc(lvs, sizeof(struct lv));
152 	if (!vg->lvs)
153 		goto out;
154 
155 	err             = 0;
156 	vg->lv_cnt      = lvs;
157 	vg->pv_cnt      = pvs;
158 	vg->extent_size = size;
159 
160 out:
161 	if (scan)
162 		pclose(scan);
163 	if (err)
164 		lvm_free_vg(vg);
165 	free(cmd);
166 	return err;
167 }
168 
169 static int
lvm_parse_lv_devices(struct vg * vg,struct lv_segment * seg,char * devices)170 lvm_parse_lv_devices(struct vg *vg, struct lv_segment *seg, char *devices)
171 {
172 	int i;
173 	uint64_t start, pe_start;
174 
175 	for (i = 0; i < strlen(devices); i++)
176 		if (strchr(",()", devices[i]))
177 			devices[i] = ' ';
178 
179         if (sscanf(devices, _NAME" %"SCNu64, seg->device, &start) != 2)
180 		return -EINVAL;
181 
182 	pe_start = -1;
183 	for (i = 0; i < vg->pv_cnt; i++)
184 		if (!strcmp(vg->pvs[i].name, seg->device)) {
185 			pe_start = vg->pvs[i].start;
186 			break;
187 		}
188 
189 	if (pe_start == -1)
190 		return -EINVAL;
191 
192 	seg->pe_start = (start * vg->extent_size) + pe_start;
193 	return 0;
194 }
195 
196 static int
lvm_scan_lvs(struct vg * vg)197 lvm_scan_lvs(struct vg *vg)
198 {
199 	char *cmd;
200 	FILE *scan;
201 	int i, err;
202 
203 	err = asprintf(&cmd, "/usr/sbin/lvs %s --noheadings --nosuffix --units=b "
204 		       "--options=lv_name,lv_size,segtype,seg_count,seg_start,"
205 		       "seg_size,devices --unbuffered 2> /dev/null", vg->name);
206 	if (err == -1)
207 		return -ENOMEM;
208 
209 	errno = 0;
210 	scan  = popen(cmd, "r");
211 	if (!scan) {
212 		err = (errno ? -errno : -ENOMEM);
213 		goto out;
214 	}
215 
216 	for (i = 0;;) {
217 		int segs;
218 		struct lv *lv;
219 		struct lv_segment seg;
220 		uint64_t size, seg_start;
221 		char type[32], name[256], dev[256], devices[1024];
222 
223 		if (i >= vg->lv_cnt)
224 			break;
225 
226 		if (lvm_read_line(scan)) {
227 			vg->lv_cnt = i;
228 			break;
229 		}
230 
231 		err = -EINVAL;
232 		lv  = vg->lvs + i;
233 
234                 if (sscanf(line, _NAME" %"SCNu64" %31s %u %"SCNu64" %"SCNu64" %1023s",
235 			   name, &size, type, &segs, &seg_start,
236 			   &seg.pe_size, devices) != 7)
237 			goto out;
238 
239 		if (seg_start)
240 			goto next;
241 
242 		if (!strcmp(type, "linear"))
243 			seg.type = LVM_SEG_TYPE_LINEAR;
244 		else
245 			seg.type = LVM_SEG_TYPE_UNKNOWN;
246 
247 		if (lvm_parse_lv_devices(vg, &seg, devices))
248 			goto out;
249 
250 		i++;
251 		lv->size          = size;
252 		lv->segments      = segs;
253 		lv->first_segment = seg;
254 
255 		err = lvm_copy_name(lv->name, name, sizeof(lv->name) - 1);
256 		if (err)
257 			goto out;
258 		err = -EINVAL;
259 
260 	next:
261 		if (lvm_next_line(scan))
262 			goto out;
263 	}
264 
265 	err = 0;
266 
267 out:
268 	if (scan)
269 		pclose(scan);
270 	free(cmd);
271 	return err;
272 }
273 
274 void
lvm_free_vg(struct vg * vg)275 lvm_free_vg(struct vg *vg)
276 {
277 	free(vg->lvs);
278 	free(vg->pvs);
279 	memset(vg, 0, sizeof(*vg));
280 }
281 
282 int
lvm_scan_vg(const char * vg_name,struct vg * vg)283 lvm_scan_vg(const char *vg_name, struct vg *vg)
284 {
285 	int err;
286 
287 	memset(vg, 0, sizeof(*vg));
288 
289 	err = lvm_open_vg(vg_name, vg);
290 	if (err)
291 		return err;
292 
293 	err = lvm_scan_lvs(vg);
294 	if (err) {
295 		lvm_free_vg(vg);
296 		return err;
297 	}
298 
299 	return 0;
300 }
301 
302 #ifdef LVM_UTIL
303 static int
usage(void)304 usage(void)
305 {
306 	printf("usage: lvm-util <vgname>\n");
307 	exit(EINVAL);
308 }
309 
310 int
main(int argc,char ** argv)311 main(int argc, char **argv)
312 {
313 	int i, err;
314 	struct vg vg;
315 	struct pv *pv;
316 	struct lv *lv;
317 	struct lv_segment *seg;
318 
319 	if (argc != 2)
320 		usage();
321 
322 	err = lvm_scan_vg(argv[1], &vg);
323 	if (err) {
324 		printf("scan failed: %d\n", err);
325 		return (err >= 0 ? err : -err);
326 	}
327 
328 
329         printf("vg %s: extent_size: %"PRIu64", pvs: %d, lvs: %d\n",
330 	       vg.name, vg.extent_size, vg.pv_cnt, vg.lv_cnt);
331 
332 	for (i = 0; i < vg.pv_cnt; i++) {
333 		pv = vg.pvs + i;
334                 printf("pv %s: start %"PRIu64"\n", pv->name, pv->start);
335 	}
336 
337 	for (i = 0; i < vg.lv_cnt; i++) {
338 		lv  = vg.lvs + i;
339 		seg = &lv->first_segment;
340                 printf("lv %s: size: %"PRIu64", segments: %u, type: %u, "
341                        "dev: %s, pe_start: %"PRIu64", pe_size: %"PRIu64"\n",
342 		       lv->name, lv->size, lv->segments, seg->type,
343 		       seg->device, seg->pe_start, seg->pe_size);
344 	}
345 
346 	lvm_free_vg(&vg);
347 	return 0;
348 }
349 #endif
350